add permission handling on frontend

This commit is contained in:
rubikscraft
2022-03-12 22:37:45 +01:00
parent 52434efdd7
commit 7026c8cb67
12 changed files with 144 additions and 16 deletions

View File

@@ -1,7 +1,10 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator'; import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
import { UserMePermissionsResponse } from 'picsur-shared/dist/dto/api/user.dto'; import { UserMePermissionsResponse } from 'picsur-shared/dist/dto/api/user.dto';
import { Permissions } from 'picsur-shared/dist/dto/permissions'; import {
Permissions,
PermissionsList
} from 'picsur-shared/dist/dto/permissions';
import { AsyncFailable, HasFailed } from 'picsur-shared/dist/types'; import { AsyncFailable, HasFailed } from 'picsur-shared/dist/types';
import { BehaviorSubject } from 'rxjs'; import { BehaviorSubject } from 'rxjs';
import { ApiService } from './api.service'; import { ApiService } from './api.service';
@@ -15,19 +18,22 @@ export class PermissionService {
this.onUser(); this.onUser();
} }
public get livePermissions() { public get live() {
return this.permissionsSubject; return this.permissionsSubject;
} }
public get permissions() { public get snapshot() {
return this.permissionsSubject.getValue(); return this.permissionsSubject.getValue();
} }
private permissionsSubject = new BehaviorSubject<Permissions>([]); // Lets be optimistic for better ux
private permissionsSubject = new BehaviorSubject<Permissions>(
PermissionsList as Permissions
);
@AutoUnsubscribe() @AutoUnsubscribe()
private onUser() { private onUser() {
return this.userService.liveUser.subscribe(async (user) => { return this.userService.live.subscribe(async (user) => {
const permissions = await this.fetchPermissions(); const permissions = await this.fetchPermissions();
if (HasFailed(permissions)) { if (HasFailed(permissions)) {
this.logger.warn(permissions.getReason()); this.logger.warn(permissions.getReason());

View File

@@ -19,11 +19,11 @@ import { KeyService } from './key.service';
export class UserService { export class UserService {
private readonly logger = console; private readonly logger = console;
public get liveUser() { public get live() {
return this.userSubject; return this.userSubject;
} }
public get user() { public get snapshot() {
return this.userSubject.getValue(); return this.userSubject.getValue();
} }

View File

@@ -1,2 +1,12 @@
<footer class="container"> <footer class="container">
<p>
Made with 🤍 by
<a class="link-unstyled" href="https://rubikscraft.nl" target="_blank"
>RubiksCraft</a
>
-
<a class="link-unstyled" href="https://github.com/rubikscraft/picsur"
>Source Code</a
>
</p>
</footer> </footer>

View File

@@ -3,5 +3,5 @@ footer {
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
margin-top: 32px; margin-top: 16px;
} }

View File

@@ -8,7 +8,7 @@
<span class="spacer"></span> <span class="spacer"></span>
<button *ngIf="!isLoggedIn" mat-stroked-button (click)="doLogin()"> <button *ngIf="!isLoggedIn && canLogIn" mat-stroked-button (click)="doLogin()">
Login Login
</button> </button>

View File

@@ -1,8 +1,10 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator'; import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
import { Permissions } from 'picsur-shared/dist/dto/permissions';
import { EUser } from 'picsur-shared/dist/entities/user.entity'; import { EUser } from 'picsur-shared/dist/entities/user.entity';
import { HasFailed } from 'picsur-shared/dist/types'; import { HasFailed } from 'picsur-shared/dist/types';
import { PermissionService } from 'src/app/api/permission.service';
import { UserService } from 'src/app/api/user.service'; import { UserService } from 'src/app/api/user.service';
import { SnackBarType } from 'src/app/models/snack-bar-type'; import { SnackBarType } from 'src/app/models/snack-bar-type';
import { UtilService } from 'src/app/util/util.service'; import { UtilService } from 'src/app/util/util.service';
@@ -16,6 +18,7 @@ export class HeaderComponent implements OnInit {
private readonly logger = console; private readonly logger = console;
private currentUser: EUser | null = null; private currentUser: EUser | null = null;
private permissions: Permissions = [];
public get user() { public get user() {
return this.currentUser; return this.currentUser;
@@ -25,24 +28,36 @@ export class HeaderComponent implements OnInit {
return this.currentUser !== null; return this.currentUser !== null;
} }
public get canLogIn() {
return this.permissions.includes('user-login');
}
constructor( constructor(
private router: Router, private router: Router,
private userService: UserService, private userService: UserService,
private permissionService: PermissionService,
private utilService: UtilService private utilService: UtilService
) {} ) {}
ngOnInit(): void { ngOnInit(): void {
this.subscribeUser(); this.subscribeUser();
this.subscribePermissions();
} }
@AutoUnsubscribe() @AutoUnsubscribe()
subscribeUser() { subscribeUser() {
return this.userService.liveUser.subscribe((user) => { return this.userService.live.subscribe((user) => {
this.logger.log('user', user);
this.currentUser = user; this.currentUser = user;
}); });
} }
@AutoUnsubscribe()
subscribePermissions() {
return this.permissionService.live.subscribe((permissions) => {
this.permissions = permissions;
});
}
doLogin() { doLogin() {
this.router.navigate(['/login']); this.router.navigate(['/login']);
} }

View File

@@ -37,7 +37,19 @@
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-12 py-2"> <div class="col-12 py-2">
<button mat-raised-button color="accent" type="submit">Login</button> <button mat-raised-button color="accent" class="mx-2" type="submit">
Login
</button>
<button
*ngIf="showRegister"
mat-raised-button
color="primary"
class="mx-2"
(click)="onRegister()"
type="button"
>
Register
</button>
</div> </div>
</form> </form>
</div> </div>

View File

@@ -0,0 +1,3 @@
mat-form-field {
width: 100%;
}

View File

@@ -1,6 +1,9 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
import { Permissions } from 'picsur-shared/dist/dto/permissions';
import { HasFailed } from 'picsur-shared/dist/types'; import { HasFailed } from 'picsur-shared/dist/types';
import { PermissionService } from 'src/app/api/permission.service';
import { UserService } from 'src/app/api/user.service'; import { UserService } from 'src/app/api/user.service';
import { SnackBarType } from 'src/app/models/snack-bar-type'; import { SnackBarType } from 'src/app/models/snack-bar-type';
import { UtilService } from 'src/app/util/util.service'; import { UtilService } from 'src/app/util/util.service';
@@ -14,11 +17,18 @@ import { LoginControl } from './login.model';
export class LoginComponent implements OnInit { export class LoginComponent implements OnInit {
private readonly logger = console; private readonly logger = console;
private permissions: Permissions = [];
public get showRegister() {
return this.permissions.includes('user-register');
}
model = new LoginControl(); model = new LoginControl();
loginFail = false; loginFail = false;
constructor( constructor(
private userService: UserService, private userService: UserService,
private permissionService: PermissionService,
private router: Router, private router: Router,
private utilService: UtilService private utilService: UtilService
) {} ) {}
@@ -26,7 +36,17 @@ export class LoginComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
if (this.userService.isLoggedIn) { if (this.userService.isLoggedIn) {
this.router.navigate(['/'], { replaceUrl: true }); this.router.navigate(['/'], { replaceUrl: true });
return;
} }
this.onPermissions();
}
@AutoUnsubscribe()
onPermissions() {
return this.permissionService.live.subscribe((permissions) => {
this.permissions = permissions;
});
} }
async onSubmit() { async onSubmit() {
@@ -45,4 +65,10 @@ export class LoginComponent implements OnInit {
this.utilService.showSnackBar('Login successful', SnackBarType.Success); this.utilService.showSnackBar('Login successful', SnackBarType.Success);
this.router.navigate(['/']); this.router.navigate(['/']);
} }
async onRegister() {
//prevent default
console.log('click');
}
} }

View File

@@ -1,8 +1,13 @@
<div class="content-border"> <div class="content-border">
<ngx-dropzone (change)="onSelect($event)"> <ngx-dropzone *ngIf="hasUploadPermission" (change)="onSelect($event)">
<div class="centered"> <div class="centered">
<h1>Upload Image</h1> <h1>Upload Image</h1>
<p>Drag and drop an image here, or click to select an image</p> <p>Drag and drop an image here, or click to select an image</p>
</div> </div>
</ngx-dropzone> </ngx-dropzone>
<div class="centered" *ngIf="!hasUploadPermission">
<h1>Please Log In</h1>
<p>You do not yet have permission to upload</p>
</div>
</div> </div>

View File

@@ -1,6 +1,9 @@
import { Component } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
import { NgxDropzoneChangeEvent } from 'ngx-dropzone'; import { NgxDropzoneChangeEvent } from 'ngx-dropzone';
import { Permissions } from 'picsur-shared/dist/dto/permissions';
import { PermissionService } from 'src/app/api/permission.service';
import { UtilService } from 'src/app/util/util.service'; import { UtilService } from 'src/app/util/util.service';
import { ProcessingViewMetadata } from '../../models/processing-view-metadata'; import { ProcessingViewMetadata } from '../../models/processing-view-metadata';
@@ -8,8 +11,31 @@ import { ProcessingViewMetadata } from '../../models/processing-view-metadata';
templateUrl: './upload.component.html', templateUrl: './upload.component.html',
styleUrls: ['./upload.component.scss'], styleUrls: ['./upload.component.scss'],
}) })
export class UploadComponent { export class UploadComponent implements OnInit {
constructor(private utilService: UtilService, private router: Router) {} private permissions: Permissions = [];
// Lets be optimistic here, this makes for a better ux
public get hasUploadPermission() {
return this.permissions.includes('image-upload');
}
constructor(
private utilService: UtilService,
private permissionService: PermissionService,
private router: Router
) {}
ngOnInit(): void {
this.onPermission();
}
@AutoUnsubscribe()
onPermission() {
return this.permissionService.live.subscribe((permissions) => {
this.permissions = permissions;
});
}
onSelect(event: NgxDropzoneChangeEvent) { onSelect(event: NgxDropzoneChangeEvent) {
if (event.addedFiles.length > 1) { if (event.addedFiles.length > 1) {
this.utilService.showSnackBar( this.utilService.showSnackBar(

View File

@@ -17,3 +17,28 @@
height: 100%; height: 100%;
width: 100%; width: 100%;
} }
.link-unstyled {
&,
&:visited,
&:hover,
&:active,
&:focus,
&:active:hover {
font-style: inherit;
color: inherit;
background-color: transparent;
font-size: inherit;
text-decoration: none;
font-variant: inherit;
font-weight: bold;
line-height: inherit;
font-family: inherit;
border-radius: inherit;
border: inherit;
outline: inherit;
box-shadow: inherit;
padding: inherit;
vertical-align: inherit;
}
}