diff --git a/.vscode/settings.json b/.vscode/settings.json index abe736e..7571d9e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,5 @@ { "vsicons.presets.angular": true, - "angular.log": "verbose" + "angular.log": "verbose", + "discord.enabled": true } diff --git a/backend/src/database/entities/image.entity.ts b/backend/src/database/entities/image.entity.ts index 4bfb41a..c7d315d 100644 --- a/backend/src/database/entities/image.entity.ts +++ b/backend/src/database/entities/image.entity.ts @@ -21,4 +21,8 @@ export class EImageBackend implements EImage { default: "image", }) file_name: string; + + // @Column({ + // nullable: false, + // }) } diff --git a/backend/src/managers/auth/guards/main.guard.ts b/backend/src/managers/auth/guards/main.guard.ts index ec76e4d..ba7fa34 100644 --- a/backend/src/managers/auth/guards/main.guard.ts +++ b/backend/src/managers/auth/guards/main.guard.ts @@ -3,6 +3,7 @@ import { Reflector } from '@nestjs/core'; import { AuthGuard } from '@nestjs/passport'; import { EUser, EUserSchema } from 'picsur-shared/dist/entities/user.entity'; import { Fail, Failable, FT, HasFailed } from 'picsur-shared/dist/types'; +import { makeUnique } from 'picsur-shared/dist/util/unique'; import { UserDbService } from '../../../collections/user-db/user-db.service'; import { Permissions } from '../../../models/constants/permissions.const'; import { isPermissionsArray } from '../../../models/validators/permissions.validator'; @@ -57,7 +58,7 @@ export class MainAuthGuard extends AuthGuard(['apikey', 'jwt', 'guest']) { // These are the permissions the user has const userPermissions = await this.usersService.getPermissions(user.id); if (HasFailed(userPermissions)) { - throw userPermissions + throw userPermissions; } context.switchToHttp().getRequest().userPermissions = userPermissions; @@ -71,16 +72,23 @@ export class MainAuthGuard extends AuthGuard(['apikey', 'jwt', 'guest']) { const handlerName = context.getHandler().name; // Fall back to class permissions if none on function // But function has higher priority than class - const permissions = - this.reflector.get('permissions', context.getHandler()) ?? + const permissionsHandler: Permissions | undefined = + this.reflector.get('permissions', context.getHandler()); + const permissionsClass: Permissions | undefined = this.reflector.get('permissions', context.getClass()); - if (permissions === undefined) + if (permissionsHandler === undefined && permissionsClass === undefined) { return Fail( FT.Internal, undefined, `${handlerName} does not have any permissions defined, denying access`, ); + } + + const permissions = makeUnique([ + ...(permissionsHandler ?? []), + ...(permissionsClass ?? []), + ]); if (!isPermissionsArray(permissions)) return Fail( diff --git a/backend/src/models/constants/roles.const.ts b/backend/src/models/constants/roles.const.ts index ce0cee5..ca7dc89 100644 --- a/backend/src/models/constants/roles.const.ts +++ b/backend/src/models/constants/roles.const.ts @@ -28,13 +28,19 @@ type SystemRole = typeof UndeletableRolesTuple[number]; const SystemRoleDefaultsTyped: { [key in SystemRole]: Permissions; } = { - guest: [Permission.ImageView, Permission.UserLogin], + guest: [ + Permission.ImageView, + Permission.ImageDeleteKey, + Permission.UserLogin, + ], user: [ Permission.ImageView, + Permission.ImageDeleteKey, + Permission.ImageManage, + Permission.ImageUpload, Permission.UserKeepLogin, Permission.UserLogin, Permission.Settings, - Permission.ImageUpload, Permission.ApiKey, ], // Grant all permissions to admin diff --git a/backend/src/routes/image/image-manage.controller.ts b/backend/src/routes/image/image-manage.controller.ts index 7d91acf..cef93b4 100644 --- a/backend/src/routes/image/image-manage.controller.ts +++ b/backend/src/routes/image/image-manage.controller.ts @@ -43,6 +43,7 @@ export class ImageManageController { } @Post('list') + @RequiredPermissions(Permission.ImageManage) @Returns(ImageListResponse) async listMyImagesPaged( @Body() body: ImageListRequest, @@ -61,6 +62,7 @@ export class ImageManageController { } @Post('delete') + @RequiredPermissions(Permission.ImageManage) @Returns(ImageDeleteResponse) async deleteImage( @Body() body: ImageDeleteRequest, diff --git a/frontend/src/app/i18n/permissions.i18n.ts b/frontend/src/app/i18n/permissions.i18n.ts index f76fdea..35cddfd 100644 --- a/frontend/src/app/i18n/permissions.i18n.ts +++ b/frontend/src/app/i18n/permissions.i18n.ts @@ -5,6 +5,8 @@ export const UIFriendlyPermissions: { } = { [Permission.ImageView]: 'View Images', [Permission.ImageUpload]: 'Upload Images', + [Permission.ImageManage]: 'Manage Own Images', + [Permission.ImageDeleteKey]: 'Use Deletekey', [Permission.UserLogin]: 'Login', [Permission.UserKeepLogin]: 'Stay Logged In', diff --git a/shared/src/dto/permissions.enum.ts b/shared/src/dto/permissions.enum.ts index a166ddf..91178ee 100644 --- a/shared/src/dto/permissions.enum.ts +++ b/shared/src/dto/permissions.enum.ts @@ -3,8 +3,10 @@ // This does not have to be a complete list of all permissions // -> the frontend and backend can be somewhat out of sync export enum Permission { - ImageView = 'image-view', - ImageUpload = 'image-upload', // Ability to upload and manage own images + ImageView = 'image-view', // Ability to view images + ImageUpload = 'image-upload', // Ability to upload images + ImageDeleteKey = 'image-delete-key', // Ability to delete images by a secret key + ImageManage = 'image-manage', // List and delete own images UserLogin = 'user-login', // Ability to log in UserKeepLogin = 'user-keep-login', // Ability to view own user details and refresh token