Commit da7d88470e415c95265205a4ebdcc1297ee5591c

Authored by Igor Kulikov
Committed by GitHub
2 parents 4596e416 6ef7e813

Merge pull request #3575 from vvlladd28/improvement/oauth2

Improvement OAuth2
Showing 23 changed files with 414 additions and 305 deletions
@@ -7,14 +7,17 @@ @@ -7,14 +7,17 @@
7 "userInfoUri": "https://graph.facebook.com/me?fields=id,name,first_name,last_name,email", 7 "userInfoUri": "https://graph.facebook.com/me?fields=id,name,first_name,last_name,email",
8 "clientAuthenticationMethod": "BASIC", 8 "clientAuthenticationMethod": "BASIC",
9 "userNameAttributeName": "email", 9 "userNameAttributeName": "email",
10 - "basic": {  
11 - "emailAttributeKey": "email",  
12 - "firstNameAttributeKey": "first_name",  
13 - "lastNameAttributeKey": "last_name",  
14 - "tenantNameStrategy": "DOMAIN" 10 + "mapperConfig": {
  11 + "type": "BASIC",
  12 + "basic": {
  13 + "emailAttributeKey": "email",
  14 + "firstNameAttributeKey": "first_name",
  15 + "lastNameAttributeKey": "last_name",
  16 + "tenantNameStrategy": "DOMAIN"
  17 + }
15 }, 18 },
16 "comment": null, 19 "comment": null,
17 - "loginButtonIcon": "mdi:facebook", 20 + "loginButtonIcon": "facebook-logo",
18 "loginButtonLabel": "Facebook", 21 "loginButtonLabel": "Facebook",
19 "helpLink": "https://developers.facebook.com/docs/facebook-login/web#logindialog" 22 "helpLink": "https://developers.facebook.com/docs/facebook-login/web#logindialog"
20 } 23 }
@@ -7,11 +7,14 @@ @@ -7,11 +7,14 @@
7 "userInfoUri": "https://api.github.com/user", 7 "userInfoUri": "https://api.github.com/user",
8 "clientAuthenticationMethod": "BASIC", 8 "clientAuthenticationMethod": "BASIC",
9 "userNameAttributeName": "login", 9 "userNameAttributeName": "login",
10 - "basic": {  
11 - "tenantNameStrategy": "DOMAIN" 10 + "mapperConfig": {
  11 + "type": "GITHUB",
  12 + "basic": {
  13 + "tenantNameStrategy": "DOMAIN"
  14 + }
12 }, 15 },
13 "comment": "In order to log into ThingsBoard you need to have user's email. You may configure and use Custom OAuth2 Mapper to get email information. Please refer to <a href=\"https://docs.github.com/en/rest/reference/users#list-email-addresses-for-the-authenticated-user\">Github Documentation</a>", 16 "comment": "In order to log into ThingsBoard you need to have user's email. You may configure and use Custom OAuth2 Mapper to get email information. Please refer to <a href=\"https://docs.github.com/en/rest/reference/users#list-email-addresses-for-the-authenticated-user\">Github Documentation</a>",
14 - "loginButtonIcon": "mdi:github", 17 + "loginButtonIcon": "github-logo",
15 "loginButtonLabel": "Github", 18 "loginButtonLabel": "Github",
16 "helpLink": "https://docs.github.com/en/developers/apps/creating-an-oauth-app" 19 "helpLink": "https://docs.github.com/en/developers/apps/creating-an-oauth-app"
17 } 20 }
@@ -8,14 +8,17 @@ @@ -8,14 +8,17 @@
8 "userInfoUri": "https://openidconnect.googleapis.com/v1/userinfo", 8 "userInfoUri": "https://openidconnect.googleapis.com/v1/userinfo",
9 "clientAuthenticationMethod": "BASIC", 9 "clientAuthenticationMethod": "BASIC",
10 "userNameAttributeName": "email", 10 "userNameAttributeName": "email",
11 - "basic": {  
12 - "emailAttributeKey": "email",  
13 - "firstNameAttributeKey": "given_name",  
14 - "lastNameAttributeKey": "family_name",  
15 - "tenantNameStrategy": "DOMAIN" 11 + "mapperConfig": {
  12 + "type": "BASIC",
  13 + "basic": {
  14 + "emailAttributeKey": "email",
  15 + "firstNameAttributeKey": "given_name",
  16 + "lastNameAttributeKey": "family_name",
  17 + "tenantNameStrategy": "DOMAIN"
  18 + }
16 }, 19 },
17 "comment": null, 20 "comment": null,
18 - "loginButtonIcon": "mdi:google", 21 + "loginButtonIcon": "google-logo",
19 "loginButtonLabel": "Google", 22 "loginButtonLabel": "Google",
20 "helpLink": "https://developers.google.com/adwords/api/docs/guides/authentication" 23 "helpLink": "https://developers.google.com/adwords/api/docs/guides/authentication"
21 } 24 }
@@ -34,8 +34,7 @@ import java.util.List; @@ -34,8 +34,7 @@ import java.util.List;
34 public class OAuth2ClientRegistrationTemplate extends SearchTextBasedWithAdditionalInfo<OAuth2ClientRegistrationTemplateId> implements HasName { 34 public class OAuth2ClientRegistrationTemplate extends SearchTextBasedWithAdditionalInfo<OAuth2ClientRegistrationTemplateId> implements HasName {
35 35
36 private String providerId; 36 private String providerId;
37 - private MapperType mapperType;  
38 - private OAuth2BasicMapperConfig basic; 37 + private OAuth2MapperConfig mapperConfig;
39 private String authorizationUri; 38 private String authorizationUri;
40 private String accessTokenUri; 39 private String accessTokenUri;
41 private List<String> scope; 40 private List<String> scope;
@@ -51,8 +50,7 @@ public class OAuth2ClientRegistrationTemplate extends SearchTextBasedWithAdditio @@ -51,8 +50,7 @@ public class OAuth2ClientRegistrationTemplate extends SearchTextBasedWithAdditio
51 public OAuth2ClientRegistrationTemplate(OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { 50 public OAuth2ClientRegistrationTemplate(OAuth2ClientRegistrationTemplate clientRegistrationTemplate) {
52 super(clientRegistrationTemplate); 51 super(clientRegistrationTemplate);
53 this.providerId = clientRegistrationTemplate.providerId; 52 this.providerId = clientRegistrationTemplate.providerId;
54 - this.mapperType = clientRegistrationTemplate.mapperType;  
55 - this.basic = clientRegistrationTemplate.basic; 53 + this.mapperConfig = clientRegistrationTemplate.mapperConfig;
56 this.authorizationUri = clientRegistrationTemplate.authorizationUri; 54 this.authorizationUri = clientRegistrationTemplate.authorizationUri;
57 this.accessTokenUri = clientRegistrationTemplate.accessTokenUri; 55 this.accessTokenUri = clientRegistrationTemplate.accessTokenUri;
58 this.scope = clientRegistrationTemplate.scope; 56 this.scope = clientRegistrationTemplate.scope;
@@ -109,17 +109,20 @@ public class OAuth2ClientRegistrationTemplateEntity extends BaseSqlEntity<OAuth2 @@ -109,17 +109,20 @@ public class OAuth2ClientRegistrationTemplateEntity extends BaseSqlEntity<OAuth2
109 this.loginButtonLabel = clientRegistrationTemplate.getLoginButtonLabel(); 109 this.loginButtonLabel = clientRegistrationTemplate.getLoginButtonLabel();
110 this.helpLink = clientRegistrationTemplate.getHelpLink(); 110 this.helpLink = clientRegistrationTemplate.getHelpLink();
111 this.additionalInfo = clientRegistrationTemplate.getAdditionalInfo(); 111 this.additionalInfo = clientRegistrationTemplate.getAdditionalInfo();
112 - this.type = clientRegistrationTemplate.getMapperType();  
113 - OAuth2BasicMapperConfig basicConfig = clientRegistrationTemplate.getBasic();  
114 - if (basicConfig != null) {  
115 - this.emailAttributeKey = basicConfig.getEmailAttributeKey();  
116 - this.firstNameAttributeKey = basicConfig.getFirstNameAttributeKey();  
117 - this.lastNameAttributeKey = basicConfig.getLastNameAttributeKey();  
118 - this.tenantNameStrategy = basicConfig.getTenantNameStrategy();  
119 - this.tenantNamePattern = basicConfig.getTenantNamePattern();  
120 - this.customerNamePattern = basicConfig.getCustomerNamePattern();  
121 - this.defaultDashboardName = basicConfig.getDefaultDashboardName();  
122 - this.alwaysFullScreen = basicConfig.isAlwaysFullScreen(); 112 + OAuth2MapperConfig mapperConfig = clientRegistrationTemplate.getMapperConfig();
  113 + if (mapperConfig != null){
  114 + this.type = mapperConfig.getType();
  115 + OAuth2BasicMapperConfig basicConfig = mapperConfig.getBasic();
  116 + if (basicConfig != null) {
  117 + this.emailAttributeKey = basicConfig.getEmailAttributeKey();
  118 + this.firstNameAttributeKey = basicConfig.getFirstNameAttributeKey();
  119 + this.lastNameAttributeKey = basicConfig.getLastNameAttributeKey();
  120 + this.tenantNameStrategy = basicConfig.getTenantNameStrategy();
  121 + this.tenantNamePattern = basicConfig.getTenantNamePattern();
  122 + this.customerNamePattern = basicConfig.getCustomerNamePattern();
  123 + this.defaultDashboardName = basicConfig.getDefaultDashboardName();
  124 + this.alwaysFullScreen = basicConfig.isAlwaysFullScreen();
  125 + }
123 } 126 }
124 } 127 }
125 128
@@ -130,18 +133,21 @@ public class OAuth2ClientRegistrationTemplateEntity extends BaseSqlEntity<OAuth2 @@ -130,18 +133,21 @@ public class OAuth2ClientRegistrationTemplateEntity extends BaseSqlEntity<OAuth2
130 clientRegistrationTemplate.setCreatedTime(createdTime); 133 clientRegistrationTemplate.setCreatedTime(createdTime);
131 clientRegistrationTemplate.setAdditionalInfo(additionalInfo); 134 clientRegistrationTemplate.setAdditionalInfo(additionalInfo);
132 135
133 - clientRegistrationTemplate.setMapperType(type);  
134 clientRegistrationTemplate.setProviderId(providerId); 136 clientRegistrationTemplate.setProviderId(providerId);
135 - clientRegistrationTemplate.setBasic(  
136 - OAuth2BasicMapperConfig.builder()  
137 - .emailAttributeKey(emailAttributeKey)  
138 - .firstNameAttributeKey(firstNameAttributeKey)  
139 - .lastNameAttributeKey(lastNameAttributeKey)  
140 - .tenantNameStrategy(tenantNameStrategy)  
141 - .tenantNamePattern(tenantNamePattern)  
142 - .customerNamePattern(customerNamePattern)  
143 - .defaultDashboardName(defaultDashboardName)  
144 - .alwaysFullScreen(alwaysFullScreen) 137 + clientRegistrationTemplate.setMapperConfig(
  138 + OAuth2MapperConfig.builder()
  139 + .type(type)
  140 + .basic(OAuth2BasicMapperConfig.builder()
  141 + .emailAttributeKey(emailAttributeKey)
  142 + .firstNameAttributeKey(firstNameAttributeKey)
  143 + .lastNameAttributeKey(lastNameAttributeKey)
  144 + .tenantNameStrategy(tenantNameStrategy)
  145 + .tenantNamePattern(tenantNamePattern)
  146 + .customerNamePattern(customerNamePattern)
  147 + .defaultDashboardName(defaultDashboardName)
  148 + .alwaysFullScreen(alwaysFullScreen)
  149 + .build()
  150 + )
145 .build() 151 .build()
146 ); 152 );
147 clientRegistrationTemplate.setAuthorizationUri(authorizationUri); 153 clientRegistrationTemplate.setAuthorizationUri(authorizationUri);
@@ -104,7 +104,13 @@ public class OAuth2ConfigTemplateServiceImpl extends AbstractEntityService imple @@ -104,7 +104,13 @@ public class OAuth2ConfigTemplateServiceImpl extends AbstractEntityService imple
104 if (StringUtils.isEmpty(clientRegistrationTemplate.getProviderId())) { 104 if (StringUtils.isEmpty(clientRegistrationTemplate.getProviderId())) {
105 throw new DataValidationException("Provider ID should be specified!"); 105 throw new DataValidationException("Provider ID should be specified!");
106 } 106 }
107 - if (clientRegistrationTemplate.getBasic() == null) { 107 + if (clientRegistrationTemplate.getMapperConfig() == null) {
  108 + throw new DataValidationException("Mapper config should be specified!");
  109 + }
  110 + if (clientRegistrationTemplate.getMapperConfig().getType() == null) {
  111 + throw new DataValidationException("Mapper type should be specified!");
  112 + }
  113 + if (clientRegistrationTemplate.getMapperConfig().getBasic() == null) {
108 throw new DataValidationException("Basic mapper config should be specified!"); 114 throw new DataValidationException("Basic mapper config should be specified!");
109 } 115 }
110 } 116 }
@@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.id.TenantId; @@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.id.TenantId;
24 import org.thingsboard.server.common.data.oauth2.MapperType; 24 import org.thingsboard.server.common.data.oauth2.MapperType;
25 import org.thingsboard.server.common.data.oauth2.OAuth2BasicMapperConfig; 25 import org.thingsboard.server.common.data.oauth2.OAuth2BasicMapperConfig;
26 import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; 26 import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate;
  27 +import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig;
27 import org.thingsboard.server.dao.exception.DataValidationException; 28 import org.thingsboard.server.dao.exception.DataValidationException;
28 import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; 29 import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService;
29 30
@@ -106,9 +107,9 @@ public class BaseOAuth2ConfigTemplateServiceTest extends AbstractServiceTest { @@ -106,9 +107,9 @@ public class BaseOAuth2ConfigTemplateServiceTest extends AbstractServiceTest {
106 OAuth2ClientRegistrationTemplate clientRegistrationTemplate = new OAuth2ClientRegistrationTemplate(); 107 OAuth2ClientRegistrationTemplate clientRegistrationTemplate = new OAuth2ClientRegistrationTemplate();
107 clientRegistrationTemplate.setProviderId(providerId); 108 clientRegistrationTemplate.setProviderId(providerId);
108 clientRegistrationTemplate.setAdditionalInfo(mapper.createObjectNode().put(UUID.randomUUID().toString(), UUID.randomUUID().toString())); 109 clientRegistrationTemplate.setAdditionalInfo(mapper.createObjectNode().put(UUID.randomUUID().toString(), UUID.randomUUID().toString()));
109 - clientRegistrationTemplate.setMapperType(MapperType.BASIC);  
110 - clientRegistrationTemplate.setBasic(  
111 - OAuth2BasicMapperConfig.builder() 110 + clientRegistrationTemplate.setMapperConfig(OAuth2MapperConfig.builder()
  111 + .type(MapperType.BASIC)
  112 + .basic(OAuth2BasicMapperConfig.builder()
112 .firstNameAttributeKey("firstName") 113 .firstNameAttributeKey("firstName")
113 .lastNameAttributeKey("lastName") 114 .lastNameAttributeKey("lastName")
114 .emailAttributeKey("email") 115 .emailAttributeKey("email")
@@ -116,7 +117,8 @@ public class BaseOAuth2ConfigTemplateServiceTest extends AbstractServiceTest { @@ -116,7 +117,8 @@ public class BaseOAuth2ConfigTemplateServiceTest extends AbstractServiceTest {
116 .defaultDashboardName("Test") 117 .defaultDashboardName("Test")
117 .alwaysFullScreen(true) 118 .alwaysFullScreen(true)
118 .build() 119 .build()
119 - ); 120 + )
  121 + .build());
120 clientRegistrationTemplate.setAuthorizationUri("authorizationUri"); 122 clientRegistrationTemplate.setAuthorizationUri("authorizationUri");
121 clientRegistrationTemplate.setAccessTokenUri("tokenUri"); 123 clientRegistrationTemplate.setAccessTokenUri("tokenUri");
122 clientRegistrationTemplate.setScope(Arrays.asList("scope1", "scope2")); 124 clientRegistrationTemplate.setScope(Arrays.asList("scope1", "scope2"));
@@ -68,6 +68,27 @@ export class AppComponent implements OnInit { @@ -68,6 +68,27 @@ export class AppComponent implements OnInit {
68 ) 68 )
69 ); 69 );
70 70
  71 + this.matIconRegistry.addSvgIconLiteral(
  72 + 'google-logo',
  73 + this.domSanitizer.bypassSecurityTrustHtml(
  74 + '<svg viewBox="0 0 48 48"><path fill="#EA4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"/><path fill="#4285F4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"/><path fill="#FBBC05" d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z"/><path fill="#34A853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"/><path fill="none" d="M0 0h48v48H0z"/></svg>'
  75 + )
  76 + );
  77 +
  78 + this.matIconRegistry.addSvgIconLiteral(
  79 + 'github-logo',
  80 + this.domSanitizer.bypassSecurityTrustHtml(
  81 + '<svg viewBox="0 0 32.7 32.7"><path d="M16.3 0C7.3 0 0 7.3 0 16.3c0 7.2 4.7 13.3 11.1 15.5.8.1 1.1-.4 1.1-.8v-2.8c-4.5 1-5.5-2.2-5.5-2.2-.7-1.9-1.8-2.4-1.8-2.4-1.5-1 .1-1 .1-1 1.6.1 2.5 1.7 2.5 1.7 1.5 2.5 3.8 1.8 4.7 1.4.1-1.1.6-1.8 1-2.2-3.6-.4-7.4-1.8-7.4-8.1 0-1.8.6-3.2 1.7-4.4-.2-.4-.7-2.1.2-4.3 0 0 1.4-.4 4.5 1.7 1.3-.4 2.7-.5 4.1-.5s2.8.2 4.1.5c3.1-2.1 4.5-1.7 4.5-1.7.9 2.2.3 3.9.2 4.3 1 1.1 1.7 2.6 1.7 4.4 0 6.3-3.8 7.6-7.4 8 .6.5 1.1 1.5 1.1 3v4.5c0 .4.3.9 1.1.8 6.5-2.2 11.1-8.3 11.1-15.5C32.6 7.3 25.3 0 16.3 0z" fill="#211c19"/></svg>'
  82 + )
  83 + );
  84 +
  85 + this.matIconRegistry.addSvgIconLiteral(
  86 + 'facebook-logo',
  87 + this.domSanitizer.bypassSecurityTrustHtml(
  88 + '<svg viewBox="0 0 263 263"><path d="M263 131.5C263 58.9 204.1 0 131.5 0S0 58.9 0 131.5c0 65.6 48.1 120 110.9 129.9v-91.9H77.5v-38h33.4v-29c0-33 19.6-51.2 49.7-51.2 14.4 0 29.4 2.6 29.4 2.6v32.4h-16.5c-16.3 0-21.4 10.1-21.4 20.5v24.7h36.4l-5.8 38h-30.6v91.9c62.8-9.9 110.9-64.3 110.9-129.9z" fill="#1877f2"/><path d="M182.7 169.5l5.8-38H152v-24.7c0-10.4 5.1-20.5 21.4-20.5H190V53.9s-15-2.6-29.4-2.6c-30 0-49.7 18.2-49.7 51.2v29H77.5v38h33.4v91.9c6.7 1.1 13.6 1.6 20.5 1.6s13.9-.5 20.5-1.6v-91.9h30.8z" fill="#fff"/></svg>'
  89 + )
  90 + );
  91 +
71 this.storageService.testLocalStorage(); 92 this.storageService.testLocalStorage();
72 93
73 this.setupTranslate(); 94 this.setupTranslate();
@@ -21,7 +21,7 @@ import { HttpClient } from '@angular/common/http'; @@ -21,7 +21,7 @@ import { HttpClient } from '@angular/common/http';
21 import { forkJoin, Observable, of, ReplaySubject, throwError } from 'rxjs'; 21 import { forkJoin, Observable, of, ReplaySubject, throwError } from 'rxjs';
22 import { catchError, map, mergeMap, tap } from 'rxjs/operators'; 22 import { catchError, map, mergeMap, tap } from 'rxjs/operators';
23 23
24 -import { LoginRequest, LoginResponse, OAuth2Client, PublicLoginRequest } from '@shared/models/login.models'; 24 +import { LoginRequest, LoginResponse, PublicLoginRequest } from '@shared/models/login.models';
25 import { ActivatedRoute, Router, UrlTree } from '@angular/router'; 25 import { ActivatedRoute, Router, UrlTree } from '@angular/router';
26 import { defaultHttpOptions } from '../http/http-utils'; 26 import { defaultHttpOptions } from '../http/http-utils';
27 import { UserService } from '../http/user.service'; 27 import { UserService } from '../http/user.service';
@@ -44,6 +44,7 @@ import { AdminService } from '@core/http/admin.service'; @@ -44,6 +44,7 @@ import { AdminService } from '@core/http/admin.service';
44 import { ActionNotificationShow } from '@core/notification/notification.actions'; 44 import { ActionNotificationShow } from '@core/notification/notification.actions';
45 import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; 45 import { MatDialog, MatDialogConfig } from '@angular/material/dialog';
46 import { AlertDialogComponent } from '@shared/components/dialog/alert-dialog.component'; 46 import { AlertDialogComponent } from '@shared/components/dialog/alert-dialog.component';
  47 +import { OAuth2ClientInfo } from '@shared/models/oauth2.models';
47 48
48 @Injectable({ 49 @Injectable({
49 providedIn: 'root' 50 providedIn: 'root'
@@ -67,7 +68,7 @@ export class AuthService { @@ -67,7 +68,7 @@ export class AuthService {
67 } 68 }
68 69
69 redirectUrl: string; 70 redirectUrl: string;
70 - oauth2Clients: Array<OAuth2Client> = null; 71 + oauth2Clients: Array<OAuth2ClientInfo> = null;
71 72
72 private refreshTokenSubject: ReplaySubject<LoginResponse> = null; 73 private refreshTokenSubject: ReplaySubject<LoginResponse> = null;
73 private jwtHelper = new JwtHelperService(); 74 private jwtHelper = new JwtHelperService();
@@ -197,13 +198,14 @@ export class AuthService { @@ -197,13 +198,14 @@ export class AuthService {
197 }); 198 });
198 } 199 }
199 200
200 - public loadOAuth2Clients(): Observable<Array<OAuth2Client>> {  
201 - return this.http.post<Array<OAuth2Client>>(`/api/noauth/oauth2Clients`, 201 + public loadOAuth2Clients(): Observable<Array<OAuth2ClientInfo>> {
  202 + return this.http.post<Array<OAuth2ClientInfo>>(`/api/noauth/oauth2Clients`,
202 null, defaultHttpOptions()).pipe( 203 null, defaultHttpOptions()).pipe(
203 - tap((OAuth2Clients) => {  
204 - this.oauth2Clients = OAuth2Clients;  
205 - })  
206 - ); 204 + catchError(err => of([])),
  205 + tap((OAuth2Clients) => {
  206 + this.oauth2Clients = OAuth2Clients;
  207 + })
  208 + );
207 } 209 }
208 210
209 private forceDefaultPlace(authState?: AuthState, path?: string, params?: any): boolean { 211 private forceDefaultPlace(authState?: AuthState, path?: string, params?: any): boolean {
@@ -18,14 +18,7 @@ import { Injectable } from '@angular/core'; @@ -18,14 +18,7 @@ import { Injectable } from '@angular/core';
18 import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; 18 import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils';
19 import { Observable } from 'rxjs'; 19 import { Observable } from 'rxjs';
20 import { HttpClient } from '@angular/common/http'; 20 import { HttpClient } from '@angular/common/http';
21 -import {  
22 - AdminSettings,  
23 - ClientProviderTemplated,  
24 - MailServerSettings,  
25 - OAuth2Settings,  
26 - SecuritySettings,  
27 - UpdateMessage  
28 -} from '@shared/models/settings.models'; 21 +import { AdminSettings, MailServerSettings, SecuritySettings, UpdateMessage } from '@shared/models/settings.models';
29 22
30 @Injectable({ 23 @Injectable({
31 providedIn: 'root' 24 providedIn: 'root'
@@ -60,19 +53,6 @@ export class AdminService { @@ -60,19 +53,6 @@ export class AdminService {
60 defaultHttpOptionsFromConfig(config)); 53 defaultHttpOptionsFromConfig(config));
61 } 54 }
62 55
63 - public getOAuth2Settings(config?: RequestConfig): Observable<OAuth2Settings> {  
64 - return this.http.get<OAuth2Settings>(`/api/oauth2/config`, defaultHttpOptionsFromConfig(config));  
65 - }  
66 -  
67 - public getOAuth2Template(config?: RequestConfig): Observable<Array<ClientProviderTemplated>> {  
68 - return this.http.get<Array<ClientProviderTemplated>>(`/api/oauth2/config/template`, defaultHttpOptionsFromConfig(config));  
69 - }  
70 -  
71 - public saveOAuth2Settings(OAuth2Setting: OAuth2Settings, config?: RequestConfig): Observable<OAuth2Settings> {  
72 - return this.http.post<OAuth2Settings>('/api/oauth2/config', OAuth2Setting,  
73 - defaultHttpOptionsFromConfig(config));  
74 - }  
75 -  
76 public checkUpdates(config?: RequestConfig): Observable<UpdateMessage> { 56 public checkUpdates(config?: RequestConfig): Observable<UpdateMessage> {
77 return this.http.get<UpdateMessage>(`/api/admin/updates`, defaultHttpOptionsFromConfig(config)); 57 return this.http.get<UpdateMessage>(`/api/admin/updates`, defaultHttpOptionsFromConfig(config));
78 } 58 }
  1 +///
  2 +/// Copyright © 2016-2020 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import { Injectable } from '@angular/core';
  18 +import { HttpClient } from '@angular/common/http';
  19 +import { defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-utils';
  20 +import { Observable } from 'rxjs';
  21 +import { OAuth2ClientRegistrationTemplate, OAuth2ClientsParams } from '@shared/models/oauth2.models';
  22 +
  23 +@Injectable({
  24 + providedIn: 'root'
  25 +})
  26 +export class OAuth2Service {
  27 +
  28 + constructor(
  29 + private http: HttpClient
  30 + ) { }
  31 +
  32 + public getOAuth2Settings(config?: RequestConfig): Observable<OAuth2ClientsParams> {
  33 + return this.http.get<OAuth2ClientsParams>(`/api/oauth2/config`, defaultHttpOptionsFromConfig(config));
  34 + }
  35 +
  36 + public getOAuth2Template(config?: RequestConfig): Observable<Array<OAuth2ClientRegistrationTemplate>> {
  37 + return this.http.get<Array<OAuth2ClientRegistrationTemplate>>(`/api/oauth2/config/template`, defaultHttpOptionsFromConfig(config));
  38 + }
  39 +
  40 + public saveOAuth2Settings(OAuth2Setting: OAuth2ClientsParams, config?: RequestConfig): Observable<OAuth2ClientsParams> {
  41 + return this.http.post<OAuth2ClientsParams>('/api/oauth2/config', OAuth2Setting,
  42 + defaultHttpOptionsFromConfig(config));
  43 + }
  44 +}
@@ -28,6 +28,7 @@ export * from './entity-relation.service'; @@ -28,6 +28,7 @@ export * from './entity-relation.service';
28 export * from './entity-view.service'; 28 export * from './entity-view.service';
29 export * from './event.service'; 29 export * from './event.service';
30 export * from './http-utils'; 30 export * from './http-utils';
  31 +export * from './oauth2.service';
31 export * from './queue.service'; 32 export * from './queue.service';
32 export * from './rule-chain.service'; 33 export * from './rule-chain.service';
33 export * from './tenant.service'; 34 export * from './tenant.service';
@@ -33,7 +33,7 @@ @@ -33,7 +33,7 @@
33 <mat-checkbox formControlName="enabled"> 33 <mat-checkbox formControlName="enabled">
34 {{ 'admin.oauth2.enable' | translate }} 34 {{ 'admin.oauth2.enable' | translate }}
35 </mat-checkbox> 35 </mat-checkbox>
36 - <section *ngIf="oauth2SettingsForm.get('enabled').value && !(isLoading$ | async)" style="margin-top: 1em;"> 36 + <section *ngIf="oauth2SettingsForm.get('enabled').value" style="margin-top: 1em;">
37 <ng-container formArrayName="domainsParams"> 37 <ng-container formArrayName="domainsParams">
38 <div class="container"> 38 <div class="container">
39 <mat-accordion multi> 39 <mat-accordion multi>
@@ -59,7 +59,7 @@ @@ -59,7 +59,7 @@
59 <section *ngFor="let domainInfo of clientDomainInfos(domain).controls; let n = index; trackBy: trackByParams" 59 <section *ngFor="let domainInfo of clientDomainInfos(domain).controls; let n = index; trackBy: trackByParams"
60 class="domains-list"> 60 class="domains-list">
61 <div [formGroupName]="n" fxLayout="row" fxLayoutGap="8px"> 61 <div [formGroupName]="n" fxLayout="row" fxLayoutGap="8px">
62 - <div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px"> 62 + <div fxFlex fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px">
63 <div fxLayout="column" fxFlex.sm="60" fxFlex.gt-sm="50"> 63 <div fxLayout="column" fxFlex.sm="60" fxFlex.gt-sm="50">
64 <div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px"> 64 <div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px">
65 <mat-form-field fxFlex="30" fxFlex.xs class="mat-block"> 65 <mat-form-field fxFlex="30" fxFlex.xs class="mat-block">
@@ -160,7 +160,7 @@ @@ -160,7 +160,7 @@
160 </mat-option> 160 </mat-option>
161 </mat-select> 161 </mat-select>
162 </mat-form-field> 162 </mat-form-field>
163 - <div [tb-help]="getHelpLink(registration)" *ngIf="getProviderName(registration) !== 'Custom'"></div> 163 + <div [tb-help]="getHelpLink(registration)" *ngIf="!isCustomProvider(registration)"></div>
164 </section> 164 </section>
165 165
166 <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px"> 166 <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px">
@@ -182,9 +182,9 @@ @@ -182,9 +182,9 @@
182 </div> 182 </div>
183 183
184 <mat-expansion-panel class="mat-elevation-z0 custom-settings" 184 <mat-expansion-panel class="mat-elevation-z0 custom-settings"
185 - [disabled]="getProviderName(registration) === 'Custom'"  
186 - [expanded]="getProviderName(registration) === 'Custom'">  
187 - <mat-expansion-panel-header [fxHide]="getProviderName(registration) === 'Custom'"> 185 + [disabled]="isCustomProvider(registration)"
  186 + [expanded]="isCustomProvider(registration)">
  187 + <mat-expansion-panel-header [fxHide]="isCustomProvider(registration)">
188 <mat-panel-description fxLayoutAlign="end center"> 188 <mat-panel-description fxLayoutAlign="end center">
189 {{ 'admin.oauth2.custom-setting' | translate }} 189 {{ 'admin.oauth2.custom-setting' | translate }}
190 </mat-panel-description> 190 </mat-panel-description>
@@ -199,7 +199,7 @@ @@ -199,7 +199,7 @@
199 <button mat-icon-button matSuffix 199 <button mat-icon-button matSuffix
200 type="button" 200 type="button"
201 (click)="toggleEditMode(registration, 'accessTokenUri')" 201 (click)="toggleEditMode(registration, 'accessTokenUri')"
202 - *ngIf="getProviderName(registration) !== 'Custom'"> 202 + *ngIf="!isCustomProvider(registration)">
203 <mat-icon class="material-icons">create</mat-icon> 203 <mat-icon class="material-icons">create</mat-icon>
204 </button> 204 </button>
205 <mat-error *ngIf="registration.get('accessTokenUri').hasError('required')"> 205 <mat-error *ngIf="registration.get('accessTokenUri').hasError('required')">
@@ -216,7 +216,7 @@ @@ -216,7 +216,7 @@
216 <button mat-icon-button matSuffix 216 <button mat-icon-button matSuffix
217 type="button" 217 type="button"
218 (click)="toggleEditMode(registration, 'authorizationUri')" 218 (click)="toggleEditMode(registration, 'authorizationUri')"
219 - *ngIf="getProviderName(registration) !== 'Custom'"> 219 + *ngIf="!isCustomProvider(registration)">
220 <mat-icon class="material-icons">create</mat-icon> 220 <mat-icon class="material-icons">create</mat-icon>
221 </button> 221 </button>
222 <mat-error *ngIf="registration.get('authorizationUri').hasError('required')"> 222 <mat-error *ngIf="registration.get('authorizationUri').hasError('required')">
@@ -235,7 +235,7 @@ @@ -235,7 +235,7 @@
235 <button mat-icon-button matSuffix 235 <button mat-icon-button matSuffix
236 type="button" aria-label="Clear" 236 type="button" aria-label="Clear"
237 (click)="toggleEditMode(registration, 'jwkSetUri')" 237 (click)="toggleEditMode(registration, 'jwkSetUri')"
238 - *ngIf="getProviderName(registration) !== 'Custom'"> 238 + *ngIf="!isCustomProvider(registration)">
239 <mat-icon class="material-icons">create</mat-icon> 239 <mat-icon class="material-icons">create</mat-icon>
240 </button> 240 </button>
241 <mat-error *ngIf="registration.get('jwkSetUri').hasError('pattern')"> 241 <mat-error *ngIf="registration.get('jwkSetUri').hasError('pattern')">
@@ -249,7 +249,7 @@ @@ -249,7 +249,7 @@
249 <button mat-icon-button matSuffix 249 <button mat-icon-button matSuffix
250 type="button" 250 type="button"
251 (click)="toggleEditMode(registration, 'userInfoUri')" 251 (click)="toggleEditMode(registration, 'userInfoUri')"
252 - *ngIf="getProviderName(registration) !== 'Custom'"> 252 + *ngIf="!isCustomProvider(registration)">
253 <mat-icon class="material-icons">create</mat-icon> 253 <mat-icon class="material-icons">create</mat-icon>
254 </button> 254 </button>
255 <mat-error *ngIf="registration.get('userInfoUri').hasError('required')"> 255 <mat-error *ngIf="registration.get('userInfoUri').hasError('required')">
@@ -271,11 +271,11 @@ @@ -271,11 +271,11 @@
271 </mat-select> 271 </mat-select>
272 </mat-form-field> 272 </mat-form-field>
273 273
274 - <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px" *ngIf="getProviderName(registration) === 'Custom'"> 274 + <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px" *ngIf="isCustomProvider(registration)">
275 <mat-form-field fxFlex class="mat-block" floatLabel="always"> 275 <mat-form-field fxFlex class="mat-block" floatLabel="always">
276 <mat-label translate>admin.oauth2.login-button-label</mat-label> 276 <mat-label translate>admin.oauth2.login-button-label</mat-label>
277 <input matInput formControlName="loginButtonLabel" 277 <input matInput formControlName="loginButtonLabel"
278 - placeholder="{{ 'admin.oauth2.login-button-label-1' | translate }}" 278 + placeholder="{{ 'admin.oauth2.login-button-label-placeholder' | translate }}"
279 required> 279 required>
280 <mat-error *ngIf="registration.get('loginButtonLabel').hasError('required')"> 280 <mat-error *ngIf="registration.get('loginButtonLabel').hasError('required')">
281 {{ 'admin.oauth2.login-button-label-required' | translate }} 281 {{ 'admin.oauth2.login-button-label-required' | translate }}
@@ -316,6 +316,10 @@ @@ -316,6 +316,10 @@
316 {{ 'admin.oauth2.scope-required' | translate }} 316 {{ 'admin.oauth2.scope-required' | translate }}
317 </mat-error> 317 </mat-error>
318 </mat-form-field> 318 </mat-form-field>
  319 + <tb-error style="display: block; margin-top: -24px;"
  320 + [error]="registration.get('scope').hasError('required')
  321 + ? ('admin.oauth2.scope-required' | translate) : ''">
  322 + </tb-error>
319 323
320 </mat-tab> 324 </mat-tab>
321 <mat-tab label="{{ 'admin.oauth2.mapper' | translate }}"> 325 <mat-tab label="{{ 'admin.oauth2.mapper' | translate }}">
@@ -332,16 +336,17 @@ @@ -332,16 +336,17 @@
332 <mat-form-field fxFlex class="mat-block"> 336 <mat-form-field fxFlex class="mat-block">
333 <mat-label translate>admin.oauth2.type</mat-label> 337 <mat-label translate>admin.oauth2.type</mat-label>
334 <mat-select formControlName="type"> 338 <mat-select formControlName="type">
335 - <mat-option *ngFor="let converterTypeExternalUser of converterTypesExternalUser"  
336 - [value]="converterTypeExternalUser">  
337 - {{ converterTypeExternalUser }} 339 + <mat-option *ngFor="let mapperConfigType of mapperConfigTypes"
  340 + [value]="mapperConfigType">
  341 + {{ mapperConfigType }}
338 </mat-option> 342 </mat-option>
339 </mat-select> 343 </mat-select>
340 </mat-form-field> 344 </mat-form-field>
341 345
342 <section formGroupName="basic" 346 <section formGroupName="basic"
343 - *ngIf="registration.get('mapperConfig.type').value === 'BASIC'">  
344 - <mat-form-field class="mat-block"> 347 + *ngIf="registration.get('mapperConfig.type').value !== mapperConfigType.CUSTOM">
  348 + <mat-form-field class="mat-block"
  349 + *ngIf="registration.get('mapperConfig.type').value !== mapperConfigType.GITHUB">
345 <mat-label translate>admin.oauth2.email-attribute-key</mat-label> 350 <mat-label translate>admin.oauth2.email-attribute-key</mat-label>
346 <input matInput formControlName="emailAttributeKey" required> 351 <input matInput formControlName="emailAttributeKey" required>
347 <mat-error 352 <mat-error
@@ -403,7 +408,7 @@ @@ -403,7 +408,7 @@
403 </section> 408 </section>
404 409
405 <section formGroupName="custom" 410 <section formGroupName="custom"
406 - *ngIf="registration.get('mapperConfig.type').value === 'CUSTOM'"> 411 + *ngIf="registration.get('mapperConfig.type').value === mapperConfigType.CUSTOM">
407 <mat-form-field class="mat-block"> 412 <mat-form-field class="mat-block">
408 <mat-label translate>admin.oauth2.url</mat-label> 413 <mat-label translate>admin.oauth2.url</mat-label>
409 <input matInput formControlName="url" required> 414 <input matInput formControlName="url" required>
@@ -41,6 +41,14 @@ @@ -41,6 +41,14 @@
41 padding-bottom: 0; 41 padding-bottom: 0;
42 } 42 }
43 } 43 }
  44 +
  45 + .mat-expansion-panel {
  46 + .mat-expansion-panel-header {
  47 + &.mat-expanded {
  48 + height: 48px;
  49 + }
  50 + }
  51 + }
44 } 52 }
45 53
46 :host ::ng-deep{ 54 :host ::ng-deep{
@@ -49,9 +57,6 @@ @@ -49,9 +57,6 @@
49 .mat-expansion-panel-body{ 57 .mat-expansion-panel-body{
50 padding: 0 2px 1em; 58 padding: 0 2px 1em;
51 } 59 }
52 - .mat-tab-label{  
53 - text-transform: none;  
54 - }  
55 .mat-form-field-suffix .mat-icon-button .mat-icon{ 60 .mat-form-field-suffix .mat-icon-button .mat-icon{
56 font-size: 18px; 61 font-size: 18px;
57 } 62 }
@@ -15,25 +15,24 @@ @@ -15,25 +15,24 @@
15 /// 15 ///
16 16
17 import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; 17 import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
18 -import { AbstractControl, FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms'; 18 +import { AbstractControl, FormArray, FormBuilder, FormGroup, ValidationErrors, Validators } from '@angular/forms';
19 import { 19 import {
20 ClientAuthenticationMethod, 20 ClientAuthenticationMethod,
21 - ClientProviderTemplated,  
22 ClientRegistration, 21 ClientRegistration,
23 DomainInfo, 22 DomainInfo,
24 DomainSchema, 23 DomainSchema,
25 domainSchemaTranslations, 24 domainSchemaTranslations,
26 - DomainsParam,  
27 MapperConfig, 25 MapperConfig,
28 MapperConfigBasic, 26 MapperConfigBasic,
29 MapperConfigCustom, 27 MapperConfigCustom,
30 MapperConfigType, 28 MapperConfigType,
31 - OAuth2Settings, 29 + OAuth2ClientRegistrationTemplate,
  30 + OAuth2ClientsDomainParams,
  31 + OAuth2ClientsParams,
32 TenantNameStrategy 32 TenantNameStrategy
33 -} from '@shared/models/settings.models'; 33 +} from '@shared/models/oauth2.models';
34 import { Store } from '@ngrx/store'; 34 import { Store } from '@ngrx/store';
35 import { AppState } from '@core/core.state'; 35 import { AppState } from '@core/core.state';
36 -import { AdminService } from '@core/http/admin.service';  
37 import { PageComponent } from '@shared/components/page.component'; 36 import { PageComponent } from '@shared/components/page.component';
38 import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard'; 37 import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard';
39 import { COMMA, ENTER } from '@angular/cdk/keycodes'; 38 import { COMMA, ENTER } from '@angular/cdk/keycodes';
@@ -42,7 +41,8 @@ import { WINDOW } from '@core/services/window.service'; @@ -42,7 +41,8 @@ import { WINDOW } from '@core/services/window.service';
42 import { forkJoin, Subscription } from 'rxjs'; 41 import { forkJoin, Subscription } from 'rxjs';
43 import { DialogService } from '@core/services/dialog.service'; 42 import { DialogService } from '@core/services/dialog.service';
44 import { TranslateService } from '@ngx-translate/core'; 43 import { TranslateService } from '@ngx-translate/core';
45 -import { isDefined } from '@core/utils'; 44 +import { isDefined, isDefinedAndNotNull } from '@core/utils';
  45 +import { OAuth2Service } from '@core/http/oauth2.service';
46 46
47 @Component({ 47 @Component({
48 selector: 'tb-oauth2-settings', 48 selector: 'tb-oauth2-settings',
@@ -53,7 +53,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha @@ -53,7 +53,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
53 53
54 private URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.,?+=&%@\-/]*)?$/; 54 private URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.,?+=&%@\-/]*)?$/;
55 private subscriptions: Subscription[] = []; 55 private subscriptions: Subscription[] = [];
56 - private templates = new Map<string, ClientProviderTemplated>(); 56 + private templates = new Map<string, OAuth2ClientRegistrationTemplate>();
57 private defaultProvider = { 57 private defaultProvider = {
58 additionalInfo: { 58 additionalInfo: {
59 providerName: 'Custom' 59 providerName: 'Custom'
@@ -75,10 +75,11 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha @@ -75,10 +75,11 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
75 readonly separatorKeysCodes: number[] = [ENTER, COMMA]; 75 readonly separatorKeysCodes: number[] = [ENTER, COMMA];
76 76
77 oauth2SettingsForm: FormGroup; 77 oauth2SettingsForm: FormGroup;
78 - oauth2Settings: OAuth2Settings; 78 + auth2ClientsParams: OAuth2ClientsParams;
79 79
80 clientAuthenticationMethods = Object.keys(ClientAuthenticationMethod); 80 clientAuthenticationMethods = Object.keys(ClientAuthenticationMethod);
81 - converterTypesExternalUser = Object.keys(MapperConfigType); 81 + mapperConfigType = MapperConfigType;
  82 + mapperConfigTypes = Object.keys(MapperConfigType);
82 tenantNameStrategies = Object.keys(TenantNameStrategy); 83 tenantNameStrategies = Object.keys(TenantNameStrategy);
83 protocols = Object.keys(DomainSchema); 84 protocols = Object.keys(DomainSchema);
84 domainSchemaTranslations = domainSchemaTranslations; 85 domainSchemaTranslations = domainSchemaTranslations;
@@ -86,7 +87,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha @@ -86,7 +87,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
86 templateProvider = ['Custom']; 87 templateProvider = ['Custom'];
87 88
88 constructor(protected store: Store<AppState>, 89 constructor(protected store: Store<AppState>,
89 - private adminService: AdminService, 90 + private oauth2Service: OAuth2Service,
90 private fb: FormBuilder, 91 private fb: FormBuilder,
91 private dialogService: DialogService, 92 private dialogService: DialogService,
92 private translate: TranslateService, 93 private translate: TranslateService,
@@ -97,13 +98,13 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha @@ -97,13 +98,13 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
97 ngOnInit(): void { 98 ngOnInit(): void {
98 this.buildOAuth2SettingsForm(); 99 this.buildOAuth2SettingsForm();
99 forkJoin([ 100 forkJoin([
100 - this.adminService.getOAuth2Template(),  
101 - this.adminService.getOAuth2Settings() 101 + this.oauth2Service.getOAuth2Template(),
  102 + this.oauth2Service.getOAuth2Settings()
102 ]).subscribe( 103 ]).subscribe(
103 - ([templates, oauth2Settings]) => { 104 + ([templates, auth2ClientsParams]) => {
104 this.initTemplates(templates); 105 this.initTemplates(templates);
105 - this.oauth2Settings = oauth2Settings;  
106 - this.initOAuth2Settings(this.oauth2Settings); 106 + this.auth2ClientsParams = auth2ClientsParams;
  107 + this.initOAuth2Settings(this.auth2ClientsParams);
107 } 108 }
108 ); 109 );
109 } 110 }
@@ -115,8 +116,11 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha @@ -115,8 +116,11 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
115 }); 116 });
116 } 117 }
117 118
118 - private initTemplates(templates: ClientProviderTemplated[]): void {  
119 - templates.map(provider => this.templates.set(provider.name, provider)); 119 + private initTemplates(templates: OAuth2ClientRegistrationTemplate[]): void {
  120 + templates.map(provider => {
  121 + delete provider.additionalInfo;
  122 + this.templates.set(provider.name, provider);
  123 + });
120 this.templateProvider.push(...Array.from(this.templates.keys())); 124 this.templateProvider.push(...Array.from(this.templates.keys()));
121 this.templateProvider.sort(); 125 this.templateProvider.sort();
122 } 126 }
@@ -169,10 +173,10 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha @@ -169,10 +173,10 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
169 }); 173 });
170 } 174 }
171 175
172 - private initOAuth2Settings(oauth2Settings: OAuth2Settings): void {  
173 - if (oauth2Settings) {  
174 - this.oauth2SettingsForm.patchValue({enabled: oauth2Settings.enabled}, {emitEvent: false});  
175 - oauth2Settings.domainsParams.forEach((domain) => { 176 + private initOAuth2Settings(auth2ClientsParams: OAuth2ClientsParams): void {
  177 + if (auth2ClientsParams) {
  178 + this.oauth2SettingsForm.patchValue({enabled: auth2ClientsParams.enabled}, {emitEvent: false});
  179 + auth2ClientsParams.domainsParams.forEach((domain) => {
176 this.domainsParams.push(this.buildDomainsForm(domain)); 180 this.domainsParams.push(this.buildDomainsForm(domain));
177 }); 181 });
178 } 182 }
@@ -204,17 +208,17 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha @@ -204,17 +208,17 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
204 return this.translate.instant('admin.oauth2.new-domain'); 208 return this.translate.instant('admin.oauth2.new-domain');
205 } 209 }
206 210
207 - private buildDomainsForm(domainParams?: DomainsParam): FormGroup { 211 + private buildDomainsForm(auth2ClientsDomainParams?: OAuth2ClientsDomainParams): FormGroup {
208 const formDomain = this.fb.group({ 212 const formDomain = this.fb.group({
209 domainInfos: this.fb.array([], Validators.required), 213 domainInfos: this.fb.array([], Validators.required),
210 clientRegistrations: this.fb.array([], Validators.required) 214 clientRegistrations: this.fb.array([], Validators.required)
211 }); 215 });
212 216
213 - if (domainParams) {  
214 - domainParams.domainInfos.forEach((domain) => { 217 + if (auth2ClientsDomainParams) {
  218 + auth2ClientsDomainParams.domainInfos.forEach((domain) => {
215 this.clientDomainInfos(formDomain).push(this.buildDomainForm(domain)); 219 this.clientDomainInfos(formDomain).push(this.buildDomainForm(domain));
216 }); 220 });
217 - domainParams.clientRegistrations.forEach((registration) => { 221 + auth2ClientsDomainParams.clientRegistrations.forEach((registration) => {
218 this.clientDomainProviders(formDomain).push(this.buildProviderForm(registration)); 222 this.clientDomainProviders(formDomain).push(this.buildProviderForm(registration));
219 }); 223 });
220 } else { 224 } else {
@@ -235,10 +239,10 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha @@ -235,10 +239,10 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
235 return domain; 239 return domain;
236 } 240 }
237 241
238 - private buildProviderForm(registrationData?: ClientRegistration): FormGroup { 242 + private buildProviderForm(clientRegistration?: ClientRegistration): FormGroup {
239 let additionalInfo = null; 243 let additionalInfo = null;
240 - if (registrationData?.additionalInfo) {  
241 - additionalInfo = JSON.parse(registrationData.additionalInfo); 244 + if (isDefinedAndNotNull(clientRegistration?.additionalInfo)) {
  245 + additionalInfo = clientRegistration.additionalInfo;
242 if (this.templateProvider.indexOf(additionalInfo.providerName) === -1) { 246 if (this.templateProvider.indexOf(additionalInfo.providerName) === -1) {
243 additionalInfo.providerName = 'Custom'; 247 additionalInfo.providerName = 'Custom';
244 } 248 }
@@ -248,73 +252,80 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha @@ -248,73 +252,80 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
248 defaultProviderName = 'Google'; 252 defaultProviderName = 'Google';
249 } 253 }
250 254
251 - const clientRegistration = this.fb.group({  
252 - id: this.fb.group({  
253 - id: [registrationData?.id?.id ? registrationData.id.id : null],  
254 - entityType: [registrationData?.id?.entityType ? registrationData.id.entityType : null]  
255 - }), 255 + const clientRegistrationFormGroup = this.fb.group({
256 additionalInfo: this.fb.group({ 256 additionalInfo: this.fb.group({
257 providerName: [additionalInfo?.providerName ? additionalInfo?.providerName : defaultProviderName, Validators.required] 257 providerName: [additionalInfo?.providerName ? additionalInfo?.providerName : defaultProviderName, Validators.required]
258 }), 258 }),
259 - loginButtonLabel: [registrationData?.loginButtonLabel ? registrationData.loginButtonLabel : null, Validators.required],  
260 - loginButtonIcon: [registrationData?.loginButtonIcon ? registrationData.loginButtonIcon : null],  
261 - clientId: [registrationData?.clientId ? registrationData.clientId : '', Validators.required],  
262 - clientSecret: [registrationData?.clientSecret ? registrationData.clientSecret : '', Validators.required],  
263 - accessTokenUri: [registrationData?.accessTokenUri ? registrationData.accessTokenUri : '', 259 + loginButtonLabel: [clientRegistration?.loginButtonLabel ? clientRegistration.loginButtonLabel : null, Validators.required],
  260 + loginButtonIcon: [clientRegistration?.loginButtonIcon ? clientRegistration.loginButtonIcon : null],
  261 + clientId: [clientRegistration?.clientId ? clientRegistration.clientId : '', Validators.required],
  262 + clientSecret: [clientRegistration?.clientSecret ? clientRegistration.clientSecret : '', Validators.required],
  263 + accessTokenUri: [clientRegistration?.accessTokenUri ? clientRegistration.accessTokenUri : '',
264 [Validators.required, 264 [Validators.required,
265 Validators.pattern(this.URL_REGEXP)]], 265 Validators.pattern(this.URL_REGEXP)]],
266 - authorizationUri: [registrationData?.authorizationUri ? registrationData.authorizationUri : '', 266 + authorizationUri: [clientRegistration?.authorizationUri ? clientRegistration.authorizationUri : '',
267 [Validators.required, 267 [Validators.required,
268 Validators.pattern(this.URL_REGEXP)]], 268 Validators.pattern(this.URL_REGEXP)]],
269 - scope: this.fb.array(registrationData?.scope ? registrationData.scope : [], Validators.required),  
270 - jwkSetUri: [registrationData?.jwkSetUri ? registrationData.jwkSetUri : '', Validators.pattern(this.URL_REGEXP)],  
271 - userInfoUri: [registrationData?.userInfoUri ? registrationData.userInfoUri : '', 269 + scope: this.fb.array(clientRegistration?.scope ? clientRegistration.scope : [], this.validateScope),
  270 + jwkSetUri: [clientRegistration?.jwkSetUri ? clientRegistration.jwkSetUri : '', Validators.pattern(this.URL_REGEXP)],
  271 + userInfoUri: [clientRegistration?.userInfoUri ? clientRegistration.userInfoUri : '',
272 [Validators.required, 272 [Validators.required,
273 Validators.pattern(this.URL_REGEXP)]], 273 Validators.pattern(this.URL_REGEXP)]],
274 clientAuthenticationMethod: [ 274 clientAuthenticationMethod: [
275 - registrationData?.clientAuthenticationMethod ? registrationData.clientAuthenticationMethod : ClientAuthenticationMethod.POST, 275 + clientRegistration?.clientAuthenticationMethod ? clientRegistration.clientAuthenticationMethod : ClientAuthenticationMethod.POST,
276 Validators.required], 276 Validators.required],
277 userNameAttributeName: [ 277 userNameAttributeName: [
278 - registrationData?.userNameAttributeName ? registrationData.userNameAttributeName : 'email', Validators.required], 278 + clientRegistration?.userNameAttributeName ? clientRegistration.userNameAttributeName : 'email', Validators.required],
279 mapperConfig: this.fb.group({ 279 mapperConfig: this.fb.group({
280 - allowUserCreation: [registrationData?.mapperConfig?.allowUserCreation ? registrationData.mapperConfig.allowUserCreation : true],  
281 - activateUser: [registrationData?.mapperConfig?.activateUser ? registrationData.mapperConfig.activateUser : false],  
282 - type: [registrationData?.mapperConfig?.type ? registrationData.mapperConfig.type : MapperConfigType.BASIC, Validators.required] 280 + allowUserCreation: [
  281 + clientRegistration?.mapperConfig?.allowUserCreation ? clientRegistration.mapperConfig.allowUserCreation : true
  282 + ],
  283 + activateUser: [clientRegistration?.mapperConfig?.activateUser ? clientRegistration.mapperConfig.activateUser : false],
  284 + type: [
  285 + clientRegistration?.mapperConfig?.type ? clientRegistration.mapperConfig.type : MapperConfigType.BASIC, Validators.required
  286 + ]
283 } 287 }
284 ) 288 )
285 }); 289 });
286 290
287 - if (registrationData) {  
288 - this.changeMapperConfigType(clientRegistration, registrationData.mapperConfig.type, registrationData.mapperConfig); 291 + if (clientRegistration) {
  292 + this.changeMapperConfigType(clientRegistrationFormGroup, clientRegistration.mapperConfig.type, clientRegistration.mapperConfig);
289 } else { 293 } else {
290 - this.changeMapperConfigType(clientRegistration, MapperConfigType.BASIC);  
291 - this.setProviderDefaultValue(defaultProviderName, clientRegistration); 294 + this.changeMapperConfigType(clientRegistrationFormGroup, MapperConfigType.BASIC);
  295 + this.setProviderDefaultValue(defaultProviderName, clientRegistrationFormGroup);
292 } 296 }
293 297
294 - this.subscriptions.push(clientRegistration.get('mapperConfig.type').valueChanges.subscribe((value) => {  
295 - this.changeMapperConfigType(clientRegistration, value); 298 + this.subscriptions.push(clientRegistrationFormGroup.get('mapperConfig.type').valueChanges.subscribe((value) => {
  299 + this.changeMapperConfigType(clientRegistrationFormGroup, value);
296 })); 300 }));
297 301
298 - this.subscriptions.push(clientRegistration.get('additionalInfo.providerName').valueChanges.subscribe((provider) => {  
299 - (clientRegistration.get('scope') as FormArray).clear();  
300 - this.setProviderDefaultValue(provider, clientRegistration); 302 + this.subscriptions.push(clientRegistrationFormGroup.get('additionalInfo.providerName').valueChanges.subscribe((provider) => {
  303 + (clientRegistrationFormGroup.get('scope') as FormArray).clear();
  304 + this.setProviderDefaultValue(provider, clientRegistrationFormGroup);
301 })); 305 }));
302 306
303 - return clientRegistration; 307 + return clientRegistrationFormGroup;
  308 + }
  309 +
  310 + private validateScope (control: AbstractControl): ValidationErrors | null {
  311 + const scope: string[] = control.value;
  312 + if (!scope || !scope.length) {
  313 + return {
  314 + required: true
  315 + };
  316 + }
  317 + return null;
304 } 318 }
305 319
306 private setProviderDefaultValue(provider: string, clientRegistration: FormGroup) { 320 private setProviderDefaultValue(provider: string, clientRegistration: FormGroup) {
307 if (provider === 'Custom') { 321 if (provider === 'Custom') {
308 - const defaultSettings = {...this.defaultProvider, ...{id: clientRegistration.get('id').value}};  
309 - clientRegistration.reset(defaultSettings, {emitEvent: false}); 322 + clientRegistration.reset(this.defaultProvider, {emitEvent: false});
310 clientRegistration.get('accessTokenUri').enable(); 323 clientRegistration.get('accessTokenUri').enable();
311 clientRegistration.get('authorizationUri').enable(); 324 clientRegistration.get('authorizationUri').enable();
312 clientRegistration.get('jwkSetUri').enable(); 325 clientRegistration.get('jwkSetUri').enable();
313 clientRegistration.get('userInfoUri').enable(); 326 clientRegistration.get('userInfoUri').enable();
314 } else { 327 } else {
315 const template = this.templates.get(provider); 328 const template = this.templates.get(provider);
316 - delete template.id;  
317 - delete template.additionalInfo;  
318 template.clientId = ''; 329 template.clientId = '';
319 template.clientSecret = ''; 330 template.clientSecret = '';
320 template.scope.forEach(() => { 331 template.scope.forEach(() => {
@@ -324,44 +335,33 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha @@ -324,44 +335,33 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
324 clientRegistration.get('authorizationUri').disable(); 335 clientRegistration.get('authorizationUri').disable();
325 clientRegistration.get('jwkSetUri').disable(); 336 clientRegistration.get('jwkSetUri').disable();
326 clientRegistration.get('userInfoUri').disable(); 337 clientRegistration.get('userInfoUri').disable();
327 - clientRegistration.patchValue(template, {emitEvent: false}); 338 + clientRegistration.patchValue(template);
328 } 339 }
329 } 340 }
330 341
331 private changeMapperConfigType(control: AbstractControl, type: MapperConfigType, predefinedValue?: MapperConfig) { 342 private changeMapperConfigType(control: AbstractControl, type: MapperConfigType, predefinedValue?: MapperConfig) {
332 const mapperConfig = control.get('mapperConfig') as FormGroup; 343 const mapperConfig = control.get('mapperConfig') as FormGroup;
333 - if (type === MapperConfigType.BASIC) {  
334 - mapperConfig.removeControl('custom');  
335 - mapperConfig.addControl('basic', this.formBasicGroup(predefinedValue?.basic));  
336 - } else { 344 + if (type === MapperConfigType.CUSTOM) {
337 mapperConfig.removeControl('basic'); 345 mapperConfig.removeControl('basic');
338 mapperConfig.addControl('custom', this.formCustomGroup(predefinedValue?.custom)); 346 mapperConfig.addControl('custom', this.formCustomGroup(predefinedValue?.custom));
  347 + } else {
  348 + mapperConfig.removeControl('custom');
  349 + mapperConfig.addControl('basic', this.formBasicGroup(predefinedValue?.basic));
339 } 350 }
340 } 351 }
341 352
342 save(): void { 353 save(): void {
343 - const setting = this.prepareFormValue(this.oauth2SettingsForm.getRawValue());  
344 - this.adminService.saveOAuth2Settings(setting).subscribe( 354 + const setting = this.oauth2SettingsForm.getRawValue();
  355 + this.oauth2Service.saveOAuth2Settings(setting).subscribe(
345 (oauth2Settings) => { 356 (oauth2Settings) => {
346 - this.oauth2Settings = oauth2Settings;  
347 - this.oauth2SettingsForm.markAsPristine(); 357 + this.auth2ClientsParams = oauth2Settings;
  358 + this.oauth2SettingsForm.patchValue(this.oauth2SettingsForm, {emitEvent: false});
348 this.oauth2SettingsForm.markAsUntouched(); 359 this.oauth2SettingsForm.markAsUntouched();
  360 + this.oauth2SettingsForm.markAsPristine();
349 } 361 }
350 ); 362 );
351 } 363 }
352 364
353 - private prepareFormValue(formValue: OAuth2Settings): OAuth2Settings{  
354 - formValue.domainsParams.forEach((setting, index) => {  
355 - setting.clientRegistrations.forEach((registration) => {  
356 - registration.additionalInfo = JSON.stringify(registration.additionalInfo);  
357 - if (registration.id.id === null) {  
358 - delete registration.id;  
359 - }  
360 - });  
361 - });  
362 - return formValue;  
363 - }  
364 -  
365 confirmForm(): FormGroup { 365 confirmForm(): FormGroup {
366 return this.oauth2SettingsForm; 366 return this.oauth2SettingsForm;
367 } 367 }
@@ -451,6 +451,10 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha @@ -451,6 +451,10 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
451 return controller.get('additionalInfo.providerName').value; 451 return controller.get('additionalInfo.providerName').value;
452 } 452 }
453 453
  454 + isCustomProvider(controller: AbstractControl): boolean {
  455 + return this.getProviderName(controller) === 'Custom';
  456 + }
  457 +
454 getHelpLink(controller: AbstractControl): string { 458 getHelpLink(controller: AbstractControl): string {
455 const provider = controller.get('additionalInfo.providerName').value; 459 const provider = controller.get('additionalInfo.providerName').value;
456 if (provider === null || provider === 'Custom') { 460 if (provider === null || provider === 'Custom') {
@@ -28,6 +28,19 @@ @@ -28,6 +28,19 @@
28 <span style="height: 4px;" *ngIf="!(isLoading$ | async)"></span> 28 <span style="height: 4px;" *ngIf="!(isLoading$ | async)"></span>
29 <div tb-toast fxLayout="column" class="layout-padding"> 29 <div tb-toast fxLayout="column" class="layout-padding">
30 <span style="height: 50px;"></span> 30 <span style="height: 50px;"></span>
  31 + <div class="oauth-container tb-default" fxLayout="column" fxLayoutGap="16px" *ngIf="oauth2Clients?.length">
  32 + <ng-container *ngFor="let oauth2Client of oauth2Clients">
  33 + <a mat-raised-button class="login-with-button" href="{{ oauth2Client.url }}">
  34 + <mat-icon class="icon" svgIcon="{{ oauth2Client.icon }}"></mat-icon>
  35 + {{ 'login.login-with' | translate: {name: oauth2Client.name} }}
  36 + </a>
  37 + </ng-container>
  38 + <div class="container-divider">
  39 + <div class="line"><mat-divider></mat-divider></div>
  40 + <div class="text mat-typography">{{ "login.or" | translate | uppercase }}</div>
  41 + <div class="line"><mat-divider></mat-divider></div>
  42 + </div>
  43 + </div>
31 <mat-form-field> 44 <mat-form-field>
32 <mat-label translate>login.username</mat-label> 45 <mat-label translate>login.username</mat-label>
33 <input id="username-input" matInput type="email" autofocus formControlName="username" email required/> 46 <input id="username-input" matInput type="email" autofocus formControlName="username" email required/>
@@ -41,7 +54,7 @@ @@ -41,7 +54,7 @@
41 <input id="password-input" matInput type="password" formControlName="password"/> 54 <input id="password-input" matInput type="password" formControlName="password"/>
42 <mat-icon matPrefix>lock</mat-icon> 55 <mat-icon matPrefix>lock</mat-icon>
43 </mat-form-field> 56 </mat-form-field>
44 - <div fxLayoutAlign="end center"> 57 + <div fxLayoutAlign="end center" class="forgot-password">
45 <button class="tb-reset-password" mat-button type="button" routerLink="/login/resetPasswordRequest">{{ 'login.forgot-password' | translate }} 58 <button class="tb-reset-password" mat-button type="button" routerLink="/login/resetPasswordRequest">{{ 'login.forgot-password' | translate }}
46 </button> 59 </button>
47 </div> 60 </div>
@@ -49,19 +62,6 @@ @@ -49,19 +62,6 @@
49 <button mat-raised-button color="accent" [disabled]="(isLoading$ | async)" 62 <button mat-raised-button color="accent" [disabled]="(isLoading$ | async)"
50 type="submit">{{ 'login.login' | translate }}</button> 63 type="submit">{{ 'login.login' | translate }}</button>
51 </div> 64 </div>
52 - <div class="oauth-container" fxLayout="column" fxLayoutGap="16px" *ngIf="oauth2Clients?.length">  
53 - <div class="container-divider">  
54 - <div class="line"><mat-divider></mat-divider></div>  
55 - <div class="text mat-typography">{{ "login.or" | translate | uppercase }}</div>  
56 - <div class="line"><mat-divider></mat-divider></div>  
57 - </div>  
58 - <ng-container *ngFor="let oauth2Client of oauth2Clients">  
59 - <a mat-raised-button color="warn" class="centered" href="{{ oauth2Client.url }}">  
60 - <mat-icon svgIcon="{{ oauth2Client.icon }}"></mat-icon>  
61 - {{ 'login.login-with' | translate: {name: oauth2Client.name} }}  
62 - </a>  
63 - </ng-container>  
64 - </div>  
65 </div> 65 </div>
66 </fieldset> 66 </fieldset>
67 </form> 67 </form>
@@ -27,8 +27,11 @@ @@ -27,8 +27,11 @@
27 width: 550px !important; 27 width: 550px !important;
28 } 28 }
29 29
30 - .tb-reset-password{  
31 - padding: 0 6px; 30 + .forgot-password {
  31 + padding: 0 0.5em 1em;
  32 + .tb-reset-password {
  33 + padding: 0 6px;
  34 + }
32 } 35 }
33 36
34 .tb-action-button{ 37 .tb-action-button{
@@ -65,9 +68,17 @@ @@ -65,9 +68,17 @@
65 min-width: 20px; 68 min-width: 20px;
66 } 69 }
67 70
68 - a.centered { 71 + a.login-with-button {
  72 + color: rgba(black, 0.87);
  73 +
69 &:hover { 74 &:hover {
70 - border-bottom: 0px; 75 + border-bottom: 0;
  76 + }
  77 +
  78 + .icon{
  79 + height: 20px;
  80 + width: 20px;
  81 + vertical-align: sub;
71 } 82 }
72 } 83 }
73 84
@@ -23,7 +23,7 @@ import { FormBuilder } from '@angular/forms'; @@ -23,7 +23,7 @@ import { FormBuilder } from '@angular/forms';
23 import { HttpErrorResponse } from '@angular/common/http'; 23 import { HttpErrorResponse } from '@angular/common/http';
24 import { Constants } from '@shared/models/constants'; 24 import { Constants } from '@shared/models/constants';
25 import { Router } from '@angular/router'; 25 import { Router } from '@angular/router';
26 -import { OAuth2Client } from '@shared/models/login.models'; 26 +import { OAuth2ClientInfo } from '@shared/models/oauth2.models';
27 27
28 @Component({ 28 @Component({
29 selector: 'tb-login', 29 selector: 'tb-login',
@@ -36,7 +36,7 @@ export class LoginComponent extends PageComponent implements OnInit { @@ -36,7 +36,7 @@ export class LoginComponent extends PageComponent implements OnInit {
36 username: '', 36 username: '',
37 password: '' 37 password: ''
38 }); 38 });
39 - oauth2Clients: Array<OAuth2Client> = null; 39 + oauth2Clients: Array<OAuth2ClientInfo> = null;
40 40
41 constructor(protected store: Store<AppState>, 41 constructor(protected store: Store<AppState>,
42 private authService: AuthService, 42 private authService: AuthService,
@@ -27,9 +27,3 @@ export interface LoginResponse { @@ -27,9 +27,3 @@ export interface LoginResponse {
27 token: string; 27 token: string;
28 refreshToken: string; 28 refreshToken: string;
29 } 29 }
30 -  
31 -export interface OAuth2Client {  
32 - name: string;  
33 - icon?: string;  
34 - url: string;  
35 -}  
  1 +///
  2 +/// Copyright © 2016-2020 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import { HasUUID } from '@shared/models/id/has-uuid';
  18 +
  19 +export interface OAuth2ClientsParams {
  20 + enabled: boolean;
  21 + domainsParams: OAuth2ClientsDomainParams[];
  22 +}
  23 +
  24 +export interface OAuth2ClientsDomainParams {
  25 + clientRegistrations: ClientRegistration[];
  26 + domainInfos: DomainInfo[];
  27 +}
  28 +
  29 +export interface DomainInfo {
  30 + name: string;
  31 + scheme: DomainSchema;
  32 +}
  33 +
  34 +export enum DomainSchema{
  35 + HTTP = 'HTTP',
  36 + HTTPS = 'HTTPS',
  37 + MIXED = 'MIXED'
  38 +}
  39 +
  40 +export const domainSchemaTranslations = new Map<DomainSchema, string>(
  41 + [
  42 + [DomainSchema.HTTP, 'admin.oauth2.domain-schema-http'],
  43 + [DomainSchema.HTTPS, 'admin.oauth2.domain-schema-https'],
  44 + [DomainSchema.MIXED, 'admin.oauth2.domain-schema-mixed']
  45 + ]
  46 +);
  47 +
  48 +export enum MapperConfigType{
  49 + BASIC = 'BASIC',
  50 + CUSTOM = 'CUSTOM',
  51 + GITHUB = 'GITHUB'
  52 +}
  53 +
  54 +export enum TenantNameStrategy{
  55 + DOMAIN = 'DOMAIN',
  56 + EMAIL = 'EMAIL',
  57 + CUSTOM = 'CUSTOM'
  58 +}
  59 +
  60 +export interface OAuth2ClientRegistrationTemplate extends ClientRegistration{
  61 + comment: string;
  62 + createdTime: number;
  63 + helpLink: string;
  64 + name: string;
  65 + providerId: string;
  66 + id: HasUUID;
  67 +}
  68 +
  69 +export interface ClientRegistration {
  70 + loginButtonLabel: string;
  71 + loginButtonIcon: string;
  72 + clientId: string;
  73 + clientSecret: string;
  74 + accessTokenUri: string;
  75 + authorizationUri: string;
  76 + scope: string[];
  77 + jwkSetUri?: string;
  78 + userInfoUri: string;
  79 + clientAuthenticationMethod: ClientAuthenticationMethod;
  80 + userNameAttributeName: string;
  81 + mapperConfig: MapperConfig;
  82 + additionalInfo: string;
  83 +}
  84 +
  85 +export enum ClientAuthenticationMethod {
  86 + BASIC = 'BASIC',
  87 + POST = 'POST'
  88 +}
  89 +
  90 +export interface MapperConfig {
  91 + allowUserCreation: boolean;
  92 + activateUser: boolean;
  93 + type: MapperConfigType;
  94 + basic?: MapperConfigBasic;
  95 + custom?: MapperConfigCustom;
  96 +}
  97 +
  98 +export interface MapperConfigBasic {
  99 + emailAttributeKey: string;
  100 + firstNameAttributeKey?: string;
  101 + lastNameAttributeKey?: string;
  102 + tenantNameStrategy: TenantNameStrategy;
  103 + tenantNamePattern?: string;
  104 + customerNamePattern?: string;
  105 + defaultDashboardName?: string;
  106 + alwaysFullScreen?: boolean;
  107 +}
  108 +
  109 +export interface MapperConfigCustom {
  110 + url: string;
  111 + username?: string;
  112 + password?: string;
  113 +}
  114 +
  115 +export interface OAuth2ClientInfo {
  116 + name: string;
  117 + icon?: string;
  118 + url: string;
  119 +}
@@ -36,6 +36,7 @@ export * from './error.models'; @@ -36,6 +36,7 @@ export * from './error.models';
36 export * from './event.models'; 36 export * from './event.models';
37 export * from './login.models'; 37 export * from './login.models';
38 export * from './material.models'; 38 export * from './material.models';
  39 +export * from './oauth2.models';
39 export * from './queue.models'; 40 export * from './queue.models';
40 export * from './relation.models'; 41 export * from './relation.models';
41 export * from './rule-chain.models'; 42 export * from './rule-chain.models';
@@ -14,9 +14,6 @@ @@ -14,9 +14,6 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { EntityId } from '@shared/models/id/entity-id';  
18 -import { TenantId } from '@shared/models/id/tenant-id';  
19 -  
20 export const smtpPortPattern: RegExp = /^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/; 17 export const smtpPortPattern: RegExp = /^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/;
21 18
22 export interface AdminSettings<T> { 19 export interface AdminSettings<T> {
@@ -63,99 +60,3 @@ export interface UpdateMessage { @@ -63,99 +60,3 @@ export interface UpdateMessage {
63 message: string; 60 message: string;
64 updateAvailable: boolean; 61 updateAvailable: boolean;
65 } 62 }
66 -  
67 -export interface OAuth2Settings {  
68 - enabled: boolean;  
69 - domainsParams: DomainsParam[];  
70 -}  
71 -  
72 -export interface DomainsParam {  
73 - clientRegistrations: ClientRegistration[];  
74 - domainInfos: DomainInfo[];  
75 -}  
76 -  
77 -export interface DomainInfo {  
78 - name: string;  
79 - scheme: DomainSchema;  
80 -}  
81 -  
82 -export enum DomainSchema{  
83 - HTTP = 'HTTP',  
84 - HTTPS = 'HTTPS',  
85 - MIXED = 'MIXED'  
86 -}  
87 -  
88 -export const domainSchemaTranslations = new Map<DomainSchema, string>(  
89 - [  
90 - [DomainSchema.HTTP, 'admin.oauth2.domain-schema-http'],  
91 - [DomainSchema.HTTPS, 'admin.oauth2.domain-schema-https'],  
92 - [DomainSchema.MIXED, 'admin.oauth2.domain-schema-mixed']  
93 - ]  
94 -);  
95 -  
96 -export enum MapperConfigType{  
97 - BASIC = 'BASIC',  
98 - CUSTOM = 'CUSTOM'  
99 -}  
100 -  
101 -export enum TenantNameStrategy{  
102 - DOMAIN = 'DOMAIN',  
103 - EMAIL = 'EMAIL',  
104 - CUSTOM = 'CUSTOM'  
105 -}  
106 -  
107 -export interface ClientProviderTemplated extends ClientRegistration{  
108 - comment: string;  
109 - createdTime: number;  
110 - helpLink: string;  
111 - name: string;  
112 - providerId: string;  
113 - tenantId: TenantId;  
114 -}  
115 -  
116 -export interface ClientRegistration {  
117 - loginButtonLabel: string;  
118 - loginButtonIcon: string;  
119 - clientId: string;  
120 - clientSecret: string;  
121 - accessTokenUri: string;  
122 - authorizationUri: string;  
123 - scope: string[];  
124 - jwkSetUri?: string;  
125 - userInfoUri: string;  
126 - clientAuthenticationMethod: ClientAuthenticationMethod;  
127 - userNameAttributeName: string;  
128 - mapperConfig: MapperConfig;  
129 - id?: EntityId;  
130 - additionalInfo: string;  
131 -}  
132 -  
133 -export enum ClientAuthenticationMethod {  
134 - BASIC = 'BASIC',  
135 - POST = 'POST'  
136 -}  
137 -  
138 -export interface MapperConfig {  
139 - allowUserCreation: boolean;  
140 - activateUser: boolean;  
141 - type: MapperConfigType;  
142 - basic?: MapperConfigBasic;  
143 - custom?: MapperConfigCustom;  
144 -}  
145 -  
146 -export interface MapperConfigBasic {  
147 - emailAttributeKey: string;  
148 - firstNameAttributeKey?: string;  
149 - lastNameAttributeKey?: string;  
150 - tenantNameStrategy: TenantNameStrategy;  
151 - tenantNamePattern?: string;  
152 - customerNamePattern?: string;  
153 - defaultDashboardName?: string;  
154 - alwaysFullScreen?: boolean;  
155 -}  
156 -  
157 -export interface MapperConfigCustom {  
158 - url: string;  
159 - username?: string;  
160 - password?: string;  
161 -}  
@@ -158,7 +158,7 @@ @@ -158,7 +158,7 @@
158 "last-name-attribute-key": "Last name attribute key", 158 "last-name-attribute-key": "Last name attribute key",
159 "login-button-icon": "Login button icon", 159 "login-button-icon": "Login button icon",
160 "login-button-label": "Provider label", 160 "login-button-label": "Provider label",
161 - "login-button-label-1": "Login with $(Provider label)", 161 + "login-button-label-placeholder": "Login with $(Provider label)",
162 "login-button-label-required": "Label is required.", 162 "login-button-label-required": "Label is required.",
163 "login-provider": "Login provider", 163 "login-provider": "Login provider",
164 "mapper": "Mapper", 164 "mapper": "Mapper",