feat(authorization): Implemented guards to avoid unauthorized access. (TV-515)
Squashed commit of the following: commit 86aa3af3f54be4ef5bfb99baece6654a7fba204f Merge: f3258e81e45fb5Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Thu Sep 9 05:42:46 2021 +0200 Merge branch 'develop' into feature/TV-515-authorization-flow commit f3258e8c6e3d51f21ec619e09c82b2d0f581bde9 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Wed Sep 8 16:43:44 2021 +0200 Fixed tests commit 91bfea1baa297f34769a33972fd61481dfa31197 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Wed Sep 8 15:55:13 2021 +0200 Removed unused pages commit d4a92fbde9d6255d8406abc23fe1479658035787 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Wed Sep 8 15:51:25 2021 +0200 Updated some styling commit dc75656ff96ff0358a2dd0a8b090b4b4938b8323 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Wed Sep 8 15:35:04 2021 +0200 Refactured guards by separating organizations into its own guard commit 24f3a0a2d821930bd682b854f98e1c9816ece08c Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Wed Sep 8 15:33:53 2021 +0200 Readded search on employees commit f1890b104c48d6dd6e263b730dbdafbc2a6fbf0f Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Wed Sep 8 14:59:24 2021 +0200 Added RoleGuard to pages needing a guard commit ef4b37e3dcc8fe26eef1bb813cfb35727ba691be Merge: 07bca2ab06436aAuthor: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Wed Sep 8 14:06:34 2021 +0200 Merge branch 'develop' into feature/TV-515-authorization-flow commit 07bca2a84d0ec970188c284ba4b950312cec57cb Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Wed Sep 8 13:26:50 2021 +0200 Added check for navigation
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
@import 'mixins/link';
|
||||
|
||||
.back-link {
|
||||
@include msfa-link(true);
|
||||
@include msfa__link(true);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,26 @@
|
||||
></path>
|
||||
</svg>
|
||||
|
||||
<svg
|
||||
*ngIf="icon === iconType.BUILDING"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 448 512"
|
||||
width="25"
|
||||
height="25"
|
||||
>
|
||||
<path
|
||||
d="M436 480h-20V24c0-13.255-10.745-24-24-24H56C42.745 0 32 10.745 32 24v456H12c-6.627 0-12 5.373-12 12v20h448v-20c0-6.627-5.373-12-12-12zM128 76c0-6.627 5.373-12 12-12h40c6.627 0 12 5.373 12 12v40c0 6.627-5.373 12-12 12h-40c-6.627 0-12-5.373-12-12V76zm0 96c0-6.627 5.373-12 12-12h40c6.627 0 12 5.373 12 12v40c0 6.627-5.373 12-12 12h-40c-6.627 0-12-5.373-12-12v-40zm52 148h-40c-6.627 0-12-5.373-12-12v-40c0-6.627 5.373-12 12-12h40c6.627 0 12 5.373 12 12v40c0 6.627-5.373 12-12 12zm76 160h-64v-84c0-6.627 5.373-12 12-12h40c6.627 0 12 5.373 12 12v84zm64-172c0 6.627-5.373 12-12 12h-40c-6.627 0-12-5.373-12-12v-40c0-6.627 5.373-12 12-12h40c6.627 0 12 5.373 12 12v40zm0-96c0 6.627-5.373 12-12 12h-40c-6.627 0-12-5.373-12-12v-40c0-6.627 5.373-12 12-12h40c6.627 0 12 5.373 12 12v40zm0-96c0 6.627-5.373 12-12 12h-40c-6.627 0-12-5.373-12-12V76c0-6.627 5.373-12 12-12h40c6.627 0 12 5.373 12 12v40z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
|
||||
<svg *ngIf="icon === iconType.LOGOUT" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="25" height="25">
|
||||
<path
|
||||
d="M497 273L329 441c-15 15-41 4.5-41-17v-96H152c-13.3 0-24-10.7-24-24v-96c0-13.3 10.7-24 24-24h136V88c0-21.4 25.9-32 41-17l168 168c9.3 9.4 9.3 24.6 0 34zM192 436v-40c0-6.6-5.4-12-12-12H96c-17.7 0-32-14.3-32-32V160c0-17.7 14.3-32 32-32h84c6.6 0 12-5.4 12-12V76c0-6.6-5.4-12-12-12H96c-53 0-96 43-96 96v192c0 53 43 96 96 96h84c6.6 0 12-5.4 12-12z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
|
||||
<svg
|
||||
*ngIf="icon === iconType.CLIPBOARD"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
||||
@@ -2,7 +2,14 @@ import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/c
|
||||
import { IconSize } from '@msfa-enums/icon-size.enum';
|
||||
import { IconType } from '@msfa-enums/icon-type.enum';
|
||||
|
||||
const CUSTOM_ICONS: IconType[] = [IconType.HOME, IconType.SETTINGS, IconType.PLUS, IconType.CLIPBOARD];
|
||||
const CUSTOM_ICONS: IconType[] = [
|
||||
IconType.HOME,
|
||||
IconType.SETTINGS,
|
||||
IconType.PLUS,
|
||||
IconType.CLIPBOARD,
|
||||
IconType.BUILDING,
|
||||
IconType.LOGOUT,
|
||||
];
|
||||
|
||||
@Component({
|
||||
selector: 'msfa-icon',
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
<div class="navigation">
|
||||
<div class="navigation__logo-wrapper">
|
||||
<a [routerLink]="['/']" aria-label="Till startsidan för FA Mina sidor">
|
||||
<a class="navigation__logo-link" [routerLink]="['/']" aria-label="Till startsidan för FA Mina sidor">
|
||||
<digi-logo af-system-name="Mina sidor för fristående aktörer" af-color="secondary"></digi-logo>
|
||||
</a>
|
||||
</div>
|
||||
<ul class="navigation__list msfa__hide-on-print">
|
||||
<li *ngIf="user" class="navigation__item">
|
||||
<ul class="navigation__list msfa__hide-on-print" *ngIf="user">
|
||||
<li class="navigation__item">
|
||||
<a routerLink="/mitt-konto" class="navigation__link">
|
||||
<msfa-icon [icon]="iconType.USER" size="l"></msfa-icon>
|
||||
<span class="navigation__text">{{ user.fullName }}</span>
|
||||
</a>
|
||||
</li>
|
||||
<li *ngIf="selectedOrganization" class="navigation__item navigation__item--without-link">
|
||||
<msfa-icon [icon]="iconType.BUILDING" size="l"></msfa-icon>
|
||||
<span class="navigation__text">{{ selectedOrganization.name }}</span>
|
||||
</li>
|
||||
<!-- <li class="navigation__item">
|
||||
<a routerLink="/" class="navigation__link">
|
||||
<msfa-icon [icon]="iconType.BELL" size="l"></msfa-icon>
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__logo-link {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&__logo {
|
||||
height: $msfa__navigation-height / 2.5;
|
||||
vertical-align: middle;
|
||||
@@ -37,16 +41,15 @@
|
||||
@include msfa__reset-list;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
gap: $digi--layout--gutter--l;
|
||||
gap: $digi--layout--gutter;
|
||||
color: var(--digi--typography--color--text--light);
|
||||
margin-right: var(--digi--layout--gutter);
|
||||
}
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__user,
|
||||
&__item--without-link,
|
||||
&__link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { IconType } from '@msfa-enums/icon-type.enum';
|
||||
import { Organization } from '@msfa-models/organization.model';
|
||||
import { User } from '@msfa-models/user.model';
|
||||
|
||||
@Component({
|
||||
@@ -10,5 +11,6 @@ import { User } from '@msfa-models/user.model';
|
||||
})
|
||||
export class NavigationComponent {
|
||||
@Input() user: User;
|
||||
@Input() selectedOrganization: Organization;
|
||||
iconType = IconType;
|
||||
}
|
||||
|
||||
@@ -11,20 +11,20 @@
|
||||
Hem
|
||||
</a>
|
||||
</li>
|
||||
<li class="sidebar__item">
|
||||
<li class="sidebar__item" *ngIf="isReceiveDeltagare">
|
||||
<a [routerLink]="['/nya-deltagare']" [routerLinkActive]="['sidebar__link--active']" class="sidebar__link">
|
||||
<msfa-icon class="sidebar__icon" [icon]="iconType.CLIPBOARD" size="xl"></msfa-icon>
|
||||
Nya deltagare
|
||||
</a>
|
||||
</li>
|
||||
<li class="sidebar__item">
|
||||
<li class="sidebar__item" *ngIf="isReportAndPlanning">
|
||||
<a [routerLink]="['/deltagare']" [routerLinkActive]="['sidebar__link--active']" class="sidebar__link">
|
||||
<msfa-icon class="sidebar__icon" [icon]="iconType.SOK_KANDIDAT" size="xl"></msfa-icon>
|
||||
Deltagarlista
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar__item">
|
||||
<li class="sidebar__item" *ngIf="isAuthAdmin">
|
||||
<a [routerLink]="['/administration']" [routerLinkActive]="['sidebar__link--active']" class="sidebar__link">
|
||||
<msfa-icon class="sidebar__icon" [icon]="iconType.SETTINGS" size="xl"></msfa-icon>
|
||||
Administration
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { IconType } from '@msfa-enums/icon-type.enum';
|
||||
import { RoleEnum } from '@msfa-enums/role.enum';
|
||||
import { Role } from '@msfa-models/role.model';
|
||||
|
||||
@Component({
|
||||
selector: 'msfa-sidebar',
|
||||
@@ -8,5 +10,16 @@ import { IconType } from '@msfa-enums/icon-type.enum';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class SidebarComponent {
|
||||
@Input() userRoles: Role[];
|
||||
iconType = IconType;
|
||||
|
||||
get isAuthAdmin(): boolean {
|
||||
return this.userRoles?.some(role => role.type === RoleEnum.MSFA_AuthAdmin);
|
||||
}
|
||||
get isReceiveDeltagare(): boolean {
|
||||
return this.userRoles?.some(role => role.type === RoleEnum.MSFA_ReceiveDeltagare);
|
||||
}
|
||||
get isReportAndPlanning(): boolean {
|
||||
return this.userRoles?.some(role => role.type === RoleEnum.MSFA_ReportAndPlanning);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
<msfa-skip-to-content mainContentId="msfa-main-content"></msfa-skip-to-content>
|
||||
|
||||
<header class="msfa__header">
|
||||
<msfa-navigation [user]="user$ | async"></msfa-navigation>
|
||||
<msfa-navigation [user]="user$ | async" [selectedOrganization]="selectedOrganization$ | async"></msfa-navigation>
|
||||
</header>
|
||||
|
||||
<msfa-sidebar class="msfa__sidebar"></msfa-sidebar>
|
||||
<msfa-sidebar class="msfa__sidebar" [userRoles]="roles$ | async"></msfa-sidebar>
|
||||
<main id="msfa-main-content" class="msfa__content">
|
||||
<digi-ng-navigation-breadcrumbs
|
||||
*ngIf="showBreadCrumbs"
|
||||
|
||||
@@ -3,12 +3,14 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
|
||||
import { UnsubscribeDirective } from '@msfa-directives/unsubscribe.directive';
|
||||
import { Organization } from '@msfa-models/organization.model';
|
||||
import { Role } from '@msfa-models/role.model';
|
||||
import { User } from '@msfa-models/user.model';
|
||||
import { AuthenticationService } from '@msfa-services/api/authentication.service';
|
||||
import { UserService } from '@msfa-services/api/user.service';
|
||||
import { mapPathsToBreadcrumbs } from '@msfa-utils/map-paths-to-breadcrumbs.util';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { filter, switchMap } from 'rxjs/operators';
|
||||
import { filter, map } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'msfa-layout',
|
||||
@@ -18,15 +20,17 @@ import { filter, switchMap } from 'rxjs/operators';
|
||||
})
|
||||
export class LayoutComponent extends UnsubscribeDirective {
|
||||
@Input() showBreadCrumbs = true;
|
||||
private startBreadcrumb: NavigationBreadcrumbsItem = {
|
||||
private readonly _startBreadcrumb: NavigationBreadcrumbsItem = {
|
||||
text: 'Start',
|
||||
routerLink: '/',
|
||||
};
|
||||
private _breadcrumbsItems$ = new BehaviorSubject<NavigationBreadcrumbsItem[]>([this.startBreadcrumb]);
|
||||
private _breadcrumbsItems$ = new BehaviorSubject<NavigationBreadcrumbsItem[]>([this._startBreadcrumb]);
|
||||
isLoggedIn$: Observable<boolean> = this.authenticationService.isLoggedIn$;
|
||||
user$: Observable<User> = this.isLoggedIn$.pipe(
|
||||
filter(loggedIn => !!loggedIn),
|
||||
switchMap(() => this.userService.user$)
|
||||
selectedOrganization$: Observable<Organization> = this.userService.selectedOrganization$;
|
||||
user$: Observable<User> = this.userService.user$;
|
||||
roles$: Observable<Role[]> = this.user$.pipe(
|
||||
filter(user => !!user),
|
||||
map(user => user.roles)
|
||||
);
|
||||
|
||||
get breadcrumbsItems(): NavigationBreadcrumbsItem[] {
|
||||
@@ -63,7 +67,7 @@ export class LayoutComponent extends UnsubscribeDirective {
|
||||
.toString()
|
||||
.split('/')
|
||||
.filter(path => !!path);
|
||||
this._breadcrumbsItems$.next(mapPathsToBreadcrumbs(paths, this.startBreadcrumb));
|
||||
this._breadcrumbsItems$.next(mapPathsToBreadcrumbs(paths, this._startBreadcrumb));
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ export enum IconType {
|
||||
SETTINGS = 'settings', // Custom
|
||||
PLUS = 'plus', // Custom
|
||||
CLIPBOARD = 'clipboard', // Custom
|
||||
BUILDING = 'building', // Custom
|
||||
LOGOUT = 'logout', // Custom
|
||||
USER = 'user',
|
||||
USERS = 'users',
|
||||
BELL = 'bell',
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
|
||||
import { environment } from '@msfa-environment';
|
||||
import { AuthenticationService } from '@msfa-services/api/authentication.service';
|
||||
import { UserService } from '@msfa-services/api/user.service';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
|
||||
@@ -10,24 +9,13 @@ import { map, switchMap } from 'rxjs/operators';
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class AuthGuard implements CanActivate {
|
||||
constructor(
|
||||
private authenticationService: AuthenticationService,
|
||||
private router: Router,
|
||||
private userService: UserService
|
||||
) {}
|
||||
constructor(private authenticationService: AuthenticationService, private router: Router) {}
|
||||
|
||||
canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {
|
||||
return this.authenticationService.isLoggedIn$.pipe(
|
||||
switchMap(loggedIn => {
|
||||
if (loggedIn) {
|
||||
return this.userService.selectedOrganization$.pipe(
|
||||
map(organization => {
|
||||
if (!organization) {
|
||||
void this.router.navigateByUrl(`/organization-picker`);
|
||||
}
|
||||
return true;
|
||||
})
|
||||
);
|
||||
return of(true);
|
||||
} else if (route.queryParams.code) {
|
||||
return this.authenticationService.login$(route.queryParams.code).pipe(map(result => !!result));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CanActivate, Router } from '@angular/router';
|
||||
import { UserService } from '@msfa-services/api/user.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class OrganizationGuard implements CanActivate {
|
||||
constructor(private router: Router, private userService: UserService) {}
|
||||
|
||||
canActivate(): Observable<boolean> {
|
||||
return this.userService.selectedOrganization$.pipe(
|
||||
map(organization => {
|
||||
if (!organization) {
|
||||
void this.router.navigateByUrl(`/organization-picker`);
|
||||
}
|
||||
return true;
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
30
apps/mina-sidor-fa/src/app/shared/guards/role.guard.ts
Normal file
30
apps/mina-sidor-fa/src/app/shared/guards/role.guard.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
|
||||
import { RoleEnum } from '@msfa-enums/role.enum';
|
||||
import { UserService } from '@msfa-services/api/user.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { filter, map } from 'rxjs/operators';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class RoleGuard implements CanActivate {
|
||||
constructor(private router: Router, private userService: UserService) {}
|
||||
|
||||
canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {
|
||||
const expectedRole: RoleEnum = route.data.expectedRole as RoleEnum;
|
||||
|
||||
return this.userService.user$.pipe(
|
||||
filter(user => !!user),
|
||||
map(({ roles }) => {
|
||||
const userHasRole = roles.some(role => role.type === expectedRole);
|
||||
|
||||
if (userHasRole) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void this.router.navigateByUrl('/obehorig');
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
import { RoleEnum } from '@msfa-enums/role.enum';
|
||||
|
||||
export interface UserInfoResponse {
|
||||
id: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
roles: string[];
|
||||
roles: RoleEnum[];
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { UserInfoResponse } from './api/user-info.response.model';
|
||||
import { mapResponseToRoles, Role } from './role.model';
|
||||
|
||||
export interface UserInfo {
|
||||
id: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
fullName: string;
|
||||
roles: string[];
|
||||
roles: Role[];
|
||||
}
|
||||
|
||||
export function mapResponseToUserInfo(data: UserInfoResponse): UserInfo {
|
||||
@@ -16,6 +17,6 @@ export function mapResponseToUserInfo(data: UserInfoResponse): UserInfo {
|
||||
firstName,
|
||||
lastName,
|
||||
fullName: `${firstName} ${lastName}`,
|
||||
roles,
|
||||
roles: mapResponseToRoles(roles),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { OrganizationResponse } from './api/organization.response.model';
|
||||
import { UserInfoResponse } from './api/user-info.response.model';
|
||||
import { mapResponseToOrganization, Organization } from './organization.model';
|
||||
import { mapResponseToRoles } from './role.model';
|
||||
import { UserInfo } from './user-info.model';
|
||||
|
||||
export interface User extends UserInfo {
|
||||
@@ -15,7 +16,7 @@ export function mapUserApiResponseToUser(userInfo: UserInfoResponse, organizatio
|
||||
firstName,
|
||||
lastName,
|
||||
fullName: `${firstName} ${lastName}`,
|
||||
roles,
|
||||
roles: mapResponseToRoles(roles),
|
||||
organizations: organizations ? organizations.map(organization => mapResponseToOrganization(organization)) : [],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -17,23 +17,34 @@ import { AuthenticationService } from './authentication.service';
|
||||
})
|
||||
export class UserService extends UnsubscribeDirective {
|
||||
private _apiBaseUrl = `${environment.api.url}/auth`;
|
||||
private _isLoggedIn$: Observable<boolean> = this.authenticationService.isLoggedIn$;
|
||||
private _organizations$ = new BehaviorSubject<Organization[]>(null);
|
||||
public organizations$: Observable<Organization[]> = this._organizations$.asObservable();
|
||||
private _user$ = new BehaviorSubject<User>(null);
|
||||
public user$: Observable<User> = this._user$.asObservable();
|
||||
private _selectedOrganizationNumber$ = new BehaviorSubject<string>(null);
|
||||
|
||||
constructor(private httpClient: HttpClient, private authenticationService: AuthenticationService) {
|
||||
super();
|
||||
this._selectedOrganizationNumber$.next(this._selectedOrganizationNumber);
|
||||
super.unsubscribeOnDestroy(
|
||||
this.authenticationService.isLoggedIn$
|
||||
this._isLoggedIn$
|
||||
.pipe(
|
||||
filter(loggedIn => !!loggedIn),
|
||||
switchMap(() => combineLatest([this._fetchUserInfo$(), this._fetchOrganizations$()]))
|
||||
switchMap(() => this._fetchOrganizations$())
|
||||
)
|
||||
.subscribe(([userInfo, organizations]) => {
|
||||
this._user$.next({ ...userInfo, organizations });
|
||||
.subscribe(organizations => {
|
||||
this._organizations$.next(organizations);
|
||||
}),
|
||||
combineLatest([this._isLoggedIn$, this.selectedOrganization$])
|
||||
.pipe(
|
||||
filter(([loggedIn, selectedOrganization]) => !!(loggedIn && selectedOrganization)),
|
||||
switchMap(() => this._fetchUserInfo$())
|
||||
)
|
||||
.subscribe(userInfo => {
|
||||
this._user$.next({ ...userInfo, organizations: this._organizations$.value });
|
||||
})
|
||||
);
|
||||
this._selectedOrganizationNumber$.next(this._selectedOrganizationNumber);
|
||||
}
|
||||
|
||||
private _fetchOrganizations$(): Observable<Organization[]> {
|
||||
@@ -55,11 +66,11 @@ export class UserService extends UnsubscribeDirective {
|
||||
}
|
||||
|
||||
public get selectedOrganization$(): Observable<Organization | null> {
|
||||
return combineLatest([this._selectedOrganizationNumber$, this._user$]).pipe(
|
||||
filter(([, user]) => !!user),
|
||||
map(([organizationNumber, user]) => {
|
||||
return combineLatest([this._selectedOrganizationNumber$, this._organizations$]).pipe(
|
||||
filter(([, organizations]) => !!organizations?.length),
|
||||
map(([organizationNumber, organizations]) => {
|
||||
return organizationNumber
|
||||
? user.organizations.find(organization => organization.organizationNumber === organizationNumber)
|
||||
? organizations.find(organization => organization.organizationNumber === organizationNumber)
|
||||
: null;
|
||||
})
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user