From a78ee1a3cfc4e7a6d08a500750edb5db0f7709a4 Mon Sep 17 00:00:00 2001 From: Milton Ch <86965029+Milton-Ch@users.noreply.github.com> Date: Fri, 11 Mar 2022 07:58:59 -0400 Subject: [PATCH] feat: support regex client attribute to validate redirect uris (#1005) * feat: support regex client property to validate redirect uris --- .../templates/jans-auth/jans-auth-config.json | 3 +- .../jans-auth/jans-auth-config.ob.json | 3 +- .../io/jans/as/client/RegisterRequest.java | 22 ++- .../AuthorizationRedirectUrisRegexTest.java | 89 +++++++++++ .../src/test/resources/testng.properties | 1 + .../client/src/test/resources/testng.xml | 1 + .../model/configuration/AppConfiguration.java | 11 ++ .../register/RegisterErrorResponseType.java | 4 +- .../model/register/RegisterRequestParam.java | 6 +- .../persistence/model/ClientAttributes.java | 12 ++ .../ws/rs/RegisterRestWebServiceImpl.java | 18 ++- .../server/service/RedirectionUriService.java | 31 ++-- .../service/RedirectionUriServiceTest.java | 142 ++++++++++++++++++ .../server/src/test/resources/testng.xml | 1 + jans-cli/cli/jca.yaml | 3 + .../docs/cli/cli-jans-authorization-server.md | 3 +- .../docs/im/im-jans-authorization-server.md | 3 +- .../docs/jans-config-api-swagger.yaml | 3 + .../templates/jans-auth/jans-auth-config.json | 3 +- .../templates/jans-auth/jans-auth-config.json | 3 +- 20 files changed, 341 insertions(+), 21 deletions(-) create mode 100644 jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/AuthorizationRedirectUrisRegexTest.java create mode 100644 jans-auth-server/server/src/test/java/io/jans/as/server/service/RedirectionUriServiceTest.java diff --git a/docker-jans-persistence-loader/templates/jans-auth/jans-auth-config.json b/docker-jans-persistence-loader/templates/jans-auth/jans-auth-config.json index 934d6fca96e..74eaa9c066b 100644 --- a/docker-jans-persistence-loader/templates/jans-auth/jans-auth-config.json +++ b/docker-jans-persistence-loader/templates/jans-auth/jans-auth-config.json @@ -435,5 +435,6 @@ }, "deviceAuthzRequestExpiresIn": 1800, "deviceAuthzTokenPollInterval": 5, - "deviceAuthzResponseTypeToProcessAuthz": "code" + "deviceAuthzResponseTypeToProcessAuthz": "code", + "redirectUrisRegexEnabled": false } diff --git a/docker-jans-persistence-loader/templates/jans-auth/jans-auth-config.ob.json b/docker-jans-persistence-loader/templates/jans-auth/jans-auth-config.ob.json index 17dc4664db8..8ebd000b25a 100644 --- a/docker-jans-persistence-loader/templates/jans-auth/jans-auth-config.ob.json +++ b/docker-jans-persistence-loader/templates/jans-auth/jans-auth-config.ob.json @@ -359,5 +359,6 @@ "deviceAuthzTokenPollInterval": 5, "deviceAuthzResponseTypeToProcessAuthz": "code", "staticKid": "%(staticKid)s", - "forceOfflineAccessScopeToEnableRefreshToken" : false + "forceOfflineAccessScopeToEnableRefreshToken" : false, + "redirectUrisRegexEnabled": false } diff --git a/jans-auth-server/client/src/main/java/io/jans/as/client/RegisterRequest.java b/jans-auth-server/client/src/main/java/io/jans/as/client/RegisterRequest.java index 121ab3e0286..4ca99821857 100644 --- a/jans-auth-server/client/src/main/java/io/jans/as/client/RegisterRequest.java +++ b/jans-auth-server/client/src/main/java/io/jans/as/client/RegisterRequest.java @@ -119,6 +119,7 @@ public class RegisterRequest extends BaseRequest { private AsymmetricSignatureAlgorithm backchannelAuthenticationRequestSigningAlg; private Boolean backchannelUserCodeParameter; private List additionalAudience; + private String redirectUrisRegex; /** * String containing a space-separated list of scope values. (correct name is 'scope' not 'scopes', see (rfc7591).) @@ -1396,6 +1397,10 @@ public Map getParameters() { parameters.put(DEFAULT_PROMPT_LOGIN.getName(), defaultPromptLogin.toString()); } + if (redirectUrisRegex != null) { + parameters.put(REDIRECT_URIS_REGEX.toString(), redirectUrisRegex.toString()) ; + } + // Custom params if (customAttributes != null && !customAttributes.isEmpty()) { for (Map.Entry entry : customAttributes.entrySet()) { @@ -1481,6 +1486,7 @@ public static RegisterRequest fromJson(JSONObject requestObject) throws JSONExce result.setBackchannelClientNotificationEndpoint(requestObject.optString(BACKCHANNEL_CLIENT_NOTIFICATION_ENDPOINT.toString())); result.setBackchannelAuthenticationRequestSigningAlg(AsymmetricSignatureAlgorithm.fromString(requestObject.optString(BACKCHANNEL_AUTHENTICATION_REQUEST_SIGNING_ALG.toString()))); result.setBackchannelUserCodeParameter(booleanOrNull(requestObject, BACKCHANNEL_USER_CODE_PARAMETER.toString())); + result.setRedirectUrisRegex(requestObject.optString(REDIRECT_URIS_REGEX.toString())); result.setDefaultPromptLogin(requestObject.optBoolean(DEFAULT_PROMPT_LOGIN.getName())); return result; @@ -1693,9 +1699,15 @@ public JSONObject getJSONParameters() throws JSONException { if (backchannelUserCodeParameter != null) { parameters.put(BACKCHANNEL_USER_CODE_PARAMETER.toString(), backchannelUserCodeParameter); } + + if (redirectUrisRegex != null) { + parameters.put(REDIRECT_URIS_REGEX.toString(), redirectUrisRegex) ; + } + if (defaultPromptLogin != null) { parameters.put(DEFAULT_PROMPT_LOGIN.getName(), defaultPromptLogin); } + // Custom params if (customAttributes != null && !customAttributes.isEmpty()) { for (Map.Entry entry : customAttributes.entrySet()) { @@ -1754,4 +1766,12 @@ public void setJwtRequestAsString(String jwtRequestAsString) { public boolean hasJwtRequestAsString() { return StringUtils.isNotBlank(jwtRequestAsString); } -} \ No newline at end of file + + public String getRedirectUrisRegex() { + return redirectUrisRegex; + } + + public void setRedirectUrisRegex(String redirectUrisRegex) { + this.redirectUrisRegex = redirectUrisRegex; + } +} diff --git a/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/AuthorizationRedirectUrisRegexTest.java b/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/AuthorizationRedirectUrisRegexTest.java new file mode 100644 index 00000000000..67676bd1078 --- /dev/null +++ b/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/AuthorizationRedirectUrisRegexTest.java @@ -0,0 +1,89 @@ +package io.jans.as.client.ws.rs; + +import io.jans.as.client.*; +import io.jans.as.model.common.ResponseType; +import io.jans.as.model.register.ApplicationType; +import io.jans.as.model.util.StringUtils; +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +import static io.jans.as.client.client.Asserter.*; +import static io.jans.as.model.register.RegisterRequestParam.*; + +/** + * Integration tests to validate redirect uris regex behavior + * + */ +public class AuthorizationRedirectUrisRegexTest extends BaseTest { + + @Parameters({"userId", "userSecret", "redirectUris", "sectorIdentifierUri", "redirectUrisRegex", "redirectUri"}) + @Test + public void requestClientValidateUsingRedirectUrisRegex( final String userId, final String userSecret, + final String redirectUris, final String sectorIdentifierUri, + final String redirectUrisRegex, final String redirectUri) { + showTitle("requestClientValidateUsingRedirectUrisRegex"); + + List responseTypes = Arrays.asList(ResponseType.CODE); + + // 1. Register client + RegisterRequest registerRequest = new RegisterRequest(ApplicationType.WEB, "jans test app", + StringUtils.spaceSeparatedToList(redirectUris)); + registerRequest.setResponseTypes(responseTypes); + registerRequest.setSectorIdentifierUri(sectorIdentifierUri); + registerRequest.setRedirectUrisRegex(redirectUrisRegex); + registerRequest.setRedirectUris(getRedirectUris()); + + RegisterClient registerClient = new RegisterClient(registrationEndpoint); + registerClient.setRequest(registerRequest); + RegisterResponse registerResponse = registerClient.exec(); + + showClient(registerClient); + assertRegisterResponseOk(registerResponse, 201, true); + + String clientId = registerResponse.getClientId(); + String registrationAccessToken = registerResponse.getRegistrationAccessToken(); + String registrationClientUri = registerResponse.getRegistrationClientUri(); + + // 2. Client read + RegisterRequest readClientRequest = new RegisterRequest(registrationAccessToken); + + RegisterClient readClient = new RegisterClient(registrationClientUri); + readClient.setRequest(readClientRequest); + RegisterResponse readClientResponse = readClient.exec(); + + showClient(readClient); + assertRegisterResponseOk(readClientResponse, 200, false); + + assertRegisterResponseClaimsNotNull(readClientResponse, RESPONSE_TYPES, REDIRECT_URIS. APPLICATION_TYPE, CLIENT_NAME, ID_TOKEN_SIGNED_RESPONSE_ALG, SCOPE); + + // 3. Request authorization and receive the authorization code. + List scopes = Arrays.asList("openid", "profile", "address", "email"); + String state = UUID.randomUUID().toString(); + + AuthorizationRequest authorizationRequest = new AuthorizationRequest(responseTypes, clientId, scopes, redirectUri, null); + authorizationRequest.setState(state); + + AuthorizationResponse authorizationResponse = authenticateResourceOwnerAndGrantAccess( + authorizationEndpoint, authorizationRequest, userId, userSecret); + + assertAuthorizationResponse(authorizationResponse, true); + + } + + private List getRedirectUris() { + return Arrays.asList("https://www.jans.org", + "http://localhost:80/jans-auth-rp/home.htm", + "https://localhost:8443/jans-auth-rp/home.htm", + "https://client.example.org/callback", + "https://client.example.org/callback2", + "https://client.other_company.example.net/callback", + "https://client.example.com/cb", + "https://client.example.com/cb1", + "https://client.example.com/cb2") ; + } + +} diff --git a/jans-auth-server/client/src/test/resources/testng.properties b/jans-auth-server/client/src/test/resources/testng.properties index a23c5cda0e7..66f2dde7086 100644 --- a/jans-auth-server/client/src/test/resources/testng.properties +++ b/jans-auth-server/client/src/test/resources/testng.properties @@ -11,6 +11,7 @@ clientSecret=${auth.client.secret} audience=https://${test.server.name} redirectUri=https://${test.server.name}/jans-auth-rp/home.htm redirectUris=https://${test.server.name}/jans-auth-rp/home.htm https://client.example.com/cb https://client.example.com/cb1 https://client.example.com/cb2 +redirectUrisRegex=/^([a-z0-9+.-]+):(?://(?:((?:[a-z0-9-._~!$&'()*+,;=:]|%[0-9A-F]{2})*)@)?((?:[a-z0-9-._~!$&'()*+,;=]|%[0-9A-F]{2})*)(?::(\\d*))?(/(?:[a-z0-9-._~!$&'()*+,;=:@/]|%[0-9A-F]{2})*)?|(/?(?:[a-z0-9-._~!$&'()*+,;=:@]|%[0-9A-F]{2})+(?:[a-z0-9-._~!$&'()*+,;=:@/]|%[0-9A-F]{2})*)?)(?:\\?((?:[a-z0-9-._~!$&'()*+,;=:/?@]|%[0-9A-F]{2})*))?(?:#((?:[a-z0-9-._~!$&'()*+,;=:/?@]|%[0-9A-F]{2})*))?$/i #redirectUris=https://${test.server.name}/jans-auth-rp/home.htm https://client.example.com/cb https://client.example.com/cb1 https://client.example.com/cb2 https://openid.implicit.client.test/login-callback.html logoutUri=https://${test.server.name}/jans-auth-rp/home.htm postLogoutRedirectUri=https://client.example.com/pl diff --git a/jans-auth-server/client/src/test/resources/testng.xml b/jans-auth-server/client/src/test/resources/testng.xml index 9906a4ec808..b6553f49644 100644 --- a/jans-auth-server/client/src/test/resources/testng.xml +++ b/jans-auth-server/client/src/test/resources/testng.xml @@ -100,6 +100,7 @@ + diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java b/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java index ba14261b107..3427ff6334c 100644 --- a/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java @@ -160,6 +160,9 @@ public class AppConfiguration implements Configuration { private int expirationNotificatorMapSizeLimit = 100000; private int expirationNotificatorIntervalInSeconds = 600; + //feature flags + private Boolean redirectUrisRegexEnabled = false; + private Boolean authenticationFiltersEnabled; private Boolean clientAuthenticationFiltersEnabled; private Boolean clientRegDefaultToCodeFlowWithRefresh; @@ -2459,4 +2462,12 @@ public int getDpopJtiCacheTime() { public void setDpopJtiCacheTime(int dpopJtiCacheTime) { this.dpopJtiCacheTime = dpopJtiCacheTime; } + + public Boolean getRedirectUrisRegexEnabled() { + return redirectUrisRegexEnabled != null && redirectUrisRegexEnabled; + } + + public void setRedirectUrisRegexEnabled(Boolean redirectUrisRegexEnabled) { + this.redirectUrisRegexEnabled = redirectUrisRegexEnabled; + } } diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/register/RegisterErrorResponseType.java b/jans-auth-server/model/src/main/java/io/jans/as/model/register/RegisterErrorResponseType.java index 59dbfe27137..3335057170c 100644 --- a/jans-auth-server/model/src/main/java/io/jans/as/model/register/RegisterErrorResponseType.java +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/register/RegisterErrorResponseType.java @@ -52,7 +52,9 @@ public enum RegisterErrorResponseType implements IErrorType { */ ACCESS_DENIED("access_denied"), - INVALID_PUBLIC_SUBJECT_IDENTIFIER_ATTRIBUTE("invalid_public_subject_identifier_attribute"); + INVALID_PUBLIC_SUBJECT_IDENTIFIER_ATTRIBUTE("invalid_public_subject_identifier_attribute"), + + INVALID_REDIRECT_URIS_REGEX("invalid_redirect_uris_regex"); private final String paramName; diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/register/RegisterRequestParam.java b/jans-auth-server/model/src/main/java/io/jans/as/model/register/RegisterRequestParam.java index b9482ef7e70..e415abcaaa4 100644 --- a/jans-auth-server/model/src/main/java/io/jans/as/model/register/RegisterRequestParam.java +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/register/RegisterRequestParam.java @@ -347,9 +347,11 @@ public enum RegisterRequestParam { BACKCHANNEL_USER_CODE_PARAMETER("backchannel_user_code_parameter"), - DEFAULT_PROMPT_LOGIN("default_prompt_login"), + PUBLIC_SUBJECT_IDENTIFIER_ATTRIBUTE("public_subject_identifier_attribute"), - PUBLIC_SUBJECT_IDENTIFIER_ATTRIBUTE("public_subject_identifier_attribute"); + REDIRECT_URIS_REGEX("redirect_uris_regex"), + + DEFAULT_PROMPT_LOGIN("default_prompt_login"); /** * Parameter name diff --git a/jans-auth-server/persistence-model/src/main/java/io/jans/as/persistence/model/ClientAttributes.java b/jans-auth-server/persistence-model/src/main/java/io/jans/as/persistence/model/ClientAttributes.java index 480e5d25a31..73daf99e2b7 100644 --- a/jans-auth-server/persistence-model/src/main/java/io/jans/as/persistence/model/ClientAttributes.java +++ b/jans-auth-server/persistence-model/src/main/java/io/jans/as/persistence/model/ClientAttributes.java @@ -92,6 +92,9 @@ public class ClientAttributes implements Serializable { @JsonProperty("jansSubAttr") private String publicSubjectIdentifierAttribute; + @JsonProperty("redirectUrisRegex") + private String redirectUrisRegex ; + @JsonProperty("jansDefaultPromptLogin") private Boolean defaultPromptLogin = false; @@ -296,6 +299,14 @@ public void setPublicSubjectIdentifierAttribute(String publicSubjectIdentifierAt this.publicSubjectIdentifierAttribute = publicSubjectIdentifierAttribute; } + public String getRedirectUrisRegex() { + return redirectUrisRegex; + } + + public void setRedirectUrisRegex(String redirectUrisRegex) { + this.redirectUrisRegex = redirectUrisRegex; + } + public Boolean getDefaultPromptLogin() { if (defaultPromptLogin == null) { defaultPromptLogin = false; @@ -329,6 +340,7 @@ public String toString() { ", authorizationEncryptedResponseAlg=" + authorizationEncryptedResponseAlg + ", authorizationEncryptedResponseEnc=" + authorizationEncryptedResponseEnc + ", publicSubjectIdentifierAttribute=" + publicSubjectIdentifierAttribute + + ", redirectUrisRegex=" + redirectUrisRegex + ", defaultPromptLogin=" + defaultPromptLogin + '}'; } diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/RegisterRestWebServiceImpl.java b/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/RegisterRestWebServiceImpl.java index 58df9a9440f..303f7582e84 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/RegisterRestWebServiceImpl.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/RegisterRestWebServiceImpl.java @@ -310,8 +310,8 @@ private Response registerClientImpl(String requestParams, HttpServletRequest htt builder.entity(jsonObjectToString(jsonObject)); - log.info("Client registered: clientId = {}, applicationType = {}, clientName = {}, redirectUris = {}, sectorIdentifierUri = {}", - client.getClientId(), client.getApplicationType(), client.getClientName(), client.getRedirectUris(), client.getSectorIdentifierUri()); + log.info("Client registered: clientId = {}, applicationType = {}, clientName = {}, redirectUris = {}, sectorIdentifierUri = {}, redirectUrisRegex = {}" , + client.getClientId(), client.getApplicationType(), client.getClientName(), client.getRedirectUris(), client.getSectorIdentifierUri(), client.getAttributes().getRedirectUrisRegex()); oAuth2AuditLog.setClientId(client.getClientId()); oAuth2AuditLog.setScope(clientScopesToString(client)); @@ -364,6 +364,16 @@ private void validateSubjectIdentifierAttribute(RegisterRequest registerRequest) ); } } + + if (StringUtils.isNotBlank(registerRequest.getRedirectUrisRegex())) { + if (Boolean.FALSE.equals(appConfiguration.getRedirectUrisRegexEnabled())) { + throw errorResponseFactory.createBadRequestException( + RegisterErrorResponseType.INVALID_REDIRECT_URIS_REGEX, + "The redirect URI's Regex is disabled." + ); + } + } + } private void setClientName(RegisterRequest r, Client client) { @@ -676,6 +686,9 @@ private void updateClientFromRequestObject(Client client, RegisterRequest reques if (requestObject.getTlsClientAuthSubjectDn() != null) { client.getAttributes().setTlsClientAuthSubjectDn(requestObject.getTlsClientAuthSubjectDn()); } + if (requestObject.getRedirectUrisRegex() != null) { + client.getAttributes().setRedirectUrisRegex(requestObject.getRedirectUrisRegex()); + } if (requestObject.getAllowSpontaneousScopes() != null) { client.getAttributes().setAllowSpontaneousScopes(requestObject.getAllowSpontaneousScopes()); } @@ -1136,6 +1149,7 @@ private JSONObject getJSONObject(Client client) throws JSONException, StringEncr Util.addToJSONObjectIfNotNull(responseJsonObject, FRONT_CHANNEL_LOGOUT_SESSION_REQUIRED.toString(), client.getFrontChannelLogoutSessionRequired()); Util.addToJSONObjectIfNotNull(responseJsonObject, BACKCHANNEL_LOGOUT_URI.toString(), client.getAttributes().getBackchannelLogoutUri()); Util.addToJSONObjectIfNotNull(responseJsonObject, BACKCHANNEL_LOGOUT_SESSION_REQUIRED.toString(), client.getAttributes().getBackchannelLogoutSessionRequired()); + Util.addToJSONObjectIfNotNull(responseJsonObject, REDIRECT_URIS_REGEX.toString(), client.getAttributes().getRedirectUrisRegex()); // Custom Params String[] scopeNames = null; diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/service/RedirectionUriService.java b/jans-auth-server/server/src/main/java/io/jans/as/server/service/RedirectionUriService.java index dec8c69269c..9e5ae857640 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/service/RedirectionUriService.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/service/RedirectionUriService.java @@ -106,20 +106,33 @@ public String validateRedirectionUri(@NotNull Client client, String redirectionU redirectUris = getSectorRedirectUris(sectorIdentifierUri).toArray(new String[0]); } - if (StringUtils.isNotBlank(redirectionUri) && redirectUris != null) { - log.debug("Validating redirection URI: clientIdentifier = {}, redirectionUri = {}, found = {}", - client.getClientId(), redirectionUri, redirectUris.length); + if (StringUtils.isBlank(sectorIdentifierUri) && redirectUris != null && redirectUris.length == 1) { + return redirectUris[0]; + } - if (isUriEqual(redirectionUri, redirectUris)) { - return redirectionUri; + if (StringUtils.isNotBlank(redirectionUri)) { + if (redirectUris != null) { + log.debug("Validating redirection URI: clientIdentifier = {}, redirectionUri = {}, found = {}", + client.getClientId(), redirectionUri, redirectUris.length); + if (isUriEqual(redirectionUri, redirectUris)) { + return redirectionUri; + } else { + log.debug("RedirectionUri didn't match with any of the client redirect uris, clientId = {}, redirectionUri = {}", client.getClientId(), redirectionUri); + } } - } else { - // Accept Request Without redirect_uri when One Registered - if (redirectUris != null && redirectUris.length == 1) { - return redirectUris[0]; + + if (appConfiguration.getRedirectUrisRegexEnabled()) { + if (redirectionUri.matches(client.getAttributes().getRedirectUrisRegex())) { + return redirectionUri; + } else { + log.debug("RedirectionUri didn't match with client regular expression, clientId = {}, redirectionUri = {}", client.getClientId(), redirectionUri); + } } + } else { + log.warn("RedirectionUri is blank, clientId = {}", client.getClientId()); } } catch (Exception e) { + log.error("Problems validating redirection uri, clientId = {}, redirectionUri = {}", client.getClientId(), redirectionUri); return null; } return null; diff --git a/jans-auth-server/server/src/test/java/io/jans/as/server/service/RedirectionUriServiceTest.java b/jans-auth-server/server/src/test/java/io/jans/as/server/service/RedirectionUriServiceTest.java new file mode 100644 index 00000000000..995dfe21cb7 --- /dev/null +++ b/jans-auth-server/server/src/test/java/io/jans/as/server/service/RedirectionUriServiceTest.java @@ -0,0 +1,142 @@ +package io.jans.as.server.service; + +import io.jans.as.common.model.registration.Client; +import io.jans.as.model.configuration.AppConfiguration; +import io.jans.as.model.error.ErrorResponseFactory; +import org.junit.Before; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import org.mockito.Spy; +import org.mockito.testng.MockitoTestNGListener; +import org.slf4j.Logger; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +@Listeners(MockitoTestNGListener.class) +public class RedirectionUriServiceTest { + + @InjectMocks + @Spy + private RedirectionUriService redirectionUriService ; + + @Mock + private Logger log; + + @Mock + private ClientService clientService; + + @Mock + private ErrorResponseFactory errorResponseFactory; + + @Mock + private AppConfiguration appConfiguration; + + @Mock + private LocalResponseCache localResponseCache; + + @Test + public void validateRedirectionUri_withValidRedirectUri_returnNotNullValue() throws Exception { + final String redirectURl = "https://test.gluu.org/jans-auth-rp/home.htm" ; + + when(redirectionUriService.getSectorRedirectUris(anyString())).thenReturn(getSectorIdentifiers()); + + final String returnValue = redirectionUriService.validateRedirectionUri(getClientForValidateRedirectionUri_full(), + redirectURl) ; + + assertNotNull(returnValue); + } + + @Test + public void validateRedirectionUri_differentRedirectUrl_returnNull() throws Exception { + final String redirectURl = "https://test.gluu.org/jans-auth-rp/home.html" ; + + when(redirectionUriService.getSectorRedirectUris(anyString())).thenReturn(getSectorIdentifiers()); + + final String returnValue = redirectionUriService.validateRedirectionUri(getClientForValidateRedirectionUri_full(), + redirectURl) ; + + assertNull(returnValue); + } + + @Test + public void validateRedirectionUri_sectorIdentifierBlank_returnSameRedirectUri() throws Exception { + final String redirectUrl = "https://test.gluu.org/jans-auth-rp/home.htm" ; + + final String returnValue = redirectionUriService.validateRedirectionUri(getClientForValidateRedirectionUri_sectorIdentifierBlank(), + redirectUrl) ; + + assertNotNull(returnValue); + assertEquals(redirectUrl, returnValue); + } + + @Test + public void validateRedirectionUri_redirectUrlNull_returnNull() throws Exception { + when(redirectionUriService.getSectorRedirectUris(anyString())).thenReturn(getSectorIdentifiers()); + + final String returnValue = redirectionUriService.validateRedirectionUri( + getClientForValidateRedirectionUri_full(), null); + + assertNull(returnValue); + } + + @Test + public void validateRedirectionUri_sectorIdentifierBlankAndRredirectUrlNull_returnNull() throws Exception { + final String returnValue = redirectionUriService.validateRedirectionUri( + getClientForValidateRedirectionUri_sectorIdentifierBlank_redirectURisNull(), null); + + assertNull(returnValue); + } + + private Client getClientForValidateRedirectionUri_full() { + final Client client = new Client(); + client.setSectorIdentifierUri("https://test.gluu.org/jans-auth/sectoridentifier/a55ede29-8f5a-461d-b06e-76caee8d40b5"); + client.setRedirectUris(new String[]{"https://client.example.com/cb2","https://client.example.com/cb1","https://client.example.com/cb", "https://test.gluu.org/jans-auth-rp/home.htm" }); + client.setClientId("4369befc-42cd-44a9-bbef-a13884ca54d0"); + client.getAttributes().setRedirectUrisRegex("/^[a-z](?:[-a-z0-9\\+\\.])*:(?:\\/\\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:])*@)?(?:\\[(?:(?:(?:[0-9a-f]{1,4}:){6}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|::(?:[0-9a-f]{1,4}:){5}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|v[0-9a-f]+\\.[-a-z0-9\\._~!\\$&'\\(\\)\\*\\+,;=:]+)\\]|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}|(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=])*)(?::[0-9]*)?(?:\\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@]))*)*|\\/(?:(?:(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@]))+)(?:\\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@]))*)*)?|(?:(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@]))+)(?:\\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@]))*)*|(?!(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@])))(?:\\?(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@])|[\\x{E000}-\\x{F8FF}\\x{F0000}-\\x{FFFFD}\\x{100000}-\\x{10FFFD}\\/\\?])*)?(?:\\#(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@])|[\\/\\?])*)?$/i\n"); + + return client ; + } + + private Client getClientForValidateRedirectionUri_sectorIdentifierBlank() { + final Client client = new Client(); + client.setSectorIdentifierUri(""); + client.setRedirectUris(new String[]{"https://client.example.com/cb2","https://client.example.com/cb1","https://client.example.com/cb", "https://test.gluu.org/jans-auth-rp/home.htm" }); + client.setClientId("4369befc-42cd-44a9-bbef-a13884ca54d0"); + client.getAttributes().setRedirectUrisRegex("/^[a-z](?:[-a-z0-9\\+\\.])*:(?:\\/\\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:])*@)?(?:\\[(?:(?:(?:[0-9a-f]{1,4}:){6}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|::(?:[0-9a-f]{1,4}:){5}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|v[0-9a-f]+\\.[-a-z0-9\\._~!\\$&'\\(\\)\\*\\+,;=:]+)\\]|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}|(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=])*)(?::[0-9]*)?(?:\\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@]))*)*|\\/(?:(?:(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@]))+)(?:\\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@]))*)*)?|(?:(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@]))+)(?:\\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@]))*)*|(?!(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@])))(?:\\?(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@])|[\\x{E000}-\\x{F8FF}\\x{F0000}-\\x{FFFFD}\\x{100000}-\\x{10FFFD}\\/\\?])*)?(?:\\#(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@])|[\\/\\?])*)?$/i\n"); + + return client ; + } + + private Client getClientForValidateRedirectionUri_sectorIdentifierBlank_redirectURisNull() { + final Client client = new Client(); + client.setSectorIdentifierUri(""); + client.setRedirectUris(null); + client.setClientId("4369befc-42cd-44a9-bbef-a13884ca54d0"); + client.getAttributes().setRedirectUrisRegex("/^[a-z](?:[-a-z0-9\\+\\.])*:(?:\\/\\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:])*@)?(?:\\[(?:(?:(?:[0-9a-f]{1,4}:){6}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|::(?:[0-9a-f]{1,4}:){5}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|v[0-9a-f]+\\.[-a-z0-9\\._~!\\$&'\\(\\)\\*\\+,;=:]+)\\]|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}|(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=])*)(?::[0-9]*)?(?:\\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@]))*)*|\\/(?:(?:(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@]))+)(?:\\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@]))*)*)?|(?:(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@]))+)(?:\\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@]))*)*|(?!(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@])))(?:\\?(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@])|[\\x{E000}-\\x{F8FF}\\x{F0000}-\\x{FFFFD}\\x{100000}-\\x{10FFFD}\\/\\?])*)?(?:\\#(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@])|[\\/\\?])*)?$/i\n"); + + return client ; + } + + private List getSectorIdentifiers() { + return Arrays.asList("https://www.jans.org", + "http://localhost:80/jans-auth-rp/home.htm", + "https://localhost:8443/jans-auth-rp/home.htm", + "https://test.gluu.org/jans-auth-rp/home.htm", + "https://test.gluu.org/jans-auth-client/test/resources/jwks.json", + "https://client.example.org/callback", + "https://client.example.org/callback2", + "https://client.other_company.example.net/callback", + "https://client.example.com/cb", + "https://client.example.com/cb1", + "https://client.example.com/cb2") ; + } + +} diff --git a/jans-auth-server/server/src/test/resources/testng.xml b/jans-auth-server/server/src/test/resources/testng.xml index 9504549f547..e5027946fc0 100644 --- a/jans-auth-server/server/src/test/resources/testng.xml +++ b/jans-auth-server/server/src/test/resources/testng.xml @@ -12,6 +12,7 @@ + diff --git a/jans-cli/cli/jca.yaml b/jans-cli/cli/jca.yaml index 7cd9bebbe00..b11494d02b9 100644 --- a/jans-cli/cli/jca.yaml +++ b/jans-cli/cli/jca.yaml @@ -4388,6 +4388,9 @@ components: type: integer description: The expiration notificator interval in seconds. example: 600 + redirectUrisRegexEnabled: + type: boolean + description: Enable/Disable redirect uris validation using regular expression. authenticationFiltersEnabled: type: boolean description: Boolean value specifying whether to enable user authentication filters. diff --git a/jans-cli/docs/cli/cli-jans-authorization-server.md b/jans-cli/docs/cli/cli-jans-authorization-server.md index f871b20b63e..01be6a8dfe3 100644 --- a/jans-cli/docs/cli/cli-jans-authorization-server.md +++ b/jans-cli/docs/cli/cli-jans-authorization-server.md @@ -486,7 +486,8 @@ Getting access token for scope https://jans.io/oauth/jans-auth-server/config/pro "discoveryCacheLifetimeInMinutes": 60, "httpLoggingEnabled": false, "httpLoggingExludePaths": null, - "externalLoggerConfiguration": null + "externalLoggerConfiguration": null, + "redirectUrisRegexEnabled": false } ``` diff --git a/jans-cli/docs/im/im-jans-authorization-server.md b/jans-cli/docs/im/im-jans-authorization-server.md index f4d61ce2f11..834c7cb5775 100644 --- a/jans-cli/docs/im/im-jans-authorization-server.md +++ b/jans-cli/docs/im/im-jans-authorization-server.md @@ -460,7 +460,8 @@ Select 1 to get all the details about Jans authorization server configuration. I "discoveryCacheLifetimeInMinutes": 60, "httpLoggingEnabled": false, "httpLoggingExludePaths": null, - "externalLoggerConfiguration": null + "externalLoggerConfiguration": null, + "redirectUrisRegexEnabled": false } ``` By selecting the 2nd option, you can modify its properties partially. diff --git a/jans-config-api/docs/jans-config-api-swagger.yaml b/jans-config-api/docs/jans-config-api-swagger.yaml index cb1613927dd..94eee97032d 100644 --- a/jans-config-api/docs/jans-config-api-swagger.yaml +++ b/jans-config-api/docs/jans-config-api-swagger.yaml @@ -4828,6 +4828,9 @@ components: staticKid: type: string description: Specifies static Kid + redirectUrisRegexEnabled: + type: boolean + description: Enable/Disable redirect uris validation using regular expression. GluuAttribute: title: GluuAttribute diff --git a/jans-linux-setup/jans_setup/openbanking/templates/jans-auth/jans-auth-config.json b/jans-linux-setup/jans_setup/openbanking/templates/jans-auth/jans-auth-config.json index c276af61b49..cc051d317cf 100644 --- a/jans-linux-setup/jans_setup/openbanking/templates/jans-auth/jans-auth-config.json +++ b/jans-linux-setup/jans_setup/openbanking/templates/jans-auth/jans-auth-config.json @@ -359,5 +359,6 @@ "deviceAuthzTokenPollInterval": 5, "deviceAuthzResponseTypeToProcessAuthz": "code", "staticKid": "%(staticKid)s", - "forceOfflineAccessScopeToEnableRefreshToken" : false + "forceOfflineAccessScopeToEnableRefreshToken" : false, + "redirectUrisRegexEnabled": false } diff --git a/jans-linux-setup/jans_setup/templates/jans-auth/jans-auth-config.json b/jans-linux-setup/jans_setup/templates/jans-auth/jans-auth-config.json index 458fae95e08..4c4e4be8a5d 100644 --- a/jans-linux-setup/jans_setup/templates/jans-auth/jans-auth-config.json +++ b/jans-linux-setup/jans_setup/templates/jans-auth/jans-auth-config.json @@ -445,5 +445,6 @@ }, "deviceAuthzRequestExpiresIn": 1800, "deviceAuthzTokenPollInterval": 5, - "deviceAuthzResponseTypeToProcessAuthz": "code" + "deviceAuthzResponseTypeToProcessAuthz": "code", + "redirectUrisRegexEnabled": false }