Allow disabling fields, add auth checks with logout, don't allow self-unadmining

This commit is contained in:
Norbi Peti 2022-03-02 23:36:17 +01:00
parent 079449d980
commit 1f45a3fb84
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
7 changed files with 54 additions and 16 deletions

View file

@ -118,6 +118,7 @@ export class UserController {
description: 'User model count',
content: {'application/json': {schema: CountSchema}},
})
@authenticate('jwt')
async count(
@param.where(User) where?: Where<User>,
): Promise<Count> {
@ -136,6 +137,7 @@ export class UserController {
},
},
})
@authenticate('jwt')
async find(
@param.filter(User) filter?: Filter<User>,
): Promise<User[]> {
@ -147,6 +149,7 @@ export class UserController {
description: 'User PATCH success count',
content: {'application/json': {schema: CountSchema}},
})
@authenticate('jwt')
async updateAll(
@requestBody({
content: {
@ -170,6 +173,7 @@ export class UserController {
},
},
})
@authenticate('jwt')
async findById(
@param.path.number('id') id: number,
@param.filter(User, {exclude: 'where'}) filter?: FilterExcludingWhere<User>
@ -181,6 +185,7 @@ export class UserController {
@response(204, {
description: 'User PATCH success',
})
@authenticate('jwt')
async updateById(
@param.path.number('id') id: number,
@requestBody({
@ -199,6 +204,7 @@ export class UserController {
@response(204, {
description: 'User DELETE success',
})
@authenticate('jwt')
async deleteById(@param.path.number('id') id: number): Promise<void> {
await this.userRepository.deleteById(id);
}

View file

@ -2,20 +2,28 @@ import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { environment } from '../environments/environment';
import { LoginService } from './auth/login.service';
import { Router } from '@angular/router';
@Injectable({
providedIn: 'root'
})
export class ApiService {
constructor(private http: HttpClient, private loginService: LoginService) {
constructor(private http: HttpClient, private loginService: LoginService, private router: Router) {
}
request(method: 'post' | 'get' | 'delete' | 'patch', url: string, body: any): Promise<any> {
return this.http.request(method, environment.backendUrl + url, {
body,
headers: {Authorization: 'Bearer ' + this.loginService.token}
}).toPromise();
}).toPromise().catch(e => {
if (e.status === 401) {
this.loginService.deleteToken();
return this.router.navigateByUrl('/auth/login');
} else {
throw e;
}
});
}
requestPage<T>(url: string, limit: number, page: number): Promise<T[]> {
@ -32,7 +40,6 @@ export class ApiService {
async logout(): Promise<void> {
await this.request('post', '/users/logout', '');
this.loginService.token = null;
this.loginService.user = null;
this.loginService.deleteToken();
}
}

View file

@ -8,12 +8,20 @@ import { User } from '../model/user.model';
})
export class LoginService {
token: string;
user: User;
private tokenP: string;
private userP: User;
get token(): string {
return this.tokenP;
}
get user(): User {
return this.userP;
}
constructor(private http: HttpClient) {
this.token = window.localStorage.getItem('token');
this.user = JSON.parse(window.localStorage.getItem('user'));
this.tokenP = window.localStorage.getItem('token');
this.userP = JSON.parse(window.localStorage.getItem('user'));
}
async createUser(email: string, password: string, name: string): Promise<void> {
@ -26,8 +34,8 @@ export class LoginService {
email,
password
}).toPromise();
this.token = resp.token;
this.user = resp.user;
this.tokenP = resp.token;
this.userP = resp.user;
window.localStorage.setItem('token', resp.token);
window.localStorage.setItem('user', JSON.stringify(resp.user));
return true;
@ -38,4 +46,11 @@ export class LoginService {
throw e;
}
}
deleteToken(): void {
this.tokenP = null;
this.userP = null;
window.localStorage.removeItem('token');
window.localStorage.removeItem('user');
}
}

View file

@ -3,7 +3,9 @@
<div *ngFor="let field of fields">
<mat-label>{{ field.title }}</mat-label>
<span [ngSwitch]="getType(item[field.name])">
<span *ngSwitchCase="'boolean'"><mat-checkbox [checked]="item[field.name]" [formControlName]="field.name"></mat-checkbox></span>
<span *ngSwitchCase="'boolean'">
<mat-checkbox [checked]="item[field.name]" [formControlName]="field.name"></mat-checkbox>
</span>
<mat-form-field *ngSwitchDefault>
<input matInput [formControlName]="field.name" type="text" [value]="item[field.name]"/>
</mat-form-field>

View file

@ -16,7 +16,7 @@ export class EditComponent<T extends Model> implements OnInit {
isLoading = true;
@Input() apiPath: string;
@Input() fields: { title: string, name: keyof T }[];
@Input() fields: { title: string, name: keyof T, readonly?: (item: T) => boolean }[];
@Input() itemType: Type<T>;
formGroup: FormGroup;
@ -30,7 +30,13 @@ export class EditComponent<T extends Model> implements OnInit {
if (!this.item && url[url.length - 1].path !== 'new') {
this.item = await this.api.request('get', this.apiPath + '/' + this.route.snapshot.url[this.route.snapshot.url.length - 1], {});
}
this.formGroup = this.fb.group(this.fields.reduce((pv, cv) => Object.assign(pv, {[cv.name]: new FormControl()}), {}));
this.formGroup = this.fb.group(this.fields.reduce((pv, cv) => {
const control = new FormControl();
if (cv.readonly && cv.readonly(this.item)) {
control.disable();
}
return Object.assign(pv, {[cv.name]: control});
}, {}));
if (this.item) {
this.formGroup.patchValue(this.item);
} else {
@ -48,7 +54,7 @@ export class EditComponent<T extends Model> implements OnInit {
} else {
await this.api.request('post', this.apiPath, this.formGroup.value);
}
await this.router.navigateByUrl(this.router.url.substring(0, this.router.url.lastIndexOf('/')));
await this.router.navigate(this.route.parent.snapshot.url.map(segment => segment.path));
} catch (e) {
alert(e.message);
}

View file

@ -1,5 +1,5 @@
<app-edit [apiPath]="'/users'" [itemType]="itemType" [fields]="[
{title: 'E-mail', name: 'email'},
{title: 'Név', name: 'name'},
{title: 'Admin', name: 'isAdmin'}
{title: 'Admin', name: 'isAdmin', readonly: isEditingSelf}
]"></app-edit>

View file

@ -1,5 +1,6 @@
import { Component, OnInit } from '@angular/core';
import { User } from '../../model/user.model';
import { LoginService } from '../../auth/login.service';
@Component({
selector: 'app-user-edit',
@ -8,8 +9,9 @@ import { User } from '../../model/user.model';
})
export class UserEditComponent implements OnInit {
itemType = User;
isEditingSelf = user => user.id === this.userService.user.id;
constructor() { }
constructor(private userService: LoginService) { }
ngOnInit(): void {
}