Files
speckle-server/packages/server/modules/core/tests/generic.spec.ts
T
2025-08-28 14:21:26 +02:00

242 lines
6.9 KiB
TypeScript

/* istanbul ignore file */
import { expect } from 'chai'
import { beforeEachContext } from '@/test/hooks'
import { validateScopes, authorizeResolver } from '@/modules/shared'
import { buildContext } from '@/modules/shared/middleware'
import type { AvailableRoles, ServerRoles } from '@speckle/shared'
import { Roles, Scopes } from '@speckle/shared'
import { throwForNotHavingServerRole } from '@/modules/shared/authz'
import { ForbiddenError } from '@/modules/shared/errors'
import { mockAdminOverride } from '@/test/mocks/global'
import type { Request } from 'express'
import type { BasicTestUser } from '@/test/authHelper'
import { createTestUser } from '@/test/authHelper'
import type { BasicTestStream } from '@/test/speckle-helpers/streamHelper'
import { createTestStream } from '@/test/speckle-helpers/streamHelper'
const adminOverrideMock = mockAdminOverride()
describe('Generic AuthN & AuthZ controller tests', () => {
before(async () => {
await beforeEachContext()
})
it('Validate scopes', async () => {
await validateScopes(undefined, undefined as unknown as string)
.then(() => {
throw new Error('This should have been rejected')
})
.catch((err) =>
expect('Your auth token does not have the required scope.').to.equal(
err.message
)
)
await validateScopes(['a'], 'b')
.then(() => {
throw new Error('This should have been rejected')
})
.catch((err) =>
expect('Your auth token does not have the required scope: b.').to.equal(
err.message
)
)
await validateScopes(['a', 'b'], 'b') // should pass
})
;(<const>[
['BS header', { req: { headers: { authorization: 'Bearer BS' } } as Request }],
[
'Null header',
{ req: { headers: { authorization: null as string | null } } as Request }
],
['Undefined header', { req: { headers: { authorization: undefined } } as Request }],
['BS token', { token: 'Bearer BS' }],
['Null token', { token: null }],
['Undefined token', { token: undefined }]
]).map(([caseName, contextInput]) =>
it(`Should create proper context ${caseName}`, async () => {
const res = await buildContext(contextInput)
expect(res.auth).to.equal(false)
})
)
it('Should validate server role', async () => {
await throwForNotHavingServerRole(
{ auth: true, role: Roles.Server.User },
Roles.Server.Admin
)
.then(() => {
throw new Error('This should have been rejected')
})
.catch((err) =>
expect('You do not have the required server role').to.equal(err.message)
)
await throwForNotHavingServerRole(
{ auth: true, role: 'HACZOR' as ServerRoles },
'133TCR3w' as ServerRoles
)
.then(() => {
throw new Error('This should have been rejected')
})
.catch((err) =>
expect('Invalid role requirement specified').to.equal(err.message)
)
await throwForNotHavingServerRole(
{ auth: true, role: Roles.Server.Admin },
'133TCR3w' as ServerRoles
)
.then(() => {
throw new Error('This should have been rejected')
})
.catch((err) =>
expect('Invalid role requirement specified').to.equal(err.message)
)
const test = await throwForNotHavingServerRole(
{ auth: true, role: Roles.Server.Admin },
Roles.Server.User
)
expect(test).to.equal(true)
})
it('Resolver Authorization Should fail nicely when roles & resources are wanky', async () => {
await authorizeResolver(null, 'foo', 'bar' as AvailableRoles, null)
.then(() => {
throw new Error('This should have been rejected')
})
.catch((err) => expect('Unknown role: bar').to.equal(err.message))
// this caught me out, but streams:read is not a valid role for now
await authorizeResolver(
'foo',
'bar' as AvailableRoles,
Scopes.Streams.Read as AvailableRoles,
null
)
.then(() => {
throw new Error('This should have been rejected')
})
.catch((err) => expect('Unknown role: streams:read').to.equal(err.message))
})
describe('Authorize resolver ', () => {
let myStream: BasicTestStream
let notMyStream: BasicTestStream
let serverOwner: BasicTestUser
let otherGuy: BasicTestUser
before(async function () {
// Seeding
serverOwner = await createTestUser({
name: 'Itsa Me',
email: 'me@example.org',
password: 'sn3aky-1337-b1m'
})
otherGuy = await createTestUser({
name: 'Some Other DUde',
email: 'otherguy@example.org',
password: 'sn3aky-1337-b1m'
})
myStream = await createTestStream(
{
name: 'My Stream 2',
isPublic: true
},
serverOwner
)
notMyStream = await createTestStream(
{
name: 'Not My Stream 1',
isPublic: false
},
otherGuy
)
})
afterEach(() => {
adminOverrideMock.disable()
})
after(() => {
adminOverrideMock.disable()
})
it('should allow stream:owners to be stream:owners', async () => {
await authorizeResolver(
serverOwner.id,
myStream.id,
Roles.Stream.Contributor,
null
)
})
it('should get the passed in role for server:admins if override enabled', async () => {
adminOverrideMock.enable(true)
await authorizeResolver(
serverOwner.id,
myStream.id,
Roles.Stream.Contributor,
null
)
})
it('should not allow server:admins to be anything if adminOverride is disabled', async () => {
try {
await authorizeResolver(
serverOwner.id,
notMyStream.id,
Roles.Stream.Contributor,
null
)
throw new Error('This should have thrown')
} catch (e) {
expect(e instanceof ForbiddenError)
}
})
it('should allow server:admins to be anything if adminOverride is enabled', async () => {
adminOverrideMock.enable(true)
await authorizeResolver(
serverOwner.id,
notMyStream.id,
Roles.Stream.Contributor,
null
)
})
it('should not allow server:users to be anything if adminOverride is disabled', async () => {
try {
await authorizeResolver(
otherGuy.id,
myStream.id,
Roles.Stream.Contributor,
null
)
throw new Error('This should have thrown')
} catch (e) {
expect(e instanceof ForbiddenError)
}
})
it('should not allow server:users to be anything if adminOverride is enabled', async () => {
adminOverrideMock.enable(true)
try {
await authorizeResolver(
otherGuy.id,
myStream.id,
Roles.Stream.Contributor,
null
)
throw new Error('This should have thrown')
} catch (e) {
expect(e instanceof ForbiddenError)
}
})
})
})