Skip to content

Commit

Permalink
fix pkg module path & prettier
Browse files Browse the repository at this point in the history
  • Loading branch information
allardy committed May 27, 2021
1 parent 33c207b commit 60953b9
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 124 deletions.
4 changes: 2 additions & 2 deletions packages/studio-be/src/core/modules/utils/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ const lookupPaths: string[] = []

if (process.pkg) {
// Modules will be picked from this location first
lookupPaths.push(path.dirname(process.execPath) + '/data/modules')
lookupPaths.push(path.dirname(process.execPath) + '/../data/modules')

// Running botpress in packages mode
lookupPaths.push(path.dirname(process.execPath) + '/modules')
lookupPaths.push(path.dirname(process.execPath) + '/../modules')
}

if (process.env.BP_MODULES_PATH) {
Expand Down
255 changes: 133 additions & 122 deletions packages/studio-be/src/core/security/router-security.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,167 +17,178 @@ const debugSuccess = DEBUG('audit:collab:success')
const debugSuperSuccess = DEBUG('audit:admin:success')
const debugSuperFailure = DEBUG('audit:admin:fail')

export const checkTokenHeader =
(authService: AuthService, audience?: string) => async (req: RequestWithUser, res: Response, next: NextFunction) => {
if (process.IS_STANDALONE) {
req.tokenUser = STANDALONE_USER
req.workspace = 'default'
return next()
}

let token

if (process.USE_JWT_COOKIES) {
if (!req.cookies[JWT_COOKIE_NAME]) {
return next(new UnauthorizedError(`${JWT_COOKIE_NAME} cookie is missing`))
}
export const checkTokenHeader = (authService: AuthService, audience?: string) => async (
req: RequestWithUser,
res: Response,
next: NextFunction
) => {
if (process.IS_STANDALONE) {
req.tokenUser = STANDALONE_USER
req.workspace = 'default'
return next()
}

token = req.cookies[JWT_COOKIE_NAME]
} else {
if (!req.headers.authorization) {
return next(new UnauthorizedError('Authorization header is missing'))
}
let token

const [scheme, bearerToken] = req.headers.authorization.split(' ')
if (scheme.toLowerCase() !== 'bearer') {
return next(new UnauthorizedError(`Unknown scheme "${scheme}"`))
}
if (process.USE_JWT_COOKIES) {
if (!req.cookies[JWT_COOKIE_NAME]) {
return next(new UnauthorizedError(`${JWT_COOKIE_NAME} cookie is missing`))
}

token = bearerToken
token = req.cookies[JWT_COOKIE_NAME]
} else {
if (!req.headers.authorization) {
return next(new UnauthorizedError('Authorization header is missing'))
}

if (!token) {
return next(new UnauthorizedError('Authentication token is missing'))
const [scheme, bearerToken] = req.headers.authorization.split(' ')
if (scheme.toLowerCase() !== 'bearer') {
return next(new UnauthorizedError(`Unknown scheme "${scheme}"`))
}

try {
const csrfToken = req.headers[CSRF_TOKEN_HEADER_LC] as string
const tokenUser = await authService.checkToken(token, csrfToken, audience)
token = bearerToken
}

if (!tokenUser) {
return next(new UnauthorizedError('Invalid authentication token'))
}
if (!token) {
return next(new UnauthorizedError('Authentication token is missing'))
}

req.tokenUser = tokenUser
req.workspace = req.headers[WORKSPACE_HEADER] as string
} catch (err) {
return next(new UnauthorizedError('Invalid authentication token'))
try {
const csrfToken = req.headers[CSRF_TOKEN_HEADER_LC] as string
const tokenUser = await authService.checkToken(token, csrfToken, audience)

if (!tokenUser) {
return next(new UnauthorizedError('Token verification failed'))
}

next()
req.tokenUser = tokenUser
req.workspace = req.headers[WORKSPACE_HEADER] as string
} catch (err) {
return next(new UnauthorizedError('Error while validating token'))
}

next()
}

/**
* This method checks if the user exists, if he has access to the requested workspace, and if his role
* allows him to do the requested operation. No other security checks should be needed.
*/
export const needPermissions =
(workspaceService: WorkspaceService) =>
(operation: string, resource: string) =>
async (req: RequestWithUser, _res: Response, next: NextFunction) => {
const err = await checkPermissions(workspaceService)(operation, resource)(req)
return next(err)
}
export const needPermissions = (workspaceService: WorkspaceService) => (operation: string, resource: string) => async (
req: RequestWithUser,
_res: Response,
next: NextFunction
) => {
const err = await checkPermissions(workspaceService)(operation, resource)(req)
return next(err)
}

/**
* Just like needPermissions but returns a boolean and can be used inside an express middleware
*/
export const hasPermissions =
(workspaceService: WorkspaceService) =>
async (req: RequestWithUser, operation: string, resource: string, noAudit?: boolean) => {
const err = await checkPermissions(workspaceService)(operation, resource, noAudit)(req)
return !err
}

const checkPermissions =
(workspaceService: WorkspaceService) =>
(operation: string, resource: string, noAudit?: boolean) =>
async (req: RequestWithUser) => {
const audit = (debugMethod: Function, args?: any) => {
if (noAudit) {
return
}

debugMethod(`${req.originalUrl} %o`, {
method: req.method,
email: req.tokenUser?.email,
operation,
resource,
ip: req.ip,
...args
})
export const hasPermissions = (workspaceService: WorkspaceService) => async (
req: RequestWithUser,
operation: string,
resource: string,
noAudit?: boolean
) => {
const err = await checkPermissions(workspaceService)(operation, resource, noAudit)(req)
return !err
}

const checkPermissions = (workspaceService: WorkspaceService) => (
operation: string,
resource: string,
noAudit?: boolean
) => async (req: RequestWithUser) => {
const audit = (debugMethod: Function, args?: any) => {
if (noAudit) {
return
}

if (!req.tokenUser) {
audit(debugFailure, { email: 'n/a', reason: 'unauthenticated' })
return new ForbiddenError('Unauthorized')
}
debugMethod(`${req.originalUrl} %o`, {
method: req.method,
email: req.tokenUser?.email,
operation,
resource,
ip: req.ip,
...args
})
}

if (!req.workspace && req.params.botId) {
req.workspace = await workspaceService.getBotWorkspaceId(req.params.botId)
}
if (!req.tokenUser) {
audit(debugFailure, { email: 'n/a', reason: 'unauthenticated' })
return new ForbiddenError('Unauthorized')
}

if (!req.workspace) {
throw new InvalidOperationError('Workspace is missing. Set header X-BP-Workspace')
}
if (!req.workspace && req.params.botId) {
req.workspace = await workspaceService.getBotWorkspaceId(req.params.botId)
}

const { email, strategy, isSuperAdmin } = req.tokenUser
if (!req.workspace) {
throw new InvalidOperationError('Workspace is missing. Set header X-BP-Workspace')
}

// The server user is used internally, and has all the permissions
if (email === SERVER_USER || isSuperAdmin) {
audit(debugSuccess, { userRole: 'superAdmin' })
return
}
const { email, strategy, isSuperAdmin } = req.tokenUser

if (!email || !strategy) {
audit(debugFailure, { reason: 'missing auth parameter' })
return new NotFoundError('Missing one of the required parameters: email or strategy')
}
// The server user is used internally, and has all the permissions
if (email === SERVER_USER || isSuperAdmin) {
audit(debugSuccess, { userRole: 'superAdmin' })
return
}

const user = await workspaceService.findUser(email, strategy, req.workspace)
if (!email || !strategy) {
audit(debugFailure, { reason: 'missing auth parameter' })
return new NotFoundError('Missing one of the required parameters: email or strategy')
}

if (!user) {
audit(debugFailure, { reason: 'missing workspace access' })
return new ForbiddenError(`User "${email}" doesn't have access to workspace "${req.workspace}"`)
}
const user = await workspaceService.findUser(email, strategy, req.workspace)

const role = await workspaceService.getRoleForUser(email, strategy, req.workspace)
if (!user) {
audit(debugFailure, { reason: 'missing workspace access' })
return new ForbiddenError(`User "${email}" doesn't have access to workspace "${req.workspace}"`)
}

if (!role || !checkRule(role.rules, operation, resource)) {
audit(debugFailure, { userRole: role?.id, reason: 'lack sufficient permissions' })
return new ForbiddenError(`user does not have sufficient permissions to "${operation}" on resource "${resource}"`)
}
const role = await workspaceService.getRoleForUser(email, strategy, req.workspace)

audit(debugSuccess, { userRole: role?.id })
if (!role || !checkRule(role.rules, operation, resource)) {
audit(debugFailure, { userRole: role?.id, reason: 'lack sufficient permissions' })
return new ForbiddenError(`user does not have sufficient permissions to "${operation}" on resource "${resource}"`)
}

const mediaPathRegex = new RegExp(/^\/api\/v(\d)\/bots\/[A-Z0-9_-]+\/media\//, 'i')
export const checkBotVisibility =
(configProvider: ConfigProvider, checkTokenHeader: RequestHandler) => async (req, res, next) => {
// '___' is a non-valid botId, but here acts as for "all bots"
// This is used in modules when they setup routes that work on a global level (they are not tied to a specific bot)
// Check the 'sso-login' module for an example
if (req.params.botId === '___' || req.originalUrl.endsWith('env.js')) {
return next()
}
audit(debugSuccess, { userRole: role?.id })
}

try {
const config = await configProvider.getBotConfig(req.params.botId)
if (config.disabled) {
// The user must be able to get the config to change the bot status
if (req.originalUrl.endsWith(`/api/v1/bots/${req.params.botId}`)) {
return next()
}
const mediaPathRegex = new RegExp(/^\/api\/v(\d)\/bots\/[A-Z0-9_-]+\/media\//, 'i')
export const checkBotVisibility = (configProvider: ConfigProvider, checkTokenHeader: RequestHandler) => async (
req,
res,
next
) => {
// '___' is a non-valid botId, but here acts as for "all bots"
// This is used in modules when they setup routes that work on a global level (they are not tied to a specific bot)
// Check the 'sso-login' module for an example
if (req.params.botId === '___' || req.originalUrl.endsWith('env.js')) {
return next()
}

return next(new NotFoundError('Bot is disabled'))
try {
const config = await configProvider.getBotConfig(req.params.botId)
if (config.disabled) {
// The user must be able to get the config to change the bot status
if (req.originalUrl.endsWith(`/api/v1/bots/${req.params.botId}`)) {
return next()
}

if (config.private && !mediaPathRegex.test(req.originalUrl)) {
return checkTokenHeader(req, res, next)
}
} catch (err) {
return next(new NotFoundError('Invalid Bot ID'))
return next(new NotFoundError('Bot is disabled'))
}

next()
if (config.private && !mediaPathRegex.test(req.originalUrl)) {
return checkTokenHeader(req, res, next)
}
} catch (err) {
return next(new NotFoundError('Invalid Bot ID'))
}

next()
}

0 comments on commit 60953b9

Please sign in to comment.