mirror of
https://github.com/CaramelFur/Picsur.git
synced 2025-11-13 23:35:39 +01:00
add role api
This commit is contained in:
@@ -35,8 +35,8 @@ export class RolesModule implements OnModuleInit {
|
||||
}
|
||||
|
||||
private async nukeRoles() {
|
||||
this.logger.error('Nuking all roles');
|
||||
const result = this.rolesService.nuke(true);
|
||||
this.logger.error('Nuking system roles');
|
||||
const result = this.rolesService.nukeSystemRoles(true);
|
||||
if (HasFailed(result)) {
|
||||
this.logger.error(`Failed to nuke roles because: ${result.getReason()}`);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
HasFailed,
|
||||
HasSuccess
|
||||
} from 'picsur-shared/dist/types';
|
||||
import { Repository } from 'typeorm';
|
||||
import { In, Repository } from 'typeorm';
|
||||
import { ERoleBackend } from '../../models/entities/role.entity';
|
||||
|
||||
@Injectable()
|
||||
@@ -79,7 +79,7 @@ export class RolesService {
|
||||
public async addPermissions(
|
||||
role: string | ERoleBackend,
|
||||
permissions: Permissions,
|
||||
): AsyncFailable<true> {
|
||||
): AsyncFailable<ERoleBackend> {
|
||||
const roleToModify = await this.resolve(role);
|
||||
if (HasFailed(roleToModify)) return roleToModify;
|
||||
|
||||
@@ -94,7 +94,7 @@ export class RolesService {
|
||||
public async removePermissions(
|
||||
role: string | ERoleBackend,
|
||||
permissions: Permissions,
|
||||
): AsyncFailable<true> {
|
||||
): AsyncFailable<ERoleBackend> {
|
||||
const roleToModify = await this.resolve(role);
|
||||
if (HasFailed(roleToModify)) return roleToModify;
|
||||
|
||||
@@ -109,7 +109,7 @@ export class RolesService {
|
||||
role: string | ERoleBackend,
|
||||
permissions: Permissions,
|
||||
allowImmutable: boolean = false,
|
||||
): AsyncFailable<true> {
|
||||
): AsyncFailable<ERoleBackend> {
|
||||
const roleToModify = await this.resolve(role);
|
||||
if (HasFailed(roleToModify)) return roleToModify;
|
||||
|
||||
@@ -120,12 +120,10 @@ export class RolesService {
|
||||
roleToModify.permissions = permissions;
|
||||
|
||||
try {
|
||||
await this.rolesRepository.save(roleToModify);
|
||||
return await this.rolesRepository.save(roleToModify);
|
||||
} catch (e: any) {
|
||||
return Fail(e?.message);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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> {
|
||||
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');
|
||||
try {
|
||||
await this.rolesRepository.delete({});
|
||||
await this.rolesRepository.delete({
|
||||
name: In(SystemRolesList),
|
||||
});
|
||||
} catch (e: any) {
|
||||
return Fail(e?.message);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
import { ExperimentModule } from './experiment/experiment.module';
|
||||
import { PrefModule } from './pref/pref.module';
|
||||
import { InfoModule } from './info/info.module';
|
||||
import { PrefModule } from './pref/pref.module';
|
||||
import { RolesApiModule } from './roles/roles.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -10,6 +11,7 @@ import { InfoModule } from './info/info.module';
|
||||
PrefModule,
|
||||
ExperimentModule,
|
||||
InfoModule,
|
||||
RolesApiModule,
|
||||
]
|
||||
})
|
||||
export class PicsurApiModule {}
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
Post,
|
||||
Request
|
||||
} from '@nestjs/common';
|
||||
import { AuthUserInfoRequest } from 'picsur-shared/dist/dto/api/auth.dto';
|
||||
import {
|
||||
AuthDeleteRequest,
|
||||
AuthLoginResponse,
|
||||
@@ -15,7 +16,10 @@ import {
|
||||
} from 'picsur-shared/dist/dto/auth.dto';
|
||||
import { HasFailed } from 'picsur-shared/dist/types';
|
||||
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 AuthFasityRequest from '../../../models/dto/authrequest.dto';
|
||||
|
||||
@@ -75,6 +79,18 @@ export class AuthController {
|
||||
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')
|
||||
@RequiredPermissions('user-manage')
|
||||
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()
|
||||
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 {
|
||||
@IsBoolean()
|
||||
@IsDefined()
|
||||
production: boolean;
|
||||
|
||||
@IsBoolean()
|
||||
@IsDefined()
|
||||
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-view', // Ability to view user info, only granted if logged in
|
||||
'user-manage',
|
||||
'role-manage',
|
||||
'syspref-manage',
|
||||
);
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
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';
|
||||
|
||||
export class EImage {
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
id?: number;
|
||||
|
||||
@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';
|
||||
|
||||
export class ERole {
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
id?: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
name: string;
|
||||
|
||||
@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';
|
||||
|
||||
export class ESysPreference {
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
id?: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@@ -10,5 +11,6 @@ export class ESysPreference {
|
||||
key: SysPreferences;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
value: string;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Exclude } from 'class-transformer';
|
||||
import {
|
||||
IsArray, IsNotEmpty,
|
||||
IsArray, IsInt, IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString
|
||||
} from 'class-validator';
|
||||
@@ -8,9 +8,11 @@ import { Roles } from '../dto/roles.dto';
|
||||
|
||||
export class EUser {
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
id?: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
username: string;
|
||||
|
||||
@IsArray()
|
||||
@@ -19,5 +21,6 @@ export class EUser {
|
||||
|
||||
@IsOptional()
|
||||
@Exclude()
|
||||
@IsString()
|
||||
password?: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user