mirror of
https://github.com/CaramelFur/Picsur.git
synced 2025-11-14 15:45:49 +01:00
add role api
This commit is contained in:
@@ -35,8 +35,8 @@ export class RolesModule implements OnModuleInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async nukeRoles() {
|
private async nukeRoles() {
|
||||||
this.logger.error('Nuking all roles');
|
this.logger.error('Nuking system roles');
|
||||||
const result = this.rolesService.nuke(true);
|
const result = this.rolesService.nukeSystemRoles(true);
|
||||||
if (HasFailed(result)) {
|
if (HasFailed(result)) {
|
||||||
this.logger.error(`Failed to nuke roles because: ${result.getReason()}`);
|
this.logger.error(`Failed to nuke roles because: ${result.getReason()}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
HasFailed,
|
HasFailed,
|
||||||
HasSuccess
|
HasSuccess
|
||||||
} from 'picsur-shared/dist/types';
|
} from 'picsur-shared/dist/types';
|
||||||
import { Repository } from 'typeorm';
|
import { In, Repository } from 'typeorm';
|
||||||
import { ERoleBackend } from '../../models/entities/role.entity';
|
import { ERoleBackend } from '../../models/entities/role.entity';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -79,7 +79,7 @@ export class RolesService {
|
|||||||
public async addPermissions(
|
public async addPermissions(
|
||||||
role: string | ERoleBackend,
|
role: string | ERoleBackend,
|
||||||
permissions: Permissions,
|
permissions: Permissions,
|
||||||
): AsyncFailable<true> {
|
): AsyncFailable<ERoleBackend> {
|
||||||
const roleToModify = await this.resolve(role);
|
const roleToModify = await this.resolve(role);
|
||||||
if (HasFailed(roleToModify)) return roleToModify;
|
if (HasFailed(roleToModify)) return roleToModify;
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@ export class RolesService {
|
|||||||
public async removePermissions(
|
public async removePermissions(
|
||||||
role: string | ERoleBackend,
|
role: string | ERoleBackend,
|
||||||
permissions: Permissions,
|
permissions: Permissions,
|
||||||
): AsyncFailable<true> {
|
): AsyncFailable<ERoleBackend> {
|
||||||
const roleToModify = await this.resolve(role);
|
const roleToModify = await this.resolve(role);
|
||||||
if (HasFailed(roleToModify)) return roleToModify;
|
if (HasFailed(roleToModify)) return roleToModify;
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ export class RolesService {
|
|||||||
role: string | ERoleBackend,
|
role: string | ERoleBackend,
|
||||||
permissions: Permissions,
|
permissions: Permissions,
|
||||||
allowImmutable: boolean = false,
|
allowImmutable: boolean = false,
|
||||||
): AsyncFailable<true> {
|
): AsyncFailable<ERoleBackend> {
|
||||||
const roleToModify = await this.resolve(role);
|
const roleToModify = await this.resolve(role);
|
||||||
if (HasFailed(roleToModify)) return roleToModify;
|
if (HasFailed(roleToModify)) return roleToModify;
|
||||||
|
|
||||||
@@ -120,12 +120,10 @@ export class RolesService {
|
|||||||
roleToModify.permissions = permissions;
|
roleToModify.permissions = permissions;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.rolesRepository.save(roleToModify);
|
return await this.rolesRepository.save(roleToModify);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
return Fail(e?.message);
|
return Fail(e?.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async findOne(name: string): AsyncFailable<ERoleBackend> {
|
public async findOne(name: string): AsyncFailable<ERoleBackend> {
|
||||||
@@ -141,14 +139,26 @@ export class RolesService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async findAll(): AsyncFailable<ERoleBackend[]> {
|
||||||
|
try {
|
||||||
|
const found = await this.rolesRepository.find();
|
||||||
|
if (!found) return Fail('No roles found');
|
||||||
|
return found as ERoleBackend[];
|
||||||
|
} catch (e: any) {
|
||||||
|
return Fail(e?.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async exists(username: string): Promise<boolean> {
|
public async exists(username: string): Promise<boolean> {
|
||||||
return HasSuccess(await this.findOne(username));
|
return HasSuccess(await this.findOne(username));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async nuke(iamsure: boolean = false): AsyncFailable<true> {
|
public async nukeSystemRoles(iamsure: boolean = false): AsyncFailable<true> {
|
||||||
if (!iamsure) return Fail('Nuke aborted');
|
if (!iamsure) return Fail('Nuke aborted');
|
||||||
try {
|
try {
|
||||||
await this.rolesRepository.delete({});
|
await this.rolesRepository.delete({
|
||||||
|
name: In(SystemRolesList),
|
||||||
|
});
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
return Fail(e?.message);
|
return Fail(e?.message);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { AuthModule } from './auth/auth.module';
|
import { AuthModule } from './auth/auth.module';
|
||||||
import { ExperimentModule } from './experiment/experiment.module';
|
import { ExperimentModule } from './experiment/experiment.module';
|
||||||
import { PrefModule } from './pref/pref.module';
|
|
||||||
import { InfoModule } from './info/info.module';
|
import { InfoModule } from './info/info.module';
|
||||||
|
import { PrefModule } from './pref/pref.module';
|
||||||
|
import { RolesApiModule } from './roles/roles.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -10,6 +11,7 @@ import { InfoModule } from './info/info.module';
|
|||||||
PrefModule,
|
PrefModule,
|
||||||
ExperimentModule,
|
ExperimentModule,
|
||||||
InfoModule,
|
InfoModule,
|
||||||
|
RolesApiModule,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class PicsurApiModule {}
|
export class PicsurApiModule {}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
Post,
|
Post,
|
||||||
Request
|
Request
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
|
import { AuthUserInfoRequest } from 'picsur-shared/dist/dto/api/auth.dto';
|
||||||
import {
|
import {
|
||||||
AuthDeleteRequest,
|
AuthDeleteRequest,
|
||||||
AuthLoginResponse,
|
AuthLoginResponse,
|
||||||
@@ -15,7 +16,10 @@ import {
|
|||||||
} from 'picsur-shared/dist/dto/auth.dto';
|
} from 'picsur-shared/dist/dto/auth.dto';
|
||||||
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, UseLocalAuth } from '../../../decorators/permissions.decorator';
|
import {
|
||||||
|
RequiredPermissions,
|
||||||
|
UseLocalAuth
|
||||||
|
} from '../../../decorators/permissions.decorator';
|
||||||
import { AuthManagerService } from '../../../managers/auth/auth.service';
|
import { AuthManagerService } from '../../../managers/auth/auth.service';
|
||||||
import AuthFasityRequest from '../../../models/dto/authrequest.dto';
|
import AuthFasityRequest from '../../../models/dto/authrequest.dto';
|
||||||
|
|
||||||
@@ -75,6 +79,18 @@ export class AuthController {
|
|||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post('info')
|
||||||
|
@RequiredPermissions('user-manage')
|
||||||
|
async getUser(@Body() body: AuthUserInfoRequest) {
|
||||||
|
const user = await this.usersService.findOne(body.username);
|
||||||
|
if (HasFailed(user)) {
|
||||||
|
this.logger.warn(user.getReason());
|
||||||
|
throw new InternalServerErrorException('Could not find user');
|
||||||
|
}
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
@Get('list')
|
@Get('list')
|
||||||
@RequiredPermissions('user-manage')
|
@RequiredPermissions('user-manage')
|
||||||
async listUsers(@Request() req: AuthFasityRequest) {
|
async listUsers(@Request() req: AuthFasityRequest) {
|
||||||
|
|||||||
113
backend/src/routes/api/roles/roles.controller.ts
Normal file
113
backend/src/routes/api/roles/roles.controller.ts
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
InternalServerErrorException,
|
||||||
|
Logger,
|
||||||
|
Post
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
RoleCreateRequest,
|
||||||
|
RoleDeleteRequest,
|
||||||
|
RoleInfoRequest,
|
||||||
|
RoleUpdateRequest
|
||||||
|
} from 'picsur-shared/dist/dto/api/roles.dto';
|
||||||
|
import { HasFailed } from 'picsur-shared/dist/types';
|
||||||
|
import { RolesService } from '../../../collections/roledb/roledb.service';
|
||||||
|
import { RequiredPermissions } from '../../../decorators/permissions.decorator';
|
||||||
|
|
||||||
|
@Controller('api/roles')
|
||||||
|
@RequiredPermissions('role-manage')
|
||||||
|
export class RolesController {
|
||||||
|
private readonly logger = new Logger('RolesController');
|
||||||
|
|
||||||
|
constructor(private rolesService: RolesService) {}
|
||||||
|
|
||||||
|
@Get('/list')
|
||||||
|
getRoles() {
|
||||||
|
const roles = this.rolesService.findAll();
|
||||||
|
if (HasFailed(roles)) {
|
||||||
|
this.logger.warn(roles.getReason());
|
||||||
|
throw new InternalServerErrorException('Could not list roles');
|
||||||
|
}
|
||||||
|
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/info')
|
||||||
|
getRole(@Body() body: RoleInfoRequest) {
|
||||||
|
const role = this.rolesService.findOne(body.name);
|
||||||
|
if (HasFailed(role)) {
|
||||||
|
this.logger.warn(role.getReason());
|
||||||
|
throw new InternalServerErrorException('Could not find role');
|
||||||
|
}
|
||||||
|
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/permissions/set')
|
||||||
|
updateRole(@Body() body: RoleUpdateRequest) {
|
||||||
|
const updatedRole = this.rolesService.setPermissions(
|
||||||
|
body.name,
|
||||||
|
body.permissions,
|
||||||
|
);
|
||||||
|
if (HasFailed(updatedRole)) {
|
||||||
|
this.logger.warn(updatedRole.getReason());
|
||||||
|
throw new InternalServerErrorException('Could not set role permissions');
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedRole;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/permissions/add')
|
||||||
|
addPermissions(@Body() body: RoleUpdateRequest) {
|
||||||
|
const updatedRole = this.rolesService.addPermissions(
|
||||||
|
body.name,
|
||||||
|
body.permissions,
|
||||||
|
);
|
||||||
|
if (HasFailed(updatedRole)) {
|
||||||
|
this.logger.warn(updatedRole.getReason());
|
||||||
|
throw new InternalServerErrorException('Could not add role permissions');
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedRole;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/permissions/remove')
|
||||||
|
removePermissions(@Body() body: RoleUpdateRequest) {
|
||||||
|
const updatedRole = this.rolesService.removePermissions(
|
||||||
|
body.name,
|
||||||
|
body.permissions,
|
||||||
|
);
|
||||||
|
if (HasFailed(updatedRole)) {
|
||||||
|
this.logger.warn(updatedRole.getReason());
|
||||||
|
throw new InternalServerErrorException(
|
||||||
|
'Could not remove role permissions',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedRole;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/create')
|
||||||
|
createRole(@Body() role: RoleCreateRequest) {
|
||||||
|
const newRole = this.rolesService.create(role.name, role.permissions);
|
||||||
|
if (HasFailed(newRole)) {
|
||||||
|
this.logger.warn(newRole.getReason());
|
||||||
|
throw new InternalServerErrorException('Could not create role');
|
||||||
|
}
|
||||||
|
|
||||||
|
return newRole;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/delete')
|
||||||
|
deleteRole(@Body() role: RoleDeleteRequest) {
|
||||||
|
const deletedRole = this.rolesService.delete(role.name);
|
||||||
|
if (HasFailed(deletedRole)) {
|
||||||
|
this.logger.warn(deletedRole.getReason());
|
||||||
|
throw new InternalServerErrorException('Could not delete role');
|
||||||
|
}
|
||||||
|
|
||||||
|
return deletedRole;
|
||||||
|
}
|
||||||
|
}
|
||||||
9
backend/src/routes/api/roles/roles.module.ts
Normal file
9
backend/src/routes/api/roles/roles.module.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { RolesModule } from '../../../collections/roledb/roledb.module';
|
||||||
|
import { RolesController } from './roles.controller';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [RolesModule],
|
||||||
|
controllers: [RolesController],
|
||||||
|
})
|
||||||
|
export class RolesApiModule {}
|
||||||
@@ -66,3 +66,9 @@ export class AuthMeResponse {
|
|||||||
@IsDefined()
|
@IsDefined()
|
||||||
newJwtToken: string;
|
newJwtToken: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class AuthUserInfoRequest {
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
username: string;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { IsDefined } from 'class-validator';
|
import { IsBoolean, IsDefined } from 'class-validator';
|
||||||
|
|
||||||
export class InfoResponse {
|
export class InfoResponse {
|
||||||
|
@IsBoolean()
|
||||||
@IsDefined()
|
@IsDefined()
|
||||||
production: boolean;
|
production: boolean;
|
||||||
|
|
||||||
|
@IsBoolean()
|
||||||
@IsDefined()
|
@IsDefined()
|
||||||
demo: boolean;
|
demo: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
26
shared/src/dto/api/roles.dto.ts
Normal file
26
shared/src/dto/api/roles.dto.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { IsArray, IsEnum, IsNotEmpty, IsString } from 'class-validator';
|
||||||
|
import { ERole } from '../../entities/role.entity';
|
||||||
|
import { Permissions, PermissionsList } from '../permissions';
|
||||||
|
|
||||||
|
export class RoleInfoRequest {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsString()
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RoleUpdateRequest {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsString()
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@IsArray()
|
||||||
|
@IsEnum(PermissionsList, { each: true })
|
||||||
|
permissions: Permissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RoleCreateRequest extends ERole {}
|
||||||
|
|
||||||
|
export class RoleDeleteRequest {
|
||||||
|
@IsNotEmpty()
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ const PermissionsTuple = tuple(
|
|||||||
'user-register', // Ability to register
|
'user-register', // Ability to register
|
||||||
'user-view', // Ability to view user info, only granted if logged in
|
'user-view', // Ability to view user info, only granted if logged in
|
||||||
'user-manage',
|
'user-manage',
|
||||||
|
'role-manage',
|
||||||
'syspref-manage',
|
'syspref-manage',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { Exclude } from 'class-transformer';
|
import { Exclude } from 'class-transformer';
|
||||||
import { IsDefined, IsEnum, IsHash, IsOptional } from 'class-validator';
|
import { IsDefined, IsEnum, IsHash, IsInt, IsOptional } from 'class-validator';
|
||||||
import { SupportedMime, SupportedMimes } from '../dto/mimes.dto';
|
import { SupportedMime, SupportedMimes } from '../dto/mimes.dto';
|
||||||
|
|
||||||
export class EImage {
|
export class EImage {
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@IsInt()
|
||||||
id?: number;
|
id?: number;
|
||||||
|
|
||||||
@IsHash('sha256')
|
@IsHash('sha256')
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import { IsArray, IsEnum, IsNotEmpty, IsOptional } from 'class-validator';
|
import { IsArray, IsEnum, IsInt, IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||||
import { Permissions, PermissionsList } from '../dto/permissions';
|
import { Permissions, PermissionsList } from '../dto/permissions';
|
||||||
|
|
||||||
export class ERole {
|
export class ERole {
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@IsInt()
|
||||||
id?: number;
|
id?: number;
|
||||||
|
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
|
@IsString()
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
@IsArray()
|
@IsArray()
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { IsEnum, IsNotEmpty, IsOptional } from 'class-validator';
|
import { IsEnum, IsInt, IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||||
import { SysPreferences } from '../dto/syspreferences.dto';
|
import { SysPreferences } from '../dto/syspreferences.dto';
|
||||||
|
|
||||||
export class ESysPreference {
|
export class ESysPreference {
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@IsInt()
|
||||||
id?: number;
|
id?: number;
|
||||||
|
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@@ -10,5 +11,6 @@ export class ESysPreference {
|
|||||||
key: SysPreferences;
|
key: SysPreferences;
|
||||||
|
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
|
@IsString()
|
||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Exclude } from 'class-transformer';
|
import { Exclude } from 'class-transformer';
|
||||||
import {
|
import {
|
||||||
IsArray, IsNotEmpty,
|
IsArray, IsInt, IsNotEmpty,
|
||||||
IsOptional,
|
IsOptional,
|
||||||
IsString
|
IsString
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
@@ -8,9 +8,11 @@ import { Roles } from '../dto/roles.dto';
|
|||||||
|
|
||||||
export class EUser {
|
export class EUser {
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@IsInt()
|
||||||
id?: number;
|
id?: number;
|
||||||
|
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
|
@IsString()
|
||||||
username: string;
|
username: string;
|
||||||
|
|
||||||
@IsArray()
|
@IsArray()
|
||||||
@@ -19,5 +21,6 @@ export class EUser {
|
|||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@Exclude()
|
@Exclude()
|
||||||
|
@IsString()
|
||||||
password?: string;
|
password?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user