migrate specialusers

This commit is contained in:
rubikscraft 2022-03-24 21:28:52 +01:00
parent 518391b497
commit 2ba9bb0ac2
No known key found for this signature in database
GPG key ID: 1463EBE9200A5CD4
7 changed files with 105 additions and 17 deletions

View file

@ -2,11 +2,6 @@ import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import * as bcrypt from 'bcrypt';
import { plainToClass } from 'class-transformer';
import {
LockedLoginUsersList,
LockedPermsUsersList,
SystemUsersList
} from 'picsur-shared/dist/dto/specialusers.dto';
import {
AsyncFailable,
Fail,
@ -19,6 +14,7 @@ import {
DefaultRolesList,
SoulBoundRolesList
} from '../../models/dto/roles.dto';
import { ImmutableUsersList, LockedLoginUsersList, UndeletableUsersList } from '../../models/dto/specialusers.dto';
import { EUserBackend } from '../../models/entities/user.entity';
import { GetCols } from '../collectionutils';
import { RolesService } from '../roledb/roledb.service';
@ -74,7 +70,7 @@ export class UsersService {
const userToModify = await this.resolve(user);
if (HasFailed(userToModify)) return userToModify;
if (SystemUsersList.includes(userToModify.username)) {
if (UndeletableUsersList.includes(userToModify.username)) {
return Fail('Cannot delete system user');
}
@ -94,7 +90,7 @@ export class UsersService {
const userToModify = await this.resolve(user);
if (HasFailed(userToModify)) return userToModify;
if (LockedPermsUsersList.includes(userToModify.username)) {
if (ImmutableUsersList.includes(userToModify.username)) {
// Just fail silently
return userToModify;
}

View file

@ -1,8 +1,8 @@
// Cannot be deleted
export const SystemUsersList = ['guest', 'admin'];
export const UndeletableUsersList = ['guest', 'admin'];
// Cannot have different permissions
export const LockedPermsUsersList = ['admin'];
export const ImmutableUsersList = ['admin'];
// Cannot login
export const LockedLoginUsersList = ['guest'];

View file

@ -6,7 +6,9 @@ import {
Logger,
Post
} from '@nestjs/common';
import { plainToClass } from 'class-transformer';
import {
GetSpecialUsersResponse,
UserCreateRequest,
UserCreateResponse,
UserDeleteRequest,
@ -22,6 +24,7 @@ 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 { ImmutableUsersList, LockedLoginUsersList, UndeletableUsersList } from '../../../models/dto/specialusers.dto';
@Controller('api/user')
@RequiredPermissions(Permission.UserManage)
@ -125,4 +128,15 @@ export class UserManageController {
return user;
}
@Get('special')
async getSpecial(): Promise<GetSpecialUsersResponse> {
const result: GetSpecialUsersResponse = {
ImmutableUsersList,
LockedLoginUsersList,
UndeletableUsersList,
};
return plainToClass(GetSpecialUsersResponse, result);
}
}

View file

@ -4,7 +4,6 @@ import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MatChipInputEvent } from '@angular/material/chips';
import { ActivatedRoute, Router } from '@angular/router';
import { UIFriendlyPermissions } from 'picsur-shared/dist/dto/permissions';
import { LockedPermsUsersList } from 'picsur-shared/dist/dto/specialusers.dto';
import { HasFailed } from 'picsur-shared/dist/types';
import { UpdateUserControl } from 'src/app/models/forms/updateuser.control';
import { SnackBarType } from 'src/app/models/snack-bar-type';
@ -23,6 +22,8 @@ enum EditMode {
styleUrls: ['./settings-users-edit.component.scss'],
})
export class SettingsUsersEditComponent implements OnInit {
private ImmutableUsersList: string[] = [];
readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
private mode: EditMode = EditMode.edit;
@ -73,6 +74,10 @@ export class SettingsUsersEditComponent implements OnInit {
this.model.putUsername(user.username);
this.model.putRoles(user.roles);
const { ImmutableUsersList } =
await this.userManageService.getSpecialRolesOptimistic();
this.ImmutableUsersList = ImmutableUsersList;
}
private async initRoles() {
@ -148,7 +153,7 @@ export class SettingsUsersEditComponent implements OnInit {
if (this.adding) {
return false;
} else {
return LockedPermsUsersList.includes(this.model.getData().username);
return this.ImmutableUsersList.includes(this.model.getData().username);
}
}
}

View file

@ -2,7 +2,6 @@ import { Component, OnInit, ViewChild } from '@angular/core';
import { MatPaginator, PageEvent } from '@angular/material/paginator';
import { Router } from '@angular/router';
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
import { SystemUsersList } from 'picsur-shared/dist/dto/specialusers.dto';
import { EUser } from 'picsur-shared/dist/entities/user.entity';
import { HasFailed } from 'picsur-shared/dist/types';
import { BehaviorSubject, Subject, throttleTime } from 'rxjs';
@ -15,6 +14,8 @@ import { UtilService } from 'src/app/util/util.service';
styleUrls: ['./settings-users.component.scss'],
})
export class SettingsUsersComponent implements OnInit {
private UndeletableUsersList: string[] = [];
public readonly displayedColumns: string[] = ['username', 'roles', 'actions'];
public readonly pageSizeOptions: number[] = [5, 10, 25, 100];
public readonly startingPageSize = this.pageSizeOptions[2];
@ -31,9 +32,13 @@ export class SettingsUsersComponent implements OnInit {
private router: Router
) {}
async ngOnInit() {
ngOnInit() {
this.subscribeToUpdate();
this.fetchUsers(this.startingPageSize, 0);
Promise.all([
this.fetchUsers(this.startingPageSize, 0),
this.initSpecialUsers(),
]).catch(console.error);
}
public addUser() {
@ -123,7 +128,20 @@ export class SettingsUsersComponent implements OnInit {
return false;
}
private async initSpecialUsers() {
const specialUsers = await this.userManageService.getSpecialUsers();
if (HasFailed(specialUsers)) {
this.utilService.showSnackBar(
'Failed to fetch special users',
SnackBarType.Error
);
return;
}
this.UndeletableUsersList = specialUsers.UndeletableUsersList;
}
isSystem(user: EUser): boolean {
return SystemUsersList.includes(user.username);
return this.UndeletableUsersList.includes(user.username);
}
}

View file

@ -1,5 +1,6 @@
import { Injectable } from '@angular/core';
import {
GetSpecialUsersResponse,
UserCreateRequest,
UserCreateResponse,
UserDeleteRequest,
@ -15,12 +16,16 @@ import { EUser } from 'picsur-shared/dist/entities/user.entity';
import { AsyncFailable, HasFailed } from 'picsur-shared/dist/types';
import { FullUserModel } from 'src/app/models/forms/fulluser.model';
import { ApiService } from './api.service';
import { CacheService } from './cache.service';
@Injectable({
providedIn: 'root',
})
export class UserManageService {
constructor(private apiService: ApiService) {}
constructor(
private apiService: ApiService,
private cacheService: CacheService
) {}
public async getUser(username: string): AsyncFailable<EUser> {
const body = {
@ -93,4 +98,38 @@ export class UserManageService {
return result;
}
public async getSpecialUsers(): AsyncFailable<GetSpecialUsersResponse> {
const cached =
this.cacheService.get<GetSpecialUsersResponse>('specialUsers');
if (cached !== null) {
return cached;
}
const result = await this.apiService.get(
GetSpecialUsersResponse,
'/api/user/special'
);
if (HasFailed(result)) {
return result;
}
this.cacheService.set('specialRoles', result);
return result;
}
public async getSpecialRolesOptimistic(): Promise<GetSpecialUsersResponse> {
const result = await this.getSpecialUsers();
if (HasFailed(result)) {
return {
ImmutableUsersList: [],
LockedLoginUsersList: [],
UndeletableUsersList: [],
};
}
return result;
}
}

View file

@ -2,7 +2,8 @@ import { Type } from 'class-transformer';
import {
IsArray,
IsDefined,
IsOptional, ValidateNested
IsOptional,
ValidateNested
} from 'class-validator';
import { EUser, NamePassUser, UsernameUser } from '../../entities/user.entity';
import { IsPosInt } from '../../validators/positive-int.validator';
@ -60,3 +61,18 @@ export class UserUpdateRequest extends UsernameUser {
}
export class UserUpdateResponse extends EUser {}
// GetSpecialUsers
export class GetSpecialUsersResponse {
@IsDefined()
@IsStringList()
UndeletableUsersList: string[];
@IsDefined()
@IsStringList()
ImmutableUsersList: string[];
@IsDefined()
@IsStringList()
LockedLoginUsersList: string[];
}