From c63f7cf6788cbb6c633fc51dcee20462f56fb878 Mon Sep 17 00:00:00 2001 From: oyurov Date: Thu, 17 Nov 2022 18:20:08 +0100 Subject: [PATCH 1/5] Added state support for Dashboards after oauth2 redirect --- .../Oauth2AuthenticationSuccessHandler.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java index 9be9d2217b..bc861692ea 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java @@ -104,10 +104,9 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(request, token, oAuth2AuthorizedClient.getAccessToken().getTokenValue(), registration); - JwtPair tokenPair = tokenFactory.createTokenPair(securityUser); - clearAuthenticationAttributes(request, response); - getRedirectStrategy().sendRedirect(request, response, baseUrl + "/?accessToken=" + tokenPair.getToken() + "&refreshToken=" + tokenPair.getRefreshToken()); + + getRedirectStrategy().sendRedirect(request, response, getRedirectUrl(baseUrl, securityUser)); systemSecurityService.logLoginAction(securityUser, new RestAuthenticationDetails(request), ActionType.LOGIN, registration.getName(), null); } catch (Exception e) { log.debug("Error occurred during processing authentication success result. " + @@ -128,4 +127,14 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS super.clearAuthenticationAttributes(request); httpCookieOAuth2AuthorizationRequestRepository.removeAuthorizationRequestCookies(request, response); } + + private String getRedirectUrl(String baseUrl, SecurityUser securityUser) { + JwtPair tokenPair = tokenFactory.createTokenPair(securityUser); + if (baseUrl.contains("state")) { + baseUrl += "&"; + } else { + baseUrl += "/?"; + } + return baseUrl + "accessToken=" + tokenPair.getToken() + "&refreshToken=" + tokenPair.getRefreshToken(); + } } From 5556fc5210763e5dfee241d9429d22dcfe627c42 Mon Sep 17 00:00:00 2001 From: oyurov Date: Fri, 18 Nov 2022 09:32:05 +0100 Subject: [PATCH 2/5] Refactor --- .../auth/oauth2/Oauth2AuthenticationSuccessHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java index bc861692ea..e76fc7960b 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java @@ -130,7 +130,7 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS private String getRedirectUrl(String baseUrl, SecurityUser securityUser) { JwtPair tokenPair = tokenFactory.createTokenPair(securityUser); - if (baseUrl.contains("state")) { + if (baseUrl.indexOf("?") > 0) { baseUrl += "&"; } else { baseUrl += "/?"; From 87d7886d6332ce2e61c05e78561c4a74daaaf56d Mon Sep 17 00:00:00 2001 From: oyurov Date: Fri, 18 Nov 2022 10:55:20 +0100 Subject: [PATCH 3/5] Added test for getRedirectUrl method --- .../Oauth2AuthenticationSuccessHandler.java | 2 +- ...auth2AuthenticationSuccessHandlerTest.java | 85 +++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 application/src/test/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandlerTest.java diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java index e76fc7960b..c96cc9ccdd 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java @@ -128,7 +128,7 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS httpCookieOAuth2AuthorizationRequestRepository.removeAuthorizationRequestCookies(request, response); } - private String getRedirectUrl(String baseUrl, SecurityUser securityUser) { + protected String getRedirectUrl(String baseUrl, SecurityUser securityUser) { JwtPair tokenPair = tokenFactory.createTokenPair(securityUser); if (baseUrl.indexOf("?") > 0) { baseUrl += "&"; diff --git a/application/src/test/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandlerTest.java b/application/src/test/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandlerTest.java new file mode 100644 index 0000000000..2a9aa33704 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandlerTest.java @@ -0,0 +1,85 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.security.auth.oauth2; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.common.data.security.UserCredentials; +import org.thingsboard.server.controller.AbstractControllerTest; +import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.dao.user.UserService; +import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.security.model.UserPrincipal; + +import java.util.UUID; + +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@DaoSqlTest +public class Oauth2AuthenticationSuccessHandlerTest extends AbstractControllerTest { + + @Autowired + protected Oauth2AuthenticationSuccessHandler oauth2AuthenticationSuccessHandler; + + private SecurityUser securityUser; + + @Before + public void before() { + UserId userId = new UserId(UUID.randomUUID()); + securityUser = createMockSecurityUser(userId); + + UserService userService = mock(UserService.class); + User user = new User(); + user.setId(userId); + user.setEmail("email"); + user.setAuthority(Authority.TENANT_ADMIN); + when(userService.findUserById(any(), eq(userId))).thenReturn(user); + + UserCredentials userCredentials = new UserCredentials(); + userCredentials.setEnabled(true); + when(userService.findUserCredentialsByUserId(any(), eq(userId))).thenReturn(userCredentials); + } + + @Test + public void testGetRedirectUrl() { + String urlWithoutParams = "http://localhost:8080/dashboardGroups/3fa13530-6597-11ed-bd76-8bd591f0ec3e"; + String urlWithParams = "http://localhost:8080/dashboardGroups/3fa13530-6597-11ed-bd76-8bd591f0ec3e?state=someState&page=1"; + + String redirectUrl = oauth2AuthenticationSuccessHandler.getRedirectUrl(urlWithoutParams, securityUser); + assertTrue(redirectUrl.contains("/?accessToken=")); + + redirectUrl = oauth2AuthenticationSuccessHandler.getRedirectUrl(urlWithParams, securityUser); + assertTrue(redirectUrl.contains("&accessToken=")); + } + + private SecurityUser createMockSecurityUser(UserId userId) { + SecurityUser securityUser = new SecurityUser(); + securityUser.setEmail("email"); + securityUser.setUserPrincipal(new UserPrincipal(UserPrincipal.Type.USER_NAME, securityUser.getEmail())); + securityUser.setAuthority(Authority.CUSTOMER_USER); + securityUser.setId(userId); + securityUser.setSessionId(UUID.randomUUID().toString()); + return securityUser; + } +} \ No newline at end of file From bf83eff7492e275a8959137bb7c4bdbc1f1f24c0 Mon Sep 17 00:00:00 2001 From: oyurov Date: Fri, 18 Nov 2022 11:27:19 +0100 Subject: [PATCH 4/5] Change modifier access to default --- .../auth/oauth2/Oauth2AuthenticationSuccessHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java index c96cc9ccdd..d80030f476 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java @@ -128,7 +128,7 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS httpCookieOAuth2AuthorizationRequestRepository.removeAuthorizationRequestCookies(request, response); } - protected String getRedirectUrl(String baseUrl, SecurityUser securityUser) { + String getRedirectUrl(String baseUrl, SecurityUser securityUser) { JwtPair tokenPair = tokenFactory.createTokenPair(securityUser); if (baseUrl.indexOf("?") > 0) { baseUrl += "&"; From 26b1d09cbdd092b6b954bdf9fab9b9492b6dd7ac Mon Sep 17 00:00:00 2001 From: oyurov Date: Fri, 18 Nov 2022 12:19:28 +0100 Subject: [PATCH 5/5] Tests improvements --- .../Oauth2AuthenticationSuccessHandler.java | 6 +-- ...auth2AuthenticationSuccessHandlerTest.java | 53 +++++++------------ 2 files changed, 21 insertions(+), 38 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java index d80030f476..7de6c3fe65 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java @@ -106,7 +106,8 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS clearAuthenticationAttributes(request, response); - getRedirectStrategy().sendRedirect(request, response, getRedirectUrl(baseUrl, securityUser)); + JwtPair tokenPair = tokenFactory.createTokenPair(securityUser); + getRedirectStrategy().sendRedirect(request, response, getRedirectUrl(baseUrl, tokenPair)); systemSecurityService.logLoginAction(securityUser, new RestAuthenticationDetails(request), ActionType.LOGIN, registration.getName(), null); } catch (Exception e) { log.debug("Error occurred during processing authentication success result. " + @@ -128,8 +129,7 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS httpCookieOAuth2AuthorizationRequestRepository.removeAuthorizationRequestCookies(request, response); } - String getRedirectUrl(String baseUrl, SecurityUser securityUser) { - JwtPair tokenPair = tokenFactory.createTokenPair(securityUser); + String getRedirectUrl(String baseUrl, JwtPair tokenPair) { if (baseUrl.indexOf("?") > 0) { baseUrl += "&"; } else { diff --git a/application/src/test/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandlerTest.java b/application/src/test/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandlerTest.java index 2a9aa33704..9b1e356b75 100644 --- a/application/src/test/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandlerTest.java +++ b/application/src/test/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandlerTest.java @@ -17,69 +17,52 @@ package org.thingsboard.server.service.security.auth.oauth2; import org.junit.Before; import org.junit.Test; +import org.mockito.Mock; import org.springframework.beans.factory.annotation.Autowired; -import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.UserId; -import org.thingsboard.server.common.data.security.Authority; -import org.thingsboard.server.common.data.security.UserCredentials; +import org.thingsboard.server.common.data.security.model.JwtPair; import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.security.model.UserPrincipal; +import org.thingsboard.server.service.security.model.token.JwtTokenFactory; import java.util.UUID; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; +import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @DaoSqlTest public class Oauth2AuthenticationSuccessHandlerTest extends AbstractControllerTest { @Autowired - protected Oauth2AuthenticationSuccessHandler oauth2AuthenticationSuccessHandler; + private Oauth2AuthenticationSuccessHandler oauth2AuthenticationSuccessHandler; + + @Mock + private JwtTokenFactory jwtTokenFactory; private SecurityUser securityUser; @Before public void before() { UserId userId = new UserId(UUID.randomUUID()); - securityUser = createMockSecurityUser(userId); - - UserService userService = mock(UserService.class); - User user = new User(); - user.setId(userId); - user.setEmail("email"); - user.setAuthority(Authority.TENANT_ADMIN); - when(userService.findUserById(any(), eq(userId))).thenReturn(user); - - UserCredentials userCredentials = new UserCredentials(); - userCredentials.setEnabled(true); - when(userService.findUserCredentialsByUserId(any(), eq(userId))).thenReturn(userCredentials); + securityUser = new SecurityUser(userId); + when(jwtTokenFactory.createTokenPair(eq(securityUser))).thenReturn(new JwtPair("testAccessToken", "testRefreshToken")); } @Test public void testGetRedirectUrl() { + JwtPair jwtPair = jwtTokenFactory.createTokenPair(securityUser); + String urlWithoutParams = "http://localhost:8080/dashboardGroups/3fa13530-6597-11ed-bd76-8bd591f0ec3e"; String urlWithParams = "http://localhost:8080/dashboardGroups/3fa13530-6597-11ed-bd76-8bd591f0ec3e?state=someState&page=1"; - String redirectUrl = oauth2AuthenticationSuccessHandler.getRedirectUrl(urlWithoutParams, securityUser); - assertTrue(redirectUrl.contains("/?accessToken=")); - - redirectUrl = oauth2AuthenticationSuccessHandler.getRedirectUrl(urlWithParams, securityUser); - assertTrue(redirectUrl.contains("&accessToken=")); - } + String redirectUrl = oauth2AuthenticationSuccessHandler.getRedirectUrl(urlWithoutParams, jwtPair); + String expectedUrl = urlWithoutParams + "/?accessToken=" + jwtPair.getToken() + "&refreshToken=" + jwtPair.getRefreshToken(); + assertEquals(expectedUrl, redirectUrl); - private SecurityUser createMockSecurityUser(UserId userId) { - SecurityUser securityUser = new SecurityUser(); - securityUser.setEmail("email"); - securityUser.setUserPrincipal(new UserPrincipal(UserPrincipal.Type.USER_NAME, securityUser.getEmail())); - securityUser.setAuthority(Authority.CUSTOMER_USER); - securityUser.setId(userId); - securityUser.setSessionId(UUID.randomUUID().toString()); - return securityUser; + redirectUrl = oauth2AuthenticationSuccessHandler.getRedirectUrl(urlWithParams, jwtPair); + expectedUrl = urlWithParams + "&accessToken=" + jwtPair.getToken() + "&refreshToken=" + jwtPair.getRefreshToken(); + assertEquals(expectedUrl, redirectUrl); } } \ No newline at end of file