diff --git a/src/server/database/repositories/user/schema.ts b/src/server/database/repositories/user/schema.ts index c5a8901d..7a2dd4b7 100644 --- a/src/server/database/repositories/user/schema.ts +++ b/src/server/database/repositories/user/schema.ts @@ -7,7 +7,7 @@ export const user = sqliteTable('users_table', { password: text().notNull(), email: text(), name: text().notNull(), - role: int().notNull(), + role: int().$type().notNull(), enabled: int().notNull().default(1), createdAt: text('created_at') .notNull() diff --git a/src/server/middleware/session.ts b/src/server/middleware/session.ts deleted file mode 100644 index 92286515..00000000 --- a/src/server/middleware/session.ts +++ /dev/null @@ -1,94 +0,0 @@ -import type { User } from '~~/services/database/repositories/user'; - -export default defineEventHandler(async (event) => { - const url = getRequestURL(event); - // If one method of a route is public, every method is public! - // Handle api routes - if ( - !url.pathname.startsWith('/api/') || - url.pathname.startsWith('/api/setup/') || - url.pathname === '/api/session' || - url.pathname === '/api/release' - ) { - return; - } - const system = await Database.system.get(); - - const session = await getSession(event, system.sessionConfig); - const authorization = getHeader(event, 'Authorization'); - - let user: User | undefined = undefined; - if (session.data.userId) { - // Handle if authenticating using Session - user = await Database.user.findById(session.data.userId); - } else if (authorization) { - // Handle if authenticating using Header - const [method, value] = authorization.split(' '); - // Support Basic Authentication - // TODO: support personal access token or similar - if (method !== 'Basic' || !value) { - throw createError({ - statusCode: 401, - statusMessage: 'Session failed', - }); - } - - const basicValue = Buffer.from(value, 'base64').toString('utf-8'); - - // Split by first ":" - const index = basicValue.indexOf(':'); - const username = basicValue.substring(0, index); - const password = basicValue.substring(index + 1); - - if (!username || !password) { - throw createError({ - statusCode: 401, - statusMessage: 'Session failed', - }); - } - - const users = await Database.user.findAll(); - const foundUser = users.find((v) => v.username === username); - - if (!foundUser) { - throw createError({ - statusCode: 401, - statusMessage: 'Session failed', - }); - } - - const userHashPassword = foundUser.password; - const passwordValid = await isPasswordValid(password, userHashPassword); - - if (!passwordValid) { - throw createError({ - statusCode: 401, - statusMessage: 'Incorrect Password', - }); - } - user = foundUser; - } - - if (!user) { - throw createError({ - statusCode: 401, - statusMessage: 'Not logged in', - }); - } - - if (!user.enabled) { - throw createError({ - statusCode: 403, - statusMessage: 'Account is disabled', - }); - } - - if (url.pathname.startsWith('/api/admin')) { - if (user.role !== 'ADMIN') { - throw createError({ - statusCode: 403, - statusMessage: 'Missing Permissions', - }); - } - } -}); diff --git a/src/server/utils/handler.ts b/src/server/utils/handler.ts index b151afcb..8cb50adb 100644 --- a/src/server/utils/handler.ts +++ b/src/server/utils/handler.ts @@ -1,29 +1,31 @@ -import type { - EventHandler, - EventHandlerRequest, - EventHandlerResponse, - H3Event, -} from 'h3'; +import type { EventHandlerRequest, EventHandlerResponse, H3Event } from 'h3'; import type { UserType } from '#db/repositories/user/types'; +type PermissionHandler< + TReq extends EventHandlerRequest, + TRes extends EventHandlerResponse, +> = { (params: { event: H3Event; user: UserType }): TRes }; + export const definePermissionEventHandler = < TReq extends EventHandlerRequest, TRes extends EventHandlerResponse, >( - handler: EventHandler + action: Action, + handler: PermissionHandler ) => { return defineEventHandler(async (event) => { const user = await getCurrentUser(event); - checkPermissions('', user); + if (!checkPermissions(action, user)) { + throw createError({ + statusCode: 403, + statusMessage: 'Forbidden', + }); + } - return await handler(event); + return await handler({ event, user }); }); }; -function checkPermissions(permission: unknown, user: UserType) { - console.log({ permission, user }); -} - /** * @throws */ diff --git a/src/server/utils/permissions.ts b/src/server/utils/permissions.ts new file mode 100644 index 00000000..f83ed6f6 --- /dev/null +++ b/src/server/utils/permissions.ts @@ -0,0 +1,31 @@ +// TODO: will need to be updated when we have more roles and actions + +export const actions = { + ADMIN: 'ADMIN', + CLIENT: 'CLIENT', +} as const; + +export type Role = number & { readonly __role: unique symbol }; + +export const roles = { + ADMIN: 1 as Role, + CLIENT: 2 as Role, +} as const; + +export type Action = keyof typeof actions; + +type MATRIX = { + readonly [key in keyof typeof actions]: readonly (typeof roles)[keyof typeof roles][]; +}; + +export const MATRIX: MATRIX = { + [actions.ADMIN]: [roles.ADMIN] as const, + [actions.CLIENT]: [roles.CLIENT, roles.ADMIN] as const, +} as const; + +export const checkPermissions = (action: Action, user: { role: Role }) => { + if (!MATRIX[action].includes(user.role)) { + return false; + } + return true; +};