mirror of
https://github.com/sasjs/server.git
synced 2025-12-11 19:44: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 { csrfProtection } from '../app'
|
||||||
import { fetchLatestAutoExec, ModeType, verifyTokenInDB } from '../utils'
|
import { fetchLatestAutoExec, ModeType, verifyTokenInDB } from '../utils'
|
||||||
import { desktopUser } from './desktop'
|
import { desktopUser } from './desktop'
|
||||||
import { authorize } from './authorize'
|
|
||||||
|
|
||||||
export const authenticateAccessToken: RequestHandler = async (
|
export const authenticateAccessToken: RequestHandler = async (
|
||||||
req,
|
req,
|
||||||
@@ -25,7 +24,7 @@ export const authenticateAccessToken: RequestHandler = async (
|
|||||||
if (user) {
|
if (user) {
|
||||||
if (user.isActive) {
|
if (user.isActive) {
|
||||||
req.user = user
|
req.user = user
|
||||||
return csrfProtection(req, res, () => authorize(req, res, next))
|
return csrfProtection(req, res, next)
|
||||||
} else return res.sendStatus(401)
|
} else return res.sendStatus(401)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -35,7 +34,7 @@ export const authenticateAccessToken: RequestHandler = async (
|
|||||||
authenticateToken(
|
authenticateToken(
|
||||||
req,
|
req,
|
||||||
res,
|
res,
|
||||||
() => authorize(req, res, next),
|
next,
|
||||||
process.env.ACCESS_TOKEN_SECRET as string,
|
process.env.ACCESS_TOKEN_SECRET as string,
|
||||||
'accessToken'
|
'accessToken'
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -13,13 +13,15 @@ export const authorize: RequestHandler = async (req, res, next) => {
|
|||||||
const dbUser = await User.findOne({ id: user.userId })
|
const dbUser = await User.findOne({ id: user.userId })
|
||||||
if (!dbUser) return res.sendStatus(401)
|
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
|
// find permission w.r.t user
|
||||||
permission = await Permission.findOne({ uri, user: dbUser._id })
|
permission = await Permission.findOne({ uri, user: dbUser._id })
|
||||||
|
|
||||||
if (permission && permission.setting === PermissionSetting.grant)
|
if (permission) {
|
||||||
return next()
|
if (permission.setting === PermissionSetting.grant) return next()
|
||||||
|
else res.sendStatus(401)
|
||||||
|
}
|
||||||
|
|
||||||
// find permission w.r.t user's groups
|
// find permission w.r.t user's groups
|
||||||
for (const group of dbUser.groups) {
|
for (const group of dbUser.groups) {
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import express from 'express'
|
import express from 'express'
|
||||||
import { runCodeValidation } from '../../utils'
|
import { runCodeValidation } from '../../utils'
|
||||||
import { CodeController } from '../../controllers/'
|
import { CodeController } from '../../controllers/'
|
||||||
|
import { authorize } from '../../middlewares'
|
||||||
|
|
||||||
const runRouter = express.Router()
|
const runRouter = express.Router()
|
||||||
|
|
||||||
const controller = new CodeController()
|
const controller = new CodeController()
|
||||||
|
|
||||||
runRouter.post('/execute', async (req, res) => {
|
runRouter.post('/execute', authorize, async (req, res) => {
|
||||||
const { error, value: body } = runCodeValidation(req.body)
|
const { error, value: body } = runCodeValidation(req.body)
|
||||||
if (error) return res.status(400).send(error.details[0].message)
|
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 { publishAppStream } from '../appStream'
|
||||||
|
|
||||||
|
import { authorize } from '../../middlewares'
|
||||||
import { multerSingle } from '../../middlewares/multer'
|
import { multerSingle } from '../../middlewares/multer'
|
||||||
import { DriveController } from '../../controllers/'
|
import { DriveController } from '../../controllers/'
|
||||||
import {
|
import {
|
||||||
@@ -19,7 +20,7 @@ const controller = new DriveController()
|
|||||||
|
|
||||||
const driveRouter = express.Router()
|
const driveRouter = express.Router()
|
||||||
|
|
||||||
driveRouter.post('/deploy', async (req, res) => {
|
driveRouter.post('/deploy', authorize, async (req, res) => {
|
||||||
const { error, value: body } = deployValidation(req.body)
|
const { error, value: body } = deployValidation(req.body)
|
||||||
if (error) return res.status(400).send(error.details[0].message)
|
if (error) return res.status(400).send(error.details[0].message)
|
||||||
|
|
||||||
@@ -48,6 +49,7 @@ driveRouter.post('/deploy', async (req, res) => {
|
|||||||
|
|
||||||
driveRouter.post(
|
driveRouter.post(
|
||||||
'/deploy/upload',
|
'/deploy/upload',
|
||||||
|
authorize,
|
||||||
(...arg) => multerSingle('file', arg),
|
(...arg) => multerSingle('file', arg),
|
||||||
async (req, res) => {
|
async (req, res) => {
|
||||||
if (!req.file) return res.status(400).send('"file" is not present.')
|
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)
|
const { error: errQ, value: query } = fileParamValidation(req.query)
|
||||||
|
|
||||||
if (errQ) return res.status(400).send(errQ.details[0].message)
|
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)
|
const { error: errQ, value: query } = folderParamValidation(req.query)
|
||||||
|
|
||||||
if (errQ) return res.status(400).send(errQ.details[0].message)
|
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)
|
const { error: errQ, value: query } = fileParamValidation(req.query)
|
||||||
|
|
||||||
if (errQ) return res.status(400).send(errQ.details[0].message)
|
if (errQ) return res.status(400).send(errQ.details[0].message)
|
||||||
@@ -151,6 +153,7 @@ driveRouter.delete('/file', async (req, res) => {
|
|||||||
|
|
||||||
driveRouter.post(
|
driveRouter.post(
|
||||||
'/file',
|
'/file',
|
||||||
|
authorize,
|
||||||
(...arg) => multerSingle('file', arg),
|
(...arg) => multerSingle('file', arg),
|
||||||
async (req, res) => {
|
async (req, res) => {
|
||||||
const { error: errQ, value: query } = fileParamValidation(req.query)
|
const { error: errQ, value: query } = fileParamValidation(req.query)
|
||||||
@@ -179,6 +182,7 @@ driveRouter.post(
|
|||||||
|
|
||||||
driveRouter.patch(
|
driveRouter.patch(
|
||||||
'/file',
|
'/file',
|
||||||
|
authorize,
|
||||||
(...arg) => multerSingle('file', arg),
|
(...arg) => multerSingle('file', arg),
|
||||||
async (req, res) => {
|
async (req, res) => {
|
||||||
const { error: errQ, value: query } = fileParamValidation(req.query)
|
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 {
|
try {
|
||||||
const response = await controller.getFileTree()
|
const response = await controller.getFileTree()
|
||||||
res.send(response)
|
res.send(response)
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import express from 'express'
|
import express from 'express'
|
||||||
import { PermissionController } from '../../controllers/'
|
import { PermissionController } from '../../controllers/'
|
||||||
import { authenticateAccessToken, verifyAdmin } from '../../middlewares'
|
import {
|
||||||
|
authenticateAccessToken,
|
||||||
|
verifyAdmin,
|
||||||
|
authorize
|
||||||
|
} from '../../middlewares'
|
||||||
import {
|
import {
|
||||||
registerPermissionValidation,
|
registerPermissionValidation,
|
||||||
updatePermissionValidation
|
updatePermissionValidation
|
||||||
@@ -9,7 +13,11 @@ import {
|
|||||||
const permissionRouter = express.Router()
|
const permissionRouter = express.Router()
|
||||||
const controller = new PermissionController()
|
const controller = new PermissionController()
|
||||||
|
|
||||||
permissionRouter.get('/', authenticateAccessToken, async (req, res) => {
|
permissionRouter.get(
|
||||||
|
'/',
|
||||||
|
authenticateAccessToken,
|
||||||
|
authorize,
|
||||||
|
async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const response = await controller.getAllPermissions()
|
const response = await controller.getAllPermissions()
|
||||||
res.send(response)
|
res.send(response)
|
||||||
@@ -18,7 +26,8 @@ permissionRouter.get('/', authenticateAccessToken, async (req, res) => {
|
|||||||
delete err.code
|
delete err.code
|
||||||
res.status(statusCode).send(err.message)
|
res.status(statusCode).send(err.message)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
permissionRouter.post(
|
permissionRouter.post(
|
||||||
'/',
|
'/',
|
||||||
|
|||||||
@@ -29,7 +29,12 @@ jest
|
|||||||
.mockImplementation(() => path.join(tmpFolder, 'uploads'))
|
.mockImplementation(() => path.join(tmpFolder, 'uploads'))
|
||||||
|
|
||||||
import appPromise from '../../../app'
|
import appPromise from '../../../app'
|
||||||
import { UserController } from '../../../controllers/'
|
import {
|
||||||
|
UserController,
|
||||||
|
PermissionController,
|
||||||
|
PermissionSetting,
|
||||||
|
PrincipalType
|
||||||
|
} from '../../../controllers/'
|
||||||
import { getTreeExample } from '../../../controllers/internal'
|
import { getTreeExample } from '../../../controllers/internal'
|
||||||
import { generateAccessToken, saveTokensInDB } from '../../../utils/'
|
import { generateAccessToken, saveTokensInDB } from '../../../utils/'
|
||||||
const { getFilesFolder } = fileUtilModules
|
const { getFilesFolder } = fileUtilModules
|
||||||
@@ -48,6 +53,7 @@ describe('drive', () => {
|
|||||||
let con: Mongoose
|
let con: Mongoose
|
||||||
let mongoServer: MongoMemoryServer
|
let mongoServer: MongoMemoryServer
|
||||||
const controller = new UserController()
|
const controller = new UserController()
|
||||||
|
const permissionController = new PermissionController()
|
||||||
|
|
||||||
let accessToken: string
|
let accessToken: string
|
||||||
|
|
||||||
@@ -58,11 +64,31 @@ describe('drive', () => {
|
|||||||
con = await mongoose.connect(mongoServer.getUri())
|
con = await mongoose.connect(mongoServer.getUri())
|
||||||
|
|
||||||
const dbUser = await controller.createUser(user)
|
const dbUser = await controller.createUser(user)
|
||||||
accessToken = generateAccessToken({
|
accessToken = await generateAndSaveToken(dbUser.id)
|
||||||
clientId,
|
permissionController.createPermission({
|
||||||
userId: dbUser.id
|
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 () => {
|
afterAll(async () => {
|
||||||
@@ -945,3 +971,12 @@ describe('drive', () => {
|
|||||||
const getExampleService = (): ServiceMember =>
|
const getExampleService = (): ServiceMember =>
|
||||||
((getTreeExample().members[0] as FolderMember).members[0] as FolderMember)
|
((getTreeExample().members[0] as FolderMember).members[0] as FolderMember)
|
||||||
.members[0] as ServiceMember
|
.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 () => {
|
it('should give a list of all permissions when user is not admin', async () => {
|
||||||
const accessToken = await generateSaveTokenAndCreateUser({
|
const dbUser = await userController.createUser({
|
||||||
...user,
|
...user,
|
||||||
username: 'get' + user.username
|
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)
|
const res = await request(app)
|
||||||
.get('/SASjsApi/permission/')
|
.get('/SASjsApi/permission/')
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.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 { MongoMemoryServer } from 'mongodb-memory-server'
|
||||||
import request from 'supertest'
|
import request from 'supertest'
|
||||||
import appPromise from '../../../app'
|
import appPromise from '../../../app'
|
||||||
import { UserController } from '../../../controllers/'
|
import {
|
||||||
|
UserController,
|
||||||
|
PermissionController,
|
||||||
|
PermissionSetting,
|
||||||
|
PrincipalType
|
||||||
|
} from '../../../controllers/'
|
||||||
import {
|
import {
|
||||||
generateAccessToken,
|
generateAccessToken,
|
||||||
saveTokensInDB,
|
saveTokensInDB,
|
||||||
@@ -41,12 +46,21 @@ describe('stp', () => {
|
|||||||
let con: Mongoose
|
let con: Mongoose
|
||||||
let mongoServer: MongoMemoryServer
|
let mongoServer: MongoMemoryServer
|
||||||
let accessToken: string
|
let accessToken: string
|
||||||
|
const userController = new UserController()
|
||||||
|
const permissionController = new PermissionController()
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
app = await appPromise
|
app = await appPromise
|
||||||
mongoServer = await MongoMemoryServer.create()
|
mongoServer = await MongoMemoryServer.create()
|
||||||
con = await mongoose.connect(mongoServer.getUri())
|
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 () => {
|
afterAll(async () => {
|
||||||
|
|||||||
@@ -2,13 +2,14 @@ import express from 'express'
|
|||||||
import { executeProgramRawValidation } from '../../utils'
|
import { executeProgramRawValidation } from '../../utils'
|
||||||
import { STPController } from '../../controllers/'
|
import { STPController } from '../../controllers/'
|
||||||
import { FileUploadController } from '../../controllers/internal'
|
import { FileUploadController } from '../../controllers/internal'
|
||||||
|
import { authorize } from '../../middlewares'
|
||||||
|
|
||||||
const stpRouter = express.Router()
|
const stpRouter = express.Router()
|
||||||
|
|
||||||
const fileUploadController = new FileUploadController()
|
const fileUploadController = new FileUploadController()
|
||||||
const controller = new STPController()
|
const controller = new STPController()
|
||||||
|
|
||||||
stpRouter.get('/execute', async (req, res) => {
|
stpRouter.get('/execute', authorize, async (req, res) => {
|
||||||
const { error, value: query } = executeProgramRawValidation(req.query)
|
const { error, value: query } = executeProgramRawValidation(req.query)
|
||||||
if (error) return res.status(400).send(error.details[0].message)
|
if (error) return res.status(400).send(error.details[0].message)
|
||||||
|
|
||||||
@@ -32,6 +33,7 @@ stpRouter.get('/execute', async (req, res) => {
|
|||||||
|
|
||||||
stpRouter.post(
|
stpRouter.post(
|
||||||
'/execute',
|
'/execute',
|
||||||
|
authorize,
|
||||||
fileUploadController.preUploadMiddleware,
|
fileUploadController.preUploadMiddleware,
|
||||||
fileUploadController.getMulterUploadObject().any(),
|
fileUploadController.getMulterUploadObject().any(),
|
||||||
async (req, res: any) => {
|
async (req, res: any) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user