Skip to content
This repository was archived by the owner on Aug 1, 2021. It is now read-only.

Commit 8bca0b3

Browse files
committed
v1.1
- Minor bugs correction - Added localization feature to SSO - Added translation feature to User Management - Silent refresh for angular-oauth2-oidc both for User Management and Admin - Changed Security Headers to accept Azure Application Insights
1 parent 13b68be commit 8bca0b3

File tree

630 files changed

+42133
-478
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

630 files changed

+42133
-478
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,17 @@ We'll love it! Please [Read the docs](https://jp-project.readthedocs.io/en/lates
8989
If you need help building or running your Jp Project platform
9090
There are several ways we can help you out.
9191

92+
## v1.1
93+
- Minor bugs correction
94+
- Added localization feature to SSO
95+
- Added translation feature to User Management
96+
- Silent refresh for angular-oauth2-oidc both for User Management and Admin
97+
- Changed Security Headers to accept Azure Application Insights
98+
99+
100+
## v1.0
101+
* First release
102+
92103
# What comes next?
93104

94105
Code coverage

src/Backend/Jp.UserManagement/jpProject_sso_log.txt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60761,3 +60761,25 @@ WHERE `s.Client`.`ClientId` = @__clientId_0
6076160761
2018-10-26 13:57:09.236 -03:00 [INF] Executing ObjectResult, writing value of type 'Jp.Infra.CrossCutting.Tools.Model.DefaultResponse`1[[System.Collections.Generic.IEnumerable`1[[Jp.Application.ViewModels.SecretViewModel, Jp.Application, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]'.
6076260762
2018-10-26 13:57:09.242 -03:00 [INF] Executed action Jp.Management.Controllers.ClientsController.Secrets (Jp.Management) in 55.4131ms
6076360763
2018-10-26 13:57:09.248 -03:00 [INF] Request finished in 92.4888ms 200 application/json; charset=utf-8
60764+
2018-12-27 14:13:18.682 -02:00 [INF] Authority URI: https://localhost:5000
60765+
2018-12-27 14:13:19.981 -02:00 [INF] User profile is available. Using 'C:\Users\bruno.brito\AppData\Local\ASP.NET\DataProtection-Keys' as key repository and Windows DPAPI to encrypt keys at rest.
60766+
2018-12-27 15:24:04.070 -02:00 [INF] Authority URI: https://localhost:5000
60767+
2018-12-27 15:24:06.355 -02:00 [INF] User profile is available. Using 'C:\Users\bruno.brito\AppData\Local\ASP.NET\DataProtection-Keys' as key repository and Windows DPAPI to encrypt keys at rest.
60768+
2018-12-27 16:09:22.837 -02:00 [INF] Authority URI: https://localhost:5000
60769+
2018-12-27 16:09:24.280 -02:00 [INF] User profile is available. Using 'C:\Users\bruno.brito\AppData\Local\ASP.NET\DataProtection-Keys' as key repository and Windows DPAPI to encrypt keys at rest.
60770+
2018-12-27 16:43:56.856 -02:00 [INF] Authority URI: https://localhost:5000
60771+
2018-12-27 16:43:58.117 -02:00 [INF] User profile is available. Using 'C:\Users\bruno.brito\AppData\Local\ASP.NET\DataProtection-Keys' as key repository and Windows DPAPI to encrypt keys at rest.
60772+
2018-12-27 16:46:28.770 -02:00 [INF] Authority URI: https://localhost:5000
60773+
2018-12-27 16:46:29.882 -02:00 [INF] User profile is available. Using 'C:\Users\bruno.brito\AppData\Local\ASP.NET\DataProtection-Keys' as key repository and Windows DPAPI to encrypt keys at rest.
60774+
2018-12-27 16:56:06.556 -02:00 [INF] Authority URI: https://localhost:5000
60775+
2018-12-27 16:56:07.667 -02:00 [INF] User profile is available. Using 'C:\Users\bruno.brito\AppData\Local\ASP.NET\DataProtection-Keys' as key repository and Windows DPAPI to encrypt keys at rest.
60776+
2018-12-27 17:00:56.720 -02:00 [INF] Authority URI: https://localhost:5000
60777+
2018-12-27 17:00:57.838 -02:00 [INF] User profile is available. Using 'C:\Users\bruno.brito\AppData\Local\ASP.NET\DataProtection-Keys' as key repository and Windows DPAPI to encrypt keys at rest.
60778+
2018-12-27 17:06:27.089 -02:00 [INF] Authority URI: https://localhost:5000
60779+
2018-12-27 17:06:28.265 -02:00 [INF] User profile is available. Using 'C:\Users\bruno.brito\AppData\Local\ASP.NET\DataProtection-Keys' as key repository and Windows DPAPI to encrypt keys at rest.
60780+
2018-12-27 17:10:29.892 -02:00 [INF] Authority URI: https://localhost:5000
60781+
2018-12-27 17:10:31.022 -02:00 [INF] User profile is available. Using 'C:\Users\bruno.brito\AppData\Local\ASP.NET\DataProtection-Keys' as key repository and Windows DPAPI to encrypt keys at rest.
60782+
2018-12-27 17:14:12.394 -02:00 [INF] Authority URI: https://localhost:5000
60783+
2018-12-27 17:14:13.548 -02:00 [INF] User profile is available. Using 'C:\Users\bruno.brito\AppData\Local\ASP.NET\DataProtection-Keys' as key repository and Windows DPAPI to encrypt keys at rest.
60784+
2018-12-27 18:46:16.995 -02:00 [INF] Authority URI: https://localhost:5000
60785+
2018-12-27 18:46:18.413 -02:00 [INF] User profile is available. Using 'C:\Users\bruno.brito\AppData\Local\ASP.NET\DataProtection-Keys' as key repository and Windows DPAPI to encrypt keys at rest.

