diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 59111b6c8602d03d4c0d954364413f6f6dfd2b3c..ca9b96403403231ff452d8fd94b9fe9207c104be 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -20,6 +20,7 @@ sonar: image: trion/ng-cli:17.3.7 only: - develop + - /^release/ script: - npm ci --force - npm run sonar -- -Dsonar.host.url=${SONAR_HOST} -Dsonar.projectKey=${SONAR_PROJECT_KEY} -Dsonar.projectName=${SONAR_PROJECT_NAME} -Dsonar.branch.name=develop -Dsonar.login=${SONAR_LOGIN_TOKEN} diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index c16ff45ea28e9f74a889c241a77b0d005624c223..610c10ccf5c6a61d2610e4be2b3fdefe9e51fcde 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -5,13 +5,16 @@ import { AppMarketRoutes } from './appmarket'; 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'; const appRoutes: Routes = [ ...WelcomeRoutes, ...AppMarketRoutes, ...ServiceUnavailableRoutes, { path: 'notfound', component: PageNotFoundComponent }, - { path: '**', redirectTo: '/welcome' } + { path: 'login-success', component: LoginSuccessComponent }, + { path: '**', redirectTo: '/welcome' }, + ]; export const routing = RouterModule.forRoot(appRoutes, { scrollPositionRestoration: 'enabled' }); diff --git a/src/app/auth/auth.module.ts b/src/app/auth/auth.module.ts index 27bb76c9fc8bfe15a5c61ce85a34c6eee4dbe0fc..cab76d65d8c483ba806570bc6daa101e29e701c3 100644 --- a/src/app/auth/auth.module.ts +++ b/src/app/auth/auth.module.ts @@ -5,6 +5,7 @@ import {AppConfigService} from '../service/appconfig.service'; import {AuthService} from './auth.service' import {AuthGuard} from './auth.guard' import {RoleGuard} from './role.guard'; +import { LoginSuccessComponent } from './login-success/login-success.component'; export const jwtOptionsFactory = (appConfig: AppConfigService) => ({ @@ -15,6 +16,9 @@ export const jwtOptionsFactory = (appConfig: AppConfigService) => ({ }); @NgModule({ + declarations: [ + LoginSuccessComponent + ], providers: [ AuthGuard, RoleGuard, diff --git a/src/app/auth/auth.service.ts b/src/app/auth/auth.service.ts index 3b4e4b9bde503ff4aa6003d120c7ac405b8d2512..ca9cd4baf71a4e61c5ebc46832ca7e0d4d821875 100644 --- a/src/app/auth/auth.service.ts +++ b/src/app/auth/auth.service.ts @@ -34,8 +34,8 @@ export class AuthService { private appConfig: AppConfigService, private jwtHelper: JwtHelperService) { } - - private storeToken(token: string): void { + //TODO make this static again and serive this feature in other way + public storeToken(token: string): void { localStorage.setItem(this.appConfig.config.tokenName, token); } diff --git a/src/app/auth/login-success/login-success.component.css b/src/app/auth/login-success/login-success.component.css new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/app/auth/login-success/login-success.component.html b/src/app/auth/login-success/login-success.component.html new file mode 100644 index 0000000000000000000000000000000000000000..bf2b4b51596bcb734c40c6d46e1ec4917d9b6f71 --- /dev/null +++ b/src/app/auth/login-success/login-success.component.html @@ -0,0 +1 @@ +<p>login-success works!</p> diff --git a/src/app/auth/login-success/login-success.component.spec.ts b/src/app/auth/login-success/login-success.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..c2a446bbc2bc60e4ade6a4f4d3fa81c78bfdc21f --- /dev/null +++ b/src/app/auth/login-success/login-success.component.spec.ts @@ -0,0 +1,36 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoginSuccessComponent } from './login-success.component'; +import { RouterTestingModule } from '@angular/router/testing'; +import { ActivatedRoute } from '@angular/router'; +import { ActivatedRouteStub } from '../../shared/test-utils'; +import { AuthService } from '../auth.service'; + +describe('LoginSuccessComponent', () => { + let component: LoginSuccessComponent; + let fixture: ComponentFixture<LoginSuccessComponent>; + + const authUserSpy = jasmine.createSpyObj('AuthService', ['storeToken']); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ LoginSuccessComponent ], + imports: [ + RouterTestingModule + ], + providers: [ + {provide: ActivatedRoute, useValue: new ActivatedRouteStub({token: '123'})}, + {provide: AuthService, useValue: authUserSpy}, + ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(LoginSuccessComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/auth/login-success/login-success.component.ts b/src/app/auth/login-success/login-success.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..d84fe015ab346f24da5bd1a2e1743b6ade19eca9 --- /dev/null +++ b/src/app/auth/login-success/login-success.component.ts @@ -0,0 +1,26 @@ +import {Component, OnInit} from '@angular/core'; +import {ActivatedRoute} from '@angular/router'; +import {AuthService} from '../auth.service'; + +@Component({ + selector: 'app-login-success', + templateUrl: './login-success.component.html', + styleUrls: ['./login-success.component.css'] +}) +export class LoginSuccessComponent implements OnInit { + constructor(private route: ActivatedRoute, + private authService: AuthService) { + } + + + ngOnInit(): void { + // Pobieranie tokena z parametrów URL + this.route.queryParams.subscribe(params => { + const token = params['token']; + const refreshToken = params['refresh_token']; + if (token) { + this.authService.storeToken(token); + } + }); + } +} diff --git a/src/app/service/appconfig.service.ts b/src/app/service/appconfig.service.ts index 46e77329d2aee5953b8563cf0114e2411917296e..c9988bcc13d5245f923bc152e29f7618cc774ac7 100644 --- a/src/app/service/appconfig.service.ts +++ b/src/app/service/appconfig.service.ts @@ -1,5 +1,5 @@ -import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; +import {Injectable} from '@angular/core'; +import {HttpClient} from '@angular/common/http'; @Injectable({ @@ -10,7 +10,8 @@ export class AppConfigService { public jwtAllowedDomains: string[] = [] - constructor(private http: HttpClient) { } + constructor(private http: HttpClient) { + } public load() { return new Promise<void>((resolve) => { @@ -23,39 +24,46 @@ export class AppConfigService { }); } + public getOidcUrl(): string { + if (this.config == null) { + return 'http://localhost:9000/oauth2/authorization/my-oidc'; + } + return this.config.oidcUrl; + } + public getApiUrl(): string { - if (this.config == null) { - return 'http://localhost/api'; - } - return this.config.apiUrl; + if (this.config == null) { + return 'http://localhost/api'; + } + return this.config.apiUrl; } public getNmaasGlobalDomainId(): number { - if (this.config == null) { - return 0; - } - return this.config.nmaas.globalDomainId || 0; + if (this.config == null) { + return 0; + } + return this.config.nmaas.globalDomainId || 0; } public getHttpTimeout(): number { - if (this.config == null) { - return 10000; - } - return this.config.http.timeout || 10000; + if (this.config == null) { + return 10000; + } + return this.config.http.timeout || 10000; } public getShowGitInfo(): boolean { - if (this.config == null) { - return false; - } - return this.config.showGitInfo || false; + if (this.config == null) { + return false; + } + return this.config.showGitInfo || false; } public getShowChangelog(): boolean { - if (this.config == null) { - return false; - } - return this.config.showChangelog || false; + if (this.config == null) { + return false; + } + return this.config.showChangelog || false; } public getSiteKey(): string { @@ -70,6 +78,6 @@ export class AppConfigService { } public getLandingProfile(): string { - return this.config.landing || '' + return this.config.landing || '' } } diff --git a/src/app/service/configuration.service.ts b/src/app/service/configuration.service.ts index 6e03d6f911758fc5dbbe32e2c3a4a92b5789b17a..9a247125cd18b4bd8f7cb2bb0e74e19cc9e62d10 100644 --- a/src/app/service/configuration.service.ts +++ b/src/app/service/configuration.service.ts @@ -14,7 +14,7 @@ export class ConfigurationService extends GenericDataService{ constructor(http: HttpClient, appConfig: AppConfigService) { super(http, appConfig); - this.uri = this.appConfig.getApiUrl() + '/configuration/' + this.uri = this.appConfig.getApiUrl() + '/configuration' } public getConfiguration(): Observable<Configuration> { diff --git a/src/app/service/domain.service.ts b/src/app/service/domain.service.ts index dfff8ef6b175c94517fe78cdbb03feafe3f65202..ead7327158be7cadb18056da214ba9224f67a410 100644 --- a/src/app/service/domain.service.ts +++ b/src/app/service/domain.service.ts @@ -24,7 +24,7 @@ export class DomainService extends GenericDataService { constructor(http: HttpClient, appConfig: AppConfigService) { super(http, appConfig); this.updateRequiredFlag = false; - this.url = this.appConfig.getApiUrl() + '/domains/'; + this.url = this.appConfig.getApiUrl() + '/domains'; } public getGlobalDomainId(): number { @@ -40,7 +40,7 @@ export class DomainService extends GenericDataService { } public getOne(domainId: number): Observable<Domain> { - return this.get<Domain>(this.url + domainId); + return this.get<Domain>(this.url + '/' + domainId); } public add(domain: Domain): Observable<Id> { @@ -48,19 +48,19 @@ export class DomainService extends GenericDataService { } public update(domain: Domain): Observable<any> { - return this.put<Domain, Id>(this.url + domain.id, domain); + return this.put<Domain, Id>(this.url + '/' + domain.id, domain); } public updateTechDetails(domain: Domain): Observable<any> { - return this.patch<Domain, Id>(this.url + domain.id, domain) + return this.patch<Domain, Id>(this.url + '/' + domain.id, domain) } public updateDcnConfigured(domain: Domain): Observable<any> { - return this.patch<Domain, Id>(this.url + domain.id + '/dcn?configured=' + domain.domainDcnDetails.dcnConfigured, null); + return this.patch<Domain, Id>(this.url + '/' + domain.id + '/dcn?configured=' + domain.domainDcnDetails.dcnConfigured, null); } public updateDomainState(domain: Domain): Observable<any> { - return this.patch<Domain, Id>(this.url + domain.id + '/state?active=' + !domain.active, null); + return this.patch<Domain, Id>(this.url + '/' + domain.id + '/state?active=' + !domain.active, null); } public remove(domainId: number, softRemove?: boolean): Observable<any> { @@ -68,15 +68,15 @@ export class DomainService extends GenericDataService { if (softRemove !== undefined) { params = params.append("softRemove", softRemove.toString()) } - return this.http.delete(this.url + domainId, {params}) + return this.http.delete(this.url + '/' + domainId, {params}) } public getMyDomains(): Observable<Domain[]> { - return this.get<Domain[]>(this.url + 'my'); + return this.get<Domain[]>(this.url + '/my'); } public getUsers(domainId: number): Observable<User[]> { - return this.get<User[]>(this.url + 'users'); + return this.get<User[]>(this.url + '/users'); } public setUpdateRequiredFlag(flag: boolean) { @@ -89,31 +89,31 @@ export class DomainService extends GenericDataService { // GROUPS public getAllDomainGroups(): Observable<DomainGroup[]> { - return this.get<DomainGroup[]>(this.url + 'group'); + return this.get<DomainGroup[]>(this.url + '/group'); } public getDomainGroup(domainGroupId: number): Observable<DomainGroup> { - return this.get<DomainGroup>(this.url + 'group/' + domainGroupId); + return this.get<DomainGroup>(this.url + '/group/' + domainGroupId); } public deleteDomainGroup(domainGroupId: number): Observable<void> { - return this.delete<void>(this.url + 'group/' + domainGroupId); + return this.delete<void>(this.url + '/group/' + domainGroupId); } public addDomainsToGroup(groupCodeName: string, domainIds: number[]): Observable<DomainGroup> { - return this.post(this.url + 'group/' + groupCodeName, domainIds); + return this.post(this.url + '/group/' + groupCodeName, domainIds); } public deleteDomainFromGroup(groupId: number, domainId: number): Observable<DomainGroup> { - return this.patch(this.url + 'group/' + groupId, domainId); + return this.patch(this.url + '/group/' + groupId, domainId); } public createDomainGroup(domainGroup: DomainGroup): Observable<Id> { - return this.post(this.url + 'group', domainGroup); + return this.post(this.url + '/group', domainGroup); } public updateDomainGroup(domainGroup: DomainGroup, id: number): Observable<Id> { - return this.put(this.url + 'group/' + id, domainGroup); + return this.put(this.url + '/group/' + id, domainGroup); } public updateDomainGroupManagers(managers: User[], id: number): Observable<DomainGroup> { @@ -121,18 +121,18 @@ export class DomainService extends GenericDataService { } public getAnnotations(): Observable<DomainAnnotation[]> { - return this.get<DomainAnnotation[]>(this.url + 'annotations') + return this.get<DomainAnnotation[]>(this.url + '/annotations') } public addAnnotations(annotation: KeyValue): Observable<void> { - return this.post(this.url + 'annotations', annotation) + return this.post(this.url + '/annotations', annotation) } - public deleteAnnotation(id: number) : Observable<void>{ - return this.delete(`${this.url}annotations/${id}`) + public deleteAnnotation(id: number) : Observable<void> { + return this.delete(`${this.url}/annotations/${id}`) } public updateAnnotation(annotation: DomainAnnotation): Observable<void> { - return this.put(`${this.url}annotations/${annotation.id}`, annotation) + return this.put(`${this.url}/annotations/${annotation.id}`, annotation) } } diff --git a/src/app/service/user.service.ts b/src/app/service/user.service.ts index 66db21d2b48f72e4a20521a5d2242480f955cc58..bfdf43c7362644120cee3c2a4ac7dfac4e6c3494 100644 --- a/src/app/service/user.service.ts +++ b/src/app/service/user.service.ts @@ -24,15 +24,15 @@ export class UserService extends GenericDataService { } public getOne(userId: number, domainId?: number): Observable<User> { - return this.get<User>((domainId === undefined ? this.getUsersUrl() : this.getDomainUsersUrl(domainId)) + userId); + return this.get<User>((domainId === undefined ? this.getUsersUrl() : this.getDomainUsersUrl(domainId)) + '/' + userId); } public deleteOne(userId: number, domainId?: number): Observable<any> { - return this.delete<any>((domainId === undefined ? this.getUsersUrl() : this.getDomainUsersUrl(domainId)) + userId); + return this.delete<any>((domainId === undefined ? this.getUsersUrl() : this.getDomainUsersUrl(domainId)) + '/' + userId); } public getRoles(userId: number, domainId?: number): Observable<UserRole[]> { - return this.get<UserRole[]>((domainId === undefined ? this.getUsersUrl() : this.getDomainUsersUrl(domainId)) + userId + '/roles'); + return this.get<UserRole[]>((domainId === undefined ? this.getUsersUrl() : this.getDomainUsersUrl(domainId)) + '/' + userId + '/roles'); } public updateUser(userId: number, user: User): Observable<any> { @@ -53,7 +53,7 @@ export class UserService extends GenericDataService { } public addRole(userId: number, role: Role, domainId?: number): Observable<any> { - const url: string = (domainId === undefined ? this.getUsersUrl() : this.getDomainUsersUrl(domainId)) + userId + '/roles'; + const url: string = (domainId === undefined ? this.getUsersUrl() : this.getDomainUsersUrl(domainId)) + '/' + userId + '/roles'; const targetDomainId: number = (domainId === undefined ? this.appConfig.getNmaasGlobalDomainId() : domainId); return this.post<UserRole, UserRole>(url, new UserRole(targetDomainId, undefined, role)); @@ -88,7 +88,7 @@ export class UserService extends GenericDataService { } protected getDomainUsersUrl(domainId: number): string { - return this.appConfig.getApiUrl() + '/domains/' + domainId + '/users/'; + return this.appConfig.getApiUrl() + '/domains/' + domainId + '/users'; } public getDomainUsersAsAdmin(domainId: number): Observable<User[]> { diff --git a/src/app/shared/test-utils.ts b/src/app/shared/test-utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..41f9b1914dbfc1d24dc56337bce64bd10d1c40a6 --- /dev/null +++ b/src/app/shared/test-utils.ts @@ -0,0 +1,31 @@ +import { convertToParamMap, ParamMap, Params } from "@angular/router"; +import { Observable, ReplaySubject } from "rxjs"; + +export class ActivatedRouteStub { + // Use a ReplaySubject to share previous values with subscribers + // and pump new values into the `paramMap` observable + private readonly subject = new ReplaySubject<ParamMap>(); + private readonly subjectQuery = new ReplaySubject<ParamMap>(); + snapshot = {}; + + constructor(initialParams?: Params, initialQueryParams?: Params) { + this.setParamMap(initialParams); + } + + /** The mock paramMap observable */ + readonly paramMap = this.subject.asObservable(); + readonly queryParamMap = this.subjectQuery.asObservable(); + + /** Set the paramMap observables's next value */ + setParamMap(params?: Params) { + this.subject.next(convertToParamMap(params)); + } + + setQueryParamMap(params?: Params) { + this.subjectQuery.next(convertToParamMap(params)); + } + + get queryParams() : Observable<ParamMap> { + return this.subject.asObservable(); + } + } \ No newline at end of file diff --git a/src/app/welcome/login/login.component.html b/src/app/welcome/login/login.component.html index 27418cc9a946d4901ca623c444d8dde34dfc66ca..7084ea8ee9fea6a7682da1aea6576fc41164ee15 100644 --- a/src/app/welcome/login/login.component.html +++ b/src/app/welcome/login/login.component.html @@ -33,6 +33,13 @@ <img alt="sso" *ngIf="ssoLoading" src="data:"/> <div *ngIf="ssoError" class="alert alert-danger">{{ssoError}}</div> </div> +<div class="form-group"> + <button type="submit" (click)="triggerOIDC()" class="btn btn-primary btn-block"> + {{ 'LOGIN.LOGIN_WITH' | translate }} + </button> + <img alt="sso" *ngIf="ssoLoading" src="data:"/> + <div *ngIf="ssoError" class="alert alert-danger">{{ssoError}}</div> +</div> <div class="form-group" style="text-align: center"> <a (click)="resetPassword = !resetPassword">{{ 'RESET_PASSWORD.FORGOT_PASSWORD_BUTTON' | translate }}</a> </div> diff --git a/src/app/welcome/login/login.component.spec.ts b/src/app/welcome/login/login.component.spec.ts index 0fd649ba25d4732652f9919c17045d22051cf1e4..35e39bc620879877c64f1d11636786124b0439f8 100644 --- a/src/app/welcome/login/login.component.spec.ts +++ b/src/app/welcome/login/login.component.spec.ts @@ -10,6 +10,7 @@ import {ConfigurationService, UserService} from '../../service'; import {SSOService} from '../../service/sso.service'; import createSpyObj = jasmine.createSpyObj; import {of} from 'rxjs'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; describe('Component: Login', () => { @@ -28,6 +29,7 @@ describe('Component: Login', () => { FormsModule, ReactiveFormsModule, RouterTestingModule, + HttpClientTestingModule, TranslateModule.forRoot({ loader: { provide: TranslateLoader, diff --git a/src/app/welcome/login/login.component.ts b/src/app/welcome/login/login.component.ts index f82e1de1e556d50bb80c48f8d1d624abdf883b80..dbfcc78194a47a0585edbfd2d35d4b8ee52d32ff 100644 --- a/src/app/welcome/login/login.component.ts +++ b/src/app/welcome/login/login.component.ts @@ -2,7 +2,7 @@ import {Component, OnInit, ViewChild, ViewEncapsulation} from '@angular/core'; import {Router} from '@angular/router'; import {AuthService} from '../../auth/auth.service'; -import {ConfigurationService, UserService} from '../../service'; +import {AppConfigService, ConfigurationService, UserService} from '../../service'; import {Configuration} from '../../model/configuration'; import {SSOService} from '../../service/sso.service'; import {SSOConfig} from '../../model/sso'; @@ -37,7 +37,8 @@ export class LoginComponent implements OnInit { private ssoService: SSOService, private fb: UntypedFormBuilder, private userService: UserService, - private translate: TranslateService) { + private translate: TranslateService, + private appConfig: AppConfigService) { this.resetPasswordForm = fb.group({ email: ['', [Validators.required, Validators.email]] }); @@ -71,6 +72,9 @@ export class LoginComponent implements OnInit { ); } + public triggerOIDC() { + window.location.href = this.appConfig.getOidcUrl(); + } public checkSSO() { const params = this.router.parseUrl(this.router.url).queryParams; diff --git a/src/config.json b/src/config.json index 3b4e008b3f93a80b48bfe1b62a590d6efd354817..cf304ab51e9b9163bb4f10cb3b444323cf2bd941 100644 --- a/src/config.json +++ b/src/config.json @@ -1,5 +1,6 @@ { "apiUrl": "http://localhost:9000/api", + "oidcUrl": "http://localhost:9000/oauth2/authorization/my-oidc", "tokenName": "token", "nmaas": { "globalDomainId": 1