migrate specialusers
This commit is contained in:
parent
518391b497
commit
2ba9bb0ac2
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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'];
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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[];
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue