test update some permissions

This commit is contained in:
Kenta420 2023-12-08 14:46:07 +07:00
parent ac64335d5b
commit 25ce65e425
15 changed files with 582 additions and 493 deletions

View file

@ -1,4 +1,4 @@
import { NgModule, inject } from '@angular/core';
import {inject, NgModule} from '@angular/core';
import {
ActivatedRouteSnapshot,
CanActivateFn,
@ -7,8 +7,9 @@ import {
RouterStateSnapshot,
Routes,
} from '@angular/router';
import { UserService } from './core/services/user.service';
import { map } from 'rxjs';
import {UserService} from './core/services/user.service';
import {map} from 'rxjs';
import {UserPermissions} from "./core/auth/userPermissions";
const authGuard: CanActivateFn = (
route: ActivatedRouteSnapshot,
@ -31,13 +32,38 @@ const authGuard: CanActivateFn = (
);
};
const loginGuard: CanActivateFn = (
const permissionsGuard: (...requiredPermissions: UserPermissions[]) => CanActivateFn = (...requiredPermissions) => (
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
) => {
const userService: UserService = inject(UserService);
const router: Router = inject(Router);
const user = userService.getCurrentUser()
if (user == null)
return router.createUrlTree(['/login'], {
queryParams: {
redirectUrl: state.url,
},
});
if (requiredPermissions.every(permission => user.permissions.includes(permission))) {
return true
}
return router.createUrlTree(['/unauthorized'], {
queryParams: {
redirectUrl: state.url,
},
});
}
const loginGuard: CanActivateFn = (
route: ActivatedRouteSnapshot
) => {
const userService: UserService = inject(UserService);
const router: Router = inject(Router);
return userService.isAuthenticated.pipe(
map((isAuth) => {
if (!isAuth) {
@ -83,15 +109,15 @@ const routes: Routes = [
import('./features/recipes/recipes.component').then(
(m) => m.RecipesComponent
),
canActivate: [authGuard],
canActivate: [authGuard, permissionsGuard(UserPermissions.THAI_PERMISSION, UserPermissions.SUPER_ADMIN)],
},
{
path: 'recipe/:productCode',
loadComponent: () =>
import(
'./features/recipes/recipe-details/recipe-details.component'
).then((m) => m.RecipeDetailsComponent),
canActivate: [authGuard],
).then((m) => m.RecipeDetailsComponent),
canActivate: [authGuard, permissionsGuard(UserPermissions.THAI_PERMISSION, UserPermissions.SUPER_ADMIN)],
},
// {
// path: 'log',
@ -100,9 +126,17 @@ const routes: Routes = [
// (m) => m.ChangelogComponent
// ),
// },
{
path: 'unauthorized',
loadComponent: () => import('./core/auth/unauthorized.component').then((m) => m.UnauthorizedComponent)
},
{
path: 'notfound',
loadComponent: () => import('./core/notfound.component').then((m) => m.NotfoundComponent)
},
{
path: '**',
redirectTo: 'recipes',
redirectTo: 'notfound',
},
],
},
@ -112,4 +146,5 @@ const routes: Routes = [
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
export class AppRoutingModule {
}

View file

@ -0,0 +1,31 @@
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-unauthorized',
standalone: true,
template: `
<div class="flex pt-[100px] justify-center h-screen">
<h1 class="text-2xl font-bold text-center text-red-500">
Unauthorized, You 🫵🏻 not have permissions
</h1>
</div>`
})
export class UnauthorizedComponent implements OnInit {
constructor(
private router: Router,
private activatedRoute: ActivatedRoute
) { }
ngOnInit(): void {
const redirectUrl = this.activatedRoute.snapshot.queryParams['redirectUrl'];
if (redirectUrl) {
this.router.navigate([redirectUrl]).catch(() => {
// redirect to login page
void this.router.navigate(['/login']);
});
}
}
}

View file

@ -0,0 +1,12 @@
export enum UserPermissions {
NO_PERMISSION,
THAI_PERMISSION = 1 << 0,
MALAY_PERMISSION = 1 << 1,
AUS_PERMISSION = 1 << 2,
SUPER_ADMIN = 1 << 3
}
export function getPermissions(perms: number) : UserPermissions[] {
return Object.values(UserPermissions)
.filter(permission => typeof permission === 'number' && (perms & permission) !== 0) as UserPermissions[];
}

View file

@ -1,7 +1,7 @@
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { ActivatedRoute, Router } from '@angular/router';
import { UserService } from '../services/user.service';
import {getPermissions} from "../auth/userPermissions";
@Component({
selector: 'app-callback',
@ -19,18 +19,19 @@ export class CallbackComponent implements OnInit {
this.route.queryParams.subscribe((params) => {
console.log(params);
if (params['email'] && params['name'] && params['picture']) {
if (params['email'] && params['name'] && params['picture'] && params['permissions']) {
this.userService.setAuth({
email: params['email'],
name: params['name'],
picture: params['picture'],
permissions: getPermissions(params['permissions'])
});
}
if (params['redirect_to']) {
this.router.navigate([params['redirect_to']]);
void this.router.navigate([params['redirect_to']]);
} else {
this.router.navigate(['/']);
void this.router.navigate(['/']);
}
});
}

View file

@ -1,5 +1,8 @@
import {UserPermissions} from "../auth/userPermissions";
export interface User {
email: string;
name: string;
picture: string;
permissions: UserPermissions[];
}

View file

@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-notfound',
standalone: true,
template: `<h1>Not Found!!!</h1>`
})
export class NotfoundComponent {
}

View file

@ -2,10 +2,9 @@ import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import {
BehaviorSubject,
Observable,
distinctUntilChanged,
map,
tap,
} from 'rxjs';
import { User } from '../models/user.model';
import { Router } from '@angular/router';
@ -31,6 +30,10 @@ export class UserService {
}
}
getCurrentUser(): User | null {
return this.currentUserSubject.value
}
logout(): void {
this.purgeAuth();
// post to api /revoke with cookie
@ -43,21 +46,6 @@ export class UserService {
});
}
getCurrentUser(): Observable<{ user: User }> {
return this.http
.get<{ user: User }>(environment.api + '/auth/user', {
withCredentials: true,
})
.pipe(
tap({
next: ({ user }) => {
this.setAuth(user);
},
error: () => this.purgeAuth(),
})
);
}
setAuth(user: User): void {
window.localStorage.setItem('user', JSON.stringify(user));
void this.currentUserSubject.next(user);

View file

@ -2,7 +2,7 @@ import { CommonModule, DatePipe } from '@angular/common';
import { Component, EventEmitter, OnInit } from '@angular/core';
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
import { Observable, first } from 'rxjs';
import {Observable, first, map} from 'rxjs';
import { RecipeService } from 'src/app/core/services/recipe.service';
import { ConfirmModal } from 'src/app/shared/modal/confirm/confirm-modal.component';
import { animate, style, transition, trigger } from '@angular/animations';
@ -11,8 +11,7 @@ import {
RecipeDetail,
RecipeDetailMat,
} from 'src/app/core/models/recipe.model';
import { Action, ActionRecord } from 'src/app/shared/actionRecord/actionRecord';
import { isEqual } from 'lodash';
import { ActionRecord } from 'src/app/shared/actionRecord/actionRecord';
import { UserService } from 'src/app/core/services/user.service';
@Component({
@ -119,10 +118,9 @@ export class RecipeDetailsComponent implements OnInit {
// get username
let username:string = ""
this._userService.getCurrentUser().subscribe((user) => {
username = user.user.name;
this._userService.currentUser.pipe(map((user) => {
if (user)
username = user.name;
let to_send = {
edit_by: username,
@ -152,8 +150,8 @@ export class RecipeDetailsComponent implements OnInit {
}
);
console.log('Sending changes');
this._router.navigate(['/recipes']);
})
void this._router.navigate(['/recipes']);
}))
@ -198,6 +196,6 @@ export class RecipeDetailsComponent implements OnInit {
onRecipeListFormChange(repl: unknown[]) {
console.log('Recipe List Form Changed', repl);
this.repl = repl as never[];
this.isValueChanged ||= repl != undefined ? true : false;
this.isValueChanged ||= repl != undefined;
}
}