Merge pull request #3371 from specklesystems/fabians/core-ioc-103

chore(server): core IoC #103  - validatePermissionsReadStream
This commit is contained in:
Alessandro Magionami
2024-10-23 09:58:14 +02:00
committed by GitHub
6 changed files with 86 additions and 52 deletions
@@ -20,6 +20,7 @@ import {
import { ContextResourceAccessRules } from '@/modules/core/helpers/token'
import { MaybeNullOrUndefined, Nullable, Optional, StreamRoles } from '@speckle/shared'
import { Knex } from 'knex'
import type express from 'express'
export type LegacyGetStreams = (params: {
cursor?: string | Date | null | undefined
@@ -361,3 +362,11 @@ export type AdminGetProjectList = (args: {
items: Stream[]
totalCount: number
}>
export type ValidatePermissionsReadStream = (
streamId: string,
req: express.Request
) => Promise<{
result: boolean
status: number
}>
@@ -4,54 +4,8 @@ const { validateScopes, authorizeResolver } = require('@/modules/shared')
const { Roles, Scopes } = require('@speckle/shared')
const { throwForNotHavingServerRole } = require('@/modules/shared/authz')
const { DatabaseError } = require('@/modules/shared/errors')
const { getStreamFactory } = require('@/modules/core/repositories/streams')
const { db } = require('@/db/knex')
module.exports = {
async validatePermissionsReadStream(streamId, req) {
const getStream = getStreamFactory({ db })
const stream = await getStream({ streamId, userId: req.context.userId })
if (stream?.isPublic) return { result: true, status: 200 }
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 stream contributor role')
return { result: false, status: 401 }
}
if (!stream) return { result: false, status: 404 }
if (!stream.isPublic && req.context.auth === false) {
req.log.debug('User is not authenticated, so cannot read from non-public stream.')
return { result: false, status: 401 }
}
if (!stream.isPublic) {
try {
await validateScopes(req.context.scopes, Scopes.Streams.Read)
} catch (e) {
req.log.info({ err: e }, 'Error while validating scopes')
return { result: false, status: 401 }
}
try {
await authorizeResolver(
req.context.userId,
streamId,
Roles.Stream.Reviewer,
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 }
},
async validatePermissionsWriteStream(streamId, req) {
if (!req.context || !req.context.auth) {
req.log.debug('User is not authenticated, so cannot write to stream.')
@@ -1,14 +1,21 @@
import zlib from 'zlib'
import { corsMiddleware } from '@/modules/core/configs/cors'
import type { Application } from 'express'
import { validatePermissionsReadStream } from '@/modules/core/rest/authUtils'
import { SpeckleObjectsStream } from '@/modules/core/rest/speckleObjectsStream'
import { pipeline, PassThrough } from 'stream'
import { getObjectsStreamFactory } from '@/modules/core/repositories/objects'
import { db } from '@/db/knex'
import { validatePermissionsReadStreamFactory } from '@/modules/core/services/streams/auth'
import { getStreamFactory } from '@/modules/core/repositories/streams'
import { authorizeResolver, validateScopes } from '@/modules/shared'
export default (app: Application) => {
const getObjectsStream = getObjectsStreamFactory({ db })
const validatePermissionsReadStream = validatePermissionsReadStreamFactory({
getStream: getStreamFactory({ db }),
validateScopes,
authorizeResolver
})
app.options('/api/getobjects/:streamId', corsMiddleware())
+10 -2
View File
@@ -2,8 +2,6 @@
const zlib = require('zlib')
const { corsMiddleware } = require('@/modules/core/configs/cors')
const { validatePermissionsReadStream } = require('./authUtils')
const { SpeckleObjectsStream } = require('./speckleObjectsStream')
const { pipeline, PassThrough } = require('stream')
const { logger } = require('@/logging/logging')
@@ -12,9 +10,19 @@ const {
getObjectChildrenStreamFactory
} = require('@/modules/core/repositories/objects')
const { db } = require('@/db/knex')
const {
validatePermissionsReadStreamFactory
} = require('@/modules/core/services/streams/auth')
const { getStreamFactory } = require('@/modules/core/repositories/streams')
const { validateScopes, authorizeResolver } = require('@/modules/shared')
const getObject = getFormattedObjectFactory({ db })
const getObjectChildrenStream = getObjectChildrenStreamFactory({ db })
const validatePermissionsReadStream = validatePermissionsReadStreamFactory({
getStream: getStreamFactory({ db }),
validateScopes,
authorizeResolver
})
module.exports = (app) => {
app.options('/objects/:streamId/:objectId', corsMiddleware())
@@ -1,7 +1,6 @@
'use strict'
const { Scopes } = require('@/modules/core/helpers/mainConstants')
import { Scopes } from '@/modules/core/helpers/mainConstants'
module.exports = [
export default [
{
name: Scopes.Streams.Read,
description:
@@ -0,0 +1,57 @@
import {
GetStream,
ValidatePermissionsReadStream
} from '@/modules/core/domain/streams/operations'
import { throwForNotHavingServerRole } from '@/modules/shared/authz'
import { AuthorizeResolver, ValidateScopes } from '@/modules/shared/domain/operations'
import { DatabaseError } from '@/modules/shared/errors'
import { Roles, Scopes } from '@speckle/shared'
export const validatePermissionsReadStreamFactory =
(deps: {
getStream: GetStream
validateScopes: ValidateScopes
authorizeResolver: AuthorizeResolver
}): ValidatePermissionsReadStream =>
async (streamId, req) => {
const stream = await deps.getStream({ streamId, userId: req.context.userId })
if (stream?.isPublic) return { result: true, status: 200 }
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 stream contributor role')
return { result: false, status: 401 }
}
if (!stream) return { result: false, status: 404 }
if (!stream.isPublic && req.context.auth === false) {
req.log.debug('User is not authenticated, so cannot read from non-public stream.')
return { result: false, status: 401 }
}
if (!stream.isPublic) {
try {
await deps.validateScopes(req.context.scopes, Scopes.Streams.Read)
} catch (e) {
req.log.info({ err: e }, 'Error while validating scopes')
return { result: false, status: 401 }
}
try {
await deps.authorizeResolver(
req.context.userId,
streamId,
Roles.Stream.Reviewer,
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 }
}