Skip to content

Commit 8c56e79

Browse files
authored
Merge pull request #286 from netgrif/NAE-2118
[NAE-2118] Implement OpenID Connector Auth for Admin node
2 parents 80b85d2 + 9458152 commit 8c56e79

18 files changed

+211
-11
lines changed

nae.json

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"providers": {
55
"auth": {
66
"address": "http://localhost:8080/api/",
7-
"authentication": "Basic",
7+
"authentication": "BasicWithRealm",
88
"endpoints": {
99
"login": "auth/login",
1010
"logout": "auth/logout",
@@ -14,12 +14,19 @@
1414
"verify": "auth/token/verify",
1515
"invite": "auth/invite",
1616
"reset": "auth/reset",
17-
"recover": "/auth/recover"
17+
"recover": "auth/recover"
1818
},
1919
"sessionBearer": "X-Auth-Token",
2020
"sessionTimeoutEnabled": false,
2121
"sessionTimeout": 900,
22-
"jwtBearer": "X-Jwt-Token"
22+
"jwtBearer": "X-Jwt-Token",
23+
"sso": {
24+
"enable": false,
25+
"clientId": "dev-cluster-worker",
26+
"redirectUrl": "http://localhost:8081/realms/netgrif-cloud-testing/protocol/openid-connect/auth",
27+
"refreshUrl": "http://localhost:8800/api/auth/login",
28+
"scopes": ["openid","email","profile","roles"]
29+
}
2330
},
2431
"resources": [
2532
{

projects/netgrif-components-core/src/assets/i18n/de.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@
313313
"login": "Benutzername",
314314
"wrongCredentials": "Falsche Anmeldeinformationen!",
315315
"loginButton": "Anmelden",
316+
"ssoButton": "SSO-Anmeldung",
316317
"reset": "Kennwort wiederherstellen",
317318
"sign": "Registrieren",
318319
"enterPass": "Ihre Kennwort eingeben"

projects/netgrif-components-core/src/assets/i18n/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@
313313
"login": "Username",
314314
"wrongCredentials": "Wrong credentials!",
315315
"loginButton": "Log in",
316+
"ssoButton": "Log with SSO",
316317
"reset": "Reset password",
317318
"sign": "Sign Up",
318319
"enterPass": "Enter your password"

projects/netgrif-components-core/src/assets/i18n/sk.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@
313313
"login": "Prihlasovacie meno",
314314
"wrongCredentials": "Nesprávne prihlasovacie údaje!",
315315
"loginButton": "Prihlásiť",
316+
"ssoButton": "Prihlásiť sa pomocou SSO",
316317
"reset": "Obnova hesla",
317318
"sign": "Registrovať",
318319
"enterPass": "Zadajte svoje heslo"

projects/netgrif-components-core/src/commons/schema.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,19 @@ export interface Auth {
4646
authentication: string;
4747
sessionBearer?: string;
4848
endpoints?: string | { [k: string]: string };
49+
sso?: Sso;
4950

5051
[k: string]: any;
5152
}
5253

54+
export interface Sso {
55+
enable: boolean;
56+
redirectUrl: string;
57+
refreshUrl: string;
58+
clientId: string;
59+
scopes: Array<string>;
60+
}
61+
5362
export interface Resource {
5463
name: string;
5564
address: string;

projects/netgrif-components-core/src/lib/forms/login/abstract-login-form.component.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ describe('AbstractLoginFormComponent', () => {
7070
template: ''
7171
})
7272
class TestLoginFormComponent extends AbstractLoginFormComponent {
73-
constructor(formBuilder: FormBuilder, protected _userService: UserService) {
74-
super(formBuilder, _userService);
73+
constructor(formBuilder: FormBuilder, protected _userService: UserService, protected _configService: ConfigurationService) {
74+
super(formBuilder, _userService, _configService);
7575
}
7676
}

projects/netgrif-components-core/src/lib/forms/login/abstract-login-form.component.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import {UserService} from '../../user/services/user.service';
55
import {User} from '../../user/models/user';
66
import {LoadingEmitter} from '../../utility/loading-emitter';
77
import {take} from 'rxjs/operators';
8+
import {ConfigurationService} from "../../configuration/configuration.service";
9+
import {Sso} from "../../../commons/schema";
810

911
@Component({
1012
selector: 'ncc-abstract-login-field',
@@ -15,6 +17,7 @@ export abstract class AbstractLoginFormComponent implements HasForm, OnDestroy {
1517
public rootFormGroup: FormGroup;
1618
public hidePassword = true;
1719
public loading: LoadingEmitter;
20+
protected showSsoButton: boolean;
1821

1922
@Input() public showSignUpButton: boolean;
2023
@Input() public showForgottenPasswordButton: boolean;
@@ -23,7 +26,7 @@ export abstract class AbstractLoginFormComponent implements HasForm, OnDestroy {
2326
@Output() public signUp: EventEmitter<void>;
2427
@Output() public formSubmit: EventEmitter<FormSubmitEvent>;
2528

26-
protected constructor(formBuilder: FormBuilder, protected _userService: UserService) {
29+
protected constructor(formBuilder: FormBuilder, protected _userService: UserService, protected _config: ConfigurationService) {
2730
this.rootFormGroup = formBuilder.group({
2831
login: [''],
2932
password: ['']
@@ -33,6 +36,8 @@ export abstract class AbstractLoginFormComponent implements HasForm, OnDestroy {
3336
this.signUp = new EventEmitter<void>();
3437
this.formSubmit = new EventEmitter<FormSubmitEvent>();
3538
this.loading = new LoadingEmitter();
39+
let ssoConfig: Sso = this._config.getConfigurationSubtree(['providers', 'auth', 'sso'])
40+
this.showSsoButton = ssoConfig?.enable
3641
}
3742

3843
ngOnDestroy(): void {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// todo 2118
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import {Component, OnDestroy} from "@angular/core";
2+
import {ActivatedRoute, Params, Router} from "@angular/router";
3+
import {Observable, throwError} from "rxjs";
4+
import {catchError} from "rxjs/operators";
5+
import {HttpClient} from "@angular/common/http";
6+
import {ConfigurationService} from "../../../configuration/configuration.service";
7+
import {LoggerService} from '../../../logger/services/logger.service';
8+
import {LoadingEmitter} from '../../../utility/loading-emitter';
9+
import {SnackBarService} from "../../../snack-bar/services/snack-bar.service";
10+
import {Sso} from "../../../../commons/schema";
11+
import {TranslateService} from "@ngx-translate/core";
12+
13+
14+
@Component({
15+
selector: 'ncc-abstract-login-field',
16+
template: ''
17+
})
18+
export abstract class AbstractLoginSsoComponent implements OnDestroy {
19+
20+
protected _ssoConfig: Sso;
21+
protected loading: LoadingEmitter;
22+
23+
protected constructor(
24+
protected _config: ConfigurationService,
25+
protected _http: HttpClient,
26+
protected _snackbar: SnackBarService,
27+
protected _log: LoggerService,
28+
protected _router: Router,
29+
protected _activeRouter: ActivatedRoute,
30+
protected _translate: TranslateService
31+
) {
32+
this._ssoConfig = this._config.getConfigurationSubtree(['providers', 'auth', 'sso']);
33+
this.loading = new LoadingEmitter();
34+
this._activeRouter.queryParams.subscribe((params) => {
35+
if (!!params.code) {
36+
this.loginFromCode(params);
37+
}
38+
});
39+
}
40+
41+
ngOnDestroy(): void {
42+
this.loading.complete();
43+
}
44+
45+
public redirectToSso(): void {
46+
let redirectUrl: string = this.getRedirectUrl();
47+
this._log.info("Redirecting to " + redirectUrl)
48+
window.location.href = redirectUrl;
49+
}
50+
51+
public loginFromCode(params: Params) {
52+
if (!params.code) {
53+
return;
54+
}
55+
56+
this.loading.on();
57+
this._log.debug('Handling access token: ' + params.code)
58+
const token$ = this.getToken({
59+
grantType: 'authorization_code',
60+
code: params.code,
61+
realmId: '', // todo send realm id
62+
redirectUri: location.origin + '/' + this._config.getConfigurationSubtree(['services', 'auth', 'toLoginRedirect']),
63+
});
64+
token$.subscribe(
65+
token => {
66+
this.loading.off();
67+
if (!!token) {
68+
this.redirectToHome();
69+
}
70+
},
71+
);
72+
}
73+
74+
protected getRedirectUrl(): string {
75+
const myQuery = this._ssoConfig.redirectUrl + '?';
76+
const options: { [index: string]: string } = {
77+
client_id: this._ssoConfig.clientId,
78+
redirect_uri: location.origin + '/' + this._config.getConfigurationSubtree(['services', 'auth', 'toLoginRedirect']),
79+
response_type: 'code',
80+
scope: this._ssoConfig.scopes.join(' '),
81+
};
82+
return myQuery + Object.keys(options).map(
83+
key => {
84+
return encodeURIComponent(key) + '=' + encodeURIComponent(options[key]);
85+
},
86+
).join('&');
87+
}
88+
89+
protected getToken(body: any): Observable<any> {
90+
const url = this._ssoConfig.refreshUrl;
91+
if (!url) {
92+
return throwError(() => new Error('Refresh URL is not defined in the config [nae.providers.auth.sso.refreshUrl]'));
93+
}
94+
return this._http.post(url, body,
95+
{headers: {'Content-Type': 'application/json'}}).pipe(
96+
catchError(error => {
97+
this.loading.off();
98+
this._snackbar.openErrorSnackBar(this._translate.instant('forms.login.wrongCredentials'));
99+
return throwError(() => error);
100+
}),
101+
);
102+
}
103+
104+
protected redirectToHome() {
105+
this._router.navigate(['/' + this._config.getConfigurationSubtree(['services', 'auth', 'onLoginRedirect'])])
106+
.then((value) => { this._log.debug('Routed to ' + value); });
107+
}
108+
}

projects/netgrif-components-core/src/lib/forms/public-api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export * from './email-submission/abstract-email-submission-form.component';
22
export * from './login/abstract-login-form.component';
3+
export * from './login/login-sso/abstract-login-sso.component';
34
export * from './registration/abstract-registration-form.component';
45
export * from './forgotten-password/abstract-forgotten-password.component';
56
export * from './models/abstract-registration.component';

0 commit comments

Comments
 (0)