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