migrate permission logic to more backend
This commit is contained in:
parent
af82a749bc
commit
2c150c3027
|
@ -1,7 +1,6 @@
|
|||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { plainToClass } from 'class-transformer';
|
||||
import { Permissions } from 'picsur-shared/dist/dto/permissions';
|
||||
import {
|
||||
AsyncFailable,
|
||||
Fail,
|
||||
|
@ -10,6 +9,7 @@ import {
|
|||
} from 'picsur-shared/dist/types';
|
||||
import { strictValidate } from 'picsur-shared/dist/util/validate';
|
||||
import { In, Repository } from 'typeorm';
|
||||
import { Permissions } from '../../models/dto/permissions.dto';
|
||||
import { ImmutableRolesList, UndeletableRolesList } from '../../models/dto/roles.dto';
|
||||
import { ERoleBackend } from '../../models/entities/role.entity';
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { Permissions } from 'picsur-shared/dist/dto/permissions';
|
||||
import { AsyncFailable, HasFailed } from 'picsur-shared/dist/types';
|
||||
import { Permissions } from '../../models/dto/permissions.dto';
|
||||
import { EUserBackend } from '../../models/entities/user.entity';
|
||||
import { RolesService } from '../roledb/roledb.service';
|
||||
import { UsersService } from './userdb.service';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { SetMetadata, UseGuards } from '@nestjs/common';
|
||||
import { Permissions } from 'picsur-shared/dist/dto/permissions';
|
||||
import { CombineDecorators } from 'picsur-shared/dist/util/decorator';
|
||||
import { LocalAuthGuard } from '../managers/auth/guards/localauth.guard';
|
||||
import { Permissions } from '../models/dto/permissions.dto';
|
||||
|
||||
export const RequiredPermissions = (...permissions: Permissions) => {
|
||||
return SetMetadata('permissions', permissions);
|
||||
|
|
|
@ -8,15 +8,13 @@ import {
|
|||
import { Reflector } from '@nestjs/core';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
import { plainToClass } from 'class-transformer';
|
||||
import {
|
||||
Permissions
|
||||
} from 'picsur-shared/dist/dto/permissions';
|
||||
import { Fail, Failable, HasFailed } from 'picsur-shared/dist/types';
|
||||
import { isPermissionsArray } from 'picsur-shared/dist/util/permissions';
|
||||
import { strictValidate } from 'picsur-shared/dist/util/validate';
|
||||
import { UsersService } from '../../../collections/userdb/userdb.service';
|
||||
import { UserRolesService } from '../../../collections/userdb/userrolesdb.service';
|
||||
import { Permissions } from '../../../models/dto/permissions.dto';
|
||||
import { EUserBackend } from '../../../models/entities/user.entity';
|
||||
import { isPermissionsArray } from '../../../models/util/permissions';
|
||||
|
||||
@Injectable()
|
||||
export class MainAuthGuard extends AuthGuard(['jwt', 'guest']) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { Permission } from 'picsur-shared/dist/dto/permissions';
|
||||
import { ImageDBService } from '../../collections/imagedb/imagedb.service';
|
||||
import { RolesService } from '../../collections/roledb/roledb.service';
|
||||
import { Permission } from '../../models/dto/permissions.dto';
|
||||
|
||||
@Injectable()
|
||||
export class DemoManagerService {
|
||||
|
|
10
backend/src/models/dto/permissions.dto.ts
Normal file
10
backend/src/models/dto/permissions.dto.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
// Config
|
||||
|
||||
import { Permission } from 'picsur-shared/dist/dto/permissions.dto';
|
||||
export { Permission } from 'picsur-shared/dist/dto/permissions.dto';
|
||||
|
||||
// Derivatives
|
||||
|
||||
export const PermissionsList: Permission[] = Object.values(Permission);
|
||||
|
||||
export type Permissions = Permission[];
|
|
@ -1,5 +1,5 @@
|
|||
import { Permission, Permissions, PermissionsList } from 'picsur-shared/dist/dto/permissions';
|
||||
import tuple from 'picsur-shared/dist/types/tuple';
|
||||
import { Permission, Permissions, PermissionsList } from './permissions.dto';
|
||||
|
||||
// Config
|
||||
|
||||
|
@ -8,7 +8,10 @@ const SoulBoundRolesTuple = tuple('guest', 'user');
|
|||
// These roles can never be modified
|
||||
const ImmutableRolesTuple = tuple('admin');
|
||||
// These roles can never be removed from the server
|
||||
const UndeletableRolesTuple = tuple(...SoulBoundRolesTuple, ...ImmutableRolesTuple);
|
||||
const UndeletableRolesTuple = tuple(
|
||||
...SoulBoundRolesTuple,
|
||||
...ImmutableRolesTuple,
|
||||
);
|
||||
// These roles will be applied by default to new users
|
||||
export const DefaultRolesList: string[] = ['user'];
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Permissions } from 'picsur-shared/dist/dto/permissions';
|
||||
import { ERole } from 'picsur-shared/dist/entities/role.entity';
|
||||
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
|
||||
import { Permissions } from '../dto/permissions.dto';
|
||||
|
||||
@Entity()
|
||||
export class ERoleBackend extends ERole {
|
||||
|
|
10
backend/src/models/util/permissions.ts
Normal file
10
backend/src/models/util/permissions.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { isArray, isEnum, isString } from 'class-validator';
|
||||
import { Permissions, PermissionsList } from '../dto/permissions.dto';
|
||||
|
||||
export function isPermissionsArray(value: any): value is Permissions {
|
||||
if (!isArray(value)) return false;
|
||||
if (!value.every((item: unknown) => isString(item))) return false;
|
||||
if (!value.every((item: string) => isEnum(item, PermissionsList)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
|
@ -1,14 +1,17 @@
|
|||
import { Controller, Get } from '@nestjs/common';
|
||||
import { plainToClass } from 'class-transformer';
|
||||
import { InfoResponse } from 'picsur-shared/dist/dto/api/info.dto';
|
||||
import { AllPermissionsResponse } from 'picsur-shared/dist/dto/api/roles.dto';
|
||||
import { HostConfigService } from '../../../config/host.config.service';
|
||||
import { NoPermissions } from '../../../decorators/permissions.decorator';
|
||||
import { PermissionsList } from '../../../models/dto/permissions.dto';
|
||||
|
||||
@Controller('api/info')
|
||||
@NoPermissions()
|
||||
export class InfoController {
|
||||
constructor(private hostConfig: HostConfigService) {}
|
||||
|
||||
@Get()
|
||||
@NoPermissions()
|
||||
async getInfo(): Promise<InfoResponse> {
|
||||
return {
|
||||
demo: this.hostConfig.isDemo(),
|
||||
|
@ -16,4 +19,14 @@ export class InfoController {
|
|||
version: this.hostConfig.getVersion(),
|
||||
};
|
||||
}
|
||||
|
||||
// List all available permissions
|
||||
@Get('/permissions')
|
||||
async getPermissions(): Promise<AllPermissionsResponse> {
|
||||
const result: AllPermissionsResponse = {
|
||||
Permissions: PermissionsList,
|
||||
};
|
||||
|
||||
return plainToClass(AllPermissionsResponse, result);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,11 +15,11 @@ import {
|
|||
UpdateSysPreferenceRequest,
|
||||
UpdateSysPreferenceResponse
|
||||
} from 'picsur-shared/dist/dto/api/pref.dto';
|
||||
import { Permission } from 'picsur-shared/dist/dto/permissions';
|
||||
import { SysPreferences } from 'picsur-shared/dist/dto/syspreferences.dto';
|
||||
import { HasFailed } from 'picsur-shared/dist/types';
|
||||
import { SysPreferenceService } from '../../../collections/syspreferencesdb/syspreferencedb.service';
|
||||
import { RequiredPermissions } from '../../../decorators/permissions.decorator';
|
||||
import { Permission } from '../../../models/dto/permissions.dto';
|
||||
|
||||
@Controller('api/pref')
|
||||
@RequiredPermissions(Permission.SysPrefManage)
|
||||
|
|
|
@ -19,16 +19,19 @@ import {
|
|||
RoleUpdateResponse,
|
||||
SpecialRolesResponse
|
||||
} from 'picsur-shared/dist/dto/api/roles.dto';
|
||||
import { Permission } from 'picsur-shared/dist/dto/permissions';
|
||||
import { HasFailed } from 'picsur-shared/dist/types';
|
||||
import { RolesService } from '../../../collections/roledb/roledb.service';
|
||||
import { RequiredPermissions } from '../../../decorators/permissions.decorator';
|
||||
import {
|
||||
Permission
|
||||
} from '../../../models/dto/permissions.dto';
|
||||
import {
|
||||
DefaultRolesList,
|
||||
ImmutableRolesList,
|
||||
SoulBoundRolesList,
|
||||
UndeletableRolesList
|
||||
} from '../../../models/dto/roles.dto';
|
||||
import { isPermissionsArray } from '../../../models/util/permissions';
|
||||
|
||||
@Controller('api/roles')
|
||||
@RequiredPermissions(Permission.RoleManage)
|
||||
|
@ -66,9 +69,14 @@ export class RolesController {
|
|||
async updateRole(
|
||||
@Body() body: RoleUpdateRequest,
|
||||
): Promise<RoleUpdateResponse> {
|
||||
const permissions = body.permissions;
|
||||
if (!isPermissionsArray(permissions)) {
|
||||
throw new InternalServerErrorException('Invalid permissions array');
|
||||
}
|
||||
|
||||
const updatedRole = await this.rolesService.setPermissions(
|
||||
body.name,
|
||||
body.permissions,
|
||||
permissions,
|
||||
);
|
||||
if (HasFailed(updatedRole)) {
|
||||
this.logger.warn(updatedRole.getReason());
|
||||
|
@ -82,7 +90,12 @@ export class RolesController {
|
|||
async createRole(
|
||||
@Body() role: RoleCreateRequest,
|
||||
): Promise<RoleCreateResponse> {
|
||||
const newRole = await this.rolesService.create(role.name, role.permissions);
|
||||
const permissions = role.permissions;
|
||||
if (!isPermissionsArray(permissions)) {
|
||||
throw new InternalServerErrorException('Invalid permissions array');
|
||||
}
|
||||
|
||||
const newRole = await this.rolesService.create(role.name, permissions);
|
||||
if (HasFailed(newRole)) {
|
||||
this.logger.warn(newRole.getReason());
|
||||
throw new InternalServerErrorException('Could not create role');
|
||||
|
@ -104,7 +117,7 @@ export class RolesController {
|
|||
return deletedRole;
|
||||
}
|
||||
|
||||
@Get('special')
|
||||
@Get('/special')
|
||||
async getSpecialRoles(): Promise<SpecialRolesResponse> {
|
||||
const result: SpecialRolesResponse = {
|
||||
SoulBoundRoles: SoulBoundRolesList,
|
||||
|
@ -115,4 +128,5 @@ export class RolesController {
|
|||
|
||||
return plainToClass(SpecialRolesResponse, result);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ import {
|
|||
UserRegisterRequest,
|
||||
UserRegisterResponse
|
||||
} from 'picsur-shared/dist/dto/api/user.dto';
|
||||
import { Permission } from 'picsur-shared/dist/dto/permissions';
|
||||
import { HasFailed } from 'picsur-shared/dist/types';
|
||||
import { UsersService } from '../../../collections/userdb/userdb.service';
|
||||
import { UserRolesService } from '../../../collections/userdb/userrolesdb.service';
|
||||
|
@ -24,6 +23,7 @@ import {
|
|||
UseLocalAuth
|
||||
} from '../../../decorators/permissions.decorator';
|
||||
import { AuthManagerService } from '../../../managers/auth/auth.service';
|
||||
import { Permission } from '../../../models/dto/permissions.dto';
|
||||
import AuthFasityRequest from '../../../models/requests/authrequest.dto';
|
||||
|
||||
@Controller('api/user')
|
||||
|
|
|
@ -20,10 +20,10 @@ import {
|
|||
UserUpdateRequest,
|
||||
UserUpdateResponse
|
||||
} from 'picsur-shared/dist/dto/api/usermanage.dto';
|
||||
import { Permission } from 'picsur-shared/dist/dto/permissions';
|
||||
import { HasFailed } from 'picsur-shared/dist/types';
|
||||
import { UsersService } from '../../../collections/userdb/userdb.service';
|
||||
import { RequiredPermissions } from '../../../decorators/permissions.decorator';
|
||||
import { Permission } from '../../../models/dto/permissions.dto';
|
||||
import { ImmutableUsersList, LockedLoginUsersList, UndeletableUsersList } from '../../../models/dto/specialusers.dto';
|
||||
|
||||
@Controller('api/user')
|
||||
|
|
|
@ -13,11 +13,11 @@ import {
|
|||
import { isHash } from 'class-validator';
|
||||
import { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { ImageMetaResponse } from 'picsur-shared/dist/dto/api/image.dto';
|
||||
import { Permission } from 'picsur-shared/dist/dto/permissions';
|
||||
import { HasFailed } from 'picsur-shared/dist/types';
|
||||
import { MultiPart } from '../../decorators/multipart.decorator';
|
||||
import { RequiredPermissions } from '../../decorators/permissions.decorator';
|
||||
import { ImageManagerService } from '../../managers/imagemanager/imagemanager.service';
|
||||
import { Permission } from '../../models/dto/permissions.dto';
|
||||
import { ImageUploadDto } from '../../models/requests/imageroute.dto';
|
||||
|
||||
@Controller('i')
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
|
||||
import { Permission, Permissions } from 'picsur-shared/dist/dto/permissions';
|
||||
import { Permission } from 'picsur-shared/dist/dto/permissions.dto';
|
||||
import { EUser } from 'picsur-shared/dist/entities/user.entity';
|
||||
import { HasFailed } from 'picsur-shared/dist/types';
|
||||
import { SnackBarType } from 'src/app/models/snack-bar-type';
|
||||
|
@ -21,7 +21,7 @@ export class HeaderComponent implements OnInit {
|
|||
@Output('onHamburgerClick') onHamburgerClick = new EventEmitter<void>();
|
||||
|
||||
private currentUser: EUser | null = null;
|
||||
private permissions: Permissions = [];
|
||||
private permissions: string[] = [];
|
||||
|
||||
public get user() {
|
||||
return this.currentUser;
|
||||
|
|
|
@ -6,8 +6,6 @@ import {
|
|||
Router,
|
||||
RouterStateSnapshot
|
||||
} from '@angular/router';
|
||||
import { Permissions } from 'picsur-shared/dist/dto/permissions';
|
||||
import { isPermissionsArray } from 'picsur-shared/dist/util/permissions';
|
||||
import { PRouteData } from '../models/picsur-routes';
|
||||
import { PermissionService } from '../services/api/permission.service';
|
||||
|
||||
|
@ -34,12 +32,13 @@ export class PermissionGuard implements CanActivate, CanActivateChild {
|
|||
}
|
||||
|
||||
private async can(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
const requiredPermissions: Permissions = this.nestedPermissions(route);
|
||||
if (!isPermissionsArray(requiredPermissions)) {
|
||||
throw new Error(
|
||||
`PermissionGuard: route data 'permissions' must be an array of Permission values`
|
||||
);
|
||||
}
|
||||
const requiredPermissions: string[] = this.nestedPermissions(route);
|
||||
// TODO: revive
|
||||
// if (!isPermissionsArray(requiredPermissions)) {
|
||||
// throw new Error(
|
||||
// `PermissionGuard: route data 'permissions' must be an array of Permission values`
|
||||
// );
|
||||
// }
|
||||
|
||||
const ourPermissions = await this.permissionService.loadedSnapshot();
|
||||
|
||||
|
@ -53,10 +52,10 @@ export class PermissionGuard implements CanActivate, CanActivateChild {
|
|||
return isOk;
|
||||
}
|
||||
|
||||
private nestedPermissions(route: ActivatedRouteSnapshot): Permissions {
|
||||
private nestedPermissions(route: ActivatedRouteSnapshot): string[] {
|
||||
const data: PRouteData = route.data;
|
||||
|
||||
let permissions: Permissions = [];
|
||||
let permissions: string[] = [];
|
||||
if (data?.permissions) {
|
||||
permissions = permissions.concat(data.permissions);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import { Permissions } from 'picsur-shared/dist/dto/permissions';
|
||||
|
||||
export interface RoleModel {
|
||||
name: string;
|
||||
permissions: Permissions;
|
||||
permissions: string[];
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { FormControl } from '@angular/forms';
|
||||
import Fuse from 'fuse.js';
|
||||
import { Permission, Permissions } from 'picsur-shared/dist/dto/permissions';
|
||||
import { BehaviorSubject, Subscription } from 'rxjs';
|
||||
import { RoleNameValidators } from './role-validators';
|
||||
import { RoleModel } from './role.model';
|
||||
|
@ -8,10 +7,10 @@ import { CreateUsernameError } from './user-validators';
|
|||
|
||||
export class UpdateRoleControl {
|
||||
// Set once
|
||||
private permissions: Permissions = [];
|
||||
private permissions: string[] = [];
|
||||
|
||||
// Variables
|
||||
private selectablePermissionsSubject = new BehaviorSubject<Permissions>([]);
|
||||
private selectablePermissionsSubject = new BehaviorSubject<string[]>([]);
|
||||
private permissionsInputSubscription: null | Subscription;
|
||||
|
||||
public rolename = new FormControl('', RoleNameValidators);
|
||||
|
@ -19,7 +18,7 @@ export class UpdateRoleControl {
|
|||
public permissionControl = new FormControl('', []);
|
||||
public selectablePermissions =
|
||||
this.selectablePermissionsSubject.asObservable();
|
||||
public selectedPermissions: Permissions = [];
|
||||
public selectedPermissions: string[] = [];
|
||||
|
||||
public get rolenameValue() {
|
||||
return this.rolename.value;
|
||||
|
@ -43,23 +42,23 @@ export class UpdateRoleControl {
|
|||
}
|
||||
}
|
||||
|
||||
public addPermission(role: Permission) {
|
||||
if (!this.selectablePermissionsSubject.value.includes(role)) return;
|
||||
public addPermission(permission: string) {
|
||||
if (!this.selectablePermissionsSubject.value.includes(permission)) return;
|
||||
|
||||
this.selectedPermissions.push(role);
|
||||
this.selectedPermissions.push(permission);
|
||||
this.clearInput();
|
||||
}
|
||||
|
||||
public removePermission(role: Permission) {
|
||||
public removePermission(permission: string) {
|
||||
this.selectedPermissions = this.selectedPermissions.filter(
|
||||
(r) => r !== role
|
||||
(r) => r !== permission
|
||||
);
|
||||
this.updateSelectablePermissions();
|
||||
}
|
||||
|
||||
// Data interaction
|
||||
|
||||
public putAllPermissions(permissions: Permissions) {
|
||||
public putAllPermissions(permissions: string[]) {
|
||||
this.permissions = permissions;
|
||||
this.updateSelectablePermissions();
|
||||
}
|
||||
|
@ -68,7 +67,7 @@ export class UpdateRoleControl {
|
|||
this.rolename.setValue(rolename);
|
||||
}
|
||||
|
||||
public putPermissions(permissions: Permissions) {
|
||||
public putPermissions(permissions: string[]) {
|
||||
this.selectedPermissions = permissions;
|
||||
this.updateSelectablePermissions();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { FormControl } from '@angular/forms';
|
||||
import Fuse from 'fuse.js';
|
||||
import { Permissions } from 'picsur-shared/dist/dto/permissions';
|
||||
import { ERole } from 'picsur-shared/dist/entities/role.entity';
|
||||
import { BehaviorSubject, Subscription } from 'rxjs';
|
||||
import { FullUserModel } from './fulluser.model';
|
||||
|
@ -74,8 +73,8 @@ export class UpdateUserControl {
|
|||
return true;
|
||||
}
|
||||
|
||||
public getEffectivePermissions(): Permissions {
|
||||
const permissions: Permissions = [];
|
||||
public getEffectivePermissions(): string[] {
|
||||
const permissions: string[] = [];
|
||||
for (const role of this.selectedRoles) {
|
||||
const fullRole = this.fullRoles.find((r) => r.name === role);
|
||||
if (!fullRole) {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { ComponentType, Portal } from '@angular/cdk/portal';
|
||||
import { Route } from '@angular/router';
|
||||
import { Permissions } from 'picsur-shared/dist/dto/permissions';
|
||||
|
||||
export type PRouteData = {
|
||||
page?: {
|
||||
|
@ -8,7 +7,7 @@ export type PRouteData = {
|
|||
icon?: string;
|
||||
category?: string;
|
||||
};
|
||||
permissions?: Permissions;
|
||||
permissions?: string[];
|
||||
noContainer?: boolean;
|
||||
sidebar?: ComponentType<unknown>;
|
||||
_sidebar_portal?: Portal<unknown>;
|
||||
|
|
|
@ -3,14 +3,11 @@ import { Component, OnInit } from '@angular/core';
|
|||
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
|
||||
import { MatChipInputEvent } from '@angular/material/chips';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import {
|
||||
Permission,
|
||||
PermissionsList
|
||||
} from 'picsur-shared/dist/dto/permissions';
|
||||
import { HasFailed } from 'picsur-shared/dist/types';
|
||||
import { UIFriendlyPermissions } from 'src/app/i18n/permissions.i18n';
|
||||
import { UpdateRoleControl } from 'src/app/models/forms/updaterole.control';
|
||||
import { SnackBarType } from 'src/app/models/snack-bar-type';
|
||||
import { PermissionService } from 'src/app/services/api/permission.service';
|
||||
import { RolesService } from 'src/app/services/api/roles.service';
|
||||
import { UtilService } from 'src/app/util/util.service';
|
||||
|
||||
|
@ -42,7 +39,8 @@ export class SettingsRolesEditComponent implements OnInit {
|
|||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private utilService: UtilService,
|
||||
private rolesService: RolesService
|
||||
private rolesService: RolesService,
|
||||
private permissionsService: PermissionService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
|
@ -70,27 +68,36 @@ export class SettingsRolesEditComponent implements OnInit {
|
|||
}
|
||||
|
||||
private async initPermissions() {
|
||||
this.model.putAllPermissions(PermissionsList);
|
||||
const allPermissions = await this.permissionsService.fetchAllPermission();
|
||||
if (HasFailed(allPermissions)) {
|
||||
this.utilService.showSnackBar(
|
||||
'Failed to fetch permissions',
|
||||
SnackBarType.Error
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.model.putAllPermissions(allPermissions);
|
||||
}
|
||||
|
||||
removePermission(permission: Permission) {
|
||||
removePermission(permission: string) {
|
||||
this.model.removePermission(permission);
|
||||
}
|
||||
|
||||
addPermission(event: MatChipInputEvent) {
|
||||
const value = (event.value ?? '').trim();
|
||||
this.model.addPermission(value as Permission);
|
||||
this.model.addPermission(value);
|
||||
}
|
||||
|
||||
selectedPermission(event: MatAutocompleteSelectedEvent): void {
|
||||
this.model.addPermission(event.option.viewValue as Permission);
|
||||
this.model.addPermission(event.option.viewValue);
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.router.navigate(['/settings/roles']);
|
||||
}
|
||||
|
||||
uiFriendlyPermission(permission: Permission) {
|
||||
uiFriendlyPermission(permission: string) {
|
||||
return UIFriendlyPermissions[permission];
|
||||
}
|
||||
|
||||
|
|
|
@ -2,9 +2,6 @@ import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core';
|
|||
import { MatPaginator } from '@angular/material/paginator';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { Router } from '@angular/router';
|
||||
import {
|
||||
Permission
|
||||
} from 'picsur-shared/dist/dto/permissions';
|
||||
import { ERole } from 'picsur-shared/dist/entities/role.entity';
|
||||
import { HasFailed } from 'picsur-shared/dist/types';
|
||||
import { UIFriendlyPermissions } from 'src/app/i18n/permissions.i18n';
|
||||
|
@ -88,7 +85,7 @@ export class SettingsRolesComponent implements OnInit, AfterViewInit {
|
|||
await this.fetchRoles();
|
||||
}
|
||||
|
||||
uiFriendlyPermission(permission: Permission) {
|
||||
uiFriendlyPermission(permission: string) {
|
||||
return UIFriendlyPermissions[permission];
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { ModuleWithProviders, NgModule } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { Permission } from 'picsur-shared/dist/dto/permissions';
|
||||
import { Permission } from 'picsur-shared/dist/dto/permissions.dto';
|
||||
import { PermissionGuard } from 'src/app/guards/permission.guard';
|
||||
import { PRoutes } from 'src/app/models/picsur-routes';
|
||||
import { SidebarResolverService } from 'src/app/services/sidebar-resolver/sidebar-resolver.service';
|
||||
|
|
|
@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core';
|
|||
import { Router } from '@angular/router';
|
||||
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
|
||||
import { NgxDropzoneChangeEvent } from 'ngx-dropzone';
|
||||
import { Permission, Permissions } from 'picsur-shared/dist/dto/permissions';
|
||||
import { Permission } from 'picsur-shared/dist/dto/permissions.dto';
|
||||
import { PermissionService } from 'src/app/services/api/permission.service';
|
||||
import { UtilService } from 'src/app/util/util.service';
|
||||
import { ProcessingViewMetadata } from '../../models/processing-view-metadata';
|
||||
|
@ -12,7 +12,7 @@ import { ProcessingViewMetadata } from '../../models/processing-view-metadata';
|
|||
styleUrls: ['./upload.component.scss'],
|
||||
})
|
||||
export class UploadComponent implements OnInit {
|
||||
private permissions: Permissions = [];
|
||||
private permissions: string[] = [];
|
||||
|
||||
// Lets be optimistic here, this makes for a better ux
|
||||
public get hasUploadPermission() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
|
||||
import { Permission, Permissions } from 'picsur-shared/dist/dto/permissions';
|
||||
import { Permission } from 'picsur-shared/dist/dto/permissions.dto';
|
||||
import { HasFailed } from 'picsur-shared/dist/types';
|
||||
import { SnackBarType } from 'src/app/models/snack-bar-type';
|
||||
import { PermissionService } from 'src/app/services/api/permission.service';
|
||||
|
@ -17,7 +17,7 @@ import { UserPassModel } from '../../../models/forms/userpass.model';
|
|||
export class LoginComponent implements OnInit {
|
||||
private readonly logger = console;
|
||||
|
||||
private permissions: Permissions = [];
|
||||
private permissions: string[] = [];
|
||||
|
||||
public get showRegister() {
|
||||
return this.permissions.includes(Permission.UserRegister);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
|
||||
import { Permission, Permissions } from 'picsur-shared/dist/dto/permissions';
|
||||
import { Permission } from 'picsur-shared/dist/dto/permissions.dto';
|
||||
import { HasFailed } from 'picsur-shared/dist/types';
|
||||
import { UserPassModel } from 'src/app/models/forms/userpass.model';
|
||||
import { SnackBarType } from 'src/app/models/snack-bar-type';
|
||||
|
@ -17,7 +17,7 @@ import { RegisterControl } from '../../../models/forms/register.control';
|
|||
export class RegisterComponent implements OnInit {
|
||||
private readonly logger = console;
|
||||
|
||||
private permissions: Permissions = [];
|
||||
private permissions: string[] = [];
|
||||
|
||||
public get showLogin() {
|
||||
return this.permissions.includes(Permission.UserLogin);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { Permission } from 'picsur-shared/dist/dto/permissions';
|
||||
import { Permission } from 'picsur-shared/dist/dto/permissions.dto';
|
||||
import { PermissionGuard } from 'src/app/guards/permission.guard';
|
||||
import { PRoutes } from 'src/app/models/picsur-routes';
|
||||
import { LoginComponent } from './login/login.component';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { Permission } from 'picsur-shared/dist/dto/permissions';
|
||||
import { Permission } from 'picsur-shared/dist/dto/permissions.dto';
|
||||
import { PermissionGuard } from 'src/app/guards/permission.guard';
|
||||
import { PRoutes } from 'src/app/models/picsur-routes';
|
||||
import { ViewComponent } from './view.component';
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
|
||||
import { AllPermissionsResponse } from 'picsur-shared/dist/dto/api/roles.dto';
|
||||
import { UserMePermissionsResponse } from 'picsur-shared/dist/dto/api/user.dto';
|
||||
import {
|
||||
Permissions,
|
||||
PermissionsList
|
||||
} from 'picsur-shared/dist/dto/permissions';
|
||||
import { AsyncFailable, HasFailed } from 'picsur-shared/dist/types';
|
||||
import { BehaviorSubject, filter, map, Observable, take } from 'rxjs';
|
||||
import { ApiService } from './api.service';
|
||||
|
@ -18,29 +15,29 @@ export class PermissionService {
|
|||
this.onUser();
|
||||
}
|
||||
|
||||
public get live(): Observable<Permissions> {
|
||||
// TODO: add full permission list as default
|
||||
public get live(): Observable<string[]> {
|
||||
return this.permissionsSubject.pipe(
|
||||
map((permissions) => permissions ?? this.defaultPermissions)
|
||||
map((permissions) => permissions ?? [])
|
||||
);
|
||||
}
|
||||
|
||||
public get snapshot(): Permissions {
|
||||
return this.permissionsSubject.getValue() ?? this.defaultPermissions;
|
||||
public get snapshot(): string[] {
|
||||
return this.permissionsSubject.getValue() ?? [];
|
||||
}
|
||||
|
||||
// This will not be optimistic, it will instead wait for correct data
|
||||
public loadedSnapshot(): Promise<Permissions> {
|
||||
public loadedSnapshot(): Promise<string[]> {
|
||||
return new Promise((resolve) => {
|
||||
const filtered = this.permissionsSubject.pipe(
|
||||
filter((permissions) => permissions !== null),
|
||||
take(1)
|
||||
);
|
||||
(filtered as Observable<Permissions>).subscribe(resolve);
|
||||
(filtered as Observable<string[]>).subscribe(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
private defaultPermissions = PermissionsList as Permissions;
|
||||
private permissionsSubject = new BehaviorSubject<Permissions | null>(null);
|
||||
private permissionsSubject = new BehaviorSubject<string[] | null>(null);
|
||||
|
||||
@AutoUnsubscribe()
|
||||
private onUser() {
|
||||
|
@ -54,7 +51,7 @@ export class PermissionService {
|
|||
});
|
||||
}
|
||||
|
||||
private async fetchPermissions(): AsyncFailable<Permissions> {
|
||||
private async fetchPermissions(): AsyncFailable<string[]> {
|
||||
const got = await this.api.get(
|
||||
UserMePermissionsResponse,
|
||||
'/api/user/me/permissions'
|
||||
|
@ -63,4 +60,15 @@ export class PermissionService {
|
|||
|
||||
return got.permissions;
|
||||
}
|
||||
|
||||
public async fetchAllPermission(): AsyncFailable<string[]> {
|
||||
const result = await this.api.get(
|
||||
AllPermissionsResponse,
|
||||
'/api/info/permissions'
|
||||
);
|
||||
|
||||
if (HasFailed(result)) return result;
|
||||
|
||||
return result.Permissions;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
UpdateSysPreferenceRequest,
|
||||
UpdateSysPreferenceResponse
|
||||
} from 'picsur-shared/dist/dto/api/pref.dto';
|
||||
import { Permission } from 'picsur-shared/dist/dto/permissions';
|
||||
import { Permission } from 'picsur-shared/dist/dto/permissions.dto';
|
||||
import {
|
||||
SysPreferences,
|
||||
SysPrefValueType
|
||||
|
@ -31,7 +31,9 @@ export class SysprefService {
|
|||
return this.sysprefObservable;
|
||||
}
|
||||
|
||||
private sysprefObservable = new BehaviorSubject<SysPreferenceBaseResponse[]>([]);
|
||||
private sysprefObservable = new BehaviorSubject<SysPreferenceBaseResponse[]>(
|
||||
[]
|
||||
);
|
||||
|
||||
constructor(
|
||||
private api: ApiService,
|
||||
|
|
|
@ -54,3 +54,10 @@ export class SpecialRolesResponse {
|
|||
@IsStringList()
|
||||
DefaultRoles: string[];
|
||||
}
|
||||
|
||||
// AllPermissions
|
||||
export class AllPermissionsResponse {
|
||||
@IsDefined()
|
||||
@IsStringList()
|
||||
Permissions: string[];
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { Type } from 'class-transformer';
|
||||
import {
|
||||
IsArray, IsDefined,
|
||||
IsEnum, IsJWT, IsString,
|
||||
IsDefined, IsJWT,
|
||||
IsString,
|
||||
ValidateNested
|
||||
} from 'class-validator';
|
||||
import { EUser, NamePassUser } from '../../entities/user.entity';
|
||||
import { Permissions, PermissionsList } from '../permissions';
|
||||
import { IsStringList } from '../../validators/string-list.validator';
|
||||
|
||||
// Api
|
||||
|
||||
|
@ -40,7 +40,6 @@ export class UserMeResponse {
|
|||
// UserMePermissions
|
||||
export class UserMePermissionsResponse {
|
||||
@IsDefined()
|
||||
@IsArray()
|
||||
@IsEnum(PermissionsList, { each: true })
|
||||
permissions: Permissions;
|
||||
@IsStringList()
|
||||
permissions: string[];
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
// Config
|
||||
|
||||
// Only add no rename
|
||||
export enum Permission {
|
||||
ImageView = 'image-view',
|
||||
ImageUpload = 'image-upload',
|
||||
|
@ -14,16 +13,3 @@ export enum Permission {
|
|||
RoleManage = 'role-manage', // Allow modification of roles
|
||||
SysPrefManage = 'syspref-manage',
|
||||
}
|
||||
|
||||
// Derivatives
|
||||
|
||||
export const PermissionsList: Permission[] = Object.values(Permission);
|
||||
|
||||
export type Permissions = Permission[];
|
||||
|
||||
// Compound permission lists
|
||||
export const AdminDashboardPermissions: Permissions = [
|
||||
Permission.UserManage,
|
||||
Permission.RoleManage,
|
||||
Permission.SysPrefManage,
|
||||
];
|
|
@ -1,7 +1,7 @@
|
|||
import { IsArray, IsEnum } from 'class-validator';
|
||||
import { Permissions, PermissionsList } from '../dto/permissions';
|
||||
import { IsDefined } from 'class-validator';
|
||||
import { EntityID } from '../validators/entity-id.validator';
|
||||
import { IsRoleName } from '../validators/role.validators';
|
||||
import { IsStringList } from '../validators/string-list.validator';
|
||||
|
||||
export class RoleNameObject {
|
||||
@IsRoleName()
|
||||
|
@ -9,9 +9,9 @@ export class RoleNameObject {
|
|||
}
|
||||
|
||||
export class RoleNamePermsObject extends RoleNameObject {
|
||||
@IsArray()
|
||||
@IsEnum(PermissionsList, { each: true })
|
||||
permissions: Permissions;
|
||||
@IsDefined()
|
||||
@IsStringList()
|
||||
permissions: string[];
|
||||
}
|
||||
|
||||
export class ERole extends RoleNamePermsObject {
|
||||
|
|
|
@ -1,19 +1,8 @@
|
|||
import { isArray, isEnum, isString } from 'class-validator';
|
||||
import { Permission, Permissions, PermissionsList } from '../dto/permissions';
|
||||
|
||||
export function isPermissionsArray(value: any): value is Permissions {
|
||||
if (!isArray(value)) return false;
|
||||
if (!value.every((item: unknown) => isString(item))) return false;
|
||||
if (!value.every((item: string) => isEnum(item, PermissionsList)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
export function HasAPermissionOf(
|
||||
compoundPermission: Permissions,
|
||||
yourPermissions: Permissions,
|
||||
compoundPermission: string[],
|
||||
yourPermissions: string[],
|
||||
): boolean {
|
||||
return compoundPermission.some((permission: Permission) =>
|
||||
return compoundPermission.some((permission: string) =>
|
||||
yourPermissions.includes(permission),
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue