Skip to content
Snippets Groups Projects
Commit c83d1956 authored by OZGCloud's avatar OZGCloud
Browse files

Merge pull request 'OZG-4321-Login' (#443) from OZG-4321-Login into master

parents 05e212c1 76a60043
No related branches found
No related tags found
No related merge requests found
Showing with 344 additions and 0 deletions
import { Mock, mock, useFromMock } from '@alfa-client/test-utils';
import { UserProfileResource } from '@alfa-client/user-profile-shared';
import { createUserProfileResource } from '../../../../libs/user-profile-shared/test/user-profile';
import { AuthConfig, OAuthService } from 'angular-oauth2-oidc';
import { fakeAsync, tick } from '@angular/core/testing';
import { AuthenticationService } from './authentication.service';
import { createAuthConfig } from '../../test/authentication.test';
import { createEnvironment } from '../../../environment-shared/test/environment';
describe('AuthenticationService', () => {
let service: AuthenticationService;
let oAuthService: Mock<OAuthService>;
let environmentConfig;
beforeEach(() => {
oAuthService = <any>{
...mock(OAuthService),
loadDiscoveryDocumentAndLogin: jest.fn().mockResolvedValue(() => Promise.resolve()),
};
environmentConfig = createEnvironment();
service = new AuthenticationService(useFromMock(oAuthService), environmentConfig);
});
describe('login', () => {
it('should configure service with authConfig', () => {
const authConfig: AuthConfig = createAuthConfig();
service.buildConfiguration = jest.fn().mockReturnValue(authConfig);
service.login();
expect(oAuthService.configure).toHaveBeenCalledWith(authConfig);
});
it('should setup automatic silent refresh', () => {
service.login();
expect(oAuthService.setupAutomaticSilentRefresh).toHaveBeenCalled();
});
it('should set tokenValidator', () => {
oAuthService.tokenValidationHandler = null;
service.login();
expect(oAuthService.tokenValidationHandler).not.toBeNull();
});
it('should load discovery document and login', () => {
service.login();
expect(oAuthService.loadDiscoveryDocumentAndLogin).toHaveBeenCalled();
});
it('should set current user', fakeAsync(() => {
service.setCurrentUser = jest.fn();
service.login();
tick();
expect(service.setCurrentUser).toHaveBeenCalled();
}));
});
describe('set current user', () => {
const identityClaims: Record<string, any> = {
['given_name']: 'Marco',
['family_name']: 'Polo',
};
beforeEach(() => {
oAuthService.getIdentityClaims.mockReturnValue(identityClaims);
});
it('should call oAuthservice to get claims', () => {
service.setCurrentUser();
expect(oAuthService.getIdentityClaims).toHaveBeenCalled();
});
it('should update currentUser', () => {
service.setCurrentUser();
expect(service.currentUserResource.firstName).toEqual('Marco');
expect(service.currentUserResource.lastName).toEqual('Polo');
});
});
describe('getCurrentUserInitials', () => {
it('should return currentUserResource', () => {
const userProfile: UserProfileResource = {
...createUserProfileResource(),
firstName: 'Marco',
lastName: 'Polo',
};
service.currentUserResource = userProfile;
const currentUserInitials: string = service.getCurrentUserInitials();
expect(currentUserInitials).toEqual('MP');
});
});
describe('logout', () => {
it('should call oAuthService revokeTokenAndLogout', () => {
service.logout();
expect(oAuthService.revokeTokenAndLogout).toHaveBeenCalled();
});
});
});
import { ENVIRONMENT_CONFIG, Environment } from '@alfa-client/environment-shared';
import { Inject, Injectable } from '@angular/core';
import { OAuthService, AuthConfig } from 'angular-oauth2-oidc';
import { JwksValidationHandler } from 'angular-oauth2-oidc-jwks';
import { UserProfileResource } from 'libs/user-profile-shared/src/lib/user-profile.model';
import { getUserNameInitials } from 'libs/user-profile-shared/src/lib/user-profile.util';
@Injectable({ providedIn: 'root' })
export class AuthenticationService {
currentUserResource: UserProfileResource;
constructor(
private oAuthService: OAuthService,
@Inject(ENVIRONMENT_CONFIG) private envConfig: Environment,
) {}
public async login(): Promise<void> {
this.oAuthService.configure(this.buildConfiguration());
this.oAuthService.setupAutomaticSilentRefresh();
this.oAuthService.tokenValidationHandler = new JwksValidationHandler();
await this.oAuthService.loadDiscoveryDocumentAndLogin();
this.setCurrentUser();
}
buildConfiguration(): AuthConfig {
return {
issuer: this.envConfig.authServer + '/realms/' + this.envConfig.realm,
tokenEndpoint:
this.envConfig.authServer +
'/realms/' +
this.envConfig.realm +
'/protocol/openid-connect/token',
redirectUri: window.location.origin + '/',
clientId: this.envConfig.clientId,
scope: 'openid profile',
requireHttps: false,
responseType: 'code',
showDebugInformation: false,
};
}
setCurrentUser(): void {
const claims: Record<string, any> = this.oAuthService.getIdentityClaims();
const userResource: UserProfileResource = <any>{
firstName: claims['given_name'],
lastName: claims['family_name'],
};
this.currentUserResource = userResource;
}
public getCurrentUserInitials(): string {
return getUserNameInitials(this.currentUserResource);
}
public logout(): void {
this.oAuthService.revokeTokenAndLogout();
}
}
import { Mock, mock } from '@alfa-client/test-utils';
import { TestBed } from '@angular/core/testing';
import { MatDialogModule } from '@angular/material/dialog';
import { HttpUnauthorizedInterceptor } from './http-unauthorized.interceptor';
import { HttpErrorResponse, HttpHandler, HttpRequest } from '@angular/common/http';
import { Subject, isEmpty } from 'rxjs';
import { AuthenticationService } from './authentication.service';
describe('HttpUnauthorizedInterceptor', () => {
let interceptor: HttpUnauthorizedInterceptor;
const authenticationService: Mock<AuthenticationService> = mock(AuthenticationService);
beforeEach(() =>
TestBed.configureTestingModule({
imports: [MatDialogModule],
providers: [
HttpUnauthorizedInterceptor,
{
provide: AuthenticationService,
useValue: authenticationService,
},
],
}),
);
beforeEach(() => {
interceptor = TestBed.inject(HttpUnauthorizedInterceptor);
});
it('should be created', () => {
expect(interceptor).toBeTruthy();
});
describe('intercept', () => {
const error: HttpErrorResponse = new HttpErrorResponse({});
const handleSubject: Subject<any> = new Subject();
const httpHandler: HttpHandler = { handle: () => handleSubject };
const request: HttpRequest<unknown> = new HttpRequest('GET', '/test');
it('should call handleError with error', () => {
interceptor.handleError = jest.fn();
interceptor.intercept(request, httpHandler).subscribe();
handleSubject.error(error);
expect(interceptor.handleError).toHaveBeenCalledWith(error);
});
});
describe('handleError', () => {
describe('on unauthorized status', () => {
const unauthorizedError: any = new HttpErrorResponse({ status: 401 });
it('should call logout on authService ', () => {
interceptor.handleError(unauthorizedError);
expect(authenticationService.logout).toHaveBeenCalled();
});
it('should return EMPTY', (done) => {
interceptor
.handleError(unauthorizedError)
.pipe(isEmpty())
.subscribe((res) => {
expect(res).toBeTruthy;
done();
});
});
});
it('should rethrow error if not unauthorized status', () => {
const anyError: any = new HttpErrorResponse({ status: 500 });
expect(() => interceptor.handleError(anyError)).toThrowError();
});
});
});
import {
HttpErrorResponse,
HttpEvent,
HttpHandler,
HttpInterceptor,
HttpRequest,
} from '@angular/common/http';
import { Injectable } from '@angular/core';
import { isUnauthorized } from '@alfa-client/tech-shared';
import { EMPTY, Observable } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { AuthenticationService } from './authentication.service';
@Injectable()
export class HttpUnauthorizedInterceptor implements HttpInterceptor {
constructor(private authenticationService: AuthenticationService) {}
public intercept(
request: HttpRequest<unknown>,
next: HttpHandler,
): Observable<HttpEvent<unknown>> {
return next
.handle(request)
.pipe(catchError((error: HttpErrorResponse) => this.handleError(error)));
}
handleError(error: HttpErrorResponse): Observable<any> {
if (isUnauthorized(error.status)) {
this.authenticationService.logout();
return EMPTY;
}
throw error;
}
}
import 'jest-preset-angular/setup-jest';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting,
} from '@angular/platform-browser-dynamic/testing';
getTestBed().resetTestEnvironment();
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), {
teardown: { destroyAfterEach: false },
});
import { AuthConfig } from 'angular-oauth2-oidc';
export function createAuthConfig(): AuthConfig {
return {};
}
{
"extends": "../../tsconfig.base.json",
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
],
"compilerOptions": {
"target": "es2020"
}
}
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"declaration": true,
"declarationMap": true,
"inlineSources": true,
"types": []
},
"exclude": ["src/**/*.spec.ts", "src/test-setup.ts", "jest.config.ts", "src/**/*.test.ts"],
"include": ["src/**/*.ts", "test/authentication.test.ts"]
}
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"target": "es2016",
"types": ["jest", "node"]
},
"files": ["src/test-setup.ts"],
"include": [
"jest.config.ts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.d.ts",
"test/authentication.test.ts"
]
}
......@@ -53,6 +53,7 @@
"@alfa-client/vorgang-shared-ui": ["libs/vorgang-shared-ui/src/index.ts"],
"@alfa-client/wiedervorlage": ["libs/wiedervorlage/src/index.ts"],
"@alfa-client/wiedervorlage-shared": ["libs/wiedervorlage-shared/src/index.ts"],
"authentication": ["libs/authentication/src/index.ts"],
"design-system": ["libs/design-system/src/index.ts"]
}
},
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment