addres users by their id

This commit is contained in:
rubikscraft
2022-04-01 13:18:05 +02:00
parent 9bcf09ff4f
commit 7c7e23696d
18 changed files with 99 additions and 90 deletions

View File

@@ -52,7 +52,10 @@ export class RolesService {
}
try {
return await this.rolesRepository.remove(roleToModify);
// Makes sure we can return the id
const cloned = plainToClass(ERoleBackend, roleToModify);
await this.rolesRepository.remove(roleToModify);
return cloned;
} catch (e: any) {
return Fail(e?.message);
}

View File

@@ -10,7 +10,6 @@ import {
HasSuccess
} from 'picsur-shared/dist/types';
import { makeUnique } from 'picsur-shared/dist/util/unique';
import { strictValidate } from 'picsur-shared/dist/util/validate';
import { Repository } from 'typeorm';
import { Permissions } from '../../models/dto/permissions.dto';
import {
@@ -74,10 +73,8 @@ export class UsersService {
return plainToClass(EUserBackend, user);
}
public async delete(
user: string | EUserBackend,
): AsyncFailable<EUserBackend> {
const userToModify = await this.resolve(user);
public async delete(uuid: string): AsyncFailable<EUserBackend> {
const userToModify = await this.findOne(uuid);
if (HasFailed(userToModify)) return userToModify;
if (UndeletableUsersList.includes(userToModify.username)) {
@@ -85,7 +82,10 @@ export class UsersService {
}
try {
return await this.usersRepository.remove(userToModify);
// Makes sure we can return the id
const cloned = plainToClass(EUserBackend, userToModify);
await this.usersRepository.remove(userToModify);
return cloned;
} catch (e: any) {
return Fail(e?.message);
}
@@ -94,10 +94,10 @@ export class UsersService {
// Updating
public async setRoles(
user: string | EUserBackend,
uuid: string,
roles: string[],
): AsyncFailable<EUserBackend> {
const userToModify = await this.resolve(user);
const userToModify = await this.findOne(uuid);
if (HasFailed(userToModify)) return userToModify;
if (ImmutableUsersList.includes(userToModify.username)) {
@@ -138,20 +138,18 @@ export class UsersService {
return true;
}
public async getPermissions(
user: string | EUserBackend,
): AsyncFailable<Permissions> {
const userToModify = await this.resolve(user);
public async getPermissions(uuid: string): AsyncFailable<Permissions> {
const userToModify = await this.findOne(uuid);
if (HasFailed(userToModify)) return userToModify;
return await this.rolesService.getPermissions(userToModify.roles);
}
public async updatePassword(
user: string | EUserBackend,
uuid: string,
password: string,
): AsyncFailable<EUserBackend> {
let userToModify = await this.resolve(user);
let userToModify = await this.findOne(uuid);
if (HasFailed(userToModify)) return userToModify;
const strength = await this.getBCryptStrength();
@@ -174,7 +172,7 @@ export class UsersService {
username: string,
password: string,
): AsyncFailable<EUserBackend> {
const user = await this.findOne(username, true);
const user = await this.findByUsername(username, true);
if (HasFailed(user)) return user;
if (LockedLoginUsersList.includes(user.username)) {
@@ -185,12 +183,12 @@ export class UsersService {
if (!(await bcrypt.compare(password, user.password)))
return Fail('Wrong password');
return await this.findOne(username);
return await this.findOne(user.id);
}
// Listing
public async findOne<B extends true | undefined = undefined>(
public async findByUsername<B extends true | undefined = undefined>(
username: string,
// Also fetch fields that aren't normally sent to the client
// (e.g. hashed password)
@@ -213,6 +211,19 @@ export class UsersService {
}
}
public async findOne(uuid: string): AsyncFailable<EUserBackend> {
try {
const found = await this.usersRepository.findOne({
where: { id: uuid },
});
if (!found) return Fail('User not found');
return found as EUserBackend;
} catch (e: any) {
return Fail(e?.message);
}
}
public async findMany(
count: number,
page: number,
@@ -231,26 +242,10 @@ export class UsersService {
}
public async exists(username: string): Promise<boolean> {
return HasSuccess(await this.findOne(username));
return HasSuccess(await this.findByUsername(username));
}
// Internal resolver
private async resolve(
user: string | EUserBackend,
): AsyncFailable<EUserBackend> {
if (typeof user === 'string') {
return await this.findOne(user);
} else {
user = plainToClass(EUserBackend, user);
const errors = await strictValidate(user);
if (errors.length > 0) {
this.logger.warn(errors);
return Fail('Invalid user');
}
return user;
}
}
// Internal
private filterAddedRoles(roles: string[]): string[] {
const filteredRoles = roles.filter(

View File

@@ -15,7 +15,6 @@ export class LocalAuthStrategy extends PassportStrategy(Strategy, 'local') {
username: string,
password: string,
): AsyncFailable<EUserBackend> {
// All this does is call the usersservice authenticate for authentication
const user = await this.usersService.authenticate(username, password);
if (HasFailed(user)) {

View File

@@ -50,7 +50,7 @@ export class MainAuthGuard extends AuthGuard(['jwt', 'guest']) {
}
// These are the permissions the user has
const userPermissions = await this.usersService.getPermissions(user);
const userPermissions = await this.usersService.getPermissions(user.id);
if (HasFailed(userPermissions)) {
this.logger.warn('User Permissions: ' + userPermissions.getReason());
throw new InternalServerErrorException();

View File

@@ -14,7 +14,7 @@ export class GuestService {
}
public async getGuestUser(): Promise<EUserBackend> {
const user = await this.usersService.findOne('guest');
const user = await this.usersService.findByUsername('guest');
if (HasFailed(user)) {
return this.fallBackUser;
}

View File

@@ -66,7 +66,7 @@ export class UserController {
@Get('me')
@RequiredPermissions(Permission.UserKeepLogin)
async me(@Request() req: AuthFasityRequest): Promise<UserMeResponse> {
const user = await this.usersService.findOne(req.user.username);
const user = await this.usersService.findOne(req.user.id);
if (HasFailed(user)) {
this.logger.warn(user.getReason());
@@ -88,7 +88,7 @@ export class UserController {
async refresh(
@Request() req: AuthFasityRequest,
): Promise<UserMePermissionsResponse> {
const permissions = await this.usersService.getPermissions(req.user);
const permissions = await this.usersService.getPermissions(req.user.id);
if (HasFailed(permissions)) {
this.logger.warn(permissions.getReason());
throw new InternalServerErrorException('Could not get permissions');

View File

@@ -78,9 +78,9 @@ export class UserManageController {
@Post('delete')
async delete(
@Body() deleteData: UserDeleteRequest,
@Body() body: UserDeleteRequest,
): Promise<UserDeleteResponse> {
const user = await this.usersService.delete(deleteData.username);
const user = await this.usersService.delete(body.id);
if (HasFailed(user)) {
this.logger.warn(user.getReason());
throw new InternalServerErrorException('Could not delete user');
@@ -91,7 +91,7 @@ export class UserManageController {
@Post('info')
async getUser(@Body() body: UserInfoRequest): Promise<UserInfoResponse> {
const user = await this.usersService.findOne(body.username);
const user = await this.usersService.findOne(body.id);
if (HasFailed(user)) {
this.logger.warn(user.getReason());
throw new InternalServerErrorException('Could not find user');
@@ -104,14 +104,14 @@ export class UserManageController {
async setPermissions(
@Body() body: UserUpdateRequest,
): Promise<UserUpdateResponse> {
let user = await this.usersService.findOne(body.username);
let user = await this.usersService.findOne(body.id);
if (HasFailed(user)) {
this.logger.warn(user.getReason());
throw new InternalServerErrorException('Could not find user');
}
if (body.roles) {
user = await this.usersService.setRoles(user, body.roles);
user = await this.usersService.setRoles(user.id, body.roles);
if (HasFailed(user)) {
this.logger.warn(user.getReason());
throw new InternalServerErrorException('Could not update user');
@@ -119,7 +119,7 @@ export class UserManageController {
}
if (body.password) {
user = await this.usersService.updatePassword(user, body.password);
user = await this.usersService.updatePassword(user.id, body.password);
if (HasFailed(user)) {
this.logger.warn(user.getReason());
throw new InternalServerErrorException('Could not update user');

View File

@@ -1,5 +0,0 @@
export interface FullUserModel {
username: string;
password: string;
roles: string[];
}

View File

@@ -1,5 +1,5 @@
import { FormControl } from '@angular/forms';
import { FullUserModel } from '../forms-dto/fulluser.dto';
import { UserCreateRequest, UserUpdateRequest } from 'picsur-shared/dist/dto/api/usermanage.dto';
import {
CreatePasswordError,
CreateUsernameError,
@@ -8,6 +8,7 @@ import {
} from '../validators/user.validator';
export class UpdateUserControl {
private id: string = '';
public username = new FormControl('', UsernameValidators);
public password = new FormControl('', PasswordValidators);
public roles = new FormControl([]);
@@ -30,6 +31,10 @@ export class UpdateUserControl {
// Data interaction
public putId(id: string) {
this.id = id;
}
public putUsername(username: string) {
this.username.setValue(username);
}
@@ -38,11 +43,18 @@ export class UpdateUserControl {
this.roles.setValue(roles);
}
public getData(): FullUserModel {
public getDataCreate(): UserCreateRequest {
return {
username: this.username.value,
password: this.password.value,
roles: this.selectedRoles,
};
}
public getDataUpdate(): UserUpdateRequest {
return {
...this.getDataCreate(),
id: this.id,
};
}
}

View File

@@ -80,11 +80,11 @@ export class SettingsRolesComponent implements OnInit, AfterViewInit {
const result = await this.rolesService.deleteRole(role.name);
if (HasFailed(result)) {
this.utilService.showSnackBar(
'Failed to delete user',
'Failed to delete role',
SnackBarType.Error
);
} else {
this.utilService.showSnackBar('User deleted', SnackBarType.Success);
this.utilService.showSnackBar('Role deleted', SnackBarType.Success);
}
}

View File

@@ -60,14 +60,14 @@ export class SettingsUsersEditComponent implements OnInit {
}
private async initUser() {
const username = this.route.snapshot.paramMap.get('username');
const uuid = this.route.snapshot.paramMap.get('uuid');
// Get special roles
const SpecialRoles = await this.staticInfo.getSpecialRoles();
this.soulBoundRoles = SpecialRoles.SoulBoundRoles;
// Check if edit or add
if (!username) {
if (!uuid) {
this.mode = EditMode.add;
this.model.putRoles(SpecialRoles.DefaultRoles);
@@ -76,10 +76,10 @@ export class SettingsUsersEditComponent implements OnInit {
// Set known data
this.mode = EditMode.edit;
this.model.putUsername(username);
this.model.putId(uuid);
// Fetch more data
const user = await this.userManageService.getUser(username);
const user = await this.userManageService.getUser(uuid);
if (HasFailed(user)) {
this.utilService.showSnackBar('Failed to get user', SnackBarType.Error);
return;
@@ -128,9 +128,8 @@ export class SettingsUsersEditComponent implements OnInit {
}
async updateUser() {
const data = this.model.getData();
if (this.adding) {
const data = this.model.getDataCreate();
const resultUser = await this.userManageService.createUser(data);
if (HasFailed(resultUser)) {
this.utilService.showSnackBar(
@@ -142,13 +141,10 @@ export class SettingsUsersEditComponent implements OnInit {
this.utilService.showSnackBar('User created', SnackBarType.Success);
} else {
const updateData = data.password
? data
: { username: data.username, roles: data.roles };
const data = this.model.getDataUpdate();
if (!data.password) delete data.password;
const resultUser = await this.userManageService.updateUser(
updateData as any
);
const resultUser = await this.userManageService.updateUser(data);
if (HasFailed(resultUser)) {
this.utilService.showSnackBar(
'Failed to update user',
@@ -167,7 +163,9 @@ export class SettingsUsersEditComponent implements OnInit {
if (this.adding) {
return false;
} else {
return this.ImmutableUsersList.includes(this.model.getData().username);
return this.ImmutableUsersList.includes(
this.model.getDataCreate().username
);
}
}
}

View File

@@ -51,7 +51,7 @@ export class SettingsUsersComponent implements OnInit {
}
public editUser(user: EUser) {
this.router.navigate(['/settings/users/edit', user.username]);
this.router.navigate(['/settings/users/edit', user.id]);
}
public async deleteUser(user: EUser) {
@@ -73,7 +73,7 @@ export class SettingsUsersComponent implements OnInit {
});
if (pressedButton === 'delete') {
const result = await this.userManageService.deleteUser(user.username);
const result = await this.userManageService.deleteUser(user.id);
if (HasFailed(result)) {
this.utilService.showSnackBar(
'Failed to delete user',

View File

@@ -10,7 +10,7 @@ const routes: PRoutes = [
component: SettingsUsersComponent,
},
{
path: 'edit/:username',
path: 'edit/:uuid',
component: SettingsUsersEditComponent,
},
{

View File

@@ -63,7 +63,7 @@ export class RegisterComponent implements OnInit {
}
if (!this.userService.isLoggedIn) {
const loginResult = this.userService.login(data.username, data.password);
const loginResult = await this.userService.login(data.username, data.password);
if (HasFailed(loginResult)) {
this.logger.warn(loginResult.getReason());
this.utilService.showSnackBar(
@@ -71,11 +71,16 @@ export class RegisterComponent implements OnInit {
SnackBarType.Error
);
}
} else {
this.utilService.showSnackBar(
'Register successful',
SnackBarType.Success
);
} else {
this.utilService.showSnackBar(
'Register successful, did not log in',
SnackBarType.Success
);
}
this.router.navigate(['/']);

View File

@@ -53,8 +53,6 @@ export class InfoService {
const serverVersion = info.version;
const clientVersion = this.getFrontendVersion();
console.log(serverVersion, clientVersion);
if (!isSemVer(serverVersion) || !isSemVer(clientVersion)) {
return Fail(`Not a valid semver: ${serverVersion} or ${clientVersion}`);
}

View File

@@ -13,7 +13,6 @@ import {
} from 'picsur-shared/dist/dto/api/usermanage.dto';
import { EUser } from 'picsur-shared/dist/entities/user.entity';
import { AsyncFailable, Open } from 'picsur-shared/dist/types';
import { FullUserModel } from 'src/app/models/forms-dto/fulluser.dto';
import { ApiService } from './api.service';
@Injectable({
@@ -22,12 +21,12 @@ import { ApiService } from './api.service';
export class UserManageService {
constructor(private api: ApiService) {}
public async getUser(username: string): AsyncFailable<EUser> {
public async getUser(id: string): AsyncFailable<EUser> {
return await this.api.post(
UserInfoRequest,
UserInfoResponse,
'api/user/info',
{ username }
{ id }
);
}
@@ -45,7 +44,7 @@ export class UserManageService {
return Open(result, 'users');
}
public async createUser(user: FullUserModel): AsyncFailable<EUser> {
public async createUser(user: UserCreateRequest): AsyncFailable<EUser> {
return await this.api.post(
UserCreateRequest,
UserCreateResponse,
@@ -54,7 +53,7 @@ export class UserManageService {
);
}
public async updateUser(user: FullUserModel): AsyncFailable<EUser> {
public async updateUser(user: UserUpdateRequest): AsyncFailable<EUser> {
return await this.api.post(
UserUpdateRequest,
UserUpdateResponse,
@@ -63,12 +62,12 @@ export class UserManageService {
);
}
public async deleteUser(username: string): AsyncFailable<EUser> {
public async deleteUser(id: string): AsyncFailable<EUser> {
return await this.api.post(
UserDeleteRequest,
UserDeleteResponse,
'/api/user/delete',
{ username }
{ id }
);
}
}

View File

@@ -29,7 +29,7 @@ export class RoleUpdateRequest extends RoleNamePermsObject {}
export class RoleUpdateResponse extends ERole {}
// RoleCreate
export class RoleCreateRequest extends ERole {}
export class RoleCreateRequest extends RoleNamePermsObject {}
export class RoleCreateResponse extends ERole {}
// RoleDelete

View File

@@ -5,10 +5,11 @@ import {
IsOptional,
ValidateNested
} from 'class-validator';
import { EUser, NamePassUser, UsernameUser } from '../../entities/user.entity';
import { EUser, NamePassUser } from '../../entities/user.entity';
import { IsPosInt } from '../../validators/positive-int.validator';
import { IsStringList } from '../../validators/string-list.validator';
import { IsPlainTextPwd } from '../../validators/user.validators';
import { IsPlainTextPwd, IsUsername } from '../../validators/user.validators';
import { EntityIDObject } from '../idobject.dto';
// UserList
export class UserListRequest {
@@ -42,15 +43,19 @@ export class UserCreateRequest extends NamePassUser {
export class UserCreateResponse extends EUser {}
// UserDelete
export class UserDeleteRequest extends UsernameUser {}
export class UserDeleteRequest extends EntityIDObject {}
export class UserDeleteResponse extends EUser {}
// UserInfo
export class UserInfoRequest extends UsernameUser {}
export class UserInfoRequest extends EntityIDObject {}
export class UserInfoResponse extends EUser {}
// UserUpdateRoles
export class UserUpdateRequest extends UsernameUser {
// UserUpdate
export class UserUpdateRequest extends EntityIDObject {
@IsOptional()
@IsUsername()
username?: string;
@IsOptional()
@IsStringList()
roles?: string[];