Showing
5 changed files
with
71 additions
and
5 deletions
@@ -79,11 +79,18 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt | @@ -79,11 +79,18 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt | ||
79 | @Qualifier("oauth2AuthenticationSuccessHandler") | 79 | @Qualifier("oauth2AuthenticationSuccessHandler") |
80 | private AuthenticationSuccessHandler oauth2AuthenticationSuccessHandler; | 80 | private AuthenticationSuccessHandler oauth2AuthenticationSuccessHandler; |
81 | 81 | ||
82 | + @Autowired(required = false) | ||
83 | + @Qualifier("oauth2AuthenticationFailureHandler") | ||
84 | + private AuthenticationFailureHandler oauth2AuthenticationFailureHandler; | ||
85 | + | ||
82 | @Autowired | 86 | @Autowired |
83 | @Qualifier("defaultAuthenticationSuccessHandler") | 87 | @Qualifier("defaultAuthenticationSuccessHandler") |
84 | private AuthenticationSuccessHandler successHandler; | 88 | private AuthenticationSuccessHandler successHandler; |
85 | 89 | ||
86 | - @Autowired private AuthenticationFailureHandler failureHandler; | 90 | + @Autowired |
91 | + @Qualifier("defaultAuthenticationFailureHandler") | ||
92 | + private AuthenticationFailureHandler failureHandler; | ||
93 | + | ||
87 | @Autowired private RestAuthenticationProvider restAuthenticationProvider; | 94 | @Autowired private RestAuthenticationProvider restAuthenticationProvider; |
88 | @Autowired private JwtAuthenticationProvider jwtAuthenticationProvider; | 95 | @Autowired private JwtAuthenticationProvider jwtAuthenticationProvider; |
89 | @Autowired private RefreshTokenAuthenticationProvider refreshTokenAuthenticationProvider; | 96 | @Autowired private RefreshTokenAuthenticationProvider refreshTokenAuthenticationProvider; |
@@ -205,7 +212,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt | @@ -205,7 +212,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt | ||
205 | .loginPage("/oauth2Login") | 212 | .loginPage("/oauth2Login") |
206 | .loginProcessingUrl(oauth2Configuration.getLoginProcessingUrl()) | 213 | .loginProcessingUrl(oauth2Configuration.getLoginProcessingUrl()) |
207 | .successHandler(oauth2AuthenticationSuccessHandler) | 214 | .successHandler(oauth2AuthenticationSuccessHandler) |
208 | - .failureHandler(failureHandler); | 215 | + .failureHandler(oauth2AuthenticationFailureHandler); |
209 | } | 216 | } |
210 | } | 217 | } |
211 | 218 |
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 | +package org.thingsboard.server.service.security.auth.oauth2; | ||
17 | + | ||
18 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
19 | +import org.springframework.security.core.AuthenticationException; | ||
20 | +import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; | ||
21 | +import org.springframework.stereotype.Component; | ||
22 | +import org.thingsboard.server.utils.MiscUtils; | ||
23 | + | ||
24 | +import javax.servlet.ServletException; | ||
25 | +import javax.servlet.http.HttpServletRequest; | ||
26 | +import javax.servlet.http.HttpServletResponse; | ||
27 | +import java.io.IOException; | ||
28 | +import java.net.URLEncoder; | ||
29 | +import java.nio.charset.StandardCharsets; | ||
30 | + | ||
31 | +@Component(value = "oauth2AuthenticationFailureHandler") | ||
32 | +@ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true") | ||
33 | +public class Oauth2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { | ||
34 | + | ||
35 | + @Override | ||
36 | + public void onAuthenticationFailure(HttpServletRequest request, | ||
37 | + HttpServletResponse response, AuthenticationException exception) | ||
38 | + throws IOException, ServletException { | ||
39 | + String baseUrl = MiscUtils.constructBaseUrl(request); | ||
40 | + getRedirectStrategy().sendRedirect(request, response, baseUrl + "/login?loginError=" + | ||
41 | + URLEncoder.encode(exception.getMessage(), StandardCharsets.UTF_8.toString())); | ||
42 | + } | ||
43 | +} |
@@ -26,7 +26,7 @@ import javax.servlet.http.HttpServletRequest; | @@ -26,7 +26,7 @@ import javax.servlet.http.HttpServletRequest; | ||
26 | import javax.servlet.http.HttpServletResponse; | 26 | import javax.servlet.http.HttpServletResponse; |
27 | import java.io.IOException; | 27 | import java.io.IOException; |
28 | 28 | ||
29 | -@Component | 29 | +@Component(value = "defaultAuthenticationFailureHandler") |
30 | public class RestAwareAuthenticationFailureHandler implements AuthenticationFailureHandler { | 30 | public class RestAwareAuthenticationFailureHandler implements AuthenticationFailureHandler { |
31 | 31 | ||
32 | private final ThingsboardErrorResponseHandler errorResponseHandler; | 32 | private final ThingsboardErrorResponseHandler errorResponseHandler; |
@@ -22,7 +22,7 @@ export default angular.module('thingsboard.api.user', [thingsboardApiLogin, | @@ -22,7 +22,7 @@ export default angular.module('thingsboard.api.user', [thingsboardApiLogin, | ||
22 | .name; | 22 | .name; |
23 | 23 | ||
24 | /*@ngInject*/ | 24 | /*@ngInject*/ |
25 | -function UserService($http, $q, $rootScope, adminService, dashboardService, timeService, loginService, toast, store, jwtHelper, $translate, $state, $location) { | 25 | +function UserService($http, $q, $rootScope, adminService, dashboardService, timeService, loginService, toast, store, jwtHelper, $translate, $state, $location, $mdDialog) { |
26 | var currentUser = null, | 26 | var currentUser = null, |
27 | currentUserDetails = null, | 27 | currentUserDetails = null, |
28 | lastPublicDashboardId = null, | 28 | lastPublicDashboardId = null, |
@@ -406,6 +406,10 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, time | @@ -406,6 +406,10 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, time | ||
406 | }, function fail() { | 406 | }, function fail() { |
407 | deferred.reject(); | 407 | deferred.reject(); |
408 | }); | 408 | }); |
409 | + } else if (locationSearch.loginError) { | ||
410 | + showLoginErrorDialog(locationSearch.loginError); | ||
411 | + $location.search('loginError', null); | ||
412 | + deferred.reject(); | ||
409 | } else { | 413 | } else { |
410 | procceedJwtTokenValidate(); | 414 | procceedJwtTokenValidate(); |
411 | } | 415 | } |
@@ -415,6 +419,17 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, time | @@ -415,6 +419,17 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, time | ||
415 | return deferred.promise; | 419 | return deferred.promise; |
416 | } | 420 | } |
417 | 421 | ||
422 | + function showLoginErrorDialog(loginError) { | ||
423 | + $translate(['login.error', | ||
424 | + 'action.close']).then(function (translations) { | ||
425 | + var alert = $mdDialog.alert() | ||
426 | + .title(translations['login.error']) | ||
427 | + .htmlContent(loginError) | ||
428 | + .ok(translations['action.close']); | ||
429 | + $mdDialog.show(alert); | ||
430 | + }); | ||
431 | + } | ||
432 | + | ||
418 | function loadIsUserTokenAccessEnabled() { | 433 | function loadIsUserTokenAccessEnabled() { |
419 | var deferred = $q.defer(); | 434 | var deferred = $q.defer(); |
420 | if (currentUser.authority === 'SYS_ADMIN' || currentUser.authority === 'TENANT_ADMIN') { | 435 | if (currentUser.authority === 'SYS_ADMIN' || currentUser.authority === 'TENANT_ADMIN') { |
@@ -1334,7 +1334,8 @@ | @@ -1334,7 +1334,8 @@ | ||
1334 | "password-link-sent-message": "Password reset link was successfully sent!", | 1334 | "password-link-sent-message": "Password reset link was successfully sent!", |
1335 | "email": "Email", | 1335 | "email": "Email", |
1336 | "login-with": "Login with {{name}}", | 1336 | "login-with": "Login with {{name}}", |
1337 | - "or": "or" | 1337 | + "or": "or", |
1338 | + "error": "Login error" | ||
1338 | }, | 1339 | }, |
1339 | "position": { | 1340 | "position": { |
1340 | "top": "Top", | 1341 | "top": "Top", |