src/Frontend/Jp.AdminUI/src/app/app.component.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,14 @@ export class AppComponent implements OnInit {
3636
this.oauthService.configure(authConfig);
3737
this.oauthService.setStorage(localStorage);
3838
this.oauthService.tokenValidationHandler = new JwksValidationHandler();
39+
3940

4041
this.settings.loadDiscoveryDocumentAndTryLogin().pipe(tap(doc => {
4142
if (!environment.production)
4243
console.log(doc);
43-
})).subscribe();
44+
})).subscribe(a => {
45+
this.oauthService.setupAutomaticSilentRefresh();
46+
});
4447
// this.oauthService.loadDiscoveryDocument().then(doc => {
4548
// if (!environment.production)
4649
// console.log(doc);

src/Frontend/Jp.AdminUI/src/app/core/auth/auth.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export const authConfig: AuthConfig = {
66
clientId: "IS4-Admin",
77
postLogoutRedirectUri: environment.Uri,
88
redirectUri: environment.Uri + "/login-callback",
9+
silentRefreshRedirectUri: environment.Uri + '/silent-refresh.html',
910
scope: "openid profile email jp_api.is4",
1011
oidc: true,
1112
};

src/Frontend/Jp.AdminUI/src/app/core/interceptors/auth.interceptor.ts

Lines changed: 83 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
import { Injectable } from '@angular/core';
22
import { OAuthService } from 'angular-oauth2-oidc';
3-
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
3+
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpErrorResponse } from '@angular/common/http';
44
import { Observable } from 'rxjs/Observable';
55
import { Router } from '@angular/router';
66
import { environment } from '@env/environment';
7+
import { BehaviorSubject, of, from } from 'rxjs';
8+
import { switchMap, catchError, take } from 'rxjs/operators';
9+
import { SettingsService } from '@core/settings/settings.service';
710

