b568e6d946
Co-authored-by: Charles Driesler <chuck@speckle.systems>
242 lines
6.9 KiB
TypeScript
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)
|
|
}
|
|
})
|
|
})
|
|
})
|