From a8d0157b0a8664e5e4d58a9524a0fa20df324381 Mon Sep 17 00:00:00 2001 From: Javier Rojas Date: Thu, 12 May 2022 17:22:10 -0400 Subject: [PATCH] feat(jans-auth-server): client registration language metadata (#1237) * feat(jans-auth-server): auth server should support client registration language metadata * feat(jans-auth-server): auth server should support client registration language metadata Signed-off-by: Javier Rojas Blum * feat(jans-auth-server): auth server should support client registration language metadata Signed-off-by: Javier Rojas Blum * feat(jans-auth-server): auth server should support client registration language metadata * feat(jans-auth-server): auth server should support client registration language metadata * feat(jans-auth-server): auth server should support client registration language metadata Signed-off-by: Javier Rojas Blum * feat(jans-auth-server): auth server should support client registration language metadata * feat(jans-auth-server): auth server should support client registration language metadata Signed-off-by: Javier Rojas Blum * feat(jans-auth-server): auth server should support client registration language metadata Signed-off-by: Javier Rojas Blum * feat(jans-auth-server): auth server should support client registration language metadata Signed-off-by: Javier Rojas Blum * feat(jans-auth-server): auth server should support client registration language metadata fix sonar Signed-off-by: Javier Rojas Blum * feat(jans-auth-server): auth server should support client registration language metadata fix sonar: remove unused imports Signed-off-by: Javier Rojas Blum * fix sonar: duplicated lines --- .../jans/as/client/AuthorizationRequest.java | 2 +- .../java/io/jans/as/client/BaseRequest.java | 2 +- .../io/jans/as/client/ClientInfoRequest.java | 3 +- .../io/jans/as/client/DeviceAuthzRequest.java | 1 + .../io/jans/as/client/RegisterRequest.java | 629 ++++++++---------- .../java/io/jans/as/client/TokenRequest.java | 2 +- .../io/jans/as/client/UserInfoRequest.java | 3 +- .../jans/as/client/client/AssertBuilder.java | 10 + .../io/jans/as/client/client/Asserter.java | 18 +- .../ClientInfoResponseAssertBuilder.java | 81 +++ .../jans/as/client/json/JsonApplierTest.java | 1 + .../client/ws/rs/AuthnScriptAliasesTest.java | 4 +- .../ws/rs/ClientLanguageMetadataTest.java | 342 ++++++++++ .../client/src/test/resources/testng.xml | 6 + .../as/common/model/registration/Client.java | 160 +++-- .../io/jans/as/model/json/JsonApplier.java | 1 + .../main/java/io/jans/as/model/util/Util.java | 12 +- .../ws/rs/ClientInfoRestWebServiceImpl.java | 20 +- .../register/ws/rs/RegisterJsonService.java | 11 +- .../register/ws/rs/RegisterService.java | 40 +- .../io/jans/orm/annotation/LanguageTag.java | 21 + .../io/jans/orm/impl/BaseEntryManager.java | 183 +++-- .../jans/orm/ldap/impl/LdapEntryManager.java | 243 +++---- jans-orm/model/pom.xml | 11 +- .../jans/orm/model/base/LocalizedString.java | 124 ++++ .../orm/model/base/LocalizedStringTest.java | 144 ++++ 26 files changed, 1463 insertions(+), 611 deletions(-) create mode 100644 jans-auth-server/client/src/test/java/io/jans/as/client/client/assertbuilders/ClientInfoResponseAssertBuilder.java create mode 100644 jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/ClientLanguageMetadataTest.java create mode 100644 jans-orm/annotation/src/main/java/io/jans/orm/annotation/LanguageTag.java create mode 100644 jans-orm/model/src/main/java/io/jans/orm/model/base/LocalizedString.java create mode 100644 jans-orm/model/src/test/java/io/jans/orm/model/base/LocalizedStringTest.java diff --git a/jans-auth-server/client/src/main/java/io/jans/as/client/AuthorizationRequest.java b/jans-auth-server/client/src/main/java/io/jans/as/client/AuthorizationRequest.java index cb9561acd08..478f48f844b 100644 --- a/jans-auth-server/client/src/main/java/io/jans/as/client/AuthorizationRequest.java +++ b/jans-auth-server/client/src/main/java/io/jans/as/client/AuthorizationRequest.java @@ -32,7 +32,7 @@ * Represents an authorization request to send to the authorization server. * * @author Javier Rojas Blum - * @version October 7, 2019 + * @version April 25, 2022 */ public class AuthorizationRequest extends BaseRequest { diff --git a/jans-auth-server/client/src/main/java/io/jans/as/client/BaseRequest.java b/jans-auth-server/client/src/main/java/io/jans/as/client/BaseRequest.java index 36dff77229b..a192ada8faa 100644 --- a/jans-auth-server/client/src/main/java/io/jans/as/client/BaseRequest.java +++ b/jans-auth-server/client/src/main/java/io/jans/as/client/BaseRequest.java @@ -20,7 +20,7 @@ /** * @author Javier Rojas Blum - * @version January 26. 2018 + * @version April 25. 2022 */ public abstract class BaseRequest { diff --git a/jans-auth-server/client/src/main/java/io/jans/as/client/ClientInfoRequest.java b/jans-auth-server/client/src/main/java/io/jans/as/client/ClientInfoRequest.java index 3d900a97eaf..b4b27fc7745 100644 --- a/jans-auth-server/client/src/main/java/io/jans/as/client/ClientInfoRequest.java +++ b/jans-auth-server/client/src/main/java/io/jans/as/client/ClientInfoRequest.java @@ -14,7 +14,8 @@ /** * Represents a Client Info request to send to the authorization server. * - * @author Javier Rojas Blum Date: 07.19.2012 + * @author Javier Rojas Blum + * @version April 25, 2022 */ public class ClientInfoRequest extends BaseRequest { diff --git a/jans-auth-server/client/src/main/java/io/jans/as/client/DeviceAuthzRequest.java b/jans-auth-server/client/src/main/java/io/jans/as/client/DeviceAuthzRequest.java index de8be845893..4bd1d9558f6 100644 --- a/jans-auth-server/client/src/main/java/io/jans/as/client/DeviceAuthzRequest.java +++ b/jans-auth-server/client/src/main/java/io/jans/as/client/DeviceAuthzRequest.java @@ -22,6 +22,7 @@ /** * Represents a device authorization request to send to the authorization server. + * @version April 25, 2022 */ public class DeviceAuthzRequest extends ClientAuthnRequest { 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 4681011c5cf..816ebdb6a0c 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 @@ -20,14 +20,16 @@ import io.jans.as.model.json.JsonApplier; import io.jans.as.model.register.ApplicationType; import io.jans.as.model.register.RegisterRequestParam; +import io.jans.orm.model.base.LocalizedString; +import jakarta.ws.rs.core.MediaType; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -import jakarta.ws.rs.core.MediaType; import java.util.*; +import java.util.function.BiFunction; import static io.jans.as.client.util.ClientUtil.*; import static io.jans.as.model.register.RegisterRequestParam.*; @@ -39,7 +41,7 @@ * * @author Javier Rojas Blum * @author Yuriy Zabrovarnyy - * @version March 17, 2022 + * @version May 10, 2022 */ public class RegisterRequest extends BaseRequest { @@ -63,15 +65,15 @@ public class RegisterRequest extends BaseRequest { private List grantTypes; private ApplicationType applicationType; private List contacts; - private String clientName; - private String logoUri; - private String clientUri; - private String policyUri; + private final LocalizedString clientName; + private final LocalizedString logoUri; + private final LocalizedString clientUri; + private final LocalizedString policyUri; + private final LocalizedString tosUri; private String frontChannelLogoutUri; private Boolean frontChannelLogoutSessionRequired; private List backchannelLogoutUris; private Boolean backchannelLogoutSessionRequired; - private String tosUri; private String jwksUri; private String jwks; private String sectorIdentifierUri; @@ -159,6 +161,12 @@ public RegisterRequest() { this.claims = new ArrayList<>(); this.customAttributes = new HashMap<>(); this.authorizedAcrValues = new ArrayList<>(); + + clientName = new LocalizedString(); + logoUri = new LocalizedString(); + clientUri = new LocalizedString(); + policyUri = new LocalizedString(); + tosUri = new LocalizedString(); } /** @@ -172,7 +180,7 @@ public RegisterRequest(ApplicationType applicationType, String clientName, List redirectUris) { this(); this.applicationType = applicationType; - this.clientName = clientName; + this.clientName.setValue(clientName); this.redirectUris = redirectUris; } @@ -450,7 +458,21 @@ public void setContacts(List contacts) { * @return The name of the Client to be presented to the user. */ public String getClientName() { - return clientName; + return clientName.getValue(); + } + + /** + * Returns the name of the Client to be presented to the user represented in a language and script. + * + * @param languageTag Language tag + * @return The name of the Client to be presented to the user. + */ + public String getClientName(String languageTag) { + return clientName.getValue(languageTag); + } + + public Set getClientNameLanguageTags() { + return clientName.getLanguageTags(); } /** @@ -459,7 +481,17 @@ public String getClientName() { * @param clientName The name of the Client to be presented to the user. */ public void setClientName(String clientName) { - this.clientName = clientName; + this.clientName.setValue(clientName); + } + + /** + * Sets the name of the Client to be presented to the user represented in a language and script. + * + * @param clientName The name of the Client to be presented to the user. + * @param locale The locale + */ + public void setClientName(String clientName, Locale locale) { + this.clientName.setValue(clientName, locale); } /** @@ -468,74 +500,174 @@ public void setClientName(String clientName) { * @return The URL that references a logo for the Client application. */ public String getLogoUri() { - return logoUri; + return logoUri.getValue(); } /** - * Sets an URL that references a logo for the Client application. + * Returns a URL that references a logo for the Client application in a language and script. + * + * @param languageTag Language tag + * @return The URL that references a logo for the Client application in a language and script. + */ + public String getLogoUri(String languageTag) { + return logoUri.getValue(languageTag); + } + + public Set getLogoUriLanguageTags() { + return logoUri.getLanguageTags(); + } + + /** + * Sets a URL that references a logo for the Client application. * * @param logoUri The URL that references a logo for the Client application. */ public void setLogoUri(String logoUri) { - this.logoUri = logoUri; + this.logoUri.setValue(logoUri); } /** - * Returns an URL of the home page of the Client. + * Sets a URL that references a logo for the Client application represented in a language and script. + * + * @param logoUri The URL that references a logo for the Client application represented in a language and script. + * @param locale The locale + */ + public void setLogoUri(String logoUri, Locale locale) { + this.logoUri.setValue(logoUri, locale); + } + + /** + * Returns a URL of the home page of the Client. * * @return The URL of the home page of the Client. */ public String getClientUri() { - return clientUri; + return clientUri.getValue(); + } + + public Set getClientUriLanguageTags() { + return clientUri.getLanguageTags(); } /** - * Sets an URL of the home page of the Client. + * Returns a URL of the home page of the Client represented in a language and script. + * + * @param languageTag Language tag + * @return The URL of the home page of the Client represented in a language and script. + */ + public String getClientUri(String languageTag) { + return clientUri.getValue(languageTag); + } + + /** + * Sets a URL of the home page of the Client. * * @param clientUri The URL of the home page of the Client. */ public void setClientUri(String clientUri) { - this.clientUri = clientUri; + this.clientUri.setValue(clientUri); } /** - * Returns an URL that the Relying Party Client provides to the End-User to read about the how the profile data + * Sets a URL of the home page of the Client represented in a language and script. + * + * @param clientUri The URL of the home page of the Client represented in a language and script. + * @param locale The locale + */ + public void setClientUri(String clientUri, Locale locale) { + this.clientUri.setValue(clientUri, locale); + } + + /** + * Returns a URL that the Relying Party Client provides to the End-User to read about how the profile data * will be used. * * @return The policy URL. */ public String getPolicyUri() { - return policyUri; + return policyUri.getValue(); } /** - * Sets an URL that the Relying Party Client provides to the End-User to read about the how the profile data will + * Returns a URL that the Relying Party Client provides to the End-User to read about how the profile data + * will be used in a language and script. + * + * @param languageTag Language tag + * @return The policy URL. + */ + public String getPolicyUri(String languageTag) { + return policyUri.getValue(languageTag); + } + + public Set getPolicyUriLanguageTags() { + return policyUri.getLanguageTags(); + } + + /** + * Sets a URL that the Relying Party Client provides to the End-User to read about how the profile data will * be used. * * @param policyUri The policy URL. */ public void setPolicyUri(String policyUri) { - this.policyUri = policyUri; + this.policyUri.setValue(policyUri); } /** - * Returns an URL that the Relying Party Client provides to the End-User to read about the Relying Party's terms + * Sets a URL that the Relying Party Client provides to the End-User to read about how the profile data will + * be used in a language and script. + * + * @param policyUri The policy URL. + * @param locale The locale + */ + public void setPolicyUri(String policyUri, Locale locale) { + this.policyUri.setValue(policyUri, locale); + } + + /** + * Returns a URL that the Relying Party Client provides to the End-User to read about the Relying Party's terms * of service. * * @return The tems of service URL. */ public String getTosUri() { - return tosUri; + return tosUri.getValue(); + } + + /** + * Returns a URL that the Relying Party Client provides to the End-User to read about the Relying Party's terms + * of service in a language and script. + * + * @param languageTag Language tag + * @return The terms of service URL. + */ + public String getTosUri(String languageTag) { + return tosUri.getValue(languageTag); + } + + public Set getTosUriLanguageTags() { + return tosUri.getLanguageTags(); } /** - * Sets an URL that the Relying Party Client provides to the End-User to read about the Relying Party's terms of + * Sets a URL that the Relying Party Client provides to the End-User to read about the Relying Party's terms of * service. * * @param tosUri The term of service URL. */ public void setTosUri(String tosUri) { - this.tosUri = tosUri; + this.tosUri.setValue(tosUri); + } + + /** + * Sets a URL that the Relying Party Client provides to the End-User to read about the Relying Party's terms of + * service in a language and script. + * + * @param locale The locale + * @param tosUri The term of service URL. + */ + public void setTosUri(String tosUri, Locale locale) { + this.tosUri.setValue(tosUri, locale); } /** @@ -1217,212 +1349,17 @@ public Map getParameters() { JsonApplier.getInstance().apply(this, parameters); - if (redirectUris != null && !redirectUris.isEmpty()) { - parameters.put(REDIRECT_URIS.toString(), toJSONArray(redirectUris).toString()); - } - if (claimsRedirectUris != null && !claimsRedirectUris.isEmpty()) { - parameters.put(CLAIMS_REDIRECT_URIS.toString(), toJSONArray(claimsRedirectUris).toString()); - } - if (responseTypes != null && !responseTypes.isEmpty()) { - parameters.put(RESPONSE_TYPES.toString(), toJSONArray(responseTypes).toString()); - } - if (grantTypes != null && !grantTypes.isEmpty()) { - parameters.put(GRANT_TYPES.toString(), toJSONArray(grantTypes).toString()); - } - if (applicationType != null) { - parameters.put(APPLICATION_TYPE.toString(), applicationType.toString()); - } - if (contacts != null && !contacts.isEmpty()) { - parameters.put(CONTACTS.toString(), toJSONArray(contacts).toString()); - } - if (StringUtils.isNotBlank(clientName)) { - parameters.put(CLIENT_NAME.toString(), clientName); - } - if (StringUtils.isNotBlank(logoUri)) { - parameters.put(LOGO_URI.toString(), logoUri); - } - if (StringUtils.isNotBlank(clientUri)) { - parameters.put(CLIENT_URI.toString(), clientUri); - } - if (StringUtils.isNotBlank(policyUri)) { - parameters.put(POLICY_URI.toString(), policyUri); - } - if (StringUtils.isNotBlank(tosUri)) { - parameters.put(TOS_URI.toString(), tosUri); - } - if (StringUtils.isNotBlank(jwksUri)) { - parameters.put(JWKS_URI.toString(), jwksUri); - } - if (StringUtils.isNotBlank(jwks)) { - parameters.put(JWKS.toString(), jwks); - } - if (StringUtils.isNotBlank(sectorIdentifierUri)) { - parameters.put(SECTOR_IDENTIFIER_URI.toString(), sectorIdentifierUri); - } - if (subjectType != null) { - parameters.put(SUBJECT_TYPE.toString(), subjectType.toString()); - } - if (StringUtils.isNotBlank(subjectIdentifierAttribute)) { - parameters.put(PUBLIC_SUBJECT_IDENTIFIER_ATTRIBUTE.getName(), subjectIdentifierAttribute); - } - if (rptAsJwt != null) { - parameters.put(RPT_AS_JWT.toString(), rptAsJwt.toString()); - } - if (accessTokenAsJwt != null) { - parameters.put(ACCESS_TOKEN_AS_JWT.toString(), accessTokenAsJwt.toString()); - } - if (accessTokenSigningAlg != null) { - parameters.put(ACCESS_TOKEN_SIGNING_ALG.toString(), accessTokenSigningAlg.toString()); - } - if (authorizationSignedResponseAlg != null) { - parameters.put(AUTHORIZATION_SIGNED_RESPONSE_ALG.toString(), authorizationSignedResponseAlg.getName()); - } - if (authorizationEncryptedResponseAlg != null) { - parameters.put(AUTHORIZATION_ENCRYPTED_RESPONSE_ALG.toString(), authorizationEncryptedResponseAlg.getName()); - } - if (authorizationEncryptedResponseEnc != null) { - parameters.put(AUTHORIZATION_ENCRYPTED_RESPONSE_ENC.toString(), authorizationEncryptedResponseEnc.getName()); - } - if (idTokenSignedResponseAlg != null) { - parameters.put(ID_TOKEN_SIGNED_RESPONSE_ALG.toString(), idTokenSignedResponseAlg.getName()); - } - if (idTokenEncryptedResponseAlg != null) { - parameters.put(ID_TOKEN_ENCRYPTED_RESPONSE_ALG.toString(), idTokenEncryptedResponseAlg.getName()); - } - if (idTokenEncryptedResponseEnc != null) { - parameters.put(ID_TOKEN_ENCRYPTED_RESPONSE_ENC.toString(), idTokenEncryptedResponseEnc.getName()); - } - if (userInfoSignedResponseAlg != null) { - parameters.put(USERINFO_SIGNED_RESPONSE_ALG.toString(), userInfoSignedResponseAlg.getName()); - } - if (userInfoEncryptedResponseAlg != null) { - parameters.put(USERINFO_ENCRYPTED_RESPONSE_ALG.toString(), userInfoEncryptedResponseAlg.getName()); - } - if (userInfoEncryptedResponseEnc != null) { - parameters.put(USERINFO_ENCRYPTED_RESPONSE_ENC.toString(), userInfoEncryptedResponseEnc.getName()); - } - if (requestObjectSigningAlg != null) { - parameters.put(REQUEST_OBJECT_SIGNING_ALG.toString(), requestObjectSigningAlg.getName()); - } - if (requestObjectEncryptionAlg != null) { - parameters.put(REQUEST_OBJECT_ENCRYPTION_ALG.toString(), requestObjectEncryptionAlg.getName()); - } - if (requestObjectEncryptionEnc != null) { - parameters.put(REQUEST_OBJECT_ENCRYPTION_ENC.toString(), requestObjectEncryptionEnc.getName()); - } - if (tokenEndpointAuthMethod != null) { - parameters.put(TOKEN_ENDPOINT_AUTH_METHOD.toString(), tokenEndpointAuthMethod.toString()); - } - if (tokenEndpointAuthSigningAlg != null) { - parameters.put(TOKEN_ENDPOINT_AUTH_SIGNING_ALG.toString(), tokenEndpointAuthSigningAlg.toString()); - } - if (defaultMaxAge != null) { - parameters.put(DEFAULT_MAX_AGE.toString(), defaultMaxAge.toString()); - } - if (requireAuthTime != null) { - parameters.put(REQUIRE_AUTH_TIME.toString(), requireAuthTime.toString()); - } - if (defaultAcrValues != null && !defaultAcrValues.isEmpty()) { - parameters.put(DEFAULT_ACR_VALUES.toString(), toJSONArray(defaultAcrValues).toString()); - } - if (StringUtils.isNotBlank(initiateLoginUri)) { - parameters.put(INITIATE_LOGIN_URI.toString(), initiateLoginUri); - } - if (postLogoutRedirectUris != null && !postLogoutRedirectUris.isEmpty()) { - parameters.put(POST_LOGOUT_REDIRECT_URIS.toString(), toJSONArray(postLogoutRedirectUris).toString()); - } - if (StringUtils.isNotBlank(frontChannelLogoutUri)) { - parameters.put(FRONT_CHANNEL_LOGOUT_URI.toString(), frontChannelLogoutUri); - } - if (frontChannelLogoutSessionRequired != null) { - parameters.put(FRONT_CHANNEL_LOGOUT_SESSION_REQUIRED.toString(), frontChannelLogoutSessionRequired.toString()); - } - if (backchannelLogoutUris != null && !backchannelLogoutUris.isEmpty()) { - parameters.put(BACKCHANNEL_LOGOUT_URI.toString(), toJSONArray(backchannelLogoutUris).toString()); - } - if (backchannelLogoutSessionRequired != null) { - parameters.put(BACKCHANNEL_LOGOUT_SESSION_REQUIRED.toString(), backchannelLogoutSessionRequired.toString()); - } - if (requestUris != null && !requestUris.isEmpty()) { - parameters.put(REQUEST_URIS.toString(), toJSONArray(requestUris).toString()); - } - if (authorizedOrigins != null && !authorizedOrigins.isEmpty()) { - parameters.put(AUTHORIZED_ORIGINS.toString(), toJSONArray(authorizedOrigins).toString()); - } - if (scope != null && !scope.isEmpty()) { - parameters.put(SCOPE.toString(), implode(scope, " ")); - } - if (StringUtils.isNotBlank(idTokenTokenBindingCnf)) { - parameters.put(ID_TOKEN_TOKEN_BINDING_CNF.toString(), idTokenTokenBindingCnf); - } - if (StringUtils.isNotBlank(tlsClientAuthSubjectDn)) { - parameters.put(TLS_CLIENT_AUTH_SUBJECT_DN.toString(), tlsClientAuthSubjectDn); - } - if (allowSpontaneousScopes != null) { - parameters.put(ALLOW_SPONTANEOUS_SCOPES.toString(), allowSpontaneousScopes.toString()); - } - if (spontaneousScopes != null && !spontaneousScopes.isEmpty()) { - parameters.put(SPONTANEOUS_SCOPES.toString(), implode(spontaneousScopes, " ")); - } - if (runIntrospectionScriptBeforeAccessTokenAsJwtCreationAndIncludeClaims != null) { - parameters.put(RUN_INTROSPECTION_SCRIPT_BEFORE_ACCESS_TOKEN_CREATION_AS_JWT_AND_INCLUDE_CLAIMS.toString(), runIntrospectionScriptBeforeAccessTokenAsJwtCreationAndIncludeClaims.toString()); - } - if (keepClientAuthorizationAfterExpiration != null) { - parameters.put(KEEP_CLIENT_AUTHORIZATION_AFTER_EXPIRATION.toString(), keepClientAuthorizationAfterExpiration.toString()); - } - if (claims != null && !claims.isEmpty()) { - parameters.put(CLAIMS.toString(), implode(claims, " ")); - } - if (accessTokenLifetime != null) { - parameters.put(ACCESS_TOKEN_LIFETIME.toString(), accessTokenLifetime.toString()); - } - if (parLifetime != null) { - parameters.put(PAR_LIFETIME.toString(), parLifetime.toString()); - } - if (requirePar != null) { - parameters.put(REQUIRE_PAR.toString(), requirePar.toString()); - } - if (StringUtils.isNotBlank(softwareId)) { - parameters.put(SOFTWARE_ID.toString(), softwareId); - } - if (StringUtils.isNotBlank(softwareVersion)) { - parameters.put(SOFTWARE_VERSION.toString(), softwareVersion); - } - if (StringUtils.isNotBlank(softwareStatement)) { - parameters.put(SOFTWARE_STATEMENT.toString(), softwareStatement); - } - if (backchannelTokenDeliveryMode != null) { - parameters.put(BACKCHANNEL_TOKEN_DELIVERY_MODE.toString(), backchannelTokenDeliveryMode.toString()); - } - if (StringUtils.isNotBlank(backchannelClientNotificationEndpoint)) { - parameters.put(BACKCHANNEL_CLIENT_NOTIFICATION_ENDPOINT.toString(), backchannelClientNotificationEndpoint); - } - if (backchannelAuthenticationRequestSigningAlg != null) { - parameters.put(BACKCHANNEL_AUTHENTICATION_REQUEST_SIGNING_ALG.toString(), backchannelAuthenticationRequestSigningAlg.toString()); - } - if (backchannelUserCodeParameter != null && backchannelUserCodeParameter) { - parameters.put(BACKCHANNEL_USER_CODE_PARAMETER.toString(), backchannelUserCodeParameter.toString()); - } - if (defaultPromptLogin != null) { - parameters.put(DEFAULT_PROMPT_LOGIN.getName(), defaultPromptLogin.toString()); - } - if (authorizedAcrValues != null && !authorizedAcrValues.isEmpty()) { - parameters.put(AUTHORIZED_ACR_VALUES.getName(), toJSONArray(authorizedAcrValues).toString()); - } - if (redirectUrisRegex != null) { - parameters.put(REDIRECT_URIS_REGEX.toString(), redirectUrisRegex.toString()); - } + clientName.addToMap(parameters, CLIENT_NAME.getName()); + clientName.addToMap(parameters, LOGO_URI.getName()); + clientName.addToMap(parameters, CLIENT_URI.getName()); + clientName.addToMap(parameters, POLICY_URI.getName()); + clientName.addToMap(parameters, TOS_URI.getName()); + + getParameters((name, value) -> { + parameters.put(name, value.toString()); + return null; + }); - // Custom params - if (customAttributes != null && !customAttributes.isEmpty()) { - for (Map.Entry entry : customAttributes.entrySet()) { - final String name = entry.getKey(); - final String value = entry.getValue(); - if (RegisterRequestParam.isCustomParameterValid(name) && StringUtils.isNotBlank(value)) { - parameters.put(name, value); - } - } - } return parameters; } @@ -1480,12 +1417,29 @@ public static RegisterRequest fromJson(JSONObject requestObject) throws JSONExce result.setGrantTypes(extractGrantTypes(requestObject)); result.setApplicationType(ApplicationType.fromString(requestObject.optString(APPLICATION_TYPE.toString()))); result.setContacts(extractListByKey(requestObject, CONTACTS.toString())); - result.setClientName(requestObject.optString(CLIENT_NAME.toString())); result.setIdTokenTokenBindingCnf(requestObject.optString(ID_TOKEN_TOKEN_BINDING_CNF.toString(), "")); - result.setLogoUri(requestObject.optString(LOGO_URI.toString())); - result.setClientUri(requestObject.optString(CLIENT_URI.toString())); - result.setPolicyUri(requestObject.optString(POLICY_URI.toString())); - result.setTosUri(requestObject.optString(TOS_URI.toString())); + + LocalizedString.fromJson(requestObject, CLIENT_NAME.getName(), (String key, Locale locale) -> { + result.setClientName(key, locale); + return null; + }); + LocalizedString.fromJson(requestObject, LOGO_URI.getName(), (String key, Locale locale) -> { + result.setLogoUri(key, locale); + return null; + }); + LocalizedString.fromJson(requestObject, CLIENT_URI.getName(), (String key, Locale locale) -> { + result.setClientUri(key, locale); + return null; + }); + LocalizedString.fromJson(requestObject, POLICY_URI.getName(), (String key, Locale locale) -> { + result.setPolicyUri(key, locale); + return null; + }); + LocalizedString.fromJson(requestObject, TOS_URI.getName(), (String key, Locale locale) -> { + result.setTosUri(key, locale); + return null; + }); + result.setJwksUri(requestObject.optString(JWKS_URI.toString())); result.setJwks(requestObject.optString(JWKS.toString())); result.setSectorIdentifierUri(requestObject.optString(SECTOR_IDENTIFIER_URI.toString())); @@ -1519,209 +1473,215 @@ public static List extractGrantTypes(JSONObject requestObject) { return new ArrayList<>(grantTypes); } - @Override public JSONObject getJSONParameters() throws JSONException { - JSONObject parameters = new JSONObject(); + final JSONObject parameters = new JSONObject(); JsonApplier.getInstance().apply(this, parameters); + Map paramsMap = parameters.toMap(); + clientName.addToMap(paramsMap, CLIENT_NAME.getName()); + logoUri.addToMap(paramsMap, LOGO_URI.getName()); + clientUri.addToMap(paramsMap, CLIENT_URI.getName()); + policyUri.addToMap(paramsMap, POLICY_URI.getName()); + tosUri.addToMap(paramsMap, TOS_URI.getName()); + parameters.clear(); + paramsMap.forEach(parameters::put); + + getParameters((name, value) -> { + parameters.put(name, value); + return null; + }); + + return parameters; + } + + public void getParameters(BiFunction function) { if (redirectUris != null && !redirectUris.isEmpty()) { - parameters.put(REDIRECT_URIS.toString(), toJSONArray(redirectUris)); + function.apply(REDIRECT_URIS.toString(), toJSONArray(redirectUris)); } if (claimsRedirectUris != null && !claimsRedirectUris.isEmpty()) { - parameters.put(CLAIMS_REDIRECT_URIS.toString(), toJSONArray(claimsRedirectUris)); + function.apply(CLAIMS_REDIRECT_URIS.toString(), toJSONArray(claimsRedirectUris)); } if (responseTypes != null && !responseTypes.isEmpty()) { - parameters.put(RESPONSE_TYPES.toString(), toJSONArray(responseTypes)); + function.apply(RESPONSE_TYPES.toString(), toJSONArray(responseTypes)); } if (grantTypes != null && !grantTypes.isEmpty()) { - parameters.put(GRANT_TYPES.toString(), toJSONArray(grantTypes)); + function.apply(GRANT_TYPES.toString(), toJSONArray(grantTypes)); } if (applicationType != null) { - parameters.put(APPLICATION_TYPE.toString(), applicationType.toString()); + function.apply(APPLICATION_TYPE.toString(), applicationType.toString()); } if (contacts != null && !contacts.isEmpty()) { - parameters.put(CONTACTS.toString(), toJSONArray(contacts)); - } - if (StringUtils.isNotBlank(clientName)) { - parameters.put(CLIENT_NAME.toString(), clientName); - } - if (StringUtils.isNotBlank(idTokenTokenBindingCnf)) { - parameters.put(ID_TOKEN_TOKEN_BINDING_CNF.toString(), idTokenTokenBindingCnf); - } - if (StringUtils.isNotBlank(tlsClientAuthSubjectDn)) { - parameters.put(TLS_CLIENT_AUTH_SUBJECT_DN.toString(), tlsClientAuthSubjectDn); - } - if (allowSpontaneousScopes != null) { - parameters.put(ALLOW_SPONTANEOUS_SCOPES.toString(), allowSpontaneousScopes); - } - if (spontaneousScopes != null && !spontaneousScopes.isEmpty()) { - parameters.put(SPONTANEOUS_SCOPES.toString(), toJSONArray(spontaneousScopes)); - } - if (runIntrospectionScriptBeforeAccessTokenAsJwtCreationAndIncludeClaims != null) { - parameters.put(RUN_INTROSPECTION_SCRIPT_BEFORE_ACCESS_TOKEN_CREATION_AS_JWT_AND_INCLUDE_CLAIMS.toString(), runIntrospectionScriptBeforeAccessTokenAsJwtCreationAndIncludeClaims); - } - if (keepClientAuthorizationAfterExpiration != null) { - parameters.put(KEEP_CLIENT_AUTHORIZATION_AFTER_EXPIRATION.toString(), keepClientAuthorizationAfterExpiration); - } - if (StringUtils.isNotBlank(logoUri)) { - parameters.put(LOGO_URI.toString(), logoUri); - } - if (StringUtils.isNotBlank(clientUri)) { - parameters.put(CLIENT_URI.toString(), clientUri); - } - if (StringUtils.isNotBlank(policyUri)) { - parameters.put(POLICY_URI.toString(), policyUri); - } - if (StringUtils.isNotBlank(tosUri)) { - parameters.put(TOS_URI.toString(), tosUri); + function.apply(CONTACTS.toString(), toJSONArray(contacts)); } + if (StringUtils.isNotBlank(jwksUri)) { - parameters.put(JWKS_URI.toString(), jwksUri); + function.apply(JWKS_URI.toString(), jwksUri); } if (StringUtils.isNotBlank(jwks)) { - parameters.put(JWKS.toString(), jwks); + function.apply(JWKS.toString(), jwks); } if (StringUtils.isNotBlank(sectorIdentifierUri)) { - parameters.put(SECTOR_IDENTIFIER_URI.toString(), sectorIdentifierUri); + function.apply(SECTOR_IDENTIFIER_URI.toString(), sectorIdentifierUri); } if (subjectType != null) { - parameters.put(SUBJECT_TYPE.toString(), subjectType.toString()); + function.apply(SUBJECT_TYPE.toString(), subjectType.toString()); } if (StringUtils.isNotBlank(subjectIdentifierAttribute)) { - parameters.put(PUBLIC_SUBJECT_IDENTIFIER_ATTRIBUTE.getName(), subjectIdentifierAttribute); + function.apply(PUBLIC_SUBJECT_IDENTIFIER_ATTRIBUTE.getName(), subjectIdentifierAttribute); } if (rptAsJwt != null) { - parameters.put(RPT_AS_JWT.toString(), rptAsJwt.toString()); + function.apply(RPT_AS_JWT.toString(), rptAsJwt.toString()); } if (accessTokenAsJwt != null) { - parameters.put(ACCESS_TOKEN_AS_JWT.toString(), accessTokenAsJwt.toString()); + function.apply(ACCESS_TOKEN_AS_JWT.toString(), accessTokenAsJwt.toString()); } if (accessTokenSigningAlg != null) { - parameters.put(ACCESS_TOKEN_SIGNING_ALG.toString(), accessTokenSigningAlg.toString()); + function.apply(ACCESS_TOKEN_SIGNING_ALG.toString(), accessTokenSigningAlg.toString()); } if (authorizationSignedResponseAlg != null) { - parameters.put(AUTHORIZATION_SIGNED_RESPONSE_ALG.toString(), authorizationSignedResponseAlg.toString()); + function.apply(AUTHORIZATION_SIGNED_RESPONSE_ALG.toString(), authorizationSignedResponseAlg.toString()); } if (authorizationEncryptedResponseAlg != null) { - parameters.put(AUTHORIZATION_ENCRYPTED_RESPONSE_ALG.toString(), authorizationEncryptedResponseAlg.toString()); + function.apply(AUTHORIZATION_ENCRYPTED_RESPONSE_ALG.toString(), authorizationEncryptedResponseAlg.toString()); } if (authorizationEncryptedResponseEnc != null) { - parameters.put(AUTHORIZATION_ENCRYPTED_RESPONSE_ENC.toString(), authorizationEncryptedResponseEnc.toString()); + function.apply(AUTHORIZATION_ENCRYPTED_RESPONSE_ENC.toString(), authorizationEncryptedResponseEnc.toString()); } + if (idTokenSignedResponseAlg != null) { - parameters.put(ID_TOKEN_SIGNED_RESPONSE_ALG.toString(), idTokenSignedResponseAlg.getName()); + function.apply(ID_TOKEN_SIGNED_RESPONSE_ALG.toString(), idTokenSignedResponseAlg.getName()); } if (idTokenEncryptedResponseAlg != null) { - parameters.put(ID_TOKEN_ENCRYPTED_RESPONSE_ALG.toString(), idTokenEncryptedResponseAlg.getName()); + function.apply(ID_TOKEN_ENCRYPTED_RESPONSE_ALG.toString(), idTokenEncryptedResponseAlg.getName()); } if (idTokenEncryptedResponseEnc != null) { - parameters.put(ID_TOKEN_ENCRYPTED_RESPONSE_ENC.toString(), idTokenEncryptedResponseEnc.getName()); + function.apply(ID_TOKEN_ENCRYPTED_RESPONSE_ENC.toString(), idTokenEncryptedResponseEnc.getName()); } if (userInfoSignedResponseAlg != null) { - parameters.put(USERINFO_SIGNED_RESPONSE_ALG.toString(), userInfoSignedResponseAlg.getName()); + function.apply(USERINFO_SIGNED_RESPONSE_ALG.toString(), userInfoSignedResponseAlg.getName()); } if (userInfoEncryptedResponseAlg != null) { - parameters.put(USERINFO_ENCRYPTED_RESPONSE_ALG.toString(), userInfoEncryptedResponseAlg.getName()); + function.apply(USERINFO_ENCRYPTED_RESPONSE_ALG.toString(), userInfoEncryptedResponseAlg.getName()); } if (userInfoEncryptedResponseEnc != null) { - parameters.put(USERINFO_ENCRYPTED_RESPONSE_ENC.toString(), userInfoEncryptedResponseEnc.getName()); + function.apply(USERINFO_ENCRYPTED_RESPONSE_ENC.toString(), userInfoEncryptedResponseEnc.getName()); } if (requestObjectSigningAlg != null) { - parameters.put(REQUEST_OBJECT_SIGNING_ALG.toString(), requestObjectSigningAlg.getName()); + function.apply(REQUEST_OBJECT_SIGNING_ALG.toString(), requestObjectSigningAlg.getName()); } if (requestObjectEncryptionAlg != null) { - parameters.put(REQUEST_OBJECT_ENCRYPTION_ALG.toString(), requestObjectEncryptionAlg.getName()); + function.apply(REQUEST_OBJECT_ENCRYPTION_ALG.toString(), requestObjectEncryptionAlg.getName()); } if (requestObjectEncryptionEnc != null) { - parameters.put(REQUEST_OBJECT_ENCRYPTION_ENC.toString(), requestObjectEncryptionEnc.getName()); + function.apply(REQUEST_OBJECT_ENCRYPTION_ENC.toString(), requestObjectEncryptionEnc.getName()); } if (tokenEndpointAuthMethod != null) { - parameters.put(TOKEN_ENDPOINT_AUTH_METHOD.toString(), tokenEndpointAuthMethod.toString()); + function.apply(TOKEN_ENDPOINT_AUTH_METHOD.toString(), tokenEndpointAuthMethod.toString()); } if (tokenEndpointAuthSigningAlg != null) { - parameters.put(TOKEN_ENDPOINT_AUTH_SIGNING_ALG.toString(), tokenEndpointAuthSigningAlg.toString()); + function.apply(TOKEN_ENDPOINT_AUTH_SIGNING_ALG.toString(), tokenEndpointAuthSigningAlg.toString()); } if (defaultMaxAge != null) { - parameters.put(DEFAULT_MAX_AGE.toString(), defaultMaxAge.toString()); + function.apply(DEFAULT_MAX_AGE.toString(), defaultMaxAge.toString()); } if (requireAuthTime != null) { - parameters.put(REQUIRE_AUTH_TIME.toString(), requireAuthTime.toString()); + function.apply(REQUIRE_AUTH_TIME.toString(), requireAuthTime.toString()); } if (defaultAcrValues != null && !defaultAcrValues.isEmpty()) { - parameters.put(DEFAULT_ACR_VALUES.toString(), toJSONArray(defaultAcrValues)); + function.apply(DEFAULT_ACR_VALUES.toString(), toJSONArray(defaultAcrValues).toString()); } if (StringUtils.isNotBlank(initiateLoginUri)) { - parameters.put(INITIATE_LOGIN_URI.toString(), initiateLoginUri); + function.apply(INITIATE_LOGIN_URI.toString(), initiateLoginUri); } if (postLogoutRedirectUris != null && !postLogoutRedirectUris.isEmpty()) { - parameters.put(POST_LOGOUT_REDIRECT_URIS.toString(), toJSONArray(postLogoutRedirectUris)); + function.apply(POST_LOGOUT_REDIRECT_URIS.toString(), toJSONArray(postLogoutRedirectUris).toString()); } if (StringUtils.isNotBlank(frontChannelLogoutUri)) { - parameters.put(FRONT_CHANNEL_LOGOUT_URI.toString(), frontChannelLogoutUri); + function.apply(FRONT_CHANNEL_LOGOUT_URI.toString(), frontChannelLogoutUri); } if (frontChannelLogoutSessionRequired != null) { - parameters.put(FRONT_CHANNEL_LOGOUT_SESSION_REQUIRED.toString(), frontChannelLogoutSessionRequired.toString()); + function.apply(FRONT_CHANNEL_LOGOUT_SESSION_REQUIRED.toString(), frontChannelLogoutSessionRequired.toString()); } if (backchannelLogoutUris != null && !backchannelLogoutUris.isEmpty()) { - parameters.put(BACKCHANNEL_LOGOUT_URI.toString(), toJSONArray(backchannelLogoutUris)); + function.apply(BACKCHANNEL_LOGOUT_URI.toString(), toJSONArray(backchannelLogoutUris).toString()); } if (backchannelLogoutSessionRequired != null) { - parameters.put(BACKCHANNEL_LOGOUT_SESSION_REQUIRED.toString(), backchannelLogoutSessionRequired.toString()); + function.apply(BACKCHANNEL_LOGOUT_SESSION_REQUIRED.toString(), backchannelLogoutSessionRequired.toString()); } if (requestUris != null && !requestUris.isEmpty()) { - parameters.put(REQUEST_URIS.toString(), toJSONArray(requestUris)); + function.apply(REQUEST_URIS.toString(), toJSONArray(requestUris).toString()); } if (authorizedOrigins != null && !authorizedOrigins.isEmpty()) { - parameters.put(AUTHORIZED_ORIGINS.toString(), toJSONArray(authorizedOrigins)); + function.apply(AUTHORIZED_ORIGINS.toString(), toJSONArray(authorizedOrigins).toString()); } if (scope != null && !scope.isEmpty()) { - parameters.put(SCOPE.toString(), implode(scope, " ")); - } - if (claims != null && !claims.isEmpty()) { - parameters.put(CLAIMS.toString(), implode(claims, " ")); - } - if (accessTokenLifetime != null) { - parameters.put(ACCESS_TOKEN_LIFETIME.toString(), accessTokenLifetime); - } - if (parLifetime != null) { - parameters.put(PAR_LIFETIME.toString(), parLifetime); - } - if (requirePar != null) { - parameters.put(REQUIRE_PAR.toString(), requirePar); + function.apply(SCOPE.toString(), implode(scope, " ")); } + if (StringUtils.isNotBlank(softwareId)) { - parameters.put(SOFTWARE_ID.toString(), softwareId); + function.apply(SOFTWARE_ID.toString(), softwareId); } if (StringUtils.isNotBlank(softwareVersion)) { - parameters.put(SOFTWARE_VERSION.toString(), softwareVersion); + function.apply(SOFTWARE_VERSION.toString(), softwareVersion); } if (StringUtils.isNotBlank(softwareStatement)) { - parameters.put(SOFTWARE_STATEMENT.toString(), softwareStatement); + function.apply(SOFTWARE_STATEMENT.toString(), softwareStatement); } if (backchannelTokenDeliveryMode != null) { - parameters.put(BACKCHANNEL_TOKEN_DELIVERY_MODE.toString(), backchannelTokenDeliveryMode); + function.apply(BACKCHANNEL_TOKEN_DELIVERY_MODE.toString(), backchannelTokenDeliveryMode.toString()); } if (StringUtils.isNotBlank(backchannelClientNotificationEndpoint)) { - parameters.put(BACKCHANNEL_CLIENT_NOTIFICATION_ENDPOINT.toString(), backchannelClientNotificationEndpoint); + function.apply(BACKCHANNEL_CLIENT_NOTIFICATION_ENDPOINT.toString(), backchannelClientNotificationEndpoint); } if (backchannelAuthenticationRequestSigningAlg != null) { - parameters.put(BACKCHANNEL_AUTHENTICATION_REQUEST_SIGNING_ALG.toString(), backchannelAuthenticationRequestSigningAlg.toString()); + function.apply(BACKCHANNEL_AUTHENTICATION_REQUEST_SIGNING_ALG.toString(), backchannelAuthenticationRequestSigningAlg.toString()); } - if (backchannelUserCodeParameter != null) { - parameters.put(BACKCHANNEL_USER_CODE_PARAMETER.toString(), backchannelUserCodeParameter); + if (backchannelUserCodeParameter != null && backchannelUserCodeParameter) { + function.apply(BACKCHANNEL_USER_CODE_PARAMETER.toString(), backchannelUserCodeParameter.toString()); + } + + if (StringUtils.isNotBlank(idTokenTokenBindingCnf)) { + function.apply(ID_TOKEN_TOKEN_BINDING_CNF.toString(), idTokenTokenBindingCnf); + } + if (StringUtils.isNotBlank(tlsClientAuthSubjectDn)) { + function.apply(TLS_CLIENT_AUTH_SUBJECT_DN.toString(), tlsClientAuthSubjectDn); + } + if (allowSpontaneousScopes != null) { + function.apply(ALLOW_SPONTANEOUS_SCOPES.toString(), allowSpontaneousScopes.toString()); + } + if (spontaneousScopes != null && !spontaneousScopes.isEmpty()) { + function.apply(SPONTANEOUS_SCOPES.toString(), implode(spontaneousScopes, " ")); + } + if (runIntrospectionScriptBeforeAccessTokenAsJwtCreationAndIncludeClaims != null) { + function.apply(RUN_INTROSPECTION_SCRIPT_BEFORE_ACCESS_TOKEN_CREATION_AS_JWT_AND_INCLUDE_CLAIMS.toString(), runIntrospectionScriptBeforeAccessTokenAsJwtCreationAndIncludeClaims.toString()); + } + if (keepClientAuthorizationAfterExpiration != null) { + function.apply(KEEP_CLIENT_AUTHORIZATION_AFTER_EXPIRATION.toString(), keepClientAuthorizationAfterExpiration.toString()); + } + if (claims != null && !claims.isEmpty()) { + function.apply(CLAIMS.toString(), implode(claims, " ")); + } + if (accessTokenLifetime != null) { + function.apply(ACCESS_TOKEN_LIFETIME.toString(), accessTokenLifetime.toString()); + } + if (parLifetime != null) { + function.apply(PAR_LIFETIME.toString(), parLifetime.toString()); + } + if (requirePar != null) { + function.apply(REQUIRE_PAR.toString(), requirePar.toString()); } if (redirectUrisRegex != null) { - parameters.put(REDIRECT_URIS_REGEX.toString(), redirectUrisRegex); + function.apply(REDIRECT_URIS_REGEX.toString(), redirectUrisRegex); } if (defaultPromptLogin != null) { - parameters.put(DEFAULT_PROMPT_LOGIN.getName(), defaultPromptLogin); + function.apply(DEFAULT_PROMPT_LOGIN.getName(), defaultPromptLogin); } if (authorizedAcrValues != null && !authorizedAcrValues.isEmpty()) { - parameters.put(AUTHORIZED_ACR_VALUES.toString(), toJSONArray(authorizedAcrValues)); + function.apply(AUTHORIZED_ACR_VALUES.toString(), toJSONArray(authorizedAcrValues)); } // Custom params @@ -1730,11 +1690,10 @@ public JSONObject getJSONParameters() throws JSONException { final String name = entry.getKey(); final String value = entry.getValue(); if (RegisterRequestParam.isCustomParameterValid(name) && StringUtils.isNotBlank(value)) { - parameters.put(name, value); + function.apply(name, value); } } } - return parameters; } public JSONObject getJsonObject() { diff --git a/jans-auth-server/client/src/main/java/io/jans/as/client/TokenRequest.java b/jans-auth-server/client/src/main/java/io/jans/as/client/TokenRequest.java index cd1eee20aec..3a650dfcd02 100644 --- a/jans-auth-server/client/src/main/java/io/jans/as/client/TokenRequest.java +++ b/jans-auth-server/client/src/main/java/io/jans/as/client/TokenRequest.java @@ -21,7 +21,7 @@ * Represents a token request to send to the authorization server. * * @author Javier Rojas Blum - * @version September 30, 2021 + * @version April 25, 2022 */ public class TokenRequest extends ClientAuthnRequest { diff --git a/jans-auth-server/client/src/main/java/io/jans/as/client/UserInfoRequest.java b/jans-auth-server/client/src/main/java/io/jans/as/client/UserInfoRequest.java index 392efcc67c5..1704ff8132f 100644 --- a/jans-auth-server/client/src/main/java/io/jans/as/client/UserInfoRequest.java +++ b/jans-auth-server/client/src/main/java/io/jans/as/client/UserInfoRequest.java @@ -15,7 +15,8 @@ /** * Represents a User Info request to send to the authorization server. * - * @author Javier Rojas Blum Date: 11.28.2011 + * @author Javier Rojas Blum + * @version April 25, 2022 */ public class UserInfoRequest extends BaseRequest { diff --git a/jans-auth-server/client/src/test/java/io/jans/as/client/client/AssertBuilder.java b/jans-auth-server/client/src/test/java/io/jans/as/client/client/AssertBuilder.java index e2ab40c0d43..15fc3503f90 100644 --- a/jans-auth-server/client/src/test/java/io/jans/as/client/client/AssertBuilder.java +++ b/jans-auth-server/client/src/test/java/io/jans/as/client/client/AssertBuilder.java @@ -1,3 +1,9 @@ +/* + * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text. + * + * Copyright (c) 2020, Janssen Project + */ + package io.jans.as.client.client; import io.jans.as.client.*; @@ -25,6 +31,10 @@ public static UserInfoResponseAssertBuilder userInfoResponse(UserInfoResponse re return new UserInfoResponseAssertBuilder(response); } + public static ClientInfoResponseAssertBuilder clientInfoResponse(ClientInfoResponse response) { + return new ClientInfoResponseAssertBuilder(response); + } + public static BackchannelAuthenticationResponseAssertBuilder backchannelAuthenticationResponse(BackchannelAuthenticationResponse response) { return new BackchannelAuthenticationResponseAssertBuilder(response); } diff --git a/jans-auth-server/client/src/test/java/io/jans/as/client/client/Asserter.java b/jans-auth-server/client/src/test/java/io/jans/as/client/client/Asserter.java index 71ad8420a34..d58c9e693f7 100644 --- a/jans-auth-server/client/src/test/java/io/jans/as/client/client/Asserter.java +++ b/jans-auth-server/client/src/test/java/io/jans/as/client/client/Asserter.java @@ -9,19 +9,19 @@ import io.jans.as.client.RegisterResponse; import io.jans.as.model.register.RegisterRequestParam; +import java.util.Arrays; + import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; /** * @author Yuriy Zabrovarnyy * @author Javier Rojas Blum - * @version February 11, 2022 + * @version April 6, 2022 */ - public class Asserter { private Asserter() { - } public static void assertRegisterResponseClaimsNotNull(RegisterResponse response, RegisterRequestParam... claimsToVerify) { @@ -29,8 +29,16 @@ public static void assertRegisterResponseClaimsNotNull(RegisterResponse response return; } for (RegisterRequestParam claim : claimsToVerify) { - assertNotNull(response.getClaims().get(claim.toString()), "Claim " + claim.toString() + " is null in response claims - code" + response.getEntity()); + assertNotNull(response.getClaims().get(claim.toString()), "Claim " + claim + " is null in response claims - code" + response.getEntity()); + } + } + + public static void assertRegisterResponseClaimsNotNull(RegisterResponse response, String... claimsToVerify) { + if (response == null || claimsToVerify == null) { + return; } + Arrays.stream(claimsToVerify).forEach( + claim -> assertNotNull(response.getClaims().get(claim), "Claim " + claim + " is null in response claims - code" + response.getEntity())); } public static void assertRegisterResponseClaimsAreContained(RegisterResponse response, RegisterRequestParam... claimsToVerify) { @@ -38,7 +46,7 @@ public static void assertRegisterResponseClaimsAreContained(RegisterResponse res return; } for (RegisterRequestParam claim : claimsToVerify) { - assertTrue(response.getClaims().containsKey(claim.toString()), "Claim " + claim.toString() + " is not contained in response claims - code" + response.getEntity()); + assertTrue(response.getClaims().containsKey(claim.toString()), "Claim " + claim + " is not contained in response claims - code" + response.getEntity()); } } } diff --git a/jans-auth-server/client/src/test/java/io/jans/as/client/client/assertbuilders/ClientInfoResponseAssertBuilder.java b/jans-auth-server/client/src/test/java/io/jans/as/client/client/assertbuilders/ClientInfoResponseAssertBuilder.java new file mode 100644 index 00000000000..68ba9897b8f --- /dev/null +++ b/jans-auth-server/client/src/test/java/io/jans/as/client/client/assertbuilders/ClientInfoResponseAssertBuilder.java @@ -0,0 +1,81 @@ +/* + * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.as.client.client.assertbuilders; + +import io.jans.as.client.ClientInfoResponse; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + +/** + * @author Javier Rojas Blum + * @version April 6, 2022 + */ +public class ClientInfoResponseAssertBuilder extends BaseAssertBuilder { + + private final ClientInfoResponse response; + private int status; + private boolean notNullClientInfoClaims; + private String[] claimsPresence; + + public ClientInfoResponseAssertBuilder(ClientInfoResponse response) { + this.response = response; + this.status = 200; + this.notNullClientInfoClaims = false; + } + + public ClientInfoResponseAssertBuilder status(int status) { + this.status = status; + return this; + } + + public ClientInfoResponseAssertBuilder notNullClientInfoClaims() { + this.notNullClientInfoClaims = true; + return this; + } + + public ClientInfoResponseAssertBuilder claimsPresence(String... claimsPresence) { + if (this.claimsPresence != null) { + List listClaims = new ArrayList<>(); + listClaims.addAll(Arrays.asList(this.claimsPresence)); + listClaims.addAll(Arrays.asList(claimsPresence)); + this.claimsPresence = listClaims.toArray(new String[0]); + } else { + this.claimsPresence = claimsPresence; + } + return this; + } + + @Override + public void check() { + assertNotNull(response, "ClientInfoResponse is null"); + + if (status == 200) { + assertEquals(response.getStatus(), status, "Unexpected response code: " + response.getEntity()); + + if (notNullClientInfoClaims) { + assertNotNull(response.getClaim("name"), "Unexpected result: displayName not found"); + assertNotNull(response.getClaim("inum"), "Unexpected result: inum not found"); + assertNotNull(response.getClaim("jansAppType"), "Unexpected result: jansAppTyp not found"); + assertNotNull(response.getClaim("jansIdTknSignedRespAlg"), "Unexpected result: jansIdTknSignedRespAlg not found"); + assertNotNull(response.getClaim("jansRedirectURI"), "Unexpected result: jansRedirectURI not found"); + assertNotNull(response.getClaim("jansScope"), "Unexpected result: jansScope not found"); + } + } + + if (claimsPresence != null) { + for (String claim : claimsPresence) { + assertNotNull(claim, "Claim name is null"); + assertNotNull(response.getClaim(claim), "ClientInfo Claim " + claim + " is not found"); + } + } + } +} diff --git a/jans-auth-server/client/src/test/java/io/jans/as/client/json/JsonApplierTest.java b/jans-auth-server/client/src/test/java/io/jans/as/client/json/JsonApplierTest.java index 730db336620..99e51fa754e 100644 --- a/jans-auth-server/client/src/test/java/io/jans/as/client/json/JsonApplierTest.java +++ b/jans-auth-server/client/src/test/java/io/jans/as/client/json/JsonApplierTest.java @@ -20,6 +20,7 @@ /** * @author Yuriy Zabrovarnyy + * @version April 25, 2022 */ public class JsonApplierTest { diff --git a/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/AuthnScriptAliasesTest.java b/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/AuthnScriptAliasesTest.java index d64fa0a10b3..70a7886dd63 100644 --- a/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/AuthnScriptAliasesTest.java +++ b/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/AuthnScriptAliasesTest.java @@ -67,10 +67,10 @@ public void acrAliasTest( @Parameters({"userId", "userSecret", "redirectUris", "redirectUri", "sectorIdentifierUri"}) @Test - public void acrAliasAuthorizedAcsValuesTest( + public void acrAliasAuthorizedAcrValuesTest( final String userId, final String userSecret, final String redirectUris, final String redirectUri, final String sectorIdentifierUri) { - showTitle("acrAliasAuthorizedAcsValuesTest"); + showTitle("acrAliasAuthorizedAcrValuesTest"); List responseTypes = Arrays.asList(ResponseType.CODE, ResponseType.ID_TOKEN); diff --git a/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/ClientLanguageMetadataTest.java b/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/ClientLanguageMetadataTest.java new file mode 100644 index 00000000000..a36929d5e30 --- /dev/null +++ b/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/ClientLanguageMetadataTest.java @@ -0,0 +1,342 @@ +/* + * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.as.client.ws.rs; + +import io.jans.as.client.*; +import io.jans.as.client.client.AssertBuilder; +import io.jans.as.client.model.authorize.Claim; +import io.jans.as.client.model.authorize.ClaimValue; +import io.jans.as.client.model.authorize.JwtAuthorizationRequest; +import io.jans.as.model.common.AuthenticationMethod; +import io.jans.as.model.common.GrantType; +import io.jans.as.model.common.ResponseType; +import io.jans.as.model.common.SubjectType; +import io.jans.as.model.crypto.AuthCryptoProvider; +import io.jans.as.model.crypto.signature.SignatureAlgorithm; +import io.jans.as.model.jwt.JwtClaimName; +import io.jans.as.model.register.ApplicationType; +import io.jans.as.model.register.RegisterRequestParam; +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.Locale; +import java.util.UUID; + +import static io.jans.as.client.client.Asserter.assertRegisterResponseClaimsNotNull; +import static io.jans.as.model.register.RegisterRequestParam.*; + +/** + * @author Javier Rojas Blum + * @version March 23, 2022 + */ +public class ClientLanguageMetadataTest extends BaseTest { + + final List SCOPE = Arrays.asList("openid", "profile", "address", "email", "phone", "user_name", "clientinfo"); + final String[] CLIENT_INFO_NAME_CLAIMS = new String[]{"name", "name#en-NZ", "name#en-CA", "name#en-GB", + "name#es", "name#es-BO", "name#ja-Hani-JP", "name#ja-Jpan-JP", "name#ja-Kana-JP", + "name#fr", "name#fr-FR", "name#fr-CA"}; + final String[] USER_INFO_CLIENT_NAME_CLAIMS = new String[]{ + CLIENT_NAME.getName(), "client_name#en-NZ", "client_name#en-CA", "client_name#en-GB", + "client_name#ja-Hani-JP", "client_name#ja-Jpan-JP", "client_name#ja-Kana-JP", + "client_name#es", "client_name#es-BO", "client_name#fr", "client_name#fr-FR", "client_name#fr-CA"}; + final String[] USER_INFO_LOGO_URI_CLAIMS = new String[]{ + LOGO_URI.getName(), "logo_uri#en-NZ", "logo_uri#en-CA", "logo_uri#en-GB", + "logo_uri#ja-Hani-JP", "logo_uri#ja-Jpan-JP", "logo_uri#ja-Kana-JP", + "logo_uri#es", "logo_uri#es-BO", "logo_uri#fr", "logo_uri#fr-FR", "logo_uri#fr-CA"}; + final String[] USER_INFO_CLIENT_URI_CLAIMS = new String[]{ + CLIENT_URI.getName(), "client_uri#en-NZ", "client_uri#en-CA", "client_uri#en-GB", + "client_uri#ja-Hani-JP", "client_uri#ja-Jpan-JP", "client_uri#ja-Kana-JP", + "client_uri#es", "client_uri#es-BO", "client_uri#fr", "client_uri#fr-FR", "client_uri#fr-CA"}; + final String[] USER_INFO_POLICY_URI_CLAIMS = new String[]{ + POLICY_URI.getName(), "policy_uri#en-NZ", "policy_uri#en-CA", "policy_uri#en-GB", + "policy_uri#ja-Hani-JP", "policy_uri#ja-Jpan-JP", "policy_uri#ja-Kana-JP", + "policy_uri#es", "policy_uri#es-BO", "policy_uri#fr", "policy_uri#fr-FR", "policy_uri#fr-CA"}; + final String[] USER_INFO_TOS_URI_CLAIMS = new String[]{ + TOS_URI.getName(), "tos_uri#en-NZ", "tos_uri#en-CA", "tos_uri#en-GB", + "tos_uri#ja-Hani-JP", "tos_uri#ja-Jpan-JP", "tos_uri#ja-Kana-JP", + "tos_uri#es", "tos_uri#es-BO", "tos_uri#fr", "tos_uri#fr-FR", "tos_uri#fr-CA" + }; + + @Parameters({"userId", "userSecret", "redirectUris", "redirectUri", "sectorIdentifierUri"}) + @Test + public void authorizationCodeFlow( + final String userId, final String userSecret, final String redirectUris, final String redirectUri, + final String sectorIdentifierUri) throws Exception { + showTitle("authorizationCodeFlow"); + + List responseTypes = Arrays.asList( + ResponseType.CODE, + ResponseType.TOKEN, + ResponseType.ID_TOKEN); + + // 1. Register client + RegisterResponse registerResponse = getRegisterResponse(redirectUris, sectorIdentifierUri, responseTypes, SCOPE, null); + + String clientId = registerResponse.getClientId(); + String clientSecret = registerResponse.getClientSecret(); + String registrationClientUri = registerResponse.getRegistrationClientUri(); + String registrationAccessToken = registerResponse.getRegistrationAccessToken(); + + // 2. Client Read + requestClientRead(registrationClientUri, registrationAccessToken); + + // 3. Request authorization and receive the authorization code. + String nonce = UUID.randomUUID().toString(); + String state = UUID.randomUUID().toString(); + + AuthorizationRequest authorizationRequest = new AuthorizationRequest(responseTypes, clientId, SCOPE, redirectUri, nonce); + authorizationRequest.setState(state); + + AuthorizationResponse authorizationResponse = authenticateResourceOwnerAndGrantAccess( + authorizationEndpoint, authorizationRequest, userId, userSecret); + + AssertBuilder.authorizationResponse(authorizationResponse) + .responseTypes(responseTypes) + .check(); + + String authorizationCode = authorizationResponse.getCode(); + String idToken = authorizationResponse.getIdToken(); + + // 4. Request access token using the authorization code. + TokenRequest tokenRequest = new TokenRequest(GrantType.AUTHORIZATION_CODE); + tokenRequest.setCode(authorizationCode); + tokenRequest.setRedirectUri(redirectUri); + tokenRequest.setAuthUsername(clientId); + tokenRequest.setAuthPassword(clientSecret); + tokenRequest.setAuthenticationMethod(AuthenticationMethod.CLIENT_SECRET_BASIC); + + TokenClient tokenClient = newTokenClient(tokenRequest); + tokenClient.setRequest(tokenRequest); + TokenResponse tokenResponse = tokenClient.exec(); + + showClient(tokenClient); + AssertBuilder.tokenResponse(tokenResponse) + .notNullRefreshToken() + .check(); + + // 5. Validate id_token + AssertBuilder.jwtParse(idToken) + .validateSignatureRSAClientEngine(jwksUri, SignatureAlgorithm.RS256) + .claimsPresence(JwtClaimName.CODE_HASH) + .notNullAuthenticationTime() + .notNullOxOpenIDConnectVersion() + .notNullAuthenticationContextClassReference() + .notNullAuthenticationMethodReferences() + .check(); + + String accessToken = tokenResponse.getAccessToken(); + + // 6. Request user info + UserInfoClient userInfoClient = new UserInfoClient(userInfoEndpoint); + userInfoClient.setExecutor(clientEngine(true)); + UserInfoResponse userInfoResponse = userInfoClient.execUserInfo(accessToken); + + showClient(userInfoClient); + AssertBuilder.userInfoResponse(userInfoResponse) + .notNullClaimsPersonalData() + .claimsPresence(JwtClaimName.EMAIL, JwtClaimName.BIRTHDATE, JwtClaimName.GENDER, JwtClaimName.MIDDLE_NAME) + .claimsPresence(JwtClaimName.NICKNAME, JwtClaimName.PREFERRED_USERNAME, JwtClaimName.PROFILE) + .claimsPresence(JwtClaimName.WEBSITE, JwtClaimName.EMAIL_VERIFIED, JwtClaimName.PHONE_NUMBER) + .claimsPresence(JwtClaimName.PHONE_NUMBER_VERIFIED, JwtClaimName.ADDRESS, JwtClaimName.USER_NAME) + .claimsNoPresence("org_name", "work_phone") + .check(); + + // 7. Request client info + ClientInfoClient clientInfoClient = new ClientInfoClient(clientInfoEndpoint); + ClientInfoResponse clientInfoResponse = clientInfoClient.execClientInfo(accessToken); + + showClient(clientInfoClient); + AssertBuilder.clientInfoResponse(clientInfoResponse) + .notNullClientInfoClaims() + .claimsPresence(CLIENT_INFO_NAME_CLAIMS) + .check(); + } + + @Parameters({"userId", "userSecret", "redirectUri", "redirectUris", "clientJwksUri", + "RS256_keyId", "dnName", "keyStoreFile", "keyStoreSecret", "sectorIdentifierUri"}) + @Test + public void requestParameterMethodRS256( + final String userId, final String userSecret, final String redirectUri, final String redirectUris, + final String clientJwksUri, final String keyId, final String dnName, final String keyStoreFile, + final String keyStoreSecret, final String sectorIdentifierUri) throws Exception { + showTitle("requestParameterMethodRS256"); + + List responseTypes = Arrays.asList( + ResponseType.CODE, + ResponseType.TOKEN, + ResponseType.ID_TOKEN); + + // 1. Dynamic Client Registration + RegisterResponse registerResponse = getRegisterResponse(redirectUris, sectorIdentifierUri, responseTypes, SCOPE, clientJwksUri); + + String clientId = registerResponse.getClientId(); + String registrationClientUri = registerResponse.getRegistrationClientUri(); + String registrationAccessToken = registerResponse.getRegistrationAccessToken(); + + // 2. Client Read + requestClientRead(registrationClientUri, registrationAccessToken); + + // 3. Request authorization + AuthCryptoProvider cryptoProvider = new AuthCryptoProvider(keyStoreFile, keyStoreSecret, dnName); + + String nonce = UUID.randomUUID().toString(); + String state = UUID.randomUUID().toString(); + + AuthorizationRequest authorizationRequest = new AuthorizationRequest(responseTypes, clientId, SCOPE, redirectUri, nonce); + authorizationRequest.setState(state); + + JwtAuthorizationRequest jwtAuthorizationRequest = new JwtAuthorizationRequest(authorizationRequest, SignatureAlgorithm.RS256, cryptoProvider); + jwtAuthorizationRequest.setKeyId(keyId); + jwtAuthorizationRequest.addUserInfoClaim(new Claim(JwtClaimName.NAME, ClaimValue.createNull())); + jwtAuthorizationRequest.addUserInfoClaim(new Claim(JwtClaimName.NICKNAME, ClaimValue.createEssential(false))); + jwtAuthorizationRequest.addUserInfoClaim(new Claim(JwtClaimName.EMAIL, ClaimValue.createNull())); + jwtAuthorizationRequest.addUserInfoClaim(new Claim(JwtClaimName.EMAIL_VERIFIED, ClaimValue.createNull())); + jwtAuthorizationRequest.addUserInfoClaim(new Claim(JwtClaimName.PICTURE, ClaimValue.createEssential(false))); + jwtAuthorizationRequest.addIdTokenClaim(new Claim(JwtClaimName.AUTHENTICATION_TIME, ClaimValue.createNull())); + jwtAuthorizationRequest.addIdTokenClaim(new Claim(JwtClaimName.AUTHENTICATION_CONTEXT_CLASS_REFERENCE, ClaimValue.createValueList(new String[]{"basic"}))); + jwtAuthorizationRequest.getIdTokenMember().setMaxAge(86400); + String authJwt = jwtAuthorizationRequest.getEncodedJwt(); + authorizationRequest.setRequest(authJwt); + + AuthorizationResponse authorizationResponse = authenticateResourceOwnerAndGrantAccess( + authorizationEndpoint, authorizationRequest, userId, userSecret); + + AssertBuilder.authorizationResponse(authorizationResponse) + .responseTypes(responseTypes) + .check(); + + String accessToken = authorizationResponse.getAccessToken(); + + // 4. Request user info + UserInfoClient userInfoClient = new UserInfoClient(userInfoEndpoint); + UserInfoResponse response3 = userInfoClient.execUserInfo(accessToken); + + showClient(userInfoClient); + AssertBuilder.userInfoResponse(response3) + .notNullClaimsPersonalData() + .claimsPresence(JwtClaimName.EMAIL) + .check(); + + // 5. Request client info + ClientInfoClient clientInfoClient = new ClientInfoClient(clientInfoEndpoint); + ClientInfoResponse clientInfoResponse = clientInfoClient.execClientInfo(accessToken); + + showClient(clientInfoClient); + AssertBuilder.clientInfoResponse(clientInfoResponse) + .notNullClientInfoClaims() + .claimsPresence(CLIENT_INFO_NAME_CLAIMS) + .check(); + } + + private RegisterResponse getRegisterResponse(String redirectUris, String sectorIdentifierUri, List responseTypes, List scopes, String clientJwksUri) { + RegisterRequest registerRequest = new RegisterRequest(ApplicationType.WEB, "jans test app", + StringUtils.spaceSeparatedToList(redirectUris)); + registerRequest.setResponseTypes(responseTypes); + registerRequest.setScope(scopes); + registerRequest.setSubjectType(SubjectType.PAIRWISE); + registerRequest.setSectorIdentifierUri(sectorIdentifierUri); + if (clientJwksUri != null) { + registerRequest.setJwksUri(clientJwksUri); + registerRequest.setRequestObjectSigningAlg(SignatureAlgorithm.RS256); + } + + registerRequest.setClientName("Nombre del cliente", new Locale("es")); + registerRequest.setClientName("Nombre del caserito", new Locale("es", "BO")); + registerRequest.setClientName("Client name", Locale.UK); + registerRequest.setClientName("Client name", Locale.CANADA); + registerRequest.setClientName("Client name", Locale.forLanguageTag("en-NZ")); + registerRequest.setClientName("Nom du client", Locale.FRENCH); + registerRequest.setClientName("Nom du client", Locale.CANADA_FRENCH); + registerRequest.setClientName("Nom du client", Locale.FRANCE); + registerRequest.setClientName("クライアント名", Locale.forLanguageTag("ja-Jpan-JP")); + registerRequest.setClientName("カナ姓※", Locale.forLanguageTag("ja-Kana-JP")); + registerRequest.setClientName("漢字姓※", Locale.forLanguageTag("ja-Hani-JP")); + + registerRequest.setLogoUri("https://gluu.org/wp-content/uploads/2020/12/logo.png"); + registerRequest.setLogoUri("https://gluu.org/wp-content/uploads/2020/12/logo.png?locale=es", new Locale("es")); + registerRequest.setLogoUri("https://gluu.org/wp-content/uploads/2020/12/logo.png?locale=es-BO", new Locale("es", "BO")); + registerRequest.setLogoUri("https://gluu.org/wp-content/uploads/2020/12/logo.png?locale=en-UK", Locale.UK); + registerRequest.setLogoUri("https://gluu.org/wp-content/uploads/2020/12/logo.png?locale=en-CA", Locale.CANADA); + registerRequest.setLogoUri("https://gluu.org/wp-content/uploads/2020/12/logo.png?locale=en-NZ", Locale.forLanguageTag("en-NZ")); + registerRequest.setLogoUri("https://gluu.org/wp-content/uploads/2020/12/logo.png?locale=fr", Locale.FRENCH); + registerRequest.setLogoUri("https://gluu.org/wp-content/uploads/2020/12/logo.png?locale=fr-CA", Locale.CANADA_FRENCH); + registerRequest.setLogoUri("https://gluu.org/wp-content/uploads/2020/12/logo.png?locale=fr-FR", Locale.FRANCE); + registerRequest.setLogoUri("https://gluu.org/wp-content/uploads/2020/12/logo.png?locale=ja-Jpan-JP", Locale.forLanguageTag("ja-Jpan-JP")); + registerRequest.setLogoUri("https://gluu.org/wp-content/uploads/2020/12/logo.png?locale=ja-Kana-JP", Locale.forLanguageTag("ja-Kana-JP")); + registerRequest.setLogoUri("https://gluu.org/wp-content/uploads/2020/12/logo.png?locale=ja-Hani-JP", Locale.forLanguageTag("ja-Hani-JP")); + + registerRequest.setClientUri("https://client-home-page/index.htm"); + registerRequest.setClientUri("https://client-home-page/index.htm?locale=es", new Locale("es")); + registerRequest.setClientUri("https://client-home-page/index.htm?locale=es-BO", new Locale("es", "BO")); + registerRequest.setClientUri("https://client-home-page/index.htm?locale=en-UK", Locale.UK); + registerRequest.setClientUri("https://client-home-page/index.htm?locale=en-CA", Locale.CANADA); + registerRequest.setClientUri("https://client-home-page/index.htm?locale=en-NZ", Locale.forLanguageTag("en-NZ")); + registerRequest.setClientUri("https://client-home-page/index.htm?locale=fr", Locale.FRENCH); + registerRequest.setClientUri("https://client-home-page/index.htm?locale=fr-CA", Locale.CANADA_FRENCH); + registerRequest.setClientUri("https://client-home-page/index.htm?locale=fr-FR", Locale.FRANCE); + registerRequest.setClientUri("https://client-home-page/index.htm?locale=ja-Jpan-JP", Locale.forLanguageTag("ja-Jpan-JP")); + registerRequest.setClientUri("https://client-home-page/index.htm?locale=ja-Kana-JP", Locale.forLanguageTag("ja-Kana-JP")); + registerRequest.setClientUri("https://client-home-page/index.htm?locale=ja-Hani-JP", Locale.forLanguageTag("ja-Hani-JP")); + + registerRequest.setPolicyUri("https://client-home-page/policy.htm"); + registerRequest.setPolicyUri("https://client-home-page/policy.htm?locale=es", new Locale("es")); + registerRequest.setPolicyUri("https://client-home-page/policy.htm?locale=es-BO", new Locale("es", "BO")); + registerRequest.setPolicyUri("https://client-home-page/policy.htm?locale=en-UK", Locale.UK); + registerRequest.setPolicyUri("https://client-home-page/policy.htm?locale=en-CA", Locale.CANADA); + registerRequest.setPolicyUri("https://client-home-page/policy.htm?locale=en-NZ", Locale.forLanguageTag("en-NZ")); + registerRequest.setPolicyUri("https://client-home-page/policy.htm?locale=fr", Locale.FRENCH); + registerRequest.setPolicyUri("https://client-home-page/policy.htm?locale=fr-CA", Locale.CANADA_FRENCH); + registerRequest.setPolicyUri("https://client-home-page/policy.htm?locale=fr-FR", Locale.FRANCE); + registerRequest.setPolicyUri("https://client-home-page/policy.htm?locale=ja-Jpan-JP", Locale.forLanguageTag("ja-Jpan-JP")); + registerRequest.setPolicyUri("https://client-home-page/policy.htm?locale=ja-Kana-JP", Locale.forLanguageTag("ja-Kana-JP")); + registerRequest.setPolicyUri("https://client-home-page/policy.htm?locale=ja-Hani-JP", Locale.forLanguageTag("ja-Hani-JP")); + + registerRequest.setTosUri("https://client-home-page/tos.htm"); + registerRequest.setTosUri("https://client-home-page/tos.htm?locale=es", new Locale("es")); + registerRequest.setTosUri("https://client-home-page/tos.htm?locale=es-BO", new Locale("es", "BO")); + registerRequest.setTosUri("https://client-home-page/tos.htm?locale=en-UK", Locale.UK); + registerRequest.setTosUri("https://client-home-page/tos.htm?locale=en-CA", Locale.CANADA); + registerRequest.setTosUri("https://client-home-page/tos.htm?locale=en-NZ", Locale.forLanguageTag("en-NZ")); + registerRequest.setTosUri("https://client-home-page/tos.htm?locale=fr", Locale.FRENCH); + registerRequest.setTosUri("https://client-home-page/tos.htm?locale=fr-CA", Locale.CANADA_FRENCH); + registerRequest.setTosUri("https://client-home-page/tos.htm?locale=fr-FR", Locale.FRANCE); + registerRequest.setTosUri("https://client-home-page/tos.htm?locale=ja-Jpan-JP", Locale.forLanguageTag("ja-Jpan-JP")); + registerRequest.setTosUri("https://client-home-page/tos.htm?locale=ja-Kana-JP", Locale.forLanguageTag("ja-Kana-JP")); + registerRequest.setTosUri("https://client-home-page/tos.htm?locale=ja-Hani-JP", Locale.forLanguageTag("ja-Hani-JP")); + + RegisterClient registerClient = newRegisterClient(registerRequest); + RegisterResponse registerResponse = registerClient.exec(); + + showClient(registerClient); + AssertBuilder.registerResponse(registerResponse).created().check(); + return registerResponse; + } + + private void requestClientRead(String registrationClientUri, String registrationAccessToken) { + RegisterRequest registerRequest = new RegisterRequest(registrationAccessToken); + + RegisterClient registerClient = new RegisterClient(registrationClientUri); + registerClient.setRequest(registerRequest); + RegisterResponse response = registerClient.exec(); + + showClient(registerClient); + AssertBuilder.registerResponse(response).ok() + .notNullRegistrationClientUri() + .check(); + assertRegisterResponseClaimsNotNull(response, APPLICATION_TYPE, POLICY_URI, SECTOR_IDENTIFIER_URI, SUBJECT_TYPE, + ID_TOKEN_SIGNED_RESPONSE_ALG, CLIENT_NAME, LOGO_URI, RegisterRequestParam.SCOPE); + + assertRegisterResponseClaimsNotNull(response, USER_INFO_CLIENT_NAME_CLAIMS); + assertRegisterResponseClaimsNotNull(response, USER_INFO_LOGO_URI_CLAIMS); + assertRegisterResponseClaimsNotNull(response, USER_INFO_CLIENT_URI_CLAIMS); + assertRegisterResponseClaimsNotNull(response, USER_INFO_POLICY_URI_CLAIMS); + assertRegisterResponseClaimsNotNull(response, USER_INFO_TOS_URI_CLAIMS); + } +} diff --git a/jans-auth-server/client/src/test/resources/testng.xml b/jans-auth-server/client/src/test/resources/testng.xml index 852733a4972..4abea285fe2 100644 --- a/jans-auth-server/client/src/test/resources/testng.xml +++ b/jans-auth-server/client/src/test/resources/testng.xml @@ -148,6 +148,12 @@ + + + + + + diff --git a/jans-auth-server/common/src/main/java/io/jans/as/common/model/registration/Client.java b/jans-auth-server/common/src/main/java/io/jans/as/common/model/registration/Client.java index 5a2dacec539..0c32da11b9e 100644 --- a/jans-auth-server/common/src/main/java/io/jans/as/common/model/registration/Client.java +++ b/jans-auth-server/common/src/main/java/io/jans/as/common/model/registration/Client.java @@ -8,33 +8,25 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; -import io.jans.as.model.common.AuthenticationMethod; -import io.jans.as.model.common.BackchannelTokenDeliveryMode; -import io.jans.as.model.common.GrantType; -import io.jans.as.model.common.ResponseType; -import io.jans.as.model.common.SubjectType; +import io.jans.as.model.common.*; import io.jans.as.model.crypto.signature.AsymmetricSignatureAlgorithm; import io.jans.as.model.register.ApplicationType; import io.jans.as.persistence.model.ClientAttributes; -import io.jans.orm.annotation.AttributeName; -import io.jans.orm.annotation.AttributesList; -import io.jans.orm.annotation.CustomObjectClass; -import io.jans.orm.annotation.DataEntry; -import io.jans.orm.annotation.Expiration; -import io.jans.orm.annotation.JsonObject; -import io.jans.orm.annotation.ObjectClass; +import io.jans.orm.annotation.*; import io.jans.orm.model.base.CustomAttribute; import io.jans.orm.model.base.DeletableEntity; +import io.jans.orm.model.base.LocalizedString; import org.apache.commons.lang.StringUtils; import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Locale; /** * @author Javier Rojas Blum - * @version July 30, 2021 + * @version April 25, 2022 */ @DataEntry(sortBy = {"displayName"}) @ObjectClass(value = "jansClnt") @@ -83,23 +75,28 @@ public class Client extends DeletableEntity implements Serializable { @AttributeName(name = "jansContact") private String[] contacts; - @AttributeName(name = "displayName") - private String clientName; - @AttributeName(name = "tknBndCnf") private String idTokenTokenBindingCnf; + @AttributeName(name = "displayName") + @LanguageTag + private LocalizedString clientName = new LocalizedString(); + @AttributeName(name = "jansLogoURI") - private String logoUri; + @LanguageTag + private LocalizedString logoUri = new LocalizedString(); @AttributeName(name = "jansClntURI") - private String clientUri; + @LanguageTag + private LocalizedString clientUri = new LocalizedString(); @AttributeName(name = "jansPolicyURI") - private String policyUri; + @LanguageTag + private LocalizedString policyUri = new LocalizedString(); @AttributeName(name = "jansTosURI") - private String tosUri; + @LanguageTag + private LocalizedString tosUri = new LocalizedString(); @AttributeName(name = "jansJwksURI") private String jwksUri; @@ -556,12 +553,24 @@ public void setContacts(String[] contacts) { this.contacts = contacts; } + public String getIdTokenTokenBindingCnf() { + return idTokenTokenBindingCnf; + } + + public void setIdTokenTokenBindingCnf(String idTokenTokenBindingCnf) { + this.idTokenTokenBindingCnf = idTokenTokenBindingCnf; + } + + public boolean isTokenBindingSupported() { + return StringUtils.isNotBlank(idTokenTokenBindingCnf); + } + /** - * Returns the name of the Client to be presented to the user. + * Returns the name of the Client to be presented to the user represented in a language and a script. * * @return The name of the Client to be presented to the user. */ - public String getClientName() { + public LocalizedString getClientName() { return clientName; } @@ -571,95 +580,135 @@ public String getClientName() { * @param clientName The name of the Client to be presented to the user. */ public void setClientName(String clientName) { - this.clientName = clientName; - } - - public String getIdTokenTokenBindingCnf() { - return idTokenTokenBindingCnf; - } - - public void setIdTokenTokenBindingCnf(String idTokenTokenBindingCnf) { - this.idTokenTokenBindingCnf = idTokenTokenBindingCnf; + this.clientName.setValue(clientName); } - public boolean isTokenBindingSupported() { - return StringUtils.isNotBlank(idTokenTokenBindingCnf); + /** + * Sets the name of the Client to be presented to the user represented in a language and a script. + * + * @param clientName The name of the Client to be presented to the user. + * @param locale The locale + */ + public void setClientName(String clientName, Locale locale) { + this.clientName.setValue(clientName, locale); } /** - * Returns an URL that references a logo for the Client application. + * Returns a URL that references a logo for the Client application represented in a language and a script. * * @return The URL of a logo image for the Client where it can be retrieved. */ - public String getLogoUri() { + public LocalizedString getLogoUri() { return logoUri; } /** - * Sets an URL that references a logo for the Client application. + * Sets a URL that references a logo for the Client application. * * @param logoUri The URL of a logo image for the Client where it can be retrieved. */ public void setLogoUri(String logoUri) { - this.logoUri = logoUri; + this.logoUri.setValue(logoUri); + } + + /** + * Sets a URL that references a logo for the Client application represented in a language and script. + * + * @param logoUri The URL of a logo image for the Client where it can be retrieved. + * @param locale The locale + */ + public void setLogoUri(String logoUri, Locale locale) { + this.logoUri.setValue(logoUri, locale); } /** - * Returns an URL of the home page of the Client. + * Returns a URL of the home page of the Client represented in a language and script * * @return The URL of the home page of the Client. */ - public String getClientUri() { + public LocalizedString getClientUri() { return clientUri; } /** - * Sets an URL of the home page of the Client. + * Sets a URL of the home page of the Client. * * @param clientUri The URL of the home page of the Client. */ public void setClientUri(String clientUri) { - this.clientUri = clientUri; + this.clientUri.setValue(clientUri); } /** - * Returns an that the Relying Party Client provides to the End-User to read about the how the profile data will - * be used. + * Sets a URL of the home page of the Client represented in a language and script. * - * @return An URL location about the how the profile data will be used. + * @param clientUri The URL of the home page of the Client. + * @param locale The locale */ - public String getPolicyUri() { + public void setClientUri(String clientUri, Locale locale) { + this.clientUri.setValue(clientUri, locale); + } + + /** + * Returns a URL that the Relying Party Client provides to the End-User to read about how the profile data will + * be used represented in a language and script. + * + * @return A URL location about how the profile data will be used. + */ + public LocalizedString getPolicyUri() { return policyUri; } /** - * Sets an that the Relying Party Client provides to the End-User to read about the how the profile data will + * Sets a URL that the Relying Party Client provides to the End-User to read about how the profile data will * be used. * - * @param policyUri An URL location about the how the profile data will be used. + * @param policyUri A URL location about how the profile data will be used. */ public void setPolicyUri(String policyUri) { - this.policyUri = policyUri; + this.policyUri.setValue(policyUri); } /** - * Returns an URL that the Relying Party Client provides to the End-User to read about the Relying Party's terms - * of service. + * Sets a URL that the Relying Party Client provides to the End-User to read about how the profile data will + * be used represented in a language and script. + * + * @param policyUri A URL location about how the profile data will be used. + * @param locale The locale + */ + public void setPolicyUri(String policyUri, Locale locale) { + this.policyUri.setValue(policyUri, locale); + } + + /** + * Returns a URL that the Relying Party Client provides to the End-User to read about the Relying Party's terms + * of service represented in a language and script. * * @return The terms of service URL. */ - public String getTosUri() { + public LocalizedString getTosUri() { return tosUri; } /** - * Sets an URL that the Relying Party Client provides to the End-User to read about the Relying Party's terms of + * Sets a URL that the Relying Party Client provides to the End-User to read about the Relying Party's terms of * service. * * @param tosUri The terms of service URL. */ public void setTosUri(String tosUri) { - this.tosUri = tosUri; + this.tosUri.setValue(tosUri); + } + + /** + * Sets a URL that the Relying Party Client provides to the End-User to read about the Relying Party's terms of + * service represented in a language and script. + * + * @param tosUri The terms of service URL. + * @param locale The Locale + */ + public void setTosUri(String tosUri, Locale locale) { + this.tosUri.setValue(tosUri, locale); } /** @@ -1235,11 +1284,11 @@ public void setBackchannelUserCodeParameter(Boolean backchannelUserCodeParameter } public String getDisplayName() { - return getClientName(); + return getClientName().getValue(); } public void setDisplayName(String displayName) { - this.clientName = displayName; + setClientName(displayName); } public String getDescription() { @@ -1249,5 +1298,4 @@ public String getDescription() { public void setDescription(String description) { this.description = description; } - } \ No newline at end of file diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/json/JsonApplier.java b/jans-auth-server/model/src/main/java/io/jans/as/model/json/JsonApplier.java index 247277e93d0..4389197f298 100644 --- a/jans-auth-server/model/src/main/java/io/jans/as/model/json/JsonApplier.java +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/json/JsonApplier.java @@ -23,6 +23,7 @@ /** * @author Yuriy Zabrovarnyy + * @version April 25, 2022 */ public class JsonApplier { diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/util/Util.java b/jans-auth-server/model/src/main/java/io/jans/as/model/util/Util.java index 42f6c70d10f..fff05e2bd16 100644 --- a/jans-auth-server/model/src/main/java/io/jans/as/model/util/Util.java +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/util/Util.java @@ -15,6 +15,7 @@ import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector; import io.jans.as.model.common.HasParamName; import io.jans.orm.annotation.AttributeEnum; +import io.jans.orm.model.base.LocalizedString; import org.apache.commons.lang.StringUtils; import org.json.JSONArray; import org.json.JSONException; @@ -45,9 +46,8 @@ /** * @author Yuriy Zabrovarnyy * @author Javier Rojas Blum - * @version September 4, 2019 + * @version April 25, 2022 */ - public class Util { private static final Logger LOG = LoggerFactory.getLogger(Util.class); @@ -175,6 +175,14 @@ public static void addToJSONObjectIfNotNullOrEmpty(JSONObject jsonObject, String } } + public static void addToJSONObjectIfNotNull(JSONObject jsonObject, String key, LocalizedString localizedString) throws JSONException { + if (jsonObject != null && localizedString != null && StringUtils.isNotBlank(key)) { + localizedString.getLanguageTags() + .forEach(languageTag -> jsonObject.put(key + (StringUtils.isBlank(languageTag) ? "" : "#" + languageTag), + localizedString.getValue(languageTag))); + } + } + public static String asString(List list) { final StringBuilder sb = new StringBuilder(); if (list != null && !list.isEmpty()) { diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/clientinfo/ws/rs/ClientInfoRestWebServiceImpl.java b/jans-auth-server/server/src/main/java/io/jans/as/server/clientinfo/ws/rs/ClientInfoRestWebServiceImpl.java index 5d3d49df07c..906e6fa6d06 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/clientinfo/ws/rs/ClientInfoRestWebServiceImpl.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/clientinfo/ws/rs/ClientInfoRestWebServiceImpl.java @@ -22,21 +22,21 @@ import io.jans.as.server.service.token.TokenService; import io.jans.as.server.util.ServerUtil; import io.jans.model.GluuAttribute; -import org.json.JSONException; -import org.json.JSONObject; -import org.slf4j.Logger; - +import io.jans.orm.model.base.LocalizedString; import jakarta.inject.Inject; import jakarta.ws.rs.Path; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.SecurityContext; +import org.json.JSONObject; +import org.slf4j.Logger; + import java.util.Set; /** * Provides interface for Client Info REST web services * * @author Javier Rojas Blum - * @version 0.9 March 27, 2015 + * @version April 25, 2022 */ @Path("/") public class ClientInfoRestWebServiceImpl implements ClientInfoRestWebService { @@ -124,12 +124,16 @@ public String getJSonResponse(Client client, Set scopes) { Object attributeValue = clientService.getAttribute(client, ldapName); String claimName = attribute.getClaimName(); - jsonObj.put(claimName, attributeValue); + + if (attributeValue instanceof LocalizedString) { + LocalizedString localizedString = (LocalizedString) attributeValue; + localizedString.addToJSON(jsonObj, claimName); + } else { + jsonObj.put(claimName, attributeValue); + } } } } - } catch (JSONException e) { - log.error(e.getMessage(), e); } catch (Exception e) { log.error(e.getMessage(), e); } diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/RegisterJsonService.java b/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/RegisterJsonService.java index 3598d41237f..b3b6eb5d32b 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/RegisterJsonService.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/RegisterJsonService.java @@ -139,17 +139,18 @@ public JSONObject getJSONObject(Client client) throws JSONException, StringEncry responseJsonObject.put(CLIENT_SECRET_EXPIRES_AT.toString(), client.getClientSecretExpiresAt() != null && client.getClientSecretExpiresAt().getTime() > 0 ? client.getClientSecretExpiresAt().getTime() / 1000 : 0); + Util.addToJSONObjectIfNotNull(responseJsonObject, CLIENT_NAME.toString(), client.getClientName()); + Util.addToJSONObjectIfNotNull(responseJsonObject, LOGO_URI.toString(), client.getLogoUri()); + Util.addToJSONObjectIfNotNull(responseJsonObject, CLIENT_URI.toString(), client.getClientUri()); + Util.addToJSONObjectIfNotNull(responseJsonObject, POLICY_URI.toString(), client.getPolicyUri()); + Util.addToJSONObjectIfNotNull(responseJsonObject, TOS_URI.toString(), client.getTosUri()); + Util.addToJSONObjectIfNotNull(responseJsonObject, REDIRECT_URIS.toString(), client.getRedirectUris()); Util.addToJSONObjectIfNotNull(responseJsonObject, CLAIMS_REDIRECT_URIS.toString(), client.getClaimRedirectUris()); Util.addToJSONObjectIfNotNull(responseJsonObject, RESPONSE_TYPES.toString(), ResponseType.toStringArray(client.getResponseTypes())); Util.addToJSONObjectIfNotNull(responseJsonObject, GRANT_TYPES.toString(), GrantType.toStringArray(client.getGrantTypes())); Util.addToJSONObjectIfNotNull(responseJsonObject, APPLICATION_TYPE.toString(), client.getApplicationType()); Util.addToJSONObjectIfNotNull(responseJsonObject, CONTACTS.toString(), client.getContacts()); - Util.addToJSONObjectIfNotNull(responseJsonObject, CLIENT_NAME.toString(), client.getClientName()); - Util.addToJSONObjectIfNotNull(responseJsonObject, LOGO_URI.toString(), client.getLogoUri()); - Util.addToJSONObjectIfNotNull(responseJsonObject, CLIENT_URI.toString(), client.getClientUri()); - Util.addToJSONObjectIfNotNull(responseJsonObject, POLICY_URI.toString(), client.getPolicyUri()); - Util.addToJSONObjectIfNotNull(responseJsonObject, TOS_URI.toString(), client.getTosUri()); Util.addToJSONObjectIfNotNull(responseJsonObject, JWKS_URI.toString(), client.getJwksUri()); Util.addToJSONObjectIfNotNull(responseJsonObject, SECTOR_IDENTIFIER_URI.toString(), client.getSectorIdentifierUri()); Util.addToJSONObjectIfNotNull(responseJsonObject, SUBJECT_TYPE.toString(), client.getSubjectType()); diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/RegisterService.java b/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/RegisterService.java index fb24872be42..144b1991f04 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/RegisterService.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/RegisterService.java @@ -23,6 +23,11 @@ import io.jans.as.server.service.ScopeService; import io.jans.orm.model.base.CustomAttribute; import io.jans.util.StringHelper; +import jakarta.ejb.Stateless; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; import org.json.JSONArray; @@ -30,16 +35,7 @@ import org.json.JSONObject; import org.slf4j.Logger; -import jakarta.ejb.Stateless; -import jakarta.inject.Inject; -import jakarta.inject.Named; -import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.Response; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import static io.jans.as.model.util.StringUtils.toList; import static org.apache.commons.lang3.BooleanUtils.isTrue; @@ -103,9 +99,6 @@ public void updateClientFromRequestObject(Client client, RegisterRequest request if (requestObject.getApplicationType() != null) { client.setApplicationType(requestObject.getApplicationType()); } - if (StringUtils.isNotBlank(requestObject.getClientName())) { - client.setClientName(requestObject.getClientName()); - } if (StringUtils.isNotBlank(requestObject.getSectorIdentifierUri())) { client.setSectorIdentifierUri(requestObject.getSectorIdentifierUri()); } @@ -153,18 +146,23 @@ public void updateClientFromRequestObject(Client client, RegisterRequest request contacts = new ArrayList<>(new HashSet<>(contacts)); // Remove repeated elements client.setContacts(contacts.toArray(new String[0])); } - if (StringUtils.isNotBlank(requestObject.getLogoUri())) { - client.setLogoUri(requestObject.getLogoUri()); + + for (String key : requestObject.getClientNameLanguageTags()) { + client.setClientName(requestObject.getClientName(key), Locale.forLanguageTag(key)); + } + for (String key : requestObject.getLogoUriLanguageTags()) { + client.setLogoUri(requestObject.getLogoUri(key), Locale.forLanguageTag(key)); } - if (StringUtils.isNotBlank(requestObject.getClientUri())) { - client.setClientUri(requestObject.getClientUri()); + for (String key : requestObject.getClientUriLanguageTags()) { + client.setClientUri(requestObject.getClientUri(key), Locale.forLanguageTag(key)); } - if (StringUtils.isNotBlank(requestObject.getPolicyUri())) { - client.setPolicyUri(requestObject.getPolicyUri()); + for (String key : requestObject.getPolicyUriLanguageTags()) { + client.setPolicyUri(requestObject.getPolicyUri(key), Locale.forLanguageTag(key)); } - if (StringUtils.isNotBlank(requestObject.getTosUri())) { - client.setTosUri(requestObject.getTosUri()); + for (String key : requestObject.getTosUriLanguageTags()) { + client.setTosUri(requestObject.getTosUri(key), Locale.forLanguageTag(key)); } + if (StringUtils.isNotBlank(requestObject.getJwksUri())) { client.setJwksUri(requestObject.getJwksUri()); } diff --git a/jans-orm/annotation/src/main/java/io/jans/orm/annotation/LanguageTag.java b/jans-orm/annotation/src/main/java/io/jans/orm/annotation/LanguageTag.java new file mode 100644 index 00000000000..58ee026010b --- /dev/null +++ b/jans-orm/annotation/src/main/java/io/jans/orm/annotation/LanguageTag.java @@ -0,0 +1,21 @@ +/* + * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.orm.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Javier Rojas Blum + * @version April 6, 2022 + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface LanguageTag { +} diff --git a/jans-orm/core/src/main/java/io/jans/orm/impl/BaseEntryManager.java b/jans-orm/core/src/main/java/io/jans/orm/impl/BaseEntryManager.java index 3d5efa139c9..4e487e7f985 100644 --- a/jans-orm/core/src/main/java/io/jans/orm/impl/BaseEntryManager.java +++ b/jans-orm/core/src/main/java/io/jans/orm/impl/BaseEntryManager.java @@ -8,16 +8,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.jans.orm.PersistenceEntryManager; -import io.jans.orm.annotation.AttributeEnum; -import io.jans.orm.annotation.AttributeName; -import io.jans.orm.annotation.AttributesList; -import io.jans.orm.annotation.CustomObjectClass; -import io.jans.orm.annotation.DN; -import io.jans.orm.annotation.DataEntry; -import io.jans.orm.annotation.Expiration; -import io.jans.orm.annotation.JsonObject; -import io.jans.orm.annotation.ObjectClass; -import io.jans.orm.annotation.SchemaEntry; +import io.jans.orm.annotation.*; import io.jans.orm.exception.EntryPersistenceException; import io.jans.orm.exception.InvalidArgumentException; import io.jans.orm.exception.MappingException; @@ -26,6 +17,7 @@ import io.jans.orm.model.AttributeDataModification; import io.jans.orm.model.AttributeDataModification.AttributeModificationType; import io.jans.orm.model.SearchScope; +import io.jans.orm.model.base.LocalizedString; import io.jans.orm.operation.PersistenceOperationService; import io.jans.orm.reflect.property.Getter; import io.jans.orm.reflect.property.PropertyAnnotation; @@ -41,23 +33,15 @@ import java.io.Serializable; import java.lang.annotation.Annotation; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; -import java.util.Set; +import java.util.stream.Collectors; /** * Abstract Entry Manager * - * @author Yuriy Movchan Date: 10.07.2010 + * @author Yuriy Movchan + * @version April 25, 2022 */ public abstract class BaseEntryManager implements PersistenceEntryManager { @@ -66,7 +50,7 @@ public abstract class BaseEntryManager implements PersistenceEntryManager { private static final Class[] LDAP_ENTRY_TYPE_ANNOTATIONS = { DataEntry.class, SchemaEntry.class, ObjectClass.class }; private static final Class[] LDAP_ENTRY_PROPERTY_ANNOTATIONS = { AttributeName.class, AttributesList.class, - JsonObject.class }; + JsonObject.class, LanguageTag.class }; private static final Class[] LDAP_CUSTOM_OBJECT_CLASS_PROPERTY_ANNOTATION = { CustomObjectClass.class }; private static final Class[] LDAP_DN_PROPERTY_ANNOTATION = { DN.class }; private static final Class[] LDAP_EXPIRATION_PROPERTY_ANNOTATION = { Expiration.class }; @@ -95,7 +79,7 @@ public abstract class BaseEntryManager implements PersistenceEntryManager { protected static final Comparator LINE_LENGHT_COMPARATOR = new LineLenghtComparator(false); protected static final int DEFAULT_PAGINATION_SIZE = 100; - + protected PersistenceOperationService operationService = null; protected PersistenceExtension persistenceExtension = null; @@ -473,7 +457,7 @@ public void remove(String primaryKey) { @Override public void remove(String primaryKey, Class entryClass) { String[] objectClasses = null; - + if (entryClass != null) { // Check entry class checkEntryClass(entryClass, false); @@ -493,7 +477,7 @@ public void removeRecursively(String primaryKey) { @Override public void removeRecursively(String primaryKey, Class entryClass) { String[] objectClasses = null; - + if (entryClass != null) { // Check entry class checkEntryClass(entryClass, false); @@ -612,11 +596,11 @@ protected String[] getAttributes(Map attributesM protected List getAttributesList(T entry, List propertiesAnnotations, boolean isIgnoreAttributesList) { Map attributesMap = getAttributesMap(entry, propertiesAnnotations, isIgnoreAttributesList); - + if (attributesMap == null) { return null; } - + return new ArrayList(attributesMap.keySet()); } @@ -757,7 +741,7 @@ protected boolean isUseEntryForceUpdate(Class entryClass) { if (dataEntry == null) { return false; } - + return ((DataEntry) dataEntry).forceUpdate(); } @@ -788,7 +772,7 @@ protected boolean isConfigurationEntry(Class entryClass) { if (dataEntry == null) { return false; } - + return dataEntry.configurationDefinition(); } @@ -852,7 +836,7 @@ protected String[] getTypeObjectClasses(Class entryClass) { if (StringHelper.isEmpty(((ObjectClass) ldapObjectClass).value())) { return EMPTY_STRING_ARRAY; } - + return new String[] { ((ObjectClass) ldapObjectClass).value() }; } @@ -925,7 +909,7 @@ protected PropertyAnnotation getExpirationProperty(Class entryClass) { if (propertiesAnnotations.size() > 1) { throw new MappingException("Entry should has only one property with annotation Expiration"); } - + return propertiesAnnotations.get(0); } @@ -983,11 +967,39 @@ protected List createEntities(Class entryClass, List filteredAttrs = attributesMap.entrySet().stream() + .filter(x -> x.getKey().toLowerCase().startsWith(finalLdapAttributeName.toLowerCase())) + .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); + + loadLocalizedString(attributesMap, localizedString, filteredAttrs); + + continue; + } + ldapAttributeName = ldapAttributeName.toLowerCase(); AttributeData attributeData = attributesMap.get(ldapAttributeName); @@ -1121,6 +1133,20 @@ public int compare(String o1, String o2) { return results; } + protected void loadLocalizedString(Map attributesMap, LocalizedString localizedString, Map filteredAttrs) { + filteredAttrs.forEach((key, value) -> { + AttributeData data = attributesMap.get(key); + if (data.getValues() != null && data.getValues().length == 1 && data.getValues()[0] instanceof Map) { + Map values = (Map) data.getValues()[0]; + values.forEach((languageTag, val) -> { + if (languageTag instanceof String && val instanceof String) { + localizedString.setValue((String) val, Locale.forLanguageTag((String) languageTag)); + } + }); + } + }); + } + @Override public List createEntities(Class entryClass, Map> entriesAttributes) { checkEntryClass(entryClass, true); @@ -1384,7 +1410,7 @@ private AttributeData getAttributeData(String propertyName, String ldapAttribute private Object[] getAttributeValues(String propertyName, boolean jsonObject, Object propertyValue, boolean multiValued) { Object[] attributeValues = new Object[1]; - + boolean nativeType = getNativeAttributeValue(propertyValue, attributeValues, multiValued); if (nativeType) { // We do conversion in getNativeAttributeValue method already @@ -1462,7 +1488,7 @@ protected Object getNativeDateMultiAttributeValue(Date dateValue) { protected boolean isAttributeMultivalued(Object[] values) { if (values.length > 1) { return true; - + } return false; @@ -1486,16 +1512,27 @@ protected List getAttributesListForPersist(Object entry, for (PropertyAnnotation propertiesAnnotation : propertiesAnnotations) { String propertyName = propertiesAnnotation.getPropertyName(); Annotation ldapAttribute; + Annotation languageTag; // Process properties with AttributeName annotation ldapAttribute = ReflectHelper.getAnnotationByType(propertiesAnnotation.getAnnotations(), AttributeName.class); + languageTag = ReflectHelper.getAnnotationByType(propertiesAnnotation.getAnnotations(), + LanguageTag.class); if (ldapAttribute != null) { - AttributeData attribute = getAttributeDataFromAttribute(entry, ldapAttribute, propertiesAnnotation, - propertyName); - if (attribute != null) { - attributes.add(attribute); - } + if (languageTag != null) { + List listAttributes = getAttributeDataFromLocalizedString( + entry, ldapAttribute, propertyName); + if (listAttributes != null) { + attributes.addAll(listAttributes); + } + } else { + AttributeData attribute = getAttributeDataFromAttribute(entry, ldapAttribute, propertiesAnnotation, + propertyName); + if (attribute != null) { + attributes.add(attribute); + } + } continue; } @@ -1542,6 +1579,40 @@ private AttributeData getAttributeDataFromAttribute(Object entry, Annotation lda return attribute; } + private List getAttributeDataFromLocalizedString( + Object entry, Annotation ldapAttribute, String propertyName) { + + Class entryClass = entry.getClass(); + + Getter getter = getGetter(entryClass, propertyName); + if (getter == null) { + throw new MappingException("Entry should has getter for property " + propertyName); + } + + Object propertyValue = getter.get(entry); + if (propertyValue == null) { + return null; + } + + if (!(propertyValue instanceof LocalizedString)) { + throw new MappingException("Entry property should be LocalizedString"); + } + + LocalizedString localizedString = (LocalizedString) propertyValue; + String ldapAttributeName = ((AttributeName) ldapAttribute).name(); + + return getAttributeDataFromLocalizedString(ldapAttributeName, localizedString); + } + + protected List getAttributeDataFromLocalizedString(String ldapAttributeName, LocalizedString localizedString) { + List listAttributes = new ArrayList<>(); + + AttributeData attributeData = new AttributeData(ldapAttributeName, localizedString.getValues()); + listAttributes.add(attributeData); + + return listAttributes; + } + private List getAttributesFromAttributesList(Object entry, Annotation ldapAttribute, String propertyName) { Class entryClass = entry.getClass(); @@ -1601,7 +1672,7 @@ private List getAttributesFromAttributesList(Object entry, Annota if (attribute != null) { if (multiValued == null) { // Detect if attribute has more than one value - multiValued = attribute.getValues().length > 1; + multiValued = attribute.getValues().length > 1; } attribute.setMultiValued(multiValued); @@ -1788,7 +1859,7 @@ private void setPropertyValue(String propertyName, Setter propertyValueSetter, O + " parameter type or has annotation JsonObject"); } } - + private List attributeToTypedList(Class listType, AttributeData attributeData) { if (listType.equals(String.class)) { ArrayList result = new ArrayList(); @@ -1802,7 +1873,7 @@ private List attributeToTypedList(Class listType, AttributeData attributeD result.add(resultValue); } - + return result; } @@ -1877,7 +1948,7 @@ private Object getListItem(String propertyName, Setter propertyNameSetter, Sette } propertyNameSetter.set(result, attribute.getName()); setPropertyValue(propertyName, propertyValueSetter, result, attribute, false); - + if ((entryPropertyMultivaluedSetter != null) && (attribute.getMultiValued() != null)) { entryPropertyMultivaluedSetter.set(result, attribute.getMultiValued()); } @@ -1887,7 +1958,7 @@ private Object getListItem(String propertyName, Setter propertyNameSetter, Sette protected Object getDNValue(Object entry) { Class entryClass = entry.getClass(); - + return getDNValue(entry, entryClass); } @@ -1924,7 +1995,7 @@ protected Integer getExpirationValue(Object entry, Class entryClass, bool if (merge && expirationAnnotation.ignoreDuringUpdate()) { return null; } - + if (expirationPropertyName == null) { // No entry expiration property return null; @@ -1947,14 +2018,14 @@ protected Integer getExpirationValue(Object entry, Class entryClass, bool // No entry expiration or null return null; } - + Integer resultExpirationValue; if (expirationValue instanceof Integer) { resultExpirationValue = (Integer) expirationValue; } else { resultExpirationValue = Integer.valueOf((int) expirationValue); } - + // TTL can't be negative if (resultExpirationValue < 0) { resultExpirationValue = 0; @@ -2074,7 +2145,7 @@ public int getHashCode(Object entry) { return key.hashCode(); } - + protected byte[][] toBinaryValues(String[] attributeValues) { byte[][] binaryValues = new byte[attributeValues.length][]; @@ -2137,9 +2208,9 @@ protected boolean isMultiValued(Class parameterType) { if (parameterType == null) { return false; } - - boolean isMultiValued = parameterType.equals(String[].class) || ReflectHelper.assignableFrom(parameterType, List.class) || ReflectHelper.assignableFrom(parameterType, AttributeEnum[].class); - + + boolean isMultiValued = parameterType.equals(String[].class) || ReflectHelper.assignableFrom(parameterType, List.class) || ReflectHelper.assignableFrom(parameterType, AttributeEnum[].class); + return isMultiValued; } @@ -2247,7 +2318,7 @@ public int compare(T entry1, T entry2) { protected void dumpAttributes(String variableName, List attributesToPersist) { System.out.println("\n" + variableName + ": START"); - + for (AttributeData attribute : attributesToPersist) { System.out.println(String.format("%s\t\t%s\t%b", attribute.getName(), Arrays.toString(attribute.getValues()), attribute.getMultiValued())); } @@ -2257,11 +2328,11 @@ protected void dumpAttributes(String variableName, List attribute protected void dumpAttributeDataModifications(String variableName, List attributeDataModifications) { System.out.println("\n" + variableName + ": START"); - + for (AttributeDataModification modification : attributeDataModifications) { String newValues = "[]"; String oldValues = "[]"; - + if ((modification.getAttribute() != null) && (modification.getAttribute().getValues() != null)) { newValues = Arrays.toString(modification.getAttribute().getValues()); } @@ -2269,12 +2340,12 @@ protected void dumpAttributeDataModifications(String variableName, List\t%s", attribute.getName(), modification.getModificationType().name(), attribute.getMultiValued(), oldValues, newValues)); } diff --git a/jans-orm/ldap/src/main/java/io/jans/orm/ldap/impl/LdapEntryManager.java b/jans-orm/ldap/src/main/java/io/jans/orm/ldap/impl/LdapEntryManager.java index 49767a80923..4b17465395e 100644 --- a/jans-orm/ldap/src/main/java/io/jans/orm/ldap/impl/LdapEntryManager.java +++ b/jans-orm/ldap/src/main/java/io/jans/orm/ldap/impl/LdapEntryManager.java @@ -6,62 +6,43 @@ package io.jans.orm.ldap.impl; -import java.io.Serializable; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.apache.commons.codec.binary.Base64; +import com.unboundid.ldap.sdk.*; +import com.unboundid.util.StaticUtils; import io.jans.orm.PersistenceEntryManager; import io.jans.orm.event.DeleteNotifier; import io.jans.orm.exception.AuthenticationException; import io.jans.orm.exception.EntryDeleteException; +import io.jans.orm.exception.EntryPersistenceException; import io.jans.orm.exception.MappingException; import io.jans.orm.exception.operation.ConnectionException; -import io.jans.orm.exception.EntryPersistenceException; import io.jans.orm.exception.operation.SearchException; import io.jans.orm.exception.operation.SearchScopeException; import io.jans.orm.impl.BaseEntryManager; import io.jans.orm.ldap.operation.LdapOperationService; import io.jans.orm.ldap.operation.impl.LdapOperationServiceImpl; -import io.jans.orm.model.AttributeData; -import io.jans.orm.model.AttributeDataModification; -import io.jans.orm.model.AttributeDataModification.AttributeModificationType; -import io.jans.orm.model.BatchOperation; -import io.jans.orm.model.DefaultBatchOperation; -import io.jans.orm.model.PagedResult; +import io.jans.orm.model.*; import io.jans.orm.model.SearchScope; -import io.jans.orm.model.SortOrder; +import io.jans.orm.model.AttributeDataModification.AttributeModificationType; +import io.jans.orm.model.base.LocalizedString; import io.jans.orm.reflect.property.PropertyAnnotation; import io.jans.orm.search.filter.Filter; import io.jans.orm.util.ArrayHelper; import io.jans.orm.util.StringHelper; +import org.apache.commons.codec.binary.Base64; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.unboundid.ldap.sdk.Attribute; -import com.unboundid.ldap.sdk.LDAPConnection; -import com.unboundid.ldap.sdk.Modification; -import com.unboundid.ldap.sdk.ModificationType; -import com.unboundid.ldap.sdk.ResultCode; -import com.unboundid.ldap.sdk.SearchResult; -import com.unboundid.ldap.sdk.SearchResultEntry; -import com.unboundid.util.StaticUtils; +import java.io.Serializable; +import java.text.ParseException; +import java.util.*; + +import static io.jans.orm.model.base.LocalizedString.*; /** * LDAP Entry Manager * - * @author Yuriy Movchan Date: 10.07.2010 + * @author Yuriy Movchan + * @version April 25, 2022 */ public class LdapEntryManager extends BaseEntryManager implements Serializable { @@ -116,14 +97,14 @@ public Void merge(Object entry) { throw new UnsupportedOperationException("Server doesn't support dynamic schema modifications"); } } else { - boolean configurationEntry = isConfigurationEntry(entryClass); + boolean configurationEntry = isConfigurationEntry(entryClass); return merge(entry, false, configurationEntry, null); } } @Override protected void updateMergeChanges(String baseDn, T entry, boolean isConfigurationUpdate, Class entryClass, Map attributesFromLdapMap, - List attributeDataModifications, boolean forceUpdate) { + List attributeDataModifications, boolean forceUpdate) { // Update object classes if entry contains custom object classes if (getSupportedLDAPVersion() > 2) { if (!isConfigurationUpdate) { @@ -309,7 +290,7 @@ public void removeByDn(String dn, String[] objectClasses) { } @Override - public int remove(String baseDN, Class entryClass, Filter filter, int count) { + public int remove(String baseDN, Class entryClass, Filter filter, int count) { if (StringHelper.isEmptyString(baseDN)) { throw new MappingException("Base DN to find entries is null"); } @@ -346,7 +327,7 @@ public int remove(String baseDN, Class entryClass, Filter filter, int cou return batchOperation.getCountEntries(); } - @Override + @Override public void removeRecursivelyFromDn(String dn, String[] objectClasses) { try { if (getOperationService().getConnectionProvider().isSupportsSubtreeDeleteRequestControl()) { @@ -366,9 +347,9 @@ public void removeRecursivelyFromDn(String dn, String[] objectClasses) { } private void removeSubtreeThroughIteration(String dn) { - SearchScope scope = SearchScope.SUB; + SearchScope scope = SearchScope.SUB; - SearchResult searchResult = null; + SearchResult searchResult = null; try { searchResult = getOperationService().search(dn, toLdapFilter(Filter.createPresenceFilter("objectClass")), toLdapSearchScope(scope), null, 0, 0, 0, null, "dn"); if (!ResultCode.SUCCESS.equals(searchResult.getResultCode())) { @@ -392,7 +373,7 @@ private void removeSubtreeThroughIteration(String dn) { } } - @Override + @Override protected List find(String dn, String[] objectClasses, Map propertiesAnnotationsMap, String... ldapReturnAttributes) { try { // Load entry @@ -410,7 +391,7 @@ protected List find(String dn, String[] objectClasses, Map List findEntries(String baseDN, Class entryClass, Filter filter, SearchScope scope, String[] ldapReturnAttributes, - BatchOperation batchOperation, int start, int count, int chunkSize) { + BatchOperation batchOperation, int start, int count, int chunkSize) { if (StringHelper.isEmptyString(baseDN)) { throw new MappingException("Base DN to find entries is null"); } @@ -460,7 +441,7 @@ public List findEntries(String baseDN, Class entryClass, Filter filter @Override public PagedResult findPagedEntries(String baseDN, Class entryClass, Filter filter, String[] ldapReturnAttributes, String sortBy, - SortOrder sortOrder, int start, int count, int chunkSize) { + SortOrder sortOrder, int start, int count, int chunkSize) { if (StringHelper.isEmptyString(baseDN)) { throw new MappingException("Base DN to find entries is null"); } @@ -503,7 +484,7 @@ public PagedResult findPagedEntries(String baseDN, Class entryClass, F @Deprecated public List findEntriesVirtualListView(String baseDN, Class entryClass, Filter filter, int start, int count, String sortBy, - SortOrder sortOrder, PagedResult vlvResponse, String[] ldapReturnAttributes) { + SortOrder sortOrder, PagedResult vlvResponse, String[] ldapReturnAttributes) { if (StringHelper.isEmptyString(baseDN)) { throw new MappingException("Base DN to find entries is null"); @@ -584,7 +565,7 @@ protected boolean contains(String baseDN, String[] objectClasses, Class e } protected List createEntities(Class entryClass, List propertiesAnnotations, - SearchResultEntry... searchResultEntries) { + SearchResultEntry... searchResultEntries) { List result = new ArrayList(searchResultEntries.length); Map> entriesAttributes = new HashMap>(100); @@ -615,7 +596,7 @@ protected List createEntities(Class entryClass, List List createEntitiesVirtualListView(Class entryClass, List propertiesAnnotations, - SearchResultEntry... searchResultEntries) { + SearchResultEntry... searchResultEntries) { List result = new LinkedList(); Map> entriesAttributes = new LinkedHashMap>(100); @@ -652,6 +633,38 @@ private List createEntitiesVirtualListView(Class entryClass, List getAttributeDataFromLocalizedString(String ldapAttributeName, LocalizedString localizedString) { + List listAttributes = new ArrayList<>(); + + localizedString.getLanguageTags().forEach(languageTag -> { + String value = localizedString.getValue(languageTag); + String key = localizedString.addLdapLanguageTag(ldapAttributeName, languageTag); + AttributeData attributeData = new AttributeData(key, value); + + listAttributes.add(attributeData); + }); + + return listAttributes; + } + + @Override + protected void loadLocalizedString(Map attributesMap, LocalizedString localizedString, Map filteredAttrs) { + filteredAttrs.forEach((key, value) -> { + AttributeData data = attributesMap.get(key); + String[] keyParts = key.split(LANG_SEPARATOR); + if (keyParts.length == 1) { + localizedString.setValue(data.getValue().toString()); + } else if (keyParts.length == 2) { + String lagTag = keyParts[1].replace(LANG_PREFIX + LANG_JOINER, ""); + localizedString.setValue( + data.getValue().toString(), + Locale.forLanguageTag(lagTag) + ); + } + }); + } + private List getAttributeDataList(SearchResultEntry entry) { if (entry == null) { return null; @@ -714,7 +727,7 @@ public boolean authenticate(String baseDN, Class entryClass, String userN searchFilter = addObjectClassFilter(searchFilter, objectClasses); } - SearchScope scope = SearchScope.SUB; + SearchScope scope = SearchScope.SUB; try { SearchResult searchResult = getOperationService().search(baseDN, toLdapFilter(searchFilter), toLdapSearchScope(scope), null, 0, 1, 1, null, LdapOperationService.UID_ARRAY); if ((searchResult == null) || (searchResult.getEntryCount() != 1)) { @@ -736,7 +749,7 @@ public boolean authenticate(String baseDN, Class entryClass, String userN @Override @Deprecated public boolean authenticate(String bindDn, String password) { - return authenticate(bindDn, null, password); + return authenticate(bindDn, null, password); } @Override @@ -779,10 +792,10 @@ public int countEntries(String baseDN, Class entryClass, Filter filter, S String[] ldapReturnAttributes; CountBatchOperation batchOperation; if (SearchScope.BASE == searchScope) { - ldapReturnAttributes = new String[] { "numsubordinates" }; // Don't load attributes + ldapReturnAttributes = new String[]{"numsubordinates"}; // Don't load attributes batchOperation = null; } else { - ldapReturnAttributes = new String[] { "" }; // Don't load attributes + ldapReturnAttributes = new String[]{""}; // Don't load attributes batchOperation = new CountBatchOperation(); } @@ -825,9 +838,9 @@ public String encodeTime(String baseDN, Date date) { } @Override - protected String encodeTime(Date date) { - return encodeTime(null, date); - } + protected String encodeTime(Date date) { + return encodeTime(null, date); + } @Override public Date decodeTime(String baseDN, String date) { @@ -846,7 +859,7 @@ public Date decodeTime(String baseDN, String date) { @Override protected Date decodeTime(String date) { - return decodeTime(null, date); + return decodeTime(null, date); } public boolean loadLdifFileContent(String ldifFileContent) { @@ -860,7 +873,7 @@ public boolean loadLdifFileContent(String ldifFileContent) { return false; } finally { if (connection != null) { - getOperationService().releaseConnection(connection); + getOperationService().releaseConnection(connection); } } } @@ -868,23 +881,23 @@ public boolean loadLdifFileContent(String ldifFileContent) { @Override public List exportEntry(String dn) { try { - SearchResultEntry searchResultEntry = getOperationService().lookup(dn, (String[]) null); + SearchResultEntry searchResultEntry = getOperationService().lookup(dn, (String[]) null); List result = getAttributeDataList(searchResultEntry); if (result != null) { return result; } - + return null; } catch (ConnectionException | SearchException ex) { throw new EntryPersistenceException(String.format("Failed to find entry: %s", dn), ex); } } - @Override - public List exportEntry(String dn, String objectClass) { - return exportEntry(dn); - } + @Override + public List exportEntry(String dn, String objectClass) { + return exportEntry(dn); + } public int getSupportedLDAPVersion() { return getOperationService().getSupportedLDAPVersion(); @@ -910,53 +923,53 @@ private com.unboundid.ldap.sdk.SearchScope toLdapSearchScope(SearchScope scope) return LDAP_SEARCH_SCOPE_CONVERTER.convertToLdapSearchScope(scope); } - @Override - public boolean hasBranchesSupport(String dn) { - return true; - } + @Override + public boolean hasBranchesSupport(String dn) { + return true; + } - @Override - public boolean hasExpirationSupport(String primaryKey) { - return false; - } + @Override + public boolean hasExpirationSupport(String primaryKey) { + return false; + } - @Override - public String getPersistenceType() { - return LdapEntryManagerFactory.PERSISTENCE_TYPE; - } + @Override + public String getPersistenceType() { + return LdapEntryManagerFactory.PERSISTENCE_TYPE; + } @Override - public String getPersistenceType(String primaryKey) { - return LdapEntryManagerFactory.PERSISTENCE_TYPE; - } + public String getPersistenceType(String primaryKey) { + return LdapEntryManagerFactory.PERSISTENCE_TYPE; + } - @Override - public PersistenceEntryManager getPersistenceEntryManager(String persistenceType) { - if (LdapEntryManagerFactory.PERSISTENCE_TYPE.equals(persistenceType)) { - return this; - } - - return null; - } + @Override + public PersistenceEntryManager getPersistenceEntryManager(String persistenceType) { + if (LdapEntryManagerFactory.PERSISTENCE_TYPE.equals(persistenceType)) { + return this; + } + + return null; + } @Override - public String[] getObjectClasses(Object entry, Class entryClass) { - String[] ojectClasses = super.getObjectClasses(entry, entryClass); + public String[] getObjectClasses(Object entry, Class entryClass) { + String[] ojectClasses = super.getObjectClasses(entry, entryClass); - Set objecClassSet = new HashSet(); + Set objecClassSet = new HashSet(); - // Add in LDAP implementation "top" by default - objecClassSet.add("top"); - objecClassSet.addAll(Arrays.asList(ojectClasses)); - return objecClassSet.toArray(new String[0]); - } + // Add in LDAP implementation "top" by default + objecClassSet.add("top"); + objecClassSet.addAll(Arrays.asList(ojectClasses)); + return objecClassSet.toArray(new String[0]); + } @Override - protected Object getNativeDateAttributeValue(Date dateValue) { - return encodeTime(dateValue); + protected Object getNativeDateAttributeValue(Date dateValue) { + return encodeTime(dateValue); } - private static class CountBatchOperation extends DefaultBatchOperation { + private static class CountBatchOperation extends DefaultBatchOperation { private int countEntries = 0; @@ -975,31 +988,31 @@ public int getCountEntries() { } } - private static final class DeleteBatchOperation extends DefaultBatchOperation { + private static final class DeleteBatchOperation extends DefaultBatchOperation { - private int countEntries = 0; - private LdapEntryManager ldapEntryManager; + private int countEntries = 0; + private LdapEntryManager ldapEntryManager; public DeleteBatchOperation(LdapEntryManager ldapEntryManager) { - this.ldapEntryManager = ldapEntryManager; - } + this.ldapEntryManager = ldapEntryManager; + } @Override - public void performAction(List entries) { - for (T entity : entries) { - try { - String dnValue = ldapEntryManager.getDNValue(entity).toString(); - if (ldapEntryManager.hasBranchesSupport(dnValue)) { - ldapEntryManager.removeRecursively(dnValue); - } else { - ldapEntryManager.remove(dnValue); - } - LOG.trace("Removed {}", dnValue); - } catch (Exception e) { - LOG.error("Failed to remove entry, entity: " + entity, e); - } - } - } + public void performAction(List entries) { + for (T entity : entries) { + try { + String dnValue = ldapEntryManager.getDNValue(entity).toString(); + if (ldapEntryManager.hasBranchesSupport(dnValue)) { + ldapEntryManager.removeRecursively(dnValue); + } else { + ldapEntryManager.remove(dnValue); + } + LOG.trace("Removed {}", dnValue); + } catch (Exception e) { + LOG.error("Failed to remove entry, entity: " + entity, e); + } + } + } @Override public boolean collectSearchResult(int size) { @@ -1010,6 +1023,6 @@ public boolean collectSearchResult(int size) { public int getCountEntries() { return countEntries; } - } + } } diff --git a/jans-orm/model/pom.xml b/jans-orm/model/pom.xml index 08c645b685a..06039b19337 100644 --- a/jans-orm/model/pom.xml +++ b/jans-orm/model/pom.xml @@ -22,6 +22,15 @@ io.jans jans-orm-util - + + org.json + json + + + org.testng + testng + test + + \ No newline at end of file diff --git a/jans-orm/model/src/main/java/io/jans/orm/model/base/LocalizedString.java b/jans-orm/model/src/main/java/io/jans/orm/model/base/LocalizedString.java new file mode 100644 index 00000000000..795682fcdc3 --- /dev/null +++ b/jans-orm/model/src/main/java/io/jans/orm/model/base/LocalizedString.java @@ -0,0 +1,124 @@ +/* + * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.orm.model.base; + +import org.apache.commons.lang.StringUtils; +import org.json.JSONObject; + +import java.io.Serializable; +import java.util.*; +import java.util.function.BiFunction; +import java.util.stream.Collectors; + +/** + * @author Javier Rojas Blum + * @version April 26, 2022 + */ +public class LocalizedString implements Serializable { + + private static final long serialVersionUID = -7651487701235873969L; + + private final Map values; + + public static final String EMPTY_LANG_TAG = ""; + public static final String LANG_SEPARATOR = ";"; + public static final String LANG_CLAIM_SEPARATOR = "#"; + public static final String LANG_PREFIX = "lang"; + public static final String LANG_JOINER = "-"; + + public LocalizedString() { + values = new HashMap<>(); + } + + public void setValue(String value) { + values.put(EMPTY_LANG_TAG, value); + } + + public void setValue(String value, Locale locale) { + values.put(getLanguageTag(locale), value); + } + + public String getValue() { + return getValue(EMPTY_LANG_TAG); + } + + public String getValue(String languageTag) { + return values.getOrDefault(languageTag, null); + } + + public Map getValues() { + return values; + } + + public int size() { + return values.size(); + } + + public Set getLanguageTags() { + return values.keySet(); + } + + public String addLdapLanguageTag(String ldapAttributeName, String languageTag) { + return ldapAttributeName + (StringUtils.isNotBlank(languageTag) ? + LANG_SEPARATOR + LANG_PREFIX + LANG_JOINER + languageTag : EMPTY_LANG_TAG); + } + + private String getLanguageTag(Locale locale) { + List keyParts = new ArrayList<>(); + keyParts.add(locale.getLanguage()); + keyParts.add(locale.getScript()); + keyParts.add(locale.getCountry()); + + return keyParts.stream() + .filter(StringUtils::isNotBlank) + .collect(Collectors.joining(LANG_JOINER)); + } + + public Map addToMap(Map map, String key) { + if (values.isEmpty()) { + return map; + } + + for (String languageTag : getLanguageTags()) { + if (StringUtils.isBlank(languageTag)) { + map.put(key, getValue()); + } else { + map.put(key + "#" + languageTag, getValue(languageTag)); + } + } + return map; + } + + public void addToJSON(JSONObject jsonObj, String claimName) { + getLanguageTags() + .forEach(languageTag -> { + StringBuilder keyStringBuilder = new StringBuilder() + .append(claimName) + .append(StringUtils.isNotBlank(languageTag) ? LANG_CLAIM_SEPARATOR + languageTag : EMPTY_LANG_TAG); + jsonObj.put(keyStringBuilder.toString(), getValue(languageTag)); + }); + } + + @Override + public String toString() { + return values.toString(); + } + + public static void fromJson( + JSONObject requestObject, String paramName, BiFunction function) { + List keys = requestObject.keySet().stream() + .filter(k -> k.startsWith(paramName)) + .collect(Collectors.toList()); + + keys.forEach(key -> { + key = key.replace(paramName, ""); + String[] keyParts = key.split(LANG_CLAIM_SEPARATOR); + String languageTag = keyParts[keyParts.length - 1]; + function.apply(requestObject.getString(paramName + key), Locale.forLanguageTag(languageTag)); + }); + } +} diff --git a/jans-orm/model/src/test/java/io/jans/orm/model/base/LocalizedStringTest.java b/jans-orm/model/src/test/java/io/jans/orm/model/base/LocalizedStringTest.java new file mode 100644 index 00000000000..d88b7e2b443 --- /dev/null +++ b/jans-orm/model/src/test/java/io/jans/orm/model/base/LocalizedStringTest.java @@ -0,0 +1,144 @@ +/* + * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text. + * + * Copyright (c) 2020, Janssen Project + */ +package io.jans.orm.model.base; + +import org.json.JSONObject; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +/** + * @author Javier Rojas Blum + * @version April 26, 2022 + */ +public class LocalizedStringTest { + + final String[] LANGUAGE_TAGS = new String[]{ + "", "es", "es-BO", + "en-GB", "en-CA", "en-NZ", + "fr", "fr-FR", "fr-CA", + "ja-Jpan-JP", "ja-Kana-JP", "ja-Hani-JP" + }; + + final String[] LOCALIZED_VALUES = new String[]{ + "Client name", "Nombre del cliente", "Nombre del cliente", + "Client name", "Client name", "Client name", + "Nom du client", "Nom du client", "Nom du client", + "クライアント名", "カナ姓※", "漢字姓※" + }; + final String[] CLAIM_NAMES = new String[]{ + "client_name", "client_name#es", "client_name#es-BO", + "client_name#en-GB", "client_name#en-CA", "client_name#en-NZ", + "client_name#fr", "client_name#fr-FR", "client_name#fr-CA", + "client_name#ja-Jpan-JP", "client_name#ja-Kana-JP", "client_name#ja-Hani-JP" + }; + + final String REQUEST_JSON = "{\n" + + " \"redirect_uris\" : [ \"https://gluu/jans-auth-rp/home.htm\", \"https://client.example.com/cb\", \"https://client.example.com/cb1\", \"https://client.example.com/cb2\" ],\n" + + " \"sector_identifier_uri\" : \"https://gluu/jans-auth/sectoridentifier/a55ede29-8f5a-461d-b06e-76caee8d40b5\",\n" + + " \"response_types\" : [ \"code\", \"token\", \"id_token\" ],\n" + + " \"client_uri\" : \"https://client-home-page/index.htm\",\n" + + " \"application_type\" : \"web\",\n" + + " \"scope\" : \"openid profile address email phone user_name clientinfo\",\n" + + " \"subject_type\" : \"pairwise\",\n" + + " \"client_name\" : \"Client name\",\n" + + " \"client_name#es\" : \"Nombre del cliente\",\n" + + " \"client_name#es-BO\" : \"Nombre del cliente\",\n" + + " \"client_name#en-GB\" : \"Client name\",\n" + + " \"client_name#en-CA\" : \"Client name\",\n" + + " \"client_name#en-NZ\" : \"Client name\",\n" + + " \"client_name#fr\" : \"Nom du client\",\n" + + " \"client_name#fr-FR\" : \"Nom du client\",\n" + + " \"client_name#fr-CA\" : \"Nom du client\",\n" + + " \"client_name#ja-Jpan-JP\" : \"クライアント名\",\n" + + " \"client_name#ja-Kana-JP\" : \"カナ姓※\",\n" + + " \"client_name#ja-Hani-JP\" : \"漢字姓※\"\n" + + "}"; + + LocalizedString localizedString; + + @BeforeClass + public void setUp() { + localizedString = new LocalizedString(); + } + + @Test + public void testSetValue() { + localizedString.setValue("Client name"); + localizedString.setValue("Nombre del cliente", new Locale("es")); + localizedString.setValue("Nombre del cliente", new Locale("es", "BO")); + localizedString.setValue("Client name", Locale.UK); + localizedString.setValue("Client name", Locale.CANADA); + localizedString.setValue("Client name", Locale.forLanguageTag("en-NZ")); + localizedString.setValue("Nom du client", Locale.FRENCH); + localizedString.setValue("Nom du client", Locale.CANADA_FRENCH); + localizedString.setValue("Nom du client", Locale.FRANCE); + localizedString.setValue("クライアント名", Locale.forLanguageTag("ja-Jpan-JP")); + localizedString.setValue("カナ姓※", Locale.forLanguageTag("ja-Kana-JP")); + localizedString.setValue("漢字姓※", Locale.forLanguageTag("ja-Hani-JP")); + + assertEquals(localizedString.size(), LANGUAGE_TAGS.length); + assertTrue(localizedString.getLanguageTags().containsAll(Arrays.asList(LANGUAGE_TAGS))); + for (int i = 0; i < LANGUAGE_TAGS.length; i++) { + assertEquals(localizedString.getValue(LANGUAGE_TAGS[i]), LOCALIZED_VALUES[i]); + } + } + + @Test(dependsOnMethods = "testSetValue") + public void testGetValue() { + assertEquals(localizedString.getValue(), "Client name"); + } + + @Test(dependsOnMethods = "testSetValue") + public void addToMap() { + Map map = new HashMap<>(); + + localizedString.addToMap(map, "client_name"); + + assertEquals(map.size(), CLAIM_NAMES.length); + for (int i = 0; i < CLAIM_NAMES.length; i++) { + assertTrue(map.containsKey(CLAIM_NAMES[i])); + assertEquals(map.get(CLAIM_NAMES[i]), LOCALIZED_VALUES[i]); + } + } + + @Test(dependsOnMethods = "testSetValue") + public void addToJSON() { + JSONObject jsonObject = new JSONObject(); + + localizedString.addToJSON(jsonObject, "client_name"); + + assertEquals(jsonObject.keySet().size(), CLAIM_NAMES.length); + for (int i = 0; i < CLAIM_NAMES.length; i++) { + assertTrue(jsonObject.keySet().contains(CLAIM_NAMES[i])); + assertEquals(jsonObject.get(CLAIM_NAMES[i]), LOCALIZED_VALUES[i]); + } + } + + @Test + public void fromJson() { + JSONObject jsonObject = new JSONObject(REQUEST_JSON); + LocalizedString myLocalizedString = new LocalizedString(); + + LocalizedString.fromJson(jsonObject, "client_name", (value, locale) -> { + myLocalizedString.setValue(value, locale); + return null; + }); + + assertEquals(myLocalizedString.size(), LANGUAGE_TAGS.length); + assertTrue(myLocalizedString.getLanguageTags().containsAll(Arrays.asList(LANGUAGE_TAGS))); + for (int i = 0; i < LANGUAGE_TAGS.length; i++) { + assertEquals(myLocalizedString.getValue(LANGUAGE_TAGS[i]), LOCALIZED_VALUES[i]); + } + } +} \ No newline at end of file