124 lines
3.8 KiB
TypeScript
124 lines
3.8 KiB
TypeScript
import DaoFactory from '../models/DaoFactory'
|
|
import CookieManager from './CookieManager'
|
|
import { buildRFC7807 } from './RFCs/RFC7807'
|
|
|
|
export interface Permission {
|
|
name: string
|
|
/**
|
|
* if set it will be usable by users
|
|
*
|
|
* else only users with the `admin.` prefix in the key can run it
|
|
*/
|
|
api: boolean
|
|
|
|
/**
|
|
* if set to true it will pass if a cookie authenticate a valid user
|
|
*/
|
|
cookie: boolean
|
|
}
|
|
|
|
/**
|
|
* validate the authentification
|
|
* @param request the request
|
|
* @param permission the permission to validate
|
|
* @returns a Response if the request is invalid, null otherwise
|
|
*
|
|
* TODO: implement rate limiting
|
|
* http/2.0 429 TOO MANY REQUESTS
|
|
* Content-Type: application/json+problem
|
|
* X-RateLimit-Limit: 1000 // number of request you cn make until hitting the rate limit
|
|
* X-RateLimit-Remaining: 0 // number of request remaining until the rate limit is atteined
|
|
* X-RateLimit-Reset: 123456789 // EPOCH time when the rate limit reset
|
|
* X-RateLimit-Reset-After: 9 // Number of seconds before the remaining Rate reset
|
|
*/
|
|
export async function validateAuth(request: Request, permission: Permission): Promise<Response | string> {
|
|
const apiKeyHeader = request.headers.get('Authorization')
|
|
const cookieHeader = request.headers.get('Cookie')
|
|
if (apiKeyHeader) {
|
|
const apiKey = apiKeyHeader.slice(apiKeyHeader.lastIndexOf(' ') + 1)
|
|
const dao = await DaoFactory.get('apiKey').findOne({
|
|
key: apiKey
|
|
})
|
|
const perm = permission.name.split('.')
|
|
const match = dao?.permissions.find((it) => {
|
|
const itSplit = it.split('.')
|
|
if (itSplit[0] === 'admin') {
|
|
itSplit.shift()
|
|
}
|
|
for (let idx = 0; idx < itSplit.length; idx++) {
|
|
const permissionLayer = itSplit[idx]
|
|
const requestPermissionLayer = perm[idx]
|
|
if (permissionLayer === '*') {
|
|
return true
|
|
} else if (permissionLayer !== requestPermissionLayer) {
|
|
return false
|
|
}
|
|
}
|
|
return itSplit.length === perm.length
|
|
// return it.endsWith(permission.name)
|
|
})
|
|
if (match && (permission.api || match.startsWith('admin.'))) {
|
|
return apiKey
|
|
} else if (permission.api) {
|
|
return buildRFC7807({
|
|
type: '/docs/errors/unauthorized-access',
|
|
status: 401,
|
|
title: 'Unauthorized access',
|
|
details: `you are missing the permission "${permission.name}" or is not an admin`
|
|
})
|
|
}
|
|
} else if (permission.api && !permission.cookie) {
|
|
return buildRFC7807({
|
|
type: '/docs/errors/unauthorized-access',
|
|
status: 401,
|
|
title: 'Unauthorized access',
|
|
details: `You MUST define an API key fo use this endpoint`
|
|
})
|
|
}
|
|
|
|
if (cookieHeader && permission.cookie) {
|
|
// TODO: make a better cookie implementation
|
|
const cookies = new CookieManager(cookieHeader)
|
|
const userCookie = cookies.get('userId')
|
|
if (!userCookie) {
|
|
return buildRFC7807({
|
|
type: '/docs/errors/unauthorized-access',
|
|
status: 401,
|
|
title: 'Unauthorized access',
|
|
details: `you must be connected to use this endpoint (missing the userId cookie)`
|
|
})
|
|
}
|
|
const dao = await DaoFactory.get('user').get(userCookie)
|
|
if (!dao) {
|
|
return buildRFC7807({
|
|
type: '/docs/errors/unauthorized-access',
|
|
status: 401,
|
|
title: 'Unauthorized access',
|
|
details: `the user does not exists`
|
|
})
|
|
}
|
|
return userCookie
|
|
} else if (!permission.api && permission.cookie) {
|
|
return buildRFC7807({
|
|
type: '/docs/errors/unauthorized-access',
|
|
status: 401,
|
|
title: 'Unauthorized access',
|
|
details: `You MUST be connected to your account to use this endpoint`
|
|
})
|
|
} else if (permission.api && permission.cookie) {
|
|
return buildRFC7807({
|
|
type: '/docs/errors/unauthorized-access',
|
|
status: 401,
|
|
title: 'Unauthorized access',
|
|
details: `You must be connected or use an API key to access this endpoint`
|
|
})
|
|
}
|
|
return buildRFC7807({
|
|
type: '/docs/errors/page-not-found',
|
|
status: 404,
|
|
title: 'Page not found',
|
|
details: `the following endpoint does not exists`
|
|
})
|
|
|
|
}
|