From 284d94f8ca6c72552c88fcbef761ea9cd89fbeed Mon Sep 17 00:00:00 2001 From: Kristaps Fabians Geikins Date: Tue, 22 Oct 2024 12:22:36 +0300 Subject: [PATCH] chore(server): core IoC #104 - validatePermissionsWriteStreamFactory --- .../modules/core/domain/streams/operations.ts | 8 ++++ .../server/modules/core/rest/authUtils.js | 45 ------------------- .../server/modules/core/rest/diffUpload.ts | 7 ++- packages/server/modules/core/rest/upload.ts | 8 +++- .../modules/core/services/streams/auth.ts | 45 ++++++++++++++++++- 5 files changed, 65 insertions(+), 48 deletions(-) delete mode 100644 packages/server/modules/core/rest/authUtils.js diff --git a/packages/server/modules/core/domain/streams/operations.ts b/packages/server/modules/core/domain/streams/operations.ts index 87488a2d1..1091b3d10 100644 --- a/packages/server/modules/core/domain/streams/operations.ts +++ b/packages/server/modules/core/domain/streams/operations.ts @@ -370,3 +370,11 @@ export type ValidatePermissionsReadStream = ( result: boolean status: number }> + +export type ValidatePermissionsWriteStream = ( + streamId: string, + req: express.Request +) => Promise<{ + result: boolean + status: number +}> diff --git a/packages/server/modules/core/rest/authUtils.js b/packages/server/modules/core/rest/authUtils.js deleted file mode 100644 index 9e00915bc..000000000 --- a/packages/server/modules/core/rest/authUtils.js +++ /dev/null @@ -1,45 +0,0 @@ -'use strict' -const { validateScopes, authorizeResolver } = require('@/modules/shared') - -const { Roles, Scopes } = require('@speckle/shared') -const { throwForNotHavingServerRole } = require('@/modules/shared/authz') -const { DatabaseError } = require('@/modules/shared/errors') - -module.exports = { - async validatePermissionsWriteStream(streamId, req) { - if (!req.context || !req.context.auth) { - req.log.debug('User is not authenticated, so cannot write to stream.') - return { result: false, status: 401 } - } - - try { - await throwForNotHavingServerRole(req.context, Roles.Server.Guest) - } catch (e) { - if (e instanceof DatabaseError) return { result: false, status: 500 } - req.log.info({ err: e }, 'Error while checking server role') - return { result: false, status: 401 } - } - - try { - await validateScopes(req.context.scopes, Scopes.Streams.Write) - } catch (e) { - req.log.info({ err: e }, 'Error while checking scopes') - return { result: false, status: 401 } - } - - try { - await authorizeResolver( - req.context.userId, - streamId, - Roles.Stream.Contributor, - req.context.resourceAccessRules - ) - } catch (e) { - if (e instanceof DatabaseError) return { result: false, status: 500 } - req.log.info({ err: e }, 'Error while checking stream contributor role') - return { result: false, status: 401 } - } - - return { result: true, status: 200 } - } -} diff --git a/packages/server/modules/core/rest/diffUpload.ts b/packages/server/modules/core/rest/diffUpload.ts index 724cf6a16..79ce5f978 100644 --- a/packages/server/modules/core/rest/diffUpload.ts +++ b/packages/server/modules/core/rest/diffUpload.ts @@ -1,13 +1,18 @@ import zlib from 'zlib' import { corsMiddleware } from '@/modules/core/configs/cors' -import { validatePermissionsWriteStream } from '@/modules/core/rest/authUtils' import { chunk } from 'lodash' import type { Application } from 'express' import { hasObjectsFactory } from '@/modules/core/repositories/objects' import { db } from '@/db/knex' +import { validatePermissionsWriteStreamFactory } from '@/modules/core/services/streams/auth' +import { authorizeResolver, validateScopes } from '@/modules/shared' export default (app: Application) => { const hasObjects = hasObjectsFactory({ db }) + const validatePermissionsWriteStream = validatePermissionsWriteStreamFactory({ + validateScopes, + authorizeResolver + }) app.options('/api/diff/:streamId', corsMiddleware()) diff --git a/packages/server/modules/core/rest/upload.ts b/packages/server/modules/core/rest/upload.ts index cd5f90183..c2e7d3f3b 100644 --- a/packages/server/modules/core/rest/upload.ts +++ b/packages/server/modules/core/rest/upload.ts @@ -1,7 +1,6 @@ import zlib from 'zlib' import { corsMiddleware } from '@/modules/core/configs/cors' import Busboy from 'busboy' -import { validatePermissionsWriteStream } from '@/modules/core/rest/authUtils' import { getFeatureFlags, maximumObjectUploadFileSizeMb @@ -21,6 +20,8 @@ import { } from '@/modules/core/repositories/objects' import { db } from '@/db/knex' import { RawSpeckleObject } from '@/modules/core/domain/objects/types' +import { validatePermissionsWriteStreamFactory } from '@/modules/core/services/streams/auth' +import { authorizeResolver, validateScopes } from '@/modules/shared' const MAX_FILE_SIZE = maximumObjectUploadFileSizeMb() * 1024 * 1024 const { FF_NO_CLOSURE_WRITES } = getFeatureFlags() @@ -44,6 +45,11 @@ if (FF_NO_CLOSURE_WRITES) { } export default (app: Router) => { + const validatePermissionsWriteStream = validatePermissionsWriteStreamFactory({ + validateScopes, + authorizeResolver + }) + app.options('/objects/:streamId', corsMiddleware()) app.post('/objects/:streamId', corsMiddleware(), async (req, res) => { diff --git a/packages/server/modules/core/services/streams/auth.ts b/packages/server/modules/core/services/streams/auth.ts index 2dae37da8..0b225d210 100644 --- a/packages/server/modules/core/services/streams/auth.ts +++ b/packages/server/modules/core/services/streams/auth.ts @@ -1,6 +1,7 @@ import { GetStream, - ValidatePermissionsReadStream + ValidatePermissionsReadStream, + ValidatePermissionsWriteStream } from '@/modules/core/domain/streams/operations' import { throwForNotHavingServerRole } from '@/modules/shared/authz' import { AuthorizeResolver, ValidateScopes } from '@/modules/shared/domain/operations' @@ -55,3 +56,45 @@ export const validatePermissionsReadStreamFactory = } return { result: true, status: 200 } } + +export const validatePermissionsWriteStreamFactory = + (deps: { + validateScopes: ValidateScopes + authorizeResolver: AuthorizeResolver + }): ValidatePermissionsWriteStream => + async (streamId, req) => { + if (!req.context || !req.context.auth) { + req.log.debug('User is not authenticated, so cannot write to stream.') + return { result: false, status: 401 } + } + + try { + await throwForNotHavingServerRole(req.context, Roles.Server.Guest) + } catch (e) { + if (e instanceof DatabaseError) return { result: false, status: 500 } + req.log.info({ err: e }, 'Error while checking server role') + return { result: false, status: 401 } + } + + try { + await deps.validateScopes(req.context.scopes, Scopes.Streams.Write) + } catch (e) { + req.log.info({ err: e }, 'Error while checking scopes') + return { result: false, status: 401 } + } + + try { + await deps.authorizeResolver( + req.context.userId, + streamId, + Roles.Stream.Contributor, + req.context.resourceAccessRules + ) + } catch (e) { + if (e instanceof DatabaseError) return { result: false, status: 500 } + req.log.info({ err: e }, 'Error while checking stream contributor role') + return { result: false, status: 401 } + } + + return { result: true, status: 200 } + }