811
@Injectable()
912
export class AuthInterceptor implements HttpInterceptor {
1013
private baseUrl: string;
14+
isRefreshingToken: boolean = false;
15+
tokenSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(null);
1116

1217
constructor(
1318
private authStorage: OAuthService,
14-
private router: Router) {
19+
public settingsService: SettingsService) {
1520
this.baseUrl = environment.ResourceServer;
1621
}
1722

@@ -24,16 +29,86 @@ export class AuthInterceptor implements HttpInterceptor {
2429

2530
if (url.startsWith(this.baseUrl.toLowerCase())) {
2631

27-
const token = this.authStorage.getAccessToken();
28-
const header = 'Bearer ' + token;
32+
return next.handle(this.addToken(req, this.authStorage.getAccessToken())).do((event: HttpEvent<any>) => { }, (error: any) => {
33+
if (error instanceof HttpErrorResponse) {
34+
switch ((<HttpErrorResponse>error).status) {
35+
case 400:
36+
return this.handle400Error(error);
37+
case 401:
38+
{
39+
this.settingsService.login();
40+
return Observable.throw(error);
41+
}
42+
}
43+
} else {
44+
return Observable.throw(error);
45+
}
46+
});
47+
}
2948

30-
const headers = req.headers
31-
.set('Authorization', header);
49+
return next.handle(req);
50+
}
3251

33-
req = req.clone({ headers });
52+
private handle400Error(error) {
53+
if (error && error.status === 400 && error.error && error.error.error === 'invalid_grant') {
54+
// If we get a 400 and the error message is 'invalid_grant', the token is no longer valid so logout.
55+
this.authStorage.logOut();
56+
this.settingsService.login();
3457
}
3558

36-
return next.handle(req);
59+
return Observable.throw(error);
60+
}
61+
62+
// In case of silentRefresh()
63+
private handle401Error(req: HttpRequest<any>, next: HttpHandler) {
64+
if (!this.isRefreshingToken) {
65+
this.isRefreshingToken = true;
66+
67+
// Reset here so that the following requests wait until the token
68+
// comes back from the refreshToken call.
69+
this.tokenSubject.next(null);
70+
71+
from(this.authStorage.silentRefresh())
72+
.pipe(switchMap(a => {
73+
this.tokenSubject.next(true);
74+
return next.handle(this.addToken(req, this.authStorage.getAccessToken()));
75+
}))
76+
.do((event: HttpEvent<any>) => { }, (err: any) => {
77+
if (err instanceof HttpErrorResponse) {
78+
// do error handling here
79+
this.settingsService.login();
80+
}
81+
});
82+
} else {
83+
return this.tokenSubject
84+
.filter(token => token != null)
85+
.pipe(take(1))
86+
.pipe(switchMap(token => {
87+
return next.handle(this.addToken(req, this.authStorage.getAccessToken()));
88+
}));
89+
}
90+
}
91+
92+
/*
93+
This method is only here so the example works.
94+
Do not include in your code, just use 'req' instead of 'this.getNewRequest(req)'.
95+
*/
96+
getNewRequest(req: HttpRequest<any>): HttpRequest<any> {
97+
if (req.url.indexOf('getData') > 0) {
98+
return new HttpRequest('GET', 'http://private-4002d-testerrorresponses.apiary-mock.com/getData');
99+
}
100+
101+
return new HttpRequest('GET', 'http://private-4002d-testerrorresponses.apiary-mock.com/getLookup');
102+
}
103+
104+
logoutUser() {
105+
// Route to the login page (implementation up to you)
106+
107+
return Observable.throw("");
108+
}
109+
110+
addToken(req: HttpRequest<any>, token: string): HttpRequest<any> {
111+
return req.clone({ setHeaders: { Authorization: 'Bearer ' + token } });
37112
}
38113

39114
private shouldIgnore(url: string): boolean {

src/Frontend/Jp.AdminUI/src/app/panel/api-resources/api-resource.service.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { Scope } from "@shared/viewModel/scope.model";
99

1010
@Injectable()
1111
export class ApiResourceService {
12-
12+
1313
constructor(private http: HttpClient) {
1414
}
1515

@@ -32,14 +32,14 @@ export class ApiResourceService {
3232
}
3333

3434
public update(model: ApiResource): Observable<DefaultResponse<boolean>> {
35-
return this.http.post<DefaultResponse<boolean>>(environment.ResourceServer + "ApiResource/update", model);
35+
return this.http.put<DefaultResponse<boolean>>(environment.ResourceServer + "ApiResource/update", model);
3636
}
3737

3838
public remove(name: string): any {
3939
const removeCommand = {
4040
name: name
4141
};
42-
return this.http.post<DefaultResponse<boolean>>(environment.ResourceServer + "ApiResource/remove", removeCommand);
42+
return this.http.post<DefaultResponse<boolean>>(environment.ResourceServer + "ApiResource/remove", { params: removeCommand });
4343
}
4444

4545

@@ -51,7 +51,9 @@ export class ApiResourceService {
5151
};
5252
return this.http.get<DefaultResponse<ApiResourceSecret[]>>(environment.ResourceServer + "ApiResource/secrets", options);
5353
}
54+
5455
public removeSecret(resourceName: string, id: number): Observable<DefaultResponse<boolean>> {
56+
5557
const removeCommand = {
5658
id: id,
5759
resourceName: resourceName

src/Frontend/Jp.AdminUI/src/app/panel/api-resources/secrets/api-secrets.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ <h3>{{ resourceName }}</h3>
3737
<tr *ngFor="let secret of apiSecrets">
3838
<td><button class="btn btn-danger btn-xs" (click)="remove(secret.id)"><i class="fa fa-times"></i></button></td>
3939
<td>{{secret.value}}</td>
40-
<th>{{ secret.type }}</th>
40+
<td>{{secret.type}}</td>
4141
<td>{{secret.expiration}}</td>
4242
<td>{{secret.description}}</td>
4343
</tr>

src/Frontend/Jp.AdminUI/src/app/panel/api-resources/secrets/api-secrets.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export class ApiResourceSecretsComponent implements OnInit {
4848
this.model = new ApiResourceSecret();
4949
this.showButtonLoading = false;
5050
this.hashTypes = [{ id: 0, text: "Sha256" }, { id: 1, text: "Sha512" }];
51-
this.secretTypes = [{ id: 'SharedSecret', text: "Shared Secret" }, { id: "X509Thumbprint", text: "X509 Thumbprint" }, { id: "X509Name", text: "X509 Name" }, { id: "X509CertificateBase64", text: "X509 Certificate Base64" }];
51+
this.secretTypes = [{ id: 'SharedSecret', text: "Shared Secret" }, { id: 'X509Thumbprint', text: "X509 Thumbprint" }, { id: 'X509Name', text: "X509 Name" }, { id: 'X509CertificateBase64', text: "X509 Certificate Base64" }];
5252
}
5353

5454
public showSuccessMessage() {

src/Frontend/Jp.AdminUI/src/app/panel/clients/clients.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export class ClientService {
3333
}
3434

3535
public update(model: Client): Observable<DefaultResponse<boolean>> {
36-
return this.http.post<DefaultResponse<boolean>>(environment.ResourceServer + "clients/update", model);
36+
return this.http.put<DefaultResponse<boolean>>(environment.ResourceServer + "clients/update", model);
3737
}
3838

3939
public remove(clientId: string): any {

src/Frontend/Jp.AdminUI/src/app/panel/clients/secrets/secrets.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ <h3>{{ client }}</h3>
3737
<tr *ngFor="let secret of clientSecrets">
3838
<td><button class="btn btn-danger btn-xs" (click)="remove(secret.id)"><i class="fa fa-times"></i></button></td>
3939
<td>{{secret.value}}</td>
40-
<th>{{ secret.type }}</th>
40+
<td>{{secret.type}}</td>
4141
<td>{{secret.expiration}}</td>
4242
<td>{{secret.description}}</td>
4343
</tr>

0 commit comments

Comments
 (0)