From e7a96dd2d7ebcbd83340640c8ed14b29733d972b Mon Sep 17 00:00:00 2001 From: pkazimierowski <pkazimierowski@man.poznan.pl> Date: Tue, 1 Apr 2025 10:48:38 +0200 Subject: [PATCH 01/25] component init --- .../link-account/link-account.component.css | 1 + .../link-account/link-account.component.html | 51 ++++++++++++++ .../link-account.component.spec.ts | 23 +++++++ .../link-account/link-account.component.ts | 66 +++++++++++++++++++ 4 files changed, 141 insertions(+) create mode 100644 src/app/welcome/link-account/link-account.component.css create mode 100644 src/app/welcome/link-account/link-account.component.html create mode 100644 src/app/welcome/link-account/link-account.component.spec.ts create mode 100644 src/app/welcome/link-account/link-account.component.ts diff --git a/src/app/welcome/link-account/link-account.component.css b/src/app/welcome/link-account/link-account.component.css new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src/app/welcome/link-account/link-account.component.css @@ -0,0 +1 @@ + diff --git a/src/app/welcome/link-account/link-account.component.html b/src/app/welcome/link-account/link-account.component.html new file mode 100644 index 00000000..8497113e --- /dev/null +++ b/src/app/welcome/link-account/link-account.component.html @@ -0,0 +1,51 @@ +<div style="display: flex; justify-content: center;"> + <div style=" +padding-bottom: 15px; + width: 60% +" class="panel panel-default"> + <div class="panel-heading">{{ 'ACCOUNT_LINKING.HEADER' | translate }}</div> + <div class="panel-body"> + <form *ngIf="user" + class="form-horizontal" #userDetailsForm="ngForm"> + <div> + <p> + {{ 'ACCOUNT_LINKING.INFO' | translate }} + </p> + </div> + <div class="form-group"> + <label class="col-sm-2 control-label">{{ 'USER_DETAILS.FIRST_NAME' | translate }}</label> + <div class="col-sm-10"> + <p class="form-control-static">{{ user.firstname }}</p> + </div> + </div> + + <div class="form-group"> + <label class="col-sm-2 control-label">{{ 'USER_DETAILS.LAST_NAME' | translate }}</label> + <div class="col-sm-10"> + <p class="form-control-static">{{ user.lastname }}</p> + </div> + </div> + + <div class="form-group"> + <label class="col-sm-2 control-label">{{ 'USER_DETAILS.EMAIL' | translate }}</label> + <div class="col-sm-10"> + <p class="form-control-static">{{ user.email }}</p> + </div> + </div> + + <div class="form-group"> + <label for="password" class="col-sm-2 control-label">{{ 'PASSWORD.PASSWORD' | translate }}</label> + <div class="col-sm-10"> + <input type="password" class="form-control" id="password" + name="password" [(ngModel)]="password"> + </div> + </div> + <button type="submit" class="btn btn-primary" + (click)="submit()">{{ 'PASSWORD.SUBMIT_BUTTON' | translate }} + </button> + + </form> + <br> + </div> + </div> +</div> diff --git a/src/app/welcome/link-account/link-account.component.spec.ts b/src/app/welcome/link-account/link-account.component.spec.ts new file mode 100644 index 00000000..9a780cda --- /dev/null +++ b/src/app/welcome/link-account/link-account.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LinkAccountComponent } from './link-account.component'; + +describe('LinkAccountComponent', () => { + let component: LinkAccountComponent; + let fixture: ComponentFixture<LinkAccountComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [LinkAccountComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(LinkAccountComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/welcome/link-account/link-account.component.ts b/src/app/welcome/link-account/link-account.component.ts new file mode 100644 index 00000000..b72bd90f --- /dev/null +++ b/src/app/welcome/link-account/link-account.component.ts @@ -0,0 +1,66 @@ +import {Component, OnDestroy, OnInit} from '@angular/core'; +import {User} from '../../model'; +import {ActivatedRoute, Router} from '@angular/router'; +import jwtDecode from 'jwt-decode'; +import {AuthService} from '../../auth/auth.service'; + + +@Component({ + selector: 'app-link-account', + templateUrl: './link-account.component.html', + styleUrl: './link-account.component.css' +}) +export class LinkAccountComponent implements OnInit, OnDestroy { + public user: User; + private token: string; + public password: string; + + constructor( + private readonly route: ActivatedRoute, + private readonly authService: AuthService, + private router: Router + ) { + } + + ngOnDestroy() { + if (!this.authService.isLogged()) { + this.authService.oidcLogout(this.token) + } + } + + ngOnInit() { + this.route.queryParams.subscribe(param => { + this.token = param['oidc_token']; + const decoded: TokenPayload = jwtDecode<TokenPayload>(this.token); + this.user = new User(); + this.user.username = decoded.sub; + this.user.firstname = decoded.given_name; + this.user.lastname = decoded.family_name; + this.user.email = decoded.email; + }) + + } + + public submit(): void { + this.authService.oidcLinkingLogin( + this.token, + this.user.email, + this.password, + this.user.username, + this.user.firstname, + this.user.lastname, + ).subscribe( + () => { + this.router.navigate(['/']); + } + ) + } +} + + +interface TokenPayload { + sub: string; + email: string; + given_name: string; + family_name: string; +} -- GitLab From 59d957b0b4b2f2be9fc6de80f29e9bba7559bff9 Mon Sep 17 00:00:00 2001 From: pkazimierowski <pkazimierowski@man.poznan.pl> Date: Tue, 1 Apr 2025 10:48:45 +0200 Subject: [PATCH 02/25] component init --- src/app/welcome/welcome.module.ts | 67 ++++++++++++++++--------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/src/app/welcome/welcome.module.ts b/src/app/welcome/welcome.module.ts index 08fc1c81..e212c4f0 100644 --- a/src/app/welcome/welcome.module.ts +++ b/src/app/welcome/welcome.module.ts @@ -20,38 +20,41 @@ import {TranslateModule} from '@ngx-translate/core'; import {PasswordResetComponent} from './passwordreset/password-reset.component'; import {PasswordStrengthMeterComponent} from 'angular-password-strength-meter'; import {PolicySubpageComponent} from './policy-subpage/policy-subpage.component'; +import {LinkAccountComponent} from './link-account/link-account.component'; @NgModule({ - declarations: [ - WelcomeComponent, - LoginComponent, - LogoutComponent, - RegistrationComponent, - ProfileComponent, - CompleteComponent, - TermsAcceptanceComponent, - PasswordResetComponent, - PolicySubpageComponent - ], - imports: [ - FormsModule, - ReactiveFormsModule, - CommonModule, - RouterModule, - SharedModule, - PipesModule, - AppMarketModule, - PasswordStrengthMeterComponent, - TranslateModule.forChild() - ], - exports: [ - WelcomeComponent - ], - providers: [ - RegistrationService, - UserService, - ChangelogService, - ContentDisplayService - ] + declarations: [ + WelcomeComponent, + LoginComponent, + LogoutComponent, + RegistrationComponent, + ProfileComponent, + CompleteComponent, + TermsAcceptanceComponent, + PasswordResetComponent, + PolicySubpageComponent, + LinkAccountComponent + ], + imports: [ + FormsModule, + ReactiveFormsModule, + CommonModule, + RouterModule, + SharedModule, + PipesModule, + AppMarketModule, + PasswordStrengthMeterComponent, + TranslateModule.forChild() + ], + exports: [ + WelcomeComponent + ], + providers: [ + RegistrationService, + UserService, + ChangelogService, + ContentDisplayService + ] }) -export class WelcomeModule {} +export class WelcomeModule { +} -- GitLab From ea12365f8e3cd185604b117cf48e830fc0d286d2 Mon Sep 17 00:00:00 2001 From: pkazimierowski <pkazimierowski@man.poznan.pl> Date: Tue, 1 Apr 2025 10:48:58 +0200 Subject: [PATCH 03/25] component init --- src/app/app.routes.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 610c10cc..31a968fe 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -6,6 +6,7 @@ import { WelcomeRoutes } from './welcome/welcome.routes'; import {ServiceUnavailableRoutes} from './service-unavailable/service-unavailable.routes'; import {PageNotFoundComponent} from './shared/page-not-found/page-not-found.component'; import {LoginSuccessComponent} from './auth/login-success/login-success.component'; +import {LinkAccountComponent} from './welcome/link-account/link-account.component'; const appRoutes: Routes = [ ...WelcomeRoutes, @@ -13,6 +14,7 @@ const appRoutes: Routes = [ ...ServiceUnavailableRoutes, { path: 'notfound', component: PageNotFoundComponent }, { path: 'login-success', component: LoginSuccessComponent }, + { path: 'login-linking', component: LinkAccountComponent}, { path: '**', redirectTo: '/welcome' }, ]; -- GitLab From 97b51919eef26808c4921befabbd89574a3e1c03 Mon Sep 17 00:00:00 2001 From: pkazimierowski <pkazimierowski@man.poznan.pl> Date: Tue, 1 Apr 2025 10:49:32 +0200 Subject: [PATCH 04/25] added new linking flow --- src/app/auth/auth.service.ts | 95 +++++++++++++++++++----------------- 1 file changed, 49 insertions(+), 46 deletions(-) diff --git a/src/app/auth/auth.service.ts b/src/app/auth/auth.service.ts index a3426c27..dcc55696 100644 --- a/src/app/auth/auth.service.ts +++ b/src/app/auth/auth.service.ts @@ -1,13 +1,11 @@ -import {BehaviorSubject, Observable, Subject, throwError as observableThrowError, of} from 'rxjs'; +import {BehaviorSubject, Observable, of, Subject, throwError as observableThrowError} from 'rxjs'; import {catchError, debounceTime, map} from 'rxjs/operators'; import {Injectable} from '@angular/core'; import {AppConfigService, ConfigurationService} from '../service'; import {JwtHelperService} from '@auth0/angular-jwt'; import {HttpClient, HttpHeaders} from '@angular/common/http'; -import {User} from '../model'; import {ProfileService} from '../service/profile.service'; import {Role, UserRole} from '../model/userrole'; -import {interval, Subscription} from 'rxjs'; export class DomainRoles { @@ -253,6 +251,49 @@ export class AuthService { return domainsWithRole; } + public oidcLinkingLogin(oidcToken: string, + email: string, + password: string, + uuid: string, + firstName: string, + lastName: string) { + const headers = new HttpHeaders({'Content-Type': 'application/json', 'Accept': 'application/json'}); + + return this.http.post(this.appConfig.config.apiUrl + '/oidc/link', + JSON.stringify( + { + 'oidcToken': oidcToken, + 'email': email, + 'password': password, + 'uuid': uuid, + 'firstName': firstName, + 'lastName': lastName, + } + ), + {headers: headers}).pipe( + debounceTime(1000), + map((res: Response) => { + const token = res && res['token']; + const oidcToken = res && res['oidcToken']; + if (token) { + this.storeToken(token); + this.storeOidcToken(oidcToken); + this.loginUsingSsoService = false; + this.isLoggedInSubject.next(true); + this.profileService.getRoles().subscribe(profile => { + this.profile = profile + this.storeRoles(profile); + return true; + }) + } else { + this.isLoggedInSubject.next(false); + return false; + } + } + ), + ) + } + public login(username: string, password: string): Observable<boolean> { // hack so test instance modal is shown onl after login localStorage.setItem(this.appConfig.getTestInstanceModalKey(), 'True'); @@ -306,49 +347,6 @@ export class AuthService { })); } - public propagateSSOLogin(userid: string): Observable<boolean> { - console.debug('propagateSSOLogin'); - console.debug('propagateSSOLogin ' + this.appConfig.config.apiUrl); - console.debug('propagateSSOLogin ' + this.appConfig.config.apiUrl + '/auth/sso/login'); - console.debug('propagateSSOLogin ' + userid); - // hack so test instance modal is shown onl after login - localStorage.setItem(this.appConfig.getTestInstanceModalKey(), 'True'); - - if (this.maintenance) { - this.isLoggedInSubject.next(false); - return of(false); - } - - const headers = new HttpHeaders({'Content-Type': 'application/json', 'Accept': 'application/json'}); - return this.http.post(this.appConfig.config.apiUrl + '/auth/sso/login', - JSON.stringify({'userid': userid}), {headers: headers}).pipe( - debounceTime(10000), - map((response: Response) => { - console.debug('SSO login response: ' + response); - // login successful if there's a jwt token in the response - const token = response && response['token']; - - if (token) { - this.storeToken(token); - console.debug('SSO AUTH | User: ' + this.getUsername()); - console.debug('SSO AUTH | Domains: ' + this.getDomains()); - console.debug('SSO AUTH | Roles: ' + this.getRoles()); - console.debug('SSO AUTH | DomainRoles: ' + this.getDomainRoles()); - this.loginUsingSsoService = true; - this.isLoggedInSubject.next(true); - return true; - } else { - // return false to indicate failed login - this.isLoggedInSubject.next(false); - return false; - } - }), - catchError((error) => { - console.error('SSO login error: ' + error.error['message']); - return observableThrowError(error); - })); - } - public logout(): void { const oidcToken = this.getOidcToken(); this.refresh = null; @@ -366,6 +364,11 @@ export class AuthService { } } + public oidcLogout(oidcToken: string): void { + this.http.get(this.appConfig.config.apiUrl + '/oidc/logout/' + oidcToken).subscribe(() => { + }) + } + public isLogged(): boolean { const token = this.getToken(); if (token == null) { -- GitLab From f0bd6b2dd5e250772460f504016b322684be7da4 Mon Sep 17 00:00:00 2001 From: pkazimierowski <pkazimierowski@man.poznan.pl> Date: Tue, 1 Apr 2025 11:19:47 +0200 Subject: [PATCH 05/25] test fixed --- .../link-account.component.spec.ts | 68 ++++++++++++++----- .../link-account/link-account.component.ts | 2 +- 2 files changed, 52 insertions(+), 18 deletions(-) diff --git a/src/app/welcome/link-account/link-account.component.spec.ts b/src/app/welcome/link-account/link-account.component.spec.ts index 9a780cda..0b0a9f98 100644 --- a/src/app/welcome/link-account/link-account.component.spec.ts +++ b/src/app/welcome/link-account/link-account.component.spec.ts @@ -1,23 +1,57 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import {ComponentFixture, TestBed} from '@angular/core/testing'; -import { LinkAccountComponent } from './link-account.component'; +import {LinkAccountComponent} from './link-account.component'; +import {ActivatedRoute} from '@angular/router'; +import {of} from 'rxjs'; +import {AuthService} from '../../auth/auth.service'; +import {TranslateFakeLoader, TranslateLoader, TranslateModule} from '@ngx-translate/core'; describe('LinkAccountComponent', () => { - let component: LinkAccountComponent; - let fixture: ComponentFixture<LinkAccountComponent>; + let component: LinkAccountComponent; + let fixture: ComponentFixture<LinkAccountComponent>; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [LinkAccountComponent] - }) - .compileComponents(); - - fixture = TestBed.createComponent(LinkAccountComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [LinkAccountComponent], + imports: [TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateFakeLoader + } + })], + providers: [ + { + provide: ActivatedRoute, + useValue: { + queryParams: of({ + oidc_token: 'mocked.jwt.token' + }), + snapshot: { + paramMap: { + get: () => null + } + } + } + }, + { + provide: AuthService, + useValue: { + isLogged: () => true, + oidcLogout: jasmine.createSpy('oidcLogout'), + oidcLinkingLogin: jasmine.createSpy('oidcLinkingLogin').and.returnValue(of({})) + } + }, - it('should create', () => { - expect(component).toBeTruthy(); - }); + ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(LinkAccountComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/welcome/link-account/link-account.component.ts b/src/app/welcome/link-account/link-account.component.ts index b72bd90f..1f4f967c 100644 --- a/src/app/welcome/link-account/link-account.component.ts +++ b/src/app/welcome/link-account/link-account.component.ts @@ -18,7 +18,7 @@ export class LinkAccountComponent implements OnInit, OnDestroy { constructor( private readonly route: ActivatedRoute, private readonly authService: AuthService, - private router: Router + private readonly router: Router ) { } -- GitLab From 1cb3b94f10e6f6d3b2448f82f36f31c9f167e2f3 Mon Sep 17 00:00:00 2001 From: pkazimierowski <pkazimierowski@man.poznan.pl> Date: Tue, 1 Apr 2025 11:23:01 +0200 Subject: [PATCH 06/25] button fix --- src/app/welcome/link-account/link-account.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/welcome/link-account/link-account.component.html b/src/app/welcome/link-account/link-account.component.html index 8497113e..fd27440e 100644 --- a/src/app/welcome/link-account/link-account.component.html +++ b/src/app/welcome/link-account/link-account.component.html @@ -41,7 +41,7 @@ padding-bottom: 15px; </div> </div> <button type="submit" class="btn btn-primary" - (click)="submit()">{{ 'PASSWORD.SUBMIT_BUTTON' | translate }} + (click)="submit()">{{ 'ACCOUNT_LINKING.CONFIRM' | translate }} </button> </form> -- GitLab From 0898c30085402da4289632a18b9324913794e4bb Mon Sep 17 00:00:00 2001 From: pkazimierowski <pkazimierowski@man.poznan.pl> Date: Tue, 1 Apr 2025 11:23:47 +0200 Subject: [PATCH 07/25] button fix --- src/app/welcome/link-account/link-account.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/welcome/link-account/link-account.component.html b/src/app/welcome/link-account/link-account.component.html index fd27440e..1ab303ed 100644 --- a/src/app/welcome/link-account/link-account.component.html +++ b/src/app/welcome/link-account/link-account.component.html @@ -1,6 +1,6 @@ <div style="display: flex; justify-content: center;"> <div style=" -padding-bottom: 15px; +margin-top: 50px; width: 60% " class="panel panel-default"> <div class="panel-heading">{{ 'ACCOUNT_LINKING.HEADER' | translate }}</div> -- GitLab From 6d220cb3ee6d6b5d2a4c759001a83ee910d377c8 Mon Sep 17 00:00:00 2001 From: pkazimierowski <pkazimierowski@man.poznan.pl> Date: Tue, 1 Apr 2025 13:07:59 +0200 Subject: [PATCH 08/25] added comment --- src/app/welcome/login/login.component.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/welcome/login/login.component.ts b/src/app/welcome/login/login.component.ts index 62e1fc26..6971d776 100644 --- a/src/app/welcome/login/login.component.ts +++ b/src/app/welcome/login/login.component.ts @@ -64,8 +64,11 @@ export class LoginComponent implements OnInit { ); } + // only for use in linking accounts public triggerOIDC() { - window.location.href = this.appConfig.getOidcUrl(); + if (this.configuration.maintenance) { + window.location.href = this.appConfig.getOidcUrl(); + } } public sendResetNotification() { -- GitLab From 3df0a175661d89d51236e4498f74836130ab7b18 Mon Sep 17 00:00:00 2001 From: pkazimierowski <pkazimierowski@man.poznan.pl> Date: Tue, 1 Apr 2025 13:08:20 +0200 Subject: [PATCH 09/25] added if statement --- src/app/auth/auth.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/auth/auth.service.ts b/src/app/auth/auth.service.ts index dcc55696..56aa2c98 100644 --- a/src/app/auth/auth.service.ts +++ b/src/app/auth/auth.service.ts @@ -275,7 +275,7 @@ export class AuthService { map((res: Response) => { const token = res && res['token']; const oidcToken = res && res['oidcToken']; - if (token) { + if (token && oidcToken) { this.storeToken(token); this.storeOidcToken(oidcToken); this.loginUsingSsoService = false; -- GitLab From c14b4a997c3a1d52d2807e9067c2daf0d14b760a Mon Sep 17 00:00:00 2001 From: pkazimierowski <pkazimierowski@man.poznan.pl> Date: Tue, 1 Apr 2025 13:34:47 +0200 Subject: [PATCH 10/25] increase coverage --- src/app/auth/auth.service.spec.ts | 80 +++++++++++++++++++++++++++---- src/app/auth/auth.service.ts | 18 ------- 2 files changed, 71 insertions(+), 27 deletions(-) diff --git a/src/app/auth/auth.service.spec.ts b/src/app/auth/auth.service.spec.ts index e9eef994..170ece28 100644 --- a/src/app/auth/auth.service.spec.ts +++ b/src/app/auth/auth.service.spec.ts @@ -3,19 +3,18 @@ import {TestBed, waitForAsync} from '@angular/core/testing'; import {AuthService} from './auth.service'; import {AppConfigService, ConfigurationService} from '../service'; import {JwtHelperService} from '@auth0/angular-jwt'; -import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing'; import {Role, UserRole} from '../model/userrole'; import {ProfileService} from '../service/profile.service'; import {Observable, of} from 'rxjs'; -import { Configuration } from '../model/configuration'; -import { HttpHandler } from '@angular/common/http'; +import {Configuration} from '../model/configuration'; describe('Service: Auth', () => { let authService: AuthService; let appConfigServiceSpy: jasmine.SpyObj<AppConfigService>; let jwtHelperServiceSpy: jasmine.SpyObj<JwtHelperService>; let maintenanceServiceSpy: jasmine.SpyObj<ConfigurationService>; - + let httpMock: HttpTestingController; let store: any = {}; beforeEach(waitForAsync(() => { @@ -23,7 +22,8 @@ describe('Service: Auth', () => { config: { apiUrl: 'http://api.url', tokenName: 'token', - } + }, + getTestInstanceModalKey: () => 'testModalKey' }; const jwtSpy = jasmine.createSpyObj('JwtHelperService', ['decodeToken', 'isTokenExpired']); jwtSpy.decodeToken.and.returnValue({ @@ -41,19 +41,19 @@ describe('Service: Auth', () => { class MockConfigurationService { protected uri: string; - + constructor() { this.uri = 'http://localhost/api'; } - + public getApiUrl(): string { return 'http://localhost/api'; } - + public getConfiguration(): Observable<Configuration> { return of<Configuration>(); } - + public updateConfiguration(configuration: Configuration): Observable<any> { return of<Configuration>(); } @@ -84,6 +84,7 @@ describe('Service: Auth', () => { ], }); + httpMock = TestBed.inject(HttpTestingController) authService = TestBed.get(AuthService); authService.profile = [userRole, userRole2] appConfigServiceSpy = TestBed.get(AppConfigService); @@ -104,6 +105,10 @@ describe('Service: Auth', () => { }); })); + afterEach(() => { + httpMock.verify(); + store = {}; + }); it('should create service', () => { expect(authService).toBeTruthy(); @@ -180,8 +185,11 @@ describe('Service: Auth', () => { }); it('should remove token on logout', () => { + store['oidc-token'] = 'some-oidc-token'; authService.logout(); expect(store['token']).not.toBeDefined(); + const req = httpMock.expectOne('http://api.url/oidc/logout/some-oidc-token'); + req.flush({}); }); it('should be logged in when token is present and valid', () => { @@ -197,4 +205,58 @@ describe('Service: Auth', () => { expect(r).toEqual(false); }); + it('should store token and oidc token in localStorage', () => { + authService.storeToken('abc123'); + expect(store['token']).toEqual('abc123'); + + authService.storeOidcToken('oidc456'); + expect(store['oidc-token']).toEqual('oidc456'); + }); + + it('should remove roles from localStorage', () => { + store['rolesToken'] = 'some_roles'; + authService.removeRoles(); + expect(store['rolesToken']).toBeUndefined(); + }); + + it('should load and parse roles from localStorage', () => { + const roles = [{domainId: 1, role: Role.ROLE_USER, domainName: 'x'}]; + store['rolesToken'] = JSON.stringify(roles); + + const result = authService.loadRoles(); + expect(result.length).toEqual(1); + expect(result[0].role).toEqual(Role.ROLE_USER); + }); + + it('should assign loaded roles to profile', () => { + const roles = [{domainId: 2, role: Role.ROLE_DOMAIN_ADMIN, domainName: 'x'}]; + store['rolesToken'] = JSON.stringify(roles); + authService.loadAndSaveRoles(); + expect(authService.profile[0].role).toEqual(Role.ROLE_DOMAIN_ADMIN); + }); + it('should stringify and store roles', () => { + const roles = [new UserRole()]; + roles[0].domainId = 1; + roles[0].role = Role.ROLE_USER; + roles[0].domainName = 'dom1'; + + authService.storeRoles(roles); + expect(store['rolesToken']).toContain('ROLE_USER'); + }); + it('should get global role from token', () => { + const result = authService.getGlobalRole(); + expect(result).toContain('ROLE_SYSTEM_ADMIN'); + }); + + it('should handle login error with catchError', waitForAsync(() => { + authService.login('user', 'pass').subscribe({ + next: () => fail('Expected error'), + error: (err) => { + expect(err.status).toEqual(401); + } + }); + + const req = httpMock.expectOne('http://api.url/auth/basic/login'); + req.flush({ message: 'Invalid credentials' }, { status: 401, statusText: 'Unauthorized' }); + })); }); diff --git a/src/app/auth/auth.service.ts b/src/app/auth/auth.service.ts index 56aa2c98..6156c8e4 100644 --- a/src/app/auth/auth.service.ts +++ b/src/app/auth/auth.service.ts @@ -15,10 +15,6 @@ export class DomainRoles { ) { } - public getDomainId(): number { - return this.domainId; - } - public getRoles(): string[] { return this.roles; } @@ -181,15 +177,6 @@ export class AuthService { return this.jwtHelper.decodeToken(token).global_role; } - public getDomainsRoles() { - const token = this.getToken(); - if (token == null) { - return null; - } - return this.jwtHelper.decodeToken(token).roles; - - } - public getDomainRoles(): Map<number, DomainRoles> { const domainRolesMap: Map<number, DomainRoles> = new Map<number, DomainRoles>(); @@ -377,11 +364,6 @@ export class AuthService { return (token ? !this.jwtHelper.isTokenExpired(token) : false); } - get isLoggedIn$(): Observable<boolean> { - return this.isLoggedInSubject.pipe( - debounceTime(100), // use debounceTime to aggregate multiple emissions https://rxjs.dev/api/operators/debounceTime - ); - } public getDomainIds(): number[] { return Array.from(new Set(this.profile.map(ur => ur.domainId))); -- GitLab From 89f9cf2bbac5bf2ec09ffa00db2eb858d3bcf187 Mon Sep 17 00:00:00 2001 From: pkazimierowski <pkazimierowski@man.poznan.pl> Date: Wed, 2 Apr 2025 12:27:07 +0200 Subject: [PATCH 11/25] fixed `if` statement --- src/app/welcome/login/login.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/welcome/login/login.component.ts b/src/app/welcome/login/login.component.ts index 6971d776..0a4481b6 100644 --- a/src/app/welcome/login/login.component.ts +++ b/src/app/welcome/login/login.component.ts @@ -66,7 +66,7 @@ export class LoginComponent implements OnInit { // only for use in linking accounts public triggerOIDC() { - if (this.configuration.maintenance) { + if (!this.configuration.maintenance) { window.location.href = this.appConfig.getOidcUrl(); } } -- GitLab From 82c3e817d72b479f36f553cbfc167bb3a07223fe Mon Sep 17 00:00:00 2001 From: pkazimierowski <pkazimierowski@man.poznan.pl> Date: Wed, 2 Apr 2025 13:36:00 +0200 Subject: [PATCH 12/25] added error handle --- .../link-account/link-account.component.html | 2 +- .../link-account/link-account.component.ts | 20 ++++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/app/welcome/link-account/link-account.component.html b/src/app/welcome/link-account/link-account.component.html index 1ab303ed..2d752052 100644 --- a/src/app/welcome/link-account/link-account.component.html +++ b/src/app/welcome/link-account/link-account.component.html @@ -43,7 +43,7 @@ margin-top: 50px; <button type="submit" class="btn btn-primary" (click)="submit()">{{ 'ACCOUNT_LINKING.CONFIRM' | translate }} </button> - + <div *ngIf="error" class="alert alert-danger" style="margin-top: 20px">{{error}}</div> </form> <br> </div> diff --git a/src/app/welcome/link-account/link-account.component.ts b/src/app/welcome/link-account/link-account.component.ts index 1f4f967c..cb60e030 100644 --- a/src/app/welcome/link-account/link-account.component.ts +++ b/src/app/welcome/link-account/link-account.component.ts @@ -3,6 +3,7 @@ import {User} from '../../model'; import {ActivatedRoute, Router} from '@angular/router'; import jwtDecode from 'jwt-decode'; import {AuthService} from '../../auth/auth.service'; +import {TranslateService} from '@ngx-translate/core'; @Component({ @@ -14,11 +15,13 @@ export class LinkAccountComponent implements OnInit, OnDestroy { public user: User; private token: string; public password: string; + public error: string; constructor( private readonly route: ActivatedRoute, private readonly authService: AuthService, - private readonly router: Router + private readonly router: Router, + private translate: TranslateService, ) { } @@ -52,9 +55,24 @@ export class LinkAccountComponent implements OnInit, OnDestroy { ).subscribe( () => { this.router.navigate(['/']); + }, + err => { + this.error = this.translate.instant(this.getMessage(err)); } ) } + private getMessage(err: any): string { + switch (err['status']) { + case 401: + return 'LOGIN.LOGIN_FAILURE_MESSAGE'; + case 406: + return 'LOGIN.APPLICATION_UNDER_MAINTENANCE_MESSAGE'; + case 409: + return 'GENERIC_MESSAGE.UNAVAILABLE_MESSAGE'; + default: + return 'GENERIC_MESSAGE.UNAVAILABLE_MESSAGE'; + } + } } -- GitLab From 5e073a2b51fb548a51cc4a77616a98f759afb864 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20=C5=81opatowski?= <llopat@man.poznan.pl> Date: Wed, 2 Apr 2025 16:18:18 +0200 Subject: [PATCH 13/25] Updated version to 1.7.0 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index a443e538..508d65aa 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { id "org.sonarqube" version "3.2.0" } -version = '1.7.0-SNAPSHOT' +version = '1.7.0' task buildGUI(type: Exec) { println 'Building using Angular CLI' -- GitLab From e382c18715720f96189f8136491918222b5d3443 Mon Sep 17 00:00:00 2001 From: kbeyro <121854496+kbeyro@users.noreply.github.com> Date: Mon, 7 Apr 2025 10:55:01 +0200 Subject: [PATCH 14/25] show error message on submit --- .../appmarket/domains/domain/domain.component.html | 5 +++++ src/app/appmarket/domains/domain/domain.component.ts | 11 +++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/app/appmarket/domains/domain/domain.component.html b/src/app/appmarket/domains/domain/domain.component.html index 00208f2f..c9c32db1 100644 --- a/src/app/appmarket/domains/domain/domain.component.html +++ b/src/app/appmarket/domains/domain/domain.component.html @@ -260,6 +260,11 @@ <div class="flex justify-content-end"> <button *ngIf="!isInMode(ComponentMode.VIEW)" type="submit" class="btn btn-primary" [disabled]="!domainForm.form.valid">{{ 'DOMAIN_DETAILS.SUBMIT_BUTTON' | translate }}</button> </div> + + <br *ngIf="errorMessage"> + <div class="alert alert-danger text-left" *ngIf="errorMessage"> + {{errorMessage}} + </div> </form> </div> diff --git a/src/app/appmarket/domains/domain/domain.component.ts b/src/app/appmarket/domains/domain/domain.component.ts index eecd96f7..5cf1f5e0 100644 --- a/src/app/appmarket/domains/domain/domain.component.ts +++ b/src/app/appmarket/domains/domain/domain.component.ts @@ -9,7 +9,7 @@ import {User} from '../../../model'; import {Observable, of} from 'rxjs'; import {UserRole} from '../../../model/userrole'; import {AuthService} from '../../../auth/auth.service'; -import {ModalComponent} from '../../../shared'; +import {ModalComponent} from '../../../shared'; import {map, shareReplay, take} from 'rxjs/operators'; import {DcnDeploymentType} from '../../../model/dcndeploymenttype'; import {CustomerNetwork} from '../../../model/customernetwork'; @@ -48,6 +48,8 @@ export class DomainComponent extends BaseComponent implements OnInit { public annotations : Observable<DomainAnnotation[]> = of([]); + public errorMessage = ""; + constructor(public domainService: DomainService, protected userService: UserService, private router: Router, @@ -103,7 +105,12 @@ export class DomainComponent extends BaseComponent implements OnInit { if (this.domainId !== undefined) { this.updateExistingDomain(); } else { - this.domainService.add(this.domain).subscribe(() => this.router.navigate(['admin/domains/'])); + this.domainService.add(this.domain).subscribe(() => { + this.router.navigate(['admin/domains/']) + }, err => { + console.error(err); + this.errorMessage = err.message; + }); } this.domainService.setUpdateRequiredFlag(true); } -- GitLab From 107b443c73582a887d1a106663665e9adda2f30e Mon Sep 17 00:00:00 2001 From: kbeyro <121854496+kbeyro@users.noreply.github.com> Date: Mon, 7 Apr 2025 10:59:37 +0200 Subject: [PATCH 15/25] delete hashed token from tabel --- .../users/access-token/access-tokens.component.html | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/app/shared/users/access-token/access-tokens.component.html b/src/app/shared/users/access-token/access-tokens.component.html index 9800061a..6ad21e04 100644 --- a/src/app/shared/users/access-token/access-tokens.component.html +++ b/src/app/shared/users/access-token/access-tokens.component.html @@ -6,22 +6,20 @@ <tr> <th scope="col">{{'TOKENS.TABLE.ID' | translate}}</th> <th scope="col">{{'TOKENS.TABLE.NAME' | translate}}</th> - <th scope="col">{{'TOKENS.TABLE.VALUE' | translate}}</th> <th scope="col">{{'TOKENS.TABLE.VALID' | translate}}</th> <th scope="col">{{'TOKENS.TABLE.ACTIONS' | translate}}</th> </tr> </thead> <tbody> <tr *ngFor="let token of tokensList"> - <td>{{token.id}}</td> - <td>{{token.name}}</td> - <td>{{token.tokenValue}}</td> - <td>{{token.valid}}</td> - <td *ngIf="token.valid"> + <td style="width: 25%;">{{token.id}}</td> + <td style="width: 25%;">{{token.name}}</td> + <td style="width: 25%;">{{token.valid}}</td> + <td style="width: 25%;" *ngIf="token.valid"> <button type="button" class="btn btn-danger" (click)="invalidate(token.id)">{{'TOKENS.BUTTON_INVALIDATE' | translate}}</button> </td> - <td *ngIf="!token.valid"> + <td style="width: 25%;" *ngIf="!token.valid"> <button type="button" class="btn btn-danger" (click)="deleteToken(token.id)">{{'TOKENS.BUTTON_DELETE' | translate}}</button> </td> -- GitLab From 2d8508c0a13aa993f9ce964e9a807c5cb17e1703 Mon Sep 17 00:00:00 2001 From: kbeyro <121854496+kbeyro@users.noreply.github.com> Date: Thu, 10 Apr 2025 14:13:56 +0200 Subject: [PATCH 16/25] fix problem with displaying error based on structure --- src/app/appmarket/domains/domain/domain.component.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/appmarket/domains/domain/domain.component.ts b/src/app/appmarket/domains/domain/domain.component.ts index 5cf1f5e0..b25fb1cc 100644 --- a/src/app/appmarket/domains/domain/domain.component.ts +++ b/src/app/appmarket/domains/domain/domain.component.ts @@ -109,7 +109,10 @@ export class DomainComponent extends BaseComponent implements OnInit { this.router.navigate(['admin/domains/']) }, err => { console.error(err); - this.errorMessage = err.message; + console.warn("error", err.statusCode) + if(err.statusCode !== 409 && err?.message !== undefined) this.errorMessage = err.message; + else this.errorMessage = err; + }); } this.domainService.setUpdateRequiredFlag(true); -- GitLab From 5656161dd6620407d3f619e67bc9947fe79114db Mon Sep 17 00:00:00 2001 From: kbeyro <121854496+kbeyro@users.noreply.github.com> Date: Thu, 10 Apr 2025 14:14:46 +0200 Subject: [PATCH 17/25] delete log --- src/app/appmarket/domains/domain/domain.component.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/appmarket/domains/domain/domain.component.ts b/src/app/appmarket/domains/domain/domain.component.ts index b25fb1cc..d74fcedd 100644 --- a/src/app/appmarket/domains/domain/domain.component.ts +++ b/src/app/appmarket/domains/domain/domain.component.ts @@ -109,7 +109,6 @@ export class DomainComponent extends BaseComponent implements OnInit { this.router.navigate(['admin/domains/']) }, err => { console.error(err); - console.warn("error", err.statusCode) if(err.statusCode !== 409 && err?.message !== undefined) this.errorMessage = err.message; else this.errorMessage = err; -- GitLab From 63bf8b1efe83823fc6d4aa31e3fffdeb8e8c26be Mon Sep 17 00:00:00 2001 From: kbeyro <121854496+kbeyro@users.noreply.github.com> Date: Fri, 11 Apr 2025 13:41:07 +0200 Subject: [PATCH 18/25] use roleExluded, fix domain name --- .../appinstancelist/appinstancelist.component.html | 4 ++-- .../appinstancelist/appinstancelist.component.ts | 11 ----------- src/app/model/app-instance.ts | 1 + src/app/shared/navbar/navbar.component.html | 3 ++- 4 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.html b/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.html index 88caaeec..d97bd6f0 100644 --- a/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.html +++ b/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.html @@ -78,7 +78,7 @@ <td class="col-lg-1 col-md-1">{{appInstance?.applicationVersion}}</td> <td class="col-lg-2 col-md-2" *ngIf="domainId === undefined || domainId === domainService.getGlobalDomainId()"> - {{getDomainNameById(appInstance?.domainId)}} + {{appInstance?.domainName}} </td> <td class="col-lg-1 col-md-1">{{appInstance?.owner?.username}}</td> <td class="col-lg-2 col-md-2">{{appInstance?.createdAt | localDate:'dd-MM-yyyy HH:mm'}}</td> @@ -128,7 +128,7 @@ <td class="col-lg-2 col-md-2">{{appInstance?.applicationName}}</td> <td class="col-lg-1 col-md-1" *ngIf="domainId === undefined || domainId === domainService.getGlobalDomainId()"> - {{getDomainNameById(appInstance?.domainId)}}</td> + {{appInstance?.domainName}}</td> <td class="col-lg-1 col-md-1">{{appInstance?.owner?.username}}</td> <td class="col-lg-2 col-md-2">{{appInstance?.createdAt | localDate:'dd-MM-yyyy HH:mm'}}</td> <td class="col-lg-3 col-md-3">{{ translateState(appInstance?.state) }}</td> diff --git a/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.ts b/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.ts index 1a95ba43..b02be4cc 100644 --- a/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.ts +++ b/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.ts @@ -52,8 +52,6 @@ export class AppInstanceListComponent implements OnInit { public selectedUsername: string; public domainId = 0; - public domains: Domain[] = []; - public searchValue = ''; public selectionOptions = [ { label: this.translateEnum(AppInstanceListSelection.ALL), value: AppInstanceListSelection.ALL }, @@ -73,9 +71,6 @@ export class AppInstanceListComponent implements OnInit { ngOnInit() { this.sessionService.registerCulture(this.translateService.currentLang); - this.domainService.getAll().subscribe(result => { - this.domains.push(...result); - }); const i = sessionStorage.getItem(this.item_number_key); if (i) { this.maxItemsOnPage = +i; @@ -110,12 +105,6 @@ export class AppInstanceListComponent implements OnInit { } - public getDomainNameById(id: number): string { - if (this.domains === undefined) { - return 'none'; - } - return this.domains.find(value => value.id === id).name; - } public translateEnum(value: AppInstanceListSelection): string { let outValue = ''; diff --git a/src/app/model/app-instance.ts b/src/app/model/app-instance.ts index 76edca2f..27119dad 100644 --- a/src/app/model/app-instance.ts +++ b/src/app/model/app-instance.ts @@ -28,6 +28,7 @@ export class AppInstance { public id: number = undefined; public domainId: number = undefined; + public domainName: string = undefined; public applicationId: number = undefined; public applicationName: string = undefined; public applicationVersion: string = undefined; diff --git a/src/app/shared/navbar/navbar.component.html b/src/app/shared/navbar/navbar.component.html index cead22d4..d41a8c16 100644 --- a/src/app/shared/navbar/navbar.component.html +++ b/src/app/shared/navbar/navbar.component.html @@ -77,7 +77,8 @@ <li *roles="['ROLE_SYSTEM_ADMIN']"><a [routerLink]="['/admin/users']">{{ 'NAVBAR.USERS' | translate }}</a> </li> - <li *roles="['ROLE_DOMAIN_ADMIN', 'ROLE_GROUP_DOMAIN_ADMIN']"><a + <li *roles="['ROLE_DOMAIN_ADMIN', 'ROLE_GROUP_DOMAIN_ADMIN']" > + <a *rolesExcluded="['ROLE_SYSTEM_ADMIN']" [routerLink]="['/domain/users']">{{ 'NAVBAR.DOMAIN_USERS' | translate }}</a> </li> <li *roles="['ROLE_SYSTEM_ADMIN']"><a -- GitLab From 570f35eaed6d155396a11bdd336e54800771e76a Mon Sep 17 00:00:00 2001 From: kbeyro <121854496+kbeyro@users.noreply.github.com> Date: Fri, 11 Apr 2025 13:56:34 +0200 Subject: [PATCH 19/25] fix test --- .../appinstance/appinstance/appinstance.component.spec.ts | 1 + .../modals/add-members-modal/add-members-modal.component.spec.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/app/appmarket/appinstance/appinstance/appinstance.component.spec.ts b/src/app/appmarket/appinstance/appinstance/appinstance.component.spec.ts index 449459e4..3634c03e 100644 --- a/src/app/appmarket/appinstance/appinstance/appinstance.component.spec.ts +++ b/src/app/appmarket/appinstance/appinstance/appinstance.component.spec.ts @@ -186,6 +186,7 @@ describe('Component: AppInstance', () => { createdAt: new Date(), descriptiveDeploymentId: 'test-oxidized-48', domainId: 4, + domainName: "Test Domain", id: 1, internalId: 'eccbaf70-7fdd-401a-bb3e-b8659bcfbdff', name: 'oxi-virt-1', diff --git a/src/app/appmarket/appinstance/modals/add-members-modal/add-members-modal.component.spec.ts b/src/app/appmarket/appinstance/modals/add-members-modal/add-members-modal.component.spec.ts index 4a613f13..0e2900d4 100644 --- a/src/app/appmarket/appinstance/modals/add-members-modal/add-members-modal.component.spec.ts +++ b/src/app/appmarket/appinstance/modals/add-members-modal/add-members-modal.component.spec.ts @@ -32,6 +32,7 @@ describe('AddMembersModalComponent', () => { createdAt: new Date(), descriptiveDeploymentId: 'test-oxidized-48', domainId: 4, + domainName: "Test Domain", id: 1, internalId: 'eccbaf70-7fdd-401a-bb3e-b8659bcfbdff', name: 'oxi-virt-1', -- GitLab From 1cd9c8b4dcd4de755dbe98748be5647d994a8540 Mon Sep 17 00:00:00 2001 From: kbeyro <121854496+kbeyro@users.noreply.github.com> Date: Fri, 11 Apr 2025 15:42:31 +0200 Subject: [PATCH 20/25] move role exluded to diffrent directive --- .../login-success/login-success.component.ts | 1 + src/app/directive/roles-exluded.directive.ts | 30 +++++++++++ src/app/directive/roles.directive.ts | 52 +++---------------- src/app/shared/navbar/navbar.component.html | 9 ++-- src/app/shared/shared.module.ts | 9 +++- 5 files changed, 50 insertions(+), 51 deletions(-) create mode 100644 src/app/directive/roles-exluded.directive.ts diff --git a/src/app/auth/login-success/login-success.component.ts b/src/app/auth/login-success/login-success.component.ts index e7fec74d..4d5444dc 100644 --- a/src/app/auth/login-success/login-success.component.ts +++ b/src/app/auth/login-success/login-success.component.ts @@ -25,6 +25,7 @@ export class LoginSuccessComponent implements OnInit { if (refreshToken) { this.authService.storeOidcToken(oidcToken); } + this.authService.loadUser(); this.router.navigate(['/']) }) diff --git a/src/app/directive/roles-exluded.directive.ts b/src/app/directive/roles-exluded.directive.ts new file mode 100644 index 00000000..6b003ab5 --- /dev/null +++ b/src/app/directive/roles-exluded.directive.ts @@ -0,0 +1,30 @@ +import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core'; +import { AuthService } from '../auth/auth.service'; + +@Directive({ + selector: '[rolesExcluded]' +}) +export class RolesExcludedDirective { + private _excluded: Array<string> = []; + + constructor( + private _templateRef: TemplateRef<any>, + private _viewContainer: ViewContainerRef, + private authService: AuthService + ) {} + + @Input() set rolesExcluded(excludedRoles: Array<string>) { + this._excluded = excludedRoles; + this.updateState(); + } + + private updateState() { + this._viewContainer.clear(); + + const hasExcludedRole = this._excluded.some(role => this.authService.hasRole(role)); + if (!hasExcludedRole) { + // If user has exluded role hide the element + this._viewContainer.createEmbeddedView(this._templateRef); + } + } +} \ No newline at end of file diff --git a/src/app/directive/roles.directive.ts b/src/app/directive/roles.directive.ts index 68f2e123..c2155e3c 100644 --- a/src/app/directive/roles.directive.ts +++ b/src/app/directive/roles.directive.ts @@ -3,10 +3,7 @@ import {Directive, Input, TemplateRef, ViewContainerRef} from '@angular/core'; class RoleState { public allowed: Array<string> = new Array<string>(); - public excluded: Array<string> = new Array<string>() } - - @Directive({ selector: '[roles]', inputs: ['roles'] @@ -15,8 +12,6 @@ export class RolesDirective { private _allowed: Array<string> = new Array<string>(); - private _excluded: Array<string> = new Array<string>(); - constructor(private _templateRef: TemplateRef<any>, private _viewContainer: ViewContainerRef, private authService: AuthService) { @@ -26,53 +21,18 @@ export class RolesDirective { @Input() set roles(allowedRoles: Array<string>) { this._allowed = allowedRoles; this.updateState({ - allowed: this._allowed, - excluded: this._excluded - }) - } - - // Excluded roles have priority than allowed roles - // If user have excluded role template would not be shown - - @Input() set rolesExcluded(excluded: Array<string>) { - this._excluded = excluded; - this.updateState({ - allowed: this._allowed, - excluded: this._excluded + allowed: this._allowed }) } updateState(state: RoleState) { this._viewContainer.clear(); - - let show: boolean = false; - let notAllowed: boolean = false; - - const allowedRoles = state.allowed; - - for (let exclude of state.excluded) { - if (this.authService.hasRole(exclude)) { - notAllowed = true; - break; - } + + + const hasAllowedRole = state.allowed.some(role => this.authService.hasRole(role)); + if (hasAllowedRole) { + this._viewContainer.createEmbeddedView(this._templateRef); } - if (notAllowed) { - this._viewContainer.clear(); - } else { - for (let allowedRole of allowedRoles) { - if (this.authService.hasRole(allowedRole)) { - show = true; - break; - } - } - - if (show) { - this._viewContainer.createEmbeddedView(this._templateRef); - } else { - this._viewContainer.clear(); - } - } - } } diff --git a/src/app/shared/navbar/navbar.component.html b/src/app/shared/navbar/navbar.component.html index d41a8c16..11b5cb2e 100644 --- a/src/app/shared/navbar/navbar.component.html +++ b/src/app/shared/navbar/navbar.component.html @@ -77,10 +77,13 @@ <li *roles="['ROLE_SYSTEM_ADMIN']"><a [routerLink]="['/admin/users']">{{ 'NAVBAR.USERS' | translate }}</a> </li> - <li *roles="['ROLE_DOMAIN_ADMIN', 'ROLE_GROUP_DOMAIN_ADMIN']" > - <a *rolesExcluded="['ROLE_SYSTEM_ADMIN']" - [routerLink]="['/domain/users']">{{ 'NAVBAR.DOMAIN_USERS' | translate }}</a> + + <li *roles="['ROLE_DOMAIN_ADMIN', 'ROLE_GROUP_DOMAIN_ADMIN']"> + <a *rolesExcluded="['ROLE_SYSTEM_ADMIN']" + [routerLink]="['/domain/users']">{{ 'NAVBAR.DOMAIN_USERS' | translate }}</a> + </li> + <li *roles="['ROLE_SYSTEM_ADMIN']"><a [routerLink]="['/admin/languages']">{{ 'NAVBAR.LANGUAGES' | translate }}</a> </li> diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index a6243e01..ae644275 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -1,6 +1,6 @@ import {DefaultLogo} from '../directive/defaultlogo.directive'; import {RolesDirective} from '../directive/roles.directive'; -import {NgModule} from '@angular/core'; +import {NgModule, NO_ERRORS_SCHEMA} from '@angular/core'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import {CommonModule, DatePipe} from '@angular/common'; @@ -65,6 +65,7 @@ import { InputGroupModule } from 'primeng/inputgroup'; import { InputGroupAddonModule } from 'primeng/inputgroupaddon'; import { ButtonModule } from 'primeng/button'; import { BrowserModule } from '@angular/platform-browser'; +import { RolesExcludedDirective } from '../directive/roles-exluded.directive'; @NgModule({ @@ -103,6 +104,7 @@ import { BrowserModule } from '@angular/platform-browser'; NavbarComponent, DefaultLogo, RolesDirective, + RolesExcludedDirective, MinLengthDirective, MaxLengthDirective, SearchComponent, @@ -179,13 +181,16 @@ import { BrowserModule } from '@angular/platform-browser'; ModalTestInstanceComponent, ModalNotificationSendComponent, DomainRolesDirective, + RolesExcludedDirective, SshKeysComponent, ModalProvideSshKeyComponent, PreferencesComponent, SortableHeaderDirective, DomainNamespaceAnnotationsComponent, AccessTokensComponent - ] + ], + schemas: [NO_ERRORS_SCHEMA], // Dodanie schematu + }) export class SharedModule { } -- GitLab From 2414c23be391925e439e2acb8fb39d08d1d8f410 Mon Sep 17 00:00:00 2001 From: kbeyro <121854496+kbeyro@users.noreply.github.com> Date: Mon, 14 Apr 2025 10:29:10 +0200 Subject: [PATCH 21/25] fix user call based on mode --- .../shared/users/list/userslist.component.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/app/shared/users/list/userslist.component.ts b/src/app/shared/users/list/userslist.component.ts index 7394575d..1ae3116b 100644 --- a/src/app/shared/users/list/userslist.component.ts +++ b/src/app/shared/users/list/userslist.component.ts @@ -101,11 +101,20 @@ export class UsersListComponent extends BaseComponent implements OnInit, OnChang } public getAllDomain() { - this.domainService.getAll().subscribe(domains => { - domains.forEach(domain => { - this.domainCache.setData(domain.id, domain) + if(this.domainMode) { + this.domainService.getMyDomains().subscribe(domains => { + domains.forEach(domain => { + this.domainCache.setData(domain.id, domain) + } + ) }) + } else { + this.domainService.getAll().subscribe(domains => { + domains.forEach(domain => { + this.domainCache.setData(domain.id, domain) + }) }) - }) + } + } public getDomainName(domainId: number): Observable<string> { -- GitLab From 80d8c5424cb915ed9cb254de69658f68e33af808 Mon Sep 17 00:00:00 2001 From: kbeyro <121854496+kbeyro@users.noreply.github.com> Date: Mon, 14 Apr 2025 10:42:59 +0200 Subject: [PATCH 22/25] add group domain admin --- src/app/appmarket/appdetails/appdetails.component.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/appmarket/appdetails/appdetails.component.ts b/src/app/appmarket/appdetails/appdetails.component.ts index e5518794..cb156314 100644 --- a/src/app/appmarket/appdetails/appdetails.component.ts +++ b/src/app/appmarket/appdetails/appdetails.component.ts @@ -146,7 +146,8 @@ export class AppDetailsComponent implements OnInit { } return this.authService.hasRole(Role[Role.ROLE_SYSTEM_ADMIN]) - || this.authService.hasDomainRole(this.domainId, Role[Role.ROLE_DOMAIN_ADMIN]); + || this.authService.hasDomainRole(this.domainId, Role[Role.ROLE_DOMAIN_ADMIN]) + || this.authService.hasDomainRole(this.domainId, Role[Role.ROLE_GROUP_DOMAIN_ADMIN]); } public isApplicationEnabledInDomain(): boolean { -- GitLab From e54db25177602c0a0590d5525cbac23c1f87a04d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20=C5=81opatowski?= <llopat@man.poznan.pl> Date: Mon, 14 Apr 2025 16:21:45 +0200 Subject: [PATCH 23/25] Updated version to 1.7.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 881b777c..2a4d1793 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "nmaas-portal", - "version": "1.7.0", + "version": "1.7.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "nmaas-portal", - "version": "1.7.0", + "version": "1.7.1", "license": "Apache 2.0", "dependencies": { "@angular/animations": "17.3.12", diff --git a/package.json b/package.json index 9402af95..8f55d7ac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nmaas-portal", - "version": "1.7.0", + "version": "1.7.1", "license": "Apache 2.0", "angular-cli": {}, "scripts": { -- GitLab From 2b999125bc407010b5381226dffa8585cc8acb9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20=C5=81opatowski?= <llopat@man.poznan.pl> Date: Mon, 14 Apr 2025 16:34:52 +0200 Subject: [PATCH 24/25] Added Mend scanning CI job --- .gitlab-ci.yml | 24 +++++++++++++++++++++++- ws/run_ws.sh | 7 ------- 2 files changed, 23 insertions(+), 8 deletions(-) delete mode 100644 ws/run_ws.sh diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ca9b9640..73cc5798 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,6 +2,7 @@ stages: - test - sonar - build + - mend test: stage: test @@ -64,4 +65,25 @@ build_and_push_release_image: docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD $DOCKER_REPOSITORY docker build -t $DOCKER_REPOSITORY_LOCAL:$IMAGE_TAG . docker push $DOCKER_REPOSITORY_LOCAL:$IMAGE_TAG - docker logout $DOCKER_REPOSITORY \ No newline at end of file + docker logout $DOCKER_REPOSITORY + +mend: + stage: mend + image: openjdk:17-jdk-slim + only: + - /^release/ + variables: + PRODUCT_NAME: "nmaas" + PROJECT_NAME: "nmaas-portal" + script: + - | + export PRODUCT_VERSION=$(echo $CI_COMMIT_BRANCH | cut -c 9-) + export PROJECT_VERSION=$PRODUCT_VERSION + apt-get update && apt-get install -y curl gnupg + curl -sL https://deb.nodesource.com/setup_11.x | bash - + apt-get -y install nodejs + npm install -g @angular/cli + npm ci + chmod +x ./gradlew + curl -LJO https://github.com/whitesource/unified-agent-distribution/releases/latest/download/wss-unified-agent.jar + java -jar wss-unified-agent.jar -userKey ${MEND_USER_KEY} -apiKey ${MEND_API_KEY} -projectVersion ${PROJECT_VERSION} -project ${PROJECT_NAME} -productVersion ${PRODUCT_VERSION} -product ${PRODUCT_NAME} -c ./ws/ws.config -d ./ \ No newline at end of file diff --git a/ws/run_ws.sh b/ws/run_ws.sh deleted file mode 100644 index 8763a587..00000000 --- a/ws/run_ws.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -echo "Downloading WhiteSource agent..." -curl -LJO https://github.com/whitesource/unified-agent-distribution/releases/latest/download/wss-unified-agent.jar - -echo "Running WhiteSource scan..." -java -jar wss-unified-agent.jar -userKey ${USER_KEY} -apiKey ${API_KEY} -projectVersion ${PROJECT_VERSION} -projectToken ${PROJECT_TOKEN} -productVersion ${PRODUCT_VERSION} -productToken ${PRODUCT_TOKEN} -c ws.config -d ../ \ No newline at end of file -- GitLab From 4dd4bae59e2f6b33e70eba7d9d3326ab13992d00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20=C5=81opatowski?= <llopat@man.poznan.pl> Date: Tue, 15 Apr 2025 08:33:30 +0200 Subject: [PATCH 25/25] Fix in Mend scanning CI job --- .gitlab-ci.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 73cc5798..525ea473 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -79,9 +79,7 @@ mend: - | export PRODUCT_VERSION=$(echo $CI_COMMIT_BRANCH | cut -c 9-) export PROJECT_VERSION=$PRODUCT_VERSION - apt-get update && apt-get install -y curl gnupg - curl -sL https://deb.nodesource.com/setup_11.x | bash - - apt-get -y install nodejs + apt-get update && apt-get install -y curl nodejs npm npm install -g @angular/cli npm ci chmod +x ./gradlew -- GitLab