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 { 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) { } catch (e: any) {
return Fail(e?.message); return Fail(e?.message);
} }

View File

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

View File

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

View File

@@ -50,7 +50,7 @@ export class MainAuthGuard extends AuthGuard(['jwt', 'guest']) {
} }
// These are the permissions the user has // 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)) { if (HasFailed(userPermissions)) {
this.logger.warn('User Permissions: ' + userPermissions.getReason()); this.logger.warn('User Permissions: ' + userPermissions.getReason());
throw new InternalServerErrorException(); throw new InternalServerErrorException();

View File

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

View File

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

View File

@@ -78,9 +78,9 @@ export class UserManageController {
@Post('delete') @Post('delete')
async delete( async delete(
@Body() deleteData: UserDeleteRequest, @Body() body: UserDeleteRequest,
): Promise<UserDeleteResponse> { ): Promise<UserDeleteResponse> {
const user = await this.usersService.delete(deleteData.username); const user = await this.usersService.delete(body.id);
if (HasFailed(user)) { if (HasFailed(user)) {
this.logger.warn(user.getReason()); this.logger.warn(user.getReason());
throw new InternalServerErrorException('Could not delete user'); throw new InternalServerErrorException('Could not delete user');
@@ -91,7 +91,7 @@ export class UserManageController {
@Post('info') @Post('info')
async getUser(@Body() body: UserInfoRequest): Promise<UserInfoResponse> { 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)) { if (HasFailed(user)) {
this.logger.warn(user.getReason()); this.logger.warn(user.getReason());
throw new InternalServerErrorException('Could not find user'); throw new InternalServerErrorException('Could not find user');
@@ -104,14 +104,14 @@ export class UserManageController {
async setPermissions( async setPermissions(
@Body() body: UserUpdateRequest, @Body() body: UserUpdateRequest,
): Promise<UserUpdateResponse> { ): Promise<UserUpdateResponse> {
let user = await this.usersService.findOne(body.username); let user = await this.usersService.findOne(body.id);
if (HasFailed(user)) { if (HasFailed(user)) {
this.logger.warn(user.getReason()); this.logger.warn(user.getReason());
throw new InternalServerErrorException('Could not find user'); throw new InternalServerErrorException('Could not find user');
} }
if (body.roles) { if (body.roles) {
user = await this.usersService.setRoles(user, body.roles); user = await this.usersService.setRoles(user.id, body.roles);
if (HasFailed(user)) { if (HasFailed(user)) {
this.logger.warn(user.getReason()); this.logger.warn(user.getReason());
throw new InternalServerErrorException('Could not update user'); throw new InternalServerErrorException('Could not update user');
@@ -119,7 +119,7 @@ export class UserManageController {
} }
if (body.password) { if (body.password) {
user = await this.usersService.updatePassword(user, body.password); user = await this.usersService.updatePassword(user.id, body.password);
if (HasFailed(user)) { if (HasFailed(user)) {
this.logger.warn(user.getReason()); this.logger.warn(user.getReason());
throw new InternalServerErrorException('Could not update user'); 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 { FormControl } from '@angular/forms';
import { FullUserModel } from '../forms-dto/fulluser.dto'; import { UserCreateRequest, UserUpdateRequest } from 'picsur-shared/dist/dto/api/usermanage.dto';
import { import {
CreatePasswordError, CreatePasswordError,
CreateUsernameError, CreateUsernameError,
@@ -8,6 +8,7 @@ import {
} from '../validators/user.validator'; } from '../validators/user.validator';
export class UpdateUserControl { export class UpdateUserControl {
private id: string = '';
public username = new FormControl('', UsernameValidators); public username = new FormControl('', UsernameValidators);
public password = new FormControl('', PasswordValidators); public password = new FormControl('', PasswordValidators);
public roles = new FormControl([]); public roles = new FormControl([]);
@@ -30,6 +31,10 @@ export class UpdateUserControl {
// Data interaction // Data interaction
public putId(id: string) {
this.id = id;
}
public putUsername(username: string) { public putUsername(username: string) {
this.username.setValue(username); this.username.setValue(username);
} }
@@ -38,11 +43,18 @@ export class UpdateUserControl {
this.roles.setValue(roles); this.roles.setValue(roles);
} }
public getData(): FullUserModel { public getDataCreate(): UserCreateRequest {
return { return {
username: this.username.value, username: this.username.value,
password: this.password.value, password: this.password.value,
roles: this.selectedRoles, 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); const result = await this.rolesService.deleteRole(role.name);
if (HasFailed(result)) { if (HasFailed(result)) {
this.utilService.showSnackBar( this.utilService.showSnackBar(
'Failed to delete user', 'Failed to delete role',
SnackBarType.Error SnackBarType.Error
); );
} else { } 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() { private async initUser() {
const username = this.route.snapshot.paramMap.get('username'); const uuid = this.route.snapshot.paramMap.get('uuid');
// Get special roles // Get special roles
const SpecialRoles = await this.staticInfo.getSpecialRoles(); const SpecialRoles = await this.staticInfo.getSpecialRoles();
this.soulBoundRoles = SpecialRoles.SoulBoundRoles; this.soulBoundRoles = SpecialRoles.SoulBoundRoles;
// Check if edit or add // Check if edit or add
if (!username) { if (!uuid) {
this.mode = EditMode.add; this.mode = EditMode.add;
this.model.putRoles(SpecialRoles.DefaultRoles); this.model.putRoles(SpecialRoles.DefaultRoles);
@@ -76,10 +76,10 @@ export class SettingsUsersEditComponent implements OnInit {
// Set known data // Set known data
this.mode = EditMode.edit; this.mode = EditMode.edit;
this.model.putUsername(username); this.model.putId(uuid);
// Fetch more data // Fetch more data
const user = await this.userManageService.getUser(username); const user = await this.userManageService.getUser(uuid);
if (HasFailed(user)) { if (HasFailed(user)) {
this.utilService.showSnackBar('Failed to get user', SnackBarType.Error); this.utilService.showSnackBar('Failed to get user', SnackBarType.Error);
return; return;
@@ -128,9 +128,8 @@ export class SettingsUsersEditComponent implements OnInit {
} }
async updateUser() { async updateUser() {
const data = this.model.getData();
if (this.adding) { if (this.adding) {
const data = this.model.getDataCreate();
const resultUser = await this.userManageService.createUser(data); const resultUser = await this.userManageService.createUser(data);
if (HasFailed(resultUser)) { if (HasFailed(resultUser)) {
this.utilService.showSnackBar( this.utilService.showSnackBar(
@@ -142,13 +141,10 @@ export class SettingsUsersEditComponent implements OnInit {
this.utilService.showSnackBar('User created', SnackBarType.Success); this.utilService.showSnackBar('User created', SnackBarType.Success);
} else { } else {
const updateData = data.password const data = this.model.getDataUpdate();
? data if (!data.password) delete data.password;
: { username: data.username, roles: data.roles };
const resultUser = await this.userManageService.updateUser( const resultUser = await this.userManageService.updateUser(data);
updateData as any
);
if (HasFailed(resultUser)) { if (HasFailed(resultUser)) {
this.utilService.showSnackBar( this.utilService.showSnackBar(
'Failed to update user', 'Failed to update user',
@@ -167,7 +163,9 @@ export class SettingsUsersEditComponent implements OnInit {
if (this.adding) { if (this.adding) {
return false; return false;
} else { } 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) { 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) { public async deleteUser(user: EUser) {
@@ -73,7 +73,7 @@ export class SettingsUsersComponent implements OnInit {
}); });
if (pressedButton === 'delete') { if (pressedButton === 'delete') {
const result = await this.userManageService.deleteUser(user.username); const result = await this.userManageService.deleteUser(user.id);
if (HasFailed(result)) { if (HasFailed(result)) {
this.utilService.showSnackBar( this.utilService.showSnackBar(
'Failed to delete user', 'Failed to delete user',

View File

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

View File

@@ -63,7 +63,7 @@ export class RegisterComponent implements OnInit {
} }
if (!this.userService.isLoggedIn) { 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)) { if (HasFailed(loginResult)) {
this.logger.warn(loginResult.getReason()); this.logger.warn(loginResult.getReason());
this.utilService.showSnackBar( this.utilService.showSnackBar(
@@ -71,11 +71,16 @@ export class RegisterComponent implements OnInit {
SnackBarType.Error SnackBarType.Error
); );
} }
} else {
this.utilService.showSnackBar( this.utilService.showSnackBar(
'Register successful', 'Register successful',
SnackBarType.Success SnackBarType.Success
); );
} else {
this.utilService.showSnackBar(
'Register successful, did not log in',
SnackBarType.Success
);
} }
this.router.navigate(['/']); this.router.navigate(['/']);

View File

@@ -53,8 +53,6 @@ export class InfoService {
const serverVersion = info.version; const serverVersion = info.version;
const clientVersion = this.getFrontendVersion(); const clientVersion = this.getFrontendVersion();
console.log(serverVersion, clientVersion);
if (!isSemVer(serverVersion) || !isSemVer(clientVersion)) { if (!isSemVer(serverVersion) || !isSemVer(clientVersion)) {
return Fail(`Not a valid semver: ${serverVersion} or ${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'; } from 'picsur-shared/dist/dto/api/usermanage.dto';
import { EUser } from 'picsur-shared/dist/entities/user.entity'; import { EUser } from 'picsur-shared/dist/entities/user.entity';
import { AsyncFailable, Open } from 'picsur-shared/dist/types'; import { AsyncFailable, Open } from 'picsur-shared/dist/types';
import { FullUserModel } from 'src/app/models/forms-dto/fulluser.dto';
import { ApiService } from './api.service'; import { ApiService } from './api.service';
@Injectable({ @Injectable({
@@ -22,12 +21,12 @@ import { ApiService } from './api.service';
export class UserManageService { export class UserManageService {
constructor(private api: ApiService) {} constructor(private api: ApiService) {}
public async getUser(username: string): AsyncFailable<EUser> { public async getUser(id: string): AsyncFailable<EUser> {
return await this.api.post( return await this.api.post(
UserInfoRequest, UserInfoRequest,
UserInfoResponse, UserInfoResponse,
'api/user/info', 'api/user/info',
{ username } { id }
); );
} }
@@ -45,7 +44,7 @@ export class UserManageService {
return Open(result, 'users'); return Open(result, 'users');
} }
public async createUser(user: FullUserModel): AsyncFailable<EUser> { public async createUser(user: UserCreateRequest): AsyncFailable<EUser> {
return await this.api.post( return await this.api.post(
UserCreateRequest, UserCreateRequest,
UserCreateResponse, 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( return await this.api.post(
UserUpdateRequest, UserUpdateRequest,
UserUpdateResponse, 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( return await this.api.post(
UserDeleteRequest, UserDeleteRequest,
UserDeleteResponse, UserDeleteResponse,
'/api/user/delete', '/api/user/delete',
{ username } { id }
); );
} }
} }

View File

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

View File

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