Commit da7d88470e415c95265205a4ebdcc1297ee5591c
Committed by
GitHub
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 | } |
ui-ngx/src/app/core/http/oauth2.service.ts
0 → 100644
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", |