Files
standardnotes-server/packages/files/src/Controller/ValetTokenAuthMiddleware.spec.ts
T

226 lines
6.2 KiB
TypeScript

import 'reflect-metadata'
import { ValetTokenAuthMiddleware } from './ValetTokenAuthMiddleware'
import { NextFunction, Request, Response } from 'express'
import { Logger } from 'winston'
import { TokenDecoderInterface, ValetTokenData } from '@standardnotes/security'
describe('ValetTokenAuthMiddleware', () => {
let tokenDecoder: TokenDecoderInterface<ValetTokenData>
let request: Request
let response: Response
let next: NextFunction
const logger = {
debug: jest.fn(),
} as unknown as jest.Mocked<Logger>
const createMiddleware = () => new ValetTokenAuthMiddleware(tokenDecoder, logger)
beforeEach(() => {
tokenDecoder = {} as jest.Mocked<TokenDecoderInterface<ValetTokenData>>
tokenDecoder.decodeToken = jest.fn().mockReturnValue({
userUuid: '1-2-3',
permittedResources: [
{
remoteIdentifier: '00000000-0000-0000-0000-000000000000',
unencryptedFileSize: 30,
},
],
permittedOperation: 'write',
uploadBytesLimit: 100,
uploadBytesUsed: 80,
})
request = {
headers: {},
query: {},
body: {},
} as jest.Mocked<Request>
response = {
locals: {},
} as jest.Mocked<Response>
response.status = jest.fn().mockReturnThis()
response.send = jest.fn()
next = jest.fn()
})
it('should authorize user with a valet token', async () => {
tokenDecoder.decodeToken = jest.fn().mockReturnValue({
userUuid: '1-2-3',
permittedResources: [
{
remoteIdentifier: '00000000-0000-0000-0000-000000000000',
unencryptedFileSize: 30,
},
],
permittedOperation: 'write',
uploadBytesLimit: -1,
uploadBytesUsed: 80,
})
request.headers['x-valet-token'] = 'valet-token'
await createMiddleware().handler(request, response, next)
expect(response.locals).toEqual({
userUuid: '1-2-3',
permittedOperation: 'write',
permittedResources: [
{
remoteIdentifier: '00000000-0000-0000-0000-000000000000',
unencryptedFileSize: 30,
},
],
uploadBytesLimit: -1,
uploadBytesUsed: 80,
})
expect(next).toHaveBeenCalled()
})
it('should authorize user with unlimited upload with a valet token', async () => {
request.headers['x-valet-token'] = 'valet-token'
tokenDecoder.decodeToken = jest.fn().mockReturnValue({
userUuid: '1-2-3',
permittedResources: [
{
remoteIdentifier: '00000000-0000-0000-0000-000000000000',
unencryptedFileSize: 10,
},
],
permittedOperation: 'write',
uploadBytesLimit: -1,
uploadBytesUsed: 80,
})
await createMiddleware().handler(request, response, next)
expect(response.locals).toEqual({
userUuid: '1-2-3',
permittedOperation: 'write',
permittedResources: [
{
remoteIdentifier: '00000000-0000-0000-0000-000000000000',
unencryptedFileSize: 10,
},
],
uploadBytesLimit: -1,
uploadBytesUsed: 80,
})
expect(next).toHaveBeenCalled()
})
it('should not authorize user with no space left for upload', async () => {
request.headers['x-valet-token'] = 'valet-token'
tokenDecoder.decodeToken = jest.fn().mockReturnValue({
userUuid: '1-2-3',
permittedResources: [
{
remoteIdentifier: '00000000-0000-0000-0000-000000000000',
unencryptedFileSize: 21,
},
],
permittedOperation: 'write',
uploadBytesLimit: 100,
uploadBytesUsed: 80,
})
await createMiddleware().handler(request, response, next)
expect(response.status).toHaveBeenCalledWith(403)
expect(next).not.toHaveBeenCalled()
})
it('should authorize user with no space left for upload for download operations', async () => {
request.headers['x-valet-token'] = 'valet-token'
tokenDecoder.decodeToken = jest.fn().mockReturnValue({
userUuid: '1-2-3',
permittedResources: [
{
remoteIdentifier: '00000000-0000-0000-0000-000000000000',
unencryptedFileSize: 21,
},
],
permittedOperation: 'read',
uploadBytesLimit: 100,
uploadBytesUsed: 80,
})
await createMiddleware().handler(request, response, next)
expect(response.locals).toEqual({
userUuid: '1-2-3',
permittedOperation: 'read',
permittedResources: [
{
remoteIdentifier: '00000000-0000-0000-0000-000000000000',
unencryptedFileSize: 21,
},
],
uploadBytesLimit: 100,
uploadBytesUsed: 80,
})
expect(next).toHaveBeenCalled()
})
it('should not authorize if request is missing valet token in headers', async () => {
await createMiddleware().handler(request, response, next)
expect(response.status).toHaveBeenCalledWith(401)
expect(next).not.toHaveBeenCalled()
})
it('should not authorize if valet token has an invalid remote resource identifier', async () => {
tokenDecoder.decodeToken = jest.fn().mockReturnValue({
userUuid: '1-2-3',
permittedResources: [
{
remoteIdentifier: '1-2-3',
unencryptedFileSize: 30,
},
],
permittedOperation: 'write',
uploadBytesLimit: -1,
uploadBytesUsed: 80,
})
request.headers['x-valet-token'] = 'valet-token'
await createMiddleware().handler(request, response, next)
expect(response.status).toHaveBeenCalledWith(401)
expect(next).not.toHaveBeenCalled()
})
it('should not authorize if auth valet token is malformed', async () => {
request.headers['x-valet-token'] = 'valet-token'
tokenDecoder.decodeToken = jest.fn().mockReturnValue(undefined)
await createMiddleware().handler(request, response, next)
expect(response.status).toHaveBeenCalledWith(401)
expect(next).not.toHaveBeenCalled()
})
it('should pass the error to next middleware if one occurres', async () => {
request.headers['x-valet-token'] = 'valet-token'
const error = new Error('Ooops')
tokenDecoder.decodeToken = jest.fn().mockImplementation(() => {
throw error
})
await createMiddleware().handler(request, response, next)
expect(response.status).not.toHaveBeenCalled()
expect(next).toHaveBeenCalledWith(error)
})
})