1
0
mirror of https://github.com/sasjs/server.git synced 2025-12-10 19:34:34 +00:00

Merge pull request #246 from sasjs/issue-240

feat: bypass authentication when route is enabled for public group
This commit is contained in:
Saad Jutt
2022-08-03 00:03:43 +05:00
committed by GitHub
8 changed files with 114 additions and 15 deletions

View File

@@ -10,7 +10,7 @@ import {
Body
} from 'tsoa'
import Group, { GroupPayload } from '../model/Group'
import Group, { GroupPayload, PUBLIC_GROUP_NAME } from '../model/Group'
import User from '../model/User'
import { UserResponse } from './user'
@@ -241,6 +241,13 @@ const updateUsersListInGroup = async (
message: 'Group not found.'
}
if (group.name === PUBLIC_GROUP_NAME)
throw {
code: 400,
status: 'Bad Request',
message: `Can't add/remove user to '${PUBLIC_GROUP_NAME}' group.`
}
const user = await User.findOne({ id: userId })
if (!user)
throw {

View File

@@ -5,7 +5,9 @@ import {
fetchLatestAutoExec,
ModeType,
verifyTokenInDB,
isAuthorizingRoute
isAuthorizingRoute,
isPublicRoute,
publicUser
} from '../utils'
import { desktopUser } from './desktop'
import { authorize } from './authorize'
@@ -41,7 +43,7 @@ export const authenticateAccessToken: RequestHandler = async (
return res.sendStatus(401)
}
authenticateToken(
await authenticateToken(
req,
res,
nextFunction,
@@ -50,8 +52,12 @@ export const authenticateAccessToken: RequestHandler = async (
)
}
export const authenticateRefreshToken: RequestHandler = (req, res, next) => {
authenticateToken(
export const authenticateRefreshToken: RequestHandler = async (
req,
res,
next
) => {
await authenticateToken(
req,
res,
next,
@@ -60,7 +66,7 @@ export const authenticateRefreshToken: RequestHandler = (req, res, next) => {
)
}
const authenticateToken = (
const authenticateToken = async (
req: Request,
res: Response,
next: NextFunction,
@@ -83,12 +89,12 @@ const authenticateToken = (
const authHeader = req.headers['authorization']
const token = authHeader?.split(' ')[1]
if (!token) return res.sendStatus(401)
jwt.verify(token, key, async (err: any, data: any) => {
if (err) return res.sendStatus(401)
try {
if (!token) throw 'Unauthorized'
const data: any = jwt.verify(token, key)
// verify this valid token's entry in DB
const user = await verifyTokenInDB(
data?.userId,
data?.clientId,
@@ -101,8 +107,16 @@ const authenticateToken = (
req.user = user
if (tokenType === 'accessToken') req.accessToken = token
return next()
} else return res.sendStatus(401)
} else throw 'Unauthorized'
}
return res.sendStatus(401)
})
throw 'Unauthorized'
} catch (error) {
if (await isPublicRoute(req)) {
req.user = publicUser
return next()
}
res.sendStatus(401)
}
}

View File

@@ -5,7 +5,7 @@ import {
PermissionSettingForRoute,
PermissionType
} from '../controllers/permission'
import { getPath } from '../utils'
import { getPath, isPublicRoute } from '../utils'
export const authorize: RequestHandler = async (req, res, next) => {
const { user } = req
@@ -17,6 +17,9 @@ export const authorize: RequestHandler = async (req, res, next) => {
// no need to check for permissions when user is admin
if (user.isAdmin) return next()
// no need to check for permissions when route is Public
if (await isPublicRoute(req)) return next()
const dbUser = await User.findOne({ id: user.userId })
if (!dbUser) return res.sendStatus(401)

View File

@@ -3,6 +3,8 @@ import { GroupDetailsResponse } from '../controllers'
import User, { IUser } from './User'
const AutoIncrement = require('mongoose-sequence')(mongoose)
export const PUBLIC_GROUP_NAME = 'Public'
export interface GroupPayload {
/**
* Name of the group

View File

@@ -5,6 +5,7 @@ import request from 'supertest'
import appPromise from '../../../app'
import { UserController, GroupController } from '../../../controllers/'
import { generateAccessToken, saveTokensInDB } from '../../../utils'
import { PUBLIC_GROUP_NAME } from '../../../model/Group'
const clientId = 'someclientID'
const adminUser = {
@@ -27,6 +28,12 @@ const group = {
description: 'DC group for testing purposes.'
}
const PUBLIC_GROUP = {
name: PUBLIC_GROUP_NAME,
description:
'A special group that can be used to bypass authentication for particular routes.'
}
const userController = new UserController()
const groupController = new GroupController()
@@ -535,6 +542,24 @@ describe('group', () => {
expect(res.text).toEqual('User not found.')
expect(res.body).toEqual({})
})
it('should respond with Bad Request when adding user to Public group', async () => {
const dbGroup = await groupController.createGroup(PUBLIC_GROUP)
const dbUser = await userController.createUser({
...user,
username: 'publicUser'
})
const res = await request(app)
.post(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`)
.auth(adminAccessToken, { type: 'bearer' })
.send()
.expect(400)
expect(res.text).toEqual(
`Can't add/remove user to '${PUBLIC_GROUP_NAME}' group.`
)
})
})
describe('RemoveUser', () => {

View File

@@ -16,6 +16,7 @@ export * from './getRunTimeAndFilePath'
export * from './getServerUrl'
export * from './instantiateLogger'
export * from './isDebugOn'
export * from './isPublicRoute'
export * from './zipped'
export * from './parseLogToArray'
export * from './removeTokensInDB'

View File

@@ -0,0 +1,31 @@
import { Request } from 'express'
import { getPath } from './getAuthorizedRoutes'
import Group, { PUBLIC_GROUP_NAME } from '../model/Group'
import Permission from '../model/Permission'
import { PermissionSettingForRoute } from '../controllers'
import { RequestUser } from '../types'
export const isPublicRoute = async (req: Request): Promise<boolean> => {
const group = await Group.findOne({ name: PUBLIC_GROUP_NAME })
if (group) {
const path = getPath(req)
const groupPermission = await Permission.findOne({
path,
group: group?._id
})
if (groupPermission?.setting === PermissionSettingForRoute.grant)
return true
}
return false
}
export const publicUser: RequestUser = {
userId: 0,
clientId: 'public_app',
username: 'publicUser',
displayName: 'Public User',
isAdmin: false,
isActive: true
}

View File

@@ -1,5 +1,5 @@
import Client from '../model/Client'
import Group from '../model/Group'
import Group, { PUBLIC_GROUP_NAME } from '../model/Group'
import User from '../model/User'
import Configuration, { ConfigurationType } from '../model/Configuration'
@@ -31,6 +31,15 @@ export const seedDB = async (): Promise<ConfigurationType> => {
console.log(`DB Seed - Group created: ${GROUP.name}`)
}
// Checking if 'Public' Group is already in the database
const publicGroupExist = await Group.findOne({ name: PUBLIC_GROUP.name })
if (!publicGroupExist) {
const group = new Group(PUBLIC_GROUP)
await group.save()
console.log(`DB Seed - Group created: ${PUBLIC_GROUP.name}`)
}
// Checking if user is already in the database
let usernameExist = await User.findOne({ username: ADMIN_USER.username })
if (!usernameExist) {
@@ -68,6 +77,13 @@ const GROUP = {
name: 'AllUsers',
description: 'Group contains all users'
}
const PUBLIC_GROUP = {
name: PUBLIC_GROUP_NAME,
description:
'A special group that can be used to bypass authentication for particular routes.'
}
const CLIENT = {
clientId: 'clientID1',
clientSecret: 'clientSecret'