mirror of
https://github.com/sasjs/server.git
synced 2025-12-10 11:24:35 +00:00
fix: add permission authorization middleware to only specific routes
This commit is contained in:
@@ -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'
|
||||
)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(
|
||||
'/',
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
Reference in New Issue
Block a user