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

fix: add permission authorization middleware to only specific routes

This commit is contained in:
2022-07-01 16:50:24 +05:00
parent 7d916ec3e9
commit f3dfc7083f
9 changed files with 106 additions and 32 deletions

View File

@@ -3,7 +3,6 @@ import jwt from 'jsonwebtoken'
import { csrfProtection } from '../app'
import { fetchLatestAutoExec, ModeType, verifyTokenInDB } from '../utils'
import { desktopUser } from './desktop'
import { authorize } from './authorize'
export const authenticateAccessToken: RequestHandler = async (
req,
@@ -25,7 +24,7 @@ export const authenticateAccessToken: RequestHandler = async (
if (user) {
if (user.isActive) {
req.user = user
return csrfProtection(req, res, () => authorize(req, res, next))
return csrfProtection(req, res, next)
} else return res.sendStatus(401)
}
}
@@ -35,7 +34,7 @@ export const authenticateAccessToken: RequestHandler = async (
authenticateToken(
req,
res,
() => authorize(req, res, next),
next,
process.env.ACCESS_TOKEN_SECRET as string,
'accessToken'
)

View File

@@ -13,13 +13,15 @@ export const authorize: RequestHandler = async (req, res, next) => {
const dbUser = await User.findOne({ id: user.userId })
if (!dbUser) return res.sendStatus(401)
const uri = req.baseUrl + req.path
const uri = req.baseUrl + req.route.path
// find permission w.r.t user
permission = await Permission.findOne({ uri, user: dbUser._id })
if (permission && permission.setting === PermissionSetting.grant)
return next()
if (permission) {
if (permission.setting === PermissionSetting.grant) return next()
else res.sendStatus(401)
}
// find permission w.r.t user's groups
for (const group of dbUser.groups) {

View File

@@ -1,12 +1,13 @@
import express from 'express'
import { runCodeValidation } from '../../utils'
import { CodeController } from '../../controllers/'
import { authorize } from '../../middlewares'
const runRouter = express.Router()
const controller = new CodeController()
runRouter.post('/execute', async (req, res) => {
runRouter.post('/execute', authorize, async (req, res) => {
const { error, value: body } = runCodeValidation(req.body)
if (error) return res.status(400).send(error.details[0].message)

View File

@@ -3,6 +3,7 @@ import { deleteFile, readFile } from '@sasjs/utils'
import { publishAppStream } from '../appStream'
import { authorize } from '../../middlewares'
import { multerSingle } from '../../middlewares/multer'
import { DriveController } from '../../controllers/'
import {
@@ -19,7 +20,7 @@ const controller = new DriveController()
const driveRouter = express.Router()
driveRouter.post('/deploy', async (req, res) => {
driveRouter.post('/deploy', authorize, async (req, res) => {
const { error, value: body } = deployValidation(req.body)
if (error) return res.status(400).send(error.details[0].message)
@@ -48,6 +49,7 @@ driveRouter.post('/deploy', async (req, res) => {
driveRouter.post(
'/deploy/upload',
authorize,
(...arg) => multerSingle('file', arg),
async (req, res) => {
if (!req.file) return res.status(400).send('"file" is not present.')
@@ -111,7 +113,7 @@ driveRouter.post(
}
)
driveRouter.get('/file', async (req, res) => {
driveRouter.get('/file', authorize, async (req, res) => {
const { error: errQ, value: query } = fileParamValidation(req.query)
if (errQ) return res.status(400).send(errQ.details[0].message)
@@ -123,7 +125,7 @@ driveRouter.get('/file', async (req, res) => {
}
})
driveRouter.get('/folder', async (req, res) => {
driveRouter.get('/folder', authorize, async (req, res) => {
const { error: errQ, value: query } = folderParamValidation(req.query)
if (errQ) return res.status(400).send(errQ.details[0].message)
@@ -136,7 +138,7 @@ driveRouter.get('/folder', async (req, res) => {
}
})
driveRouter.delete('/file', async (req, res) => {
driveRouter.delete('/file', authorize, async (req, res) => {
const { error: errQ, value: query } = fileParamValidation(req.query)
if (errQ) return res.status(400).send(errQ.details[0].message)
@@ -151,6 +153,7 @@ driveRouter.delete('/file', async (req, res) => {
driveRouter.post(
'/file',
authorize,
(...arg) => multerSingle('file', arg),
async (req, res) => {
const { error: errQ, value: query } = fileParamValidation(req.query)
@@ -179,6 +182,7 @@ driveRouter.post(
driveRouter.patch(
'/file',
authorize,
(...arg) => multerSingle('file', arg),
async (req, res) => {
const { error: errQ, value: query } = fileParamValidation(req.query)
@@ -205,7 +209,7 @@ driveRouter.patch(
}
)
driveRouter.get('/fileTree', async (req, res) => {
driveRouter.get('/fileTree', authorize, async (req, res) => {
try {
const response = await controller.getFileTree()
res.send(response)

View File

@@ -1,6 +1,10 @@
import express from 'express'
import { PermissionController } from '../../controllers/'
import { authenticateAccessToken, verifyAdmin } from '../../middlewares'
import {
authenticateAccessToken,
verifyAdmin,
authorize
} from '../../middlewares'
import {
registerPermissionValidation,
updatePermissionValidation
@@ -9,16 +13,21 @@ import {
const permissionRouter = express.Router()
const controller = new PermissionController()
permissionRouter.get('/', authenticateAccessToken, async (req, res) => {
try {
const response = await controller.getAllPermissions()
res.send(response)
} catch (err: any) {
const statusCode = err.code
delete err.code
res.status(statusCode).send(err.message)
permissionRouter.get(
'/',
authenticateAccessToken,
authorize,
async (req, res) => {
try {
const response = await controller.getAllPermissions()
res.send(response)
} catch (err: any) {
const statusCode = err.code
delete err.code
res.status(statusCode).send(err.message)
}
}
})
)
permissionRouter.post(
'/',

View File

@@ -29,7 +29,12 @@ jest
.mockImplementation(() => path.join(tmpFolder, 'uploads'))
import appPromise from '../../../app'
import { UserController } from '../../../controllers/'
import {
UserController,
PermissionController,
PermissionSetting,
PrincipalType
} from '../../../controllers/'
import { getTreeExample } from '../../../controllers/internal'
import { generateAccessToken, saveTokensInDB } from '../../../utils/'
const { getFilesFolder } = fileUtilModules
@@ -48,6 +53,7 @@ describe('drive', () => {
let con: Mongoose
let mongoServer: MongoMemoryServer
const controller = new UserController()
const permissionController = new PermissionController()
let accessToken: string
@@ -58,11 +64,31 @@ describe('drive', () => {
con = await mongoose.connect(mongoServer.getUri())
const dbUser = await controller.createUser(user)
accessToken = generateAccessToken({
clientId,
userId: dbUser.id
accessToken = await generateAndSaveToken(dbUser.id)
permissionController.createPermission({
uri: '/SASjsApi/drive/deploy',
principalType: PrincipalType.user,
principalId: dbUser.id,
setting: PermissionSetting.grant
})
permissionController.createPermission({
uri: '/SASjsApi/drive/deploy/upload',
principalType: PrincipalType.user,
principalId: dbUser.id,
setting: PermissionSetting.grant
})
permissionController.createPermission({
uri: '/SASjsApi/drive/file',
principalType: PrincipalType.user,
principalId: dbUser.id,
setting: PermissionSetting.grant
})
permissionController.createPermission({
uri: '/SASjsApi/drive/folder',
principalType: PrincipalType.user,
principalId: dbUser.id,
setting: PermissionSetting.grant
})
await saveTokensInDB(dbUser.id, clientId, accessToken, 'refreshToken')
})
afterAll(async () => {
@@ -945,3 +971,12 @@ describe('drive', () => {
const getExampleService = (): ServiceMember =>
((getTreeExample().members[0] as FolderMember).members[0] as FolderMember)
.members[0] as ServiceMember
const generateAndSaveToken = async (userId: number) => {
const adminAccessToken = generateAccessToken({
clientId,
userId
})
await saveTokensInDB(userId, clientId, adminAccessToken, 'refreshToken')
return adminAccessToken
}

View File

@@ -440,17 +440,25 @@ describe('permission', () => {
})
it('should give a list of all permissions when user is not admin', async () => {
const accessToken = await generateSaveTokenAndCreateUser({
const dbUser = await userController.createUser({
...user,
username: 'get' + user.username
})
const accessToken = await generateAndSaveToken(dbUser.id)
await permissionController.createPermission({
uri: '/SASjsApi/permission/',
principalType: PrincipalType.user,
principalId: dbUser.id,
setting: PermissionSetting.grant
})
const res = await request(app)
.get('/SASjsApi/permission/')
.auth(accessToken, { type: 'bearer' })
.send()
.expect(200)
expect(res.body).toHaveLength(2)
expect(res.body).toHaveLength(3)
})
})
})

View File

@@ -4,7 +4,12 @@ import mongoose, { Mongoose } from 'mongoose'
import { MongoMemoryServer } from 'mongodb-memory-server'
import request from 'supertest'
import appPromise from '../../../app'
import { UserController } from '../../../controllers/'
import {
UserController,
PermissionController,
PermissionSetting,
PrincipalType
} from '../../../controllers/'
import {
generateAccessToken,
saveTokensInDB,
@@ -41,12 +46,21 @@ describe('stp', () => {
let con: Mongoose
let mongoServer: MongoMemoryServer
let accessToken: string
const userController = new UserController()
const permissionController = new PermissionController()
beforeAll(async () => {
app = await appPromise
mongoServer = await MongoMemoryServer.create()
con = await mongoose.connect(mongoServer.getUri())
accessToken = await generateSaveTokenAndCreateUser(user)
const dbUser = await userController.createUser(user)
accessToken = await generateAndSaveToken(dbUser.id)
await permissionController.createPermission({
uri: '/SASjsApi/stp/execute',
principalType: PrincipalType.user,
principalId: dbUser.id,
setting: PermissionSetting.grant
})
})
afterAll(async () => {

View File

@@ -2,13 +2,14 @@ import express from 'express'
import { executeProgramRawValidation } from '../../utils'
import { STPController } from '../../controllers/'
import { FileUploadController } from '../../controllers/internal'
import { authorize } from '../../middlewares'
const stpRouter = express.Router()
const fileUploadController = new FileUploadController()
const controller = new STPController()
stpRouter.get('/execute', async (req, res) => {
stpRouter.get('/execute', authorize, async (req, res) => {
const { error, value: query } = executeProgramRawValidation(req.query)
if (error) return res.status(400).send(error.details[0].message)
@@ -32,6 +33,7 @@ stpRouter.get('/execute', async (req, res) => {
stpRouter.post(
'/execute',
authorize,
fileUploadController.preUploadMiddleware,
fileUploadController.getMulterUploadObject().any(),
async (req, res: any) => {