Skip to content
This repository has been archived by the owner on Sep 15, 2023. It is now read-only.

Release/4.4.x #229

Merged
merged 72 commits into from
Aug 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
aa54d91
implemented A7 report
Jun 17, 2022
4d8df0e
changed retrieval of data rooms from userroles instead of realm_access
Jun 20, 2022
503ad68
added missing translations
Jun 20, 2022
c66c8a5
added missing translation
Jun 20, 2022
0147cf8
changed JSON key for API endpoint
Jun 24, 2022
ef2d139
canton is now being preselected if only one is available
Jun 24, 2022
9af6cf7
Merge pull request #211 from admin-ch/feature/VACCINECER-2250_Entwick…
ndinat Jun 28, 2022
aed58f3
fixed translations
Jun 28, 2022
85d59c0
Merge pull request #215 from admin-ch/feature/VACCINECER-2250_Entwick…
ndinat Jun 28, 2022
0c88510
release 4.4.0
Jun 28, 2022
58bb10c
fixed tests; release 4.4.0
Jun 28, 2022
c5ca81e
Merge remote-tracking branch 'origin/release/4.4.x' into release/4.4.x
Jun 28, 2022
31f078c
release 4.4.1
Jun 28, 2022
6b44637
release 4.4.2
Jun 30, 2022
a34c280
improved A7 report:
Jul 1, 2022
983fec9
Merge pull request #216 from admin-ch/feature/VACCINECER-2250_Verbess…
ndinat Jul 1, 2022
5722f04
release 4.4.3
Jul 1, 2022
323cfd8
release 4.4.4
Jul 1, 2022
a1b8534
Changed values of specific data room keys to match values for the bac…
Jul 1, 2022
011625a
Merge pull request #217 from admin-ch/feature/VACCINECER-2250_Verbess…
ndinat Jul 1, 2022
4fe766b
release 4.4.5
Jul 1, 2022
a9c7862
changed translations
Jul 4, 2022
412312f
release 4.4.6
Jul 4, 2022
f7c0b74
extracted report fields date from, date to, data room selection and c…
Jul 5, 2022
2ffa15b
Merge pull request #218 from admin-ch/feature/extract-report-fields-t…
ndinat Jul 5, 2022
33bd05c
Dates in report are now being sent as UTC
Jul 5, 2022
e60160f
Merge pull request #219 from admin-ch/feature/VACINECER-2272_A7_Datum…
ndinat Jul 5, 2022
4f0c9fc
start date and end date now have a min date value of 01.05.2021
Jul 5, 2022
dcdc23b
Merge pull request #220 from admin-ch/feature/VACINECER-2266_Report_A…
ndinat Jul 6, 2022
2107568
release 4.4.7
Jul 6, 2022
f686bcf
implemented report A4/A6 with unit search
Jul 11, 2022
6c54eb4
Merge remote-tracking branch 'origin/release/4.4.x' into feature/VACC…
Jul 11, 2022
ca0a2b6
moved certificate types to bottom
Jul 11, 2022
1e03fd8
pressing enter in the search box now applies the search instead of tr…
Jul 11, 2022
d3ea7b1
added some missing translations
Jul 11, 2022
236de31
changing data room now resets the selected profiles
Jul 11, 2022
c3a73a4
nodes matching the search are now being expanded automatically
Jul 12, 2022
16d161f
chips are now selectable, if selected they show detailed information
Jul 12, 2022
1820a84
Merge pull request #221 from admin-ch/feature/VACCINECER-2178_UI_zur_…
ndinat Jul 12, 2022
99a54f4
implemented issuer search
Jul 15, 2022
80be7c7
Oblique's spinner now only shows on requests to the management service
Jul 15, 2022
5a128de
Enter does not send the request anymore if search values are invalid;…
Jul 15, 2022
9329efc
Merge pull request #223 from admin-ch/bugfix/VACCINECER-2247_UI_Spinn…
ndinat Jul 15, 2022
21c55c2
Merge pull request #222 from admin-ch/feature/VACCINECER-2179_UI_zur_…
ndinat Jul 15, 2022
44d6f88
release 4.4.8
Jul 18, 2022
1e4b566
adjusted api url
Jul 18, 2022
2e5ee8f
release 4.4.9
Jul 18, 2022
a7d5e05
added missing translations
Jul 18, 2022
4b32dc4
release 4.4.10
Jul 18, 2022
7296bca
user profiles table is now shoing on each tree, not only the last
Jul 19, 2022
15a4638
Merge pull request #224 from admin-ch/bugfix/show-user-profiles-inbet…
ndinat Jul 19, 2022
7da6143
release 4.4.11
Jul 19, 2022
cbafd8d
unit tree now keeps being expanded when switching between unit search…
Jul 20, 2022
95f7bdc
adjusted translations
Jul 21, 2022
4f4dd90
implemented max period days validator; implemented max selected user …
Jul 21, 2022
88177a6
show error code if excel limit is exceeded
Jul 21, 2022
3634340
Merge pull request #225 from admin-ch/feature/VACINECER-2287_WebUI_-_…
ndinat Jul 21, 2022
7b91b65
Merge pull request #226 from admin-ch/feature/VACCINECER-2276_WebUI_T…
ndinat Jul 21, 2022
fa331a1
Merge pull request #227 from admin-ch/feature/VACCINECER-2290_WebUI_Z…
ndinat Jul 21, 2022
073a3ad
Merge pull request #228 from admin-ch/feature/show-error-if-excel-lim…
ndinat Jul 21, 2022
c0f00f9
release 4.4.12
Jul 21, 2022
3b8d9c7
fixed maxPeriod
Jul 21, 2022
a2cbbcb
fixed translations
Jul 21, 2022
40fbb53
release 4.4.13
Jul 21, 2022
0167245
no request for profiles if authority is null
Jul 21, 2022
7c2ea6a
release 4.4.14
Jul 21, 2022
07ada75
fixed progress bar being displayed when nothing is loading
Jul 22, 2022
15ee7cd
issuer search now goes to page 1 when search has been applied
Jul 22, 2022
5d148bb
added counter for selected users
Jul 22, 2022
ee79e22
release 4.4.15
Jul 22, 2022
4d9dd12
fixed wrong translations
Jul 22, 2022
4332dd7
release 4.4.16
Jul 22, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34,460 changes: 3,744 additions & 30,716 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cc-management-ui",
"version": "4.3.3",
"version": "4.4.16",
"scripts": {
"start": "ng serve",
"build": "ng build",
Expand Down
7 changes: 4 additions & 3 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http';
import {APP_INITIALIZER, CUSTOM_ELEMENTS_SCHEMA, LOCALE_ID, NgModule} from '@angular/core';
import {APP_INITIALIZER, CUSTOM_ELEMENTS_SCHEMA, Inject, LOCALE_ID, NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {MatTooltipModule} from '@angular/material/tooltip';
Expand Down Expand Up @@ -79,10 +79,11 @@ export class AppModule {
constructor(
private readonly config: ObMasterLayoutConfig,
meta: ObDocumentMetaService,
interceptor: ObHttpApiInterceptorConfig
interceptor: ObHttpApiInterceptorConfig,
@Inject('HOST') private readonly managementServiceHost: string
) {
ObLanguageService.locales.en = 'en-GB';
interceptor.api.url = '/v1';
interceptor.api.url = `${managementServiceHost}/api/v1`;
interceptor.api.notification.active = false;
meta.titleSuffix = 'application.title';
config.homePageRoute = '/dashboard';
Expand Down
191 changes: 172 additions & 19 deletions src/app/auth/auth.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ import {of, ReplaySubject} from 'rxjs';
import {Claims, OauthService} from './oauth.service';
import {AuthFunction, AuthService} from './auth.service';
import {ApiService} from 'shared/api.service';
import {DataRoomCode} from "shared/model";
import SpyInstance = jest.SpyInstance;

describe('AuthService', () => {
let service: AuthService;
let apiService: ApiService;
let oauthService: OauthService;

let nextSpy: SpyInstance;
let authorizedFunctionsNextSpy: SpyInstance;
let authorizedDataRoomsNextSpy: SpyInstance;

const authFunctionsMock: AuthFunction[] = [AuthFunction.MAIN, AuthFunction.CERTIFICATE_REVOCATION];
let claimsMock: Claims;
Expand Down Expand Up @@ -40,7 +42,9 @@ describe('AuthService', () => {

beforeEach(() => {
// @ts-ignore
nextSpy = jest.spyOn(service.authorizedFunctions, 'next');
authorizedFunctionsNextSpy = jest.spyOn(service.authorizedFunctions, 'next');
// @ts-ignore
authorizedDataRoomsNextSpy = jest.spyOn(service.authorizedDataRooms, 'next');
claimsMock = {userroles: [AuthFunction.MAIN, AuthFunction.CERTIFICATE_REVOCATION]} as Claims;
});

Expand All @@ -54,6 +58,12 @@ describe('AuthService', () => {
});
});

describe('authorizedDataRooms$', () => {
it('should be defined', () => {
expect(service.authorizedDataRooms$).toBeDefined();
});
});

describe('hasAuthorizationFor$', () => {
it.each<[AuthFunction, AuthFunction[], boolean]>([
[AuthFunction.MAIN, [AuthFunction.MAIN], true],
Expand All @@ -64,9 +74,9 @@ describe('AuthService', () => {
[AuthFunction.MAIN, [AuthFunction.BULK_OPERATIONS, AuthFunction.CERTIFICATE_REVOCATION], false],
['MAIN' as AuthFunction, [AuthFunction.MAIN], false],
['' as AuthFunction, [], false]
// @ts-ignore
])(
'when neededIdentifier is %s, and current AuthFunctions are %s, should emit %s',
// @ts-ignore
(neededIdentifier, currentAuthFunctions, expected, done) => {
service.hasAuthorizationFor$(neededIdentifier).subscribe(received => {
expect(received).toBe(expected);
Expand All @@ -81,23 +91,166 @@ describe('AuthService', () => {

describe('oauthService.claims$', () => {
beforeEach(() => {
nextSpy.mockReset();
authorizedFunctionsNextSpy.mockReset();
});

it.each<[Claims | null, AuthFunction[]]>([
[null, null],
[{} as Claims, null],
[{userroles: null} as Claims, null],
[{userroles: []} as Claims, null],
[{userroles: ['not', 'empty']} as Claims, authFunctionsMock]
])('when claims is %s, should call next with %s', (claims, expected) => {
oauthServiceClaims.next(claims);
if (expected) {
// @ts-ignore
expect(nextSpy).toHaveBeenCalledWith(expected);
} else {
expect(nextSpy).not.toHaveBeenCalled();
}
});
describe('authorizedFunctions', () => {
it.each<[Claims | null, AuthFunction[]]>([
[null, null],
[{} as Claims, null],
[{userroles: null} as Claims, null],
[{userroles: []} as Claims, null],
[{userroles: ['not', 'empty']} as Claims, authFunctionsMock]
])('when claims is %s, should call next with %s', (claims, expected) => {
oauthServiceClaims.next(claims);
if (expected) {
// @ts-ignore
expect(authorizedFunctionsNextSpy).toHaveBeenCalledWith(expected);
} else {
expect(authorizedFunctionsNextSpy).not.toHaveBeenCalled();
}
})
})

describe('authorizedDataRooms', () => {
it.each<[Claims | null, DataRoomCode[]]>([
[null, []],
[{} as Claims, []],
[{userroles: null} as Claims, []],
[{userroles: {}} as Claims, []],
[{
userroles: []
} as Claims, []],
[{
userroles: [
"bag-cc-dr_bs",
"bag-cc-dr_sh",
"bag-cc-covid_app_manager",
"bag-cc-vacccert_creator",
"bag-cc-dr_sg",
"bag-cc-dr_lu",
"bag-cc-dr_nw",
"bag-cc-rep_user_stats",
"bag-cc-dr_bl",
"bag-cc-dr_bv_intern",
"bag-cc-dr_ju",
"bag-cc-dr_fr",
"bag-cc-recoverycert_antibody_creator",
"bag-cc-rep_user_agg",
"bag-cc-recoverycert_creator",
"bag-cc-rep_user_detail",
"bag-cc-dr_be",
"bag-cc-recoverycert_rat_creator",
"offline_access",
"default-roles-bag-covidcertificate",
"bag-cc-vacccert_tourist_creator",
"bag-cc-dr_ne",
"uma_authorization",
"bag-cc-web_ui_revocator",
"bag-cc-dr_vs",
"bag-cc-dr_ar",
"bag-cc-dr_ti",
"bag-cc-dr_tg",
"bag-cc-web_ui_user",
"bag-cc-dr_sz",
"bag-cc-dr_ai",
"bag-cc-dr_ow",
"bag-cc-testccert_creator",
"bag-cc-dr_zg",
"bag-cc-bulkcert_manager",
"bag-cc-dr_vd",
"bag-cc-dr_zh",
"bag-cc-dr_armee",
"bag-cc-dr_gr",
"bag-cc-dr_gl",
"bag-cc-dr_ag",
"bag-cc-dr_ur",
"bag-cc-api_gw_user",
"bag-cc-dr_ge",
"bag-cc-exceptcert_creator",
"bag-cc-dr_so"
]
} as Claims, [
DataRoomCode.BS,
DataRoomCode.SH,
DataRoomCode.SG,
DataRoomCode.LU,
DataRoomCode.NW,
DataRoomCode.BL,
DataRoomCode.BV_INTERN,
DataRoomCode.JU,
DataRoomCode.FR,
DataRoomCode.BE,
DataRoomCode.NE,
DataRoomCode.VS,
DataRoomCode.AR,
DataRoomCode.TI,
DataRoomCode.TG,
DataRoomCode.SZ,
DataRoomCode.AI,
DataRoomCode.OW,
DataRoomCode.ZG,
DataRoomCode.VD,
DataRoomCode.ZH,
DataRoomCode.ARMEE,
DataRoomCode.GR,
DataRoomCode.GL,
DataRoomCode.AG,
DataRoomCode.UR,
DataRoomCode.GE,
DataRoomCode.SO,
]],
[{
userroles: [
"bag-cc-dr_be",
"bag-cc-recoverycert_rat_creator",
"offline_access",
"default-roles-bag-covidcertificate",
"bag-cc-vacccert_tourist_creator",
"bag-cc-dr_ne",
"uma_authorization",
"bag-cc-web_ui_revocator",
"bag-cc-dr_ow",
"bag-cc-testccert_creator",
"bag-cc-dr_zg",
"bag-cc-bulkcert_manager",
"bag-cc-dr_vd",
"bag-cc-dr_zh",
"bag-cc-dr_armee",
"bag-cc-dr_gr",
"bag-cc-dr_gl",
"bag-cc-dr_ag",
"bag-cc-dr_ur",
"bag-cc-api_gw_user",
"bag-cc-dr_ge",
"bag-cc-exceptcert_creator",
"bag-cc-dr_so"
]
} as Claims, [
DataRoomCode.BE,
DataRoomCode.NE,
DataRoomCode.OW,
DataRoomCode.ZG,
DataRoomCode.VD,
DataRoomCode.ZH,
DataRoomCode.ARMEE,
DataRoomCode.GR,
DataRoomCode.GL,
DataRoomCode.AG,
DataRoomCode.UR,
DataRoomCode.GE,
DataRoomCode.SO,
]],
])('when claims is %s, should call next with %s', (claims, expected) => {
oauthServiceClaims.next(claims);
if (expected) {
// @ts-ignore
expect(authorizedDataRoomsNextSpy).toHaveBeenCalledWith(expected);
} else {
expect(authorizedDataRoomsNextSpy).not.toHaveBeenCalled();
}
})
})

});
});
33 changes: 30 additions & 3 deletions src/app/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {Claims, OauthService} from './oauth.service';
import {Observable, of, ReplaySubject, Subscription} from 'rxjs';
import {map, switchMap} from 'rxjs/operators';
import {HttpParams} from '@angular/common/http';
import {DataRoomCode} from "shared/model";

export enum AuthFunction {
// Navigation
Expand Down Expand Up @@ -51,20 +52,29 @@ export enum AuthFunction {
})
export class AuthService implements OnDestroy {
public authorizedFunctions$: Observable<AuthFunction[]>;
public authorizedDataRooms$: Observable<DataRoomCode[]>;
private authorizedFunctions: ReplaySubject<AuthFunction[]> = new ReplaySubject<AuthFunction[]>(1);
private authorizedDataRooms: ReplaySubject<DataRoomCode[]> = new ReplaySubject<DataRoomCode[]>(1);
private claimsSubscription: Subscription;

private readonly URL: string = 'authorization/current/web-ui';

constructor(private http: ApiService, private oauthService: OauthService) {
this.authorizedFunctions$ = this.authorizedFunctions.asObservable();
this.claimsSubscription = oauthService.claims$
.pipe(switchMap(claims => this.getAuthorizedFunctions(claims)))
this.authorizedDataRooms$ = this.authorizedDataRooms.asObservable();
this.claimsSubscription = oauthService.claims$.subscribe(claims => {
this.emitAuthorizedDataRooms(claims)
})

this.claimsSubscription.add(oauthService.claims$
.pipe(
switchMap(claims => this.getAuthorizedFunctions(claims)),
)
.subscribe(authorizedFunctions => {
if (authorizedFunctions != null) {
this.authorizedFunctions.next(authorizedFunctions);
}
});
}));
}

ngOnDestroy(): void {
Expand Down Expand Up @@ -95,4 +105,21 @@ export class AuthService implements OnDestroy {
return of(null);
}
}

private emitAuthorizedDataRooms(claims: Claims): void {
if (claims?.userroles?.length) {
const authorizedDataRooms: DataRoomCode[] = []
for (const role of claims.userroles) {
Object.keys(DataRoomCode).forEach(code => {
const lowerCaseCode = code.toLocaleLowerCase()
if (role.endsWith(`bag-cc-dr_${lowerCaseCode}`)) {
authorizedDataRooms.push(DataRoomCode[code])
}
})
}
this.authorizedDataRooms.next(authorizedDataRooms);
} else {
this.authorizedDataRooms.next([]);
}
}
}
3 changes: 3 additions & 0 deletions src/app/auth/oauth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ export interface Claims {
sub: string;
typ: string;
allowedOrigins: string[];
realm_access: {
roles: string[]
}
userroles: string[];
resourceAccess: {
account: {
Expand Down
14 changes: 14 additions & 0 deletions src/app/report/errorStateMatcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {InjectionToken} from "@angular/core";
import {ErrorStateMatcher} from "@angular/material/core";
import {FormControl} from "@angular/forms";

export const REPORT_ERROR_STATE_MATCHER = new InjectionToken<ErrorStateMatcher>('ReportErrorStateMatcher')

/** Use custom error state matcher for inputs in mat-form-field, otherwise switching between report types results
* in inputs being shown as invalid initially. */
export class ReportErrorStateMatcher implements ErrorStateMatcher {
isErrorState(control: FormControl | null): boolean {
return !!(control && control.invalid && control.touched);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {SharedModule} from 'shared/shared.module';
import {ReportGenerationComponent} from './report-generation.component';
import {MatHorizontalStepper} from '@angular/material/stepper';
import {HttpClientTestingModule} from '@angular/common/http/testing';
import {TranslateModule} from "@ngx-translate/core";

describe('ReportGenerationComponent', () => {
let component: ReportGenerationComponent;
Expand All @@ -16,7 +17,7 @@ describe('ReportGenerationComponent', () => {

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ObliqueTestingModule, SharedModule, HttpClientTestingModule],
imports: [ObliqueTestingModule, SharedModule, HttpClientTestingModule, TranslateModule],
providers: [
{
provide: MatHorizontalStepper,
Expand Down
Loading