diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java b/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java index 152fa7c63fa..74f6de16c01 100644 --- a/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java @@ -411,7 +411,7 @@ public void setFeatureFlags(List featureFlags) { this.featureFlags = featureFlags; } - public Boolean getUseNestedJwtDuringEncryption() { + public Boolean isUseNestedJwtDuringEncryption() { if (useNestedJwtDuringEncryption == null) useNestedJwtDuringEncryption = true; return useNestedJwtDuringEncryption; } diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/model/token/JwrService.java b/jans-auth-server/server/src/main/java/io/jans/as/server/model/token/JwrService.java index 55a317f3275..1f88d8b606b 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/model/token/JwrService.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/model/token/JwrService.java @@ -92,7 +92,7 @@ private Jwt signJwt(Jwt jwt, Client client) throws Exception { private Jwe encryptJwe(Jwe jwe, Client client) throws Exception { - if (BooleanUtils.isTrue(appConfiguration.getUseNestedJwtDuringEncryption())) { + if (BooleanUtils.isTrue(appConfiguration.isUseNestedJwtDuringEncryption()) ){ JwtSigner jwtSigner = JwtSigner.newJwtSigner(appConfiguration, webKeysConfiguration, client); Jwt jwt = jwtSigner.newJwt(); jwt.setClaims(jwe.getClaims()); diff --git a/jans-config-api/docs/jans-config-api-swagger-auto.yaml b/jans-config-api/docs/jans-config-api-swagger-auto.yaml index e5f39debe13..4b121925771 100644 --- a/jans-config-api/docs/jans-config-api-swagger-auto.yaml +++ b/jans-config-api/docs/jans-config-api-swagger-auto.yaml @@ -2947,18 +2947,18 @@ components: type: string whitePagesCanView: type: boolean - adminCanEdit: + adminCanAccess: + type: boolean + userCanView: type: boolean adminCanView: type: boolean userCanAccess: type: boolean - userCanView: - type: boolean - adminCanAccess: - type: boolean userCanEdit: type: boolean + adminCanEdit: + type: boolean baseDn: type: string PatchRequest: @@ -3284,8 +3284,6 @@ components: format: int32 displayName: type: string - tokenBindingSupported: - type: boolean authenticationMethod: type: string enum: @@ -3297,6 +3295,8 @@ components: - tls_client_auth - self_signed_tls_client_auth - none + tokenBindingSupported: + type: boolean baseDn: type: string inum: @@ -3386,10 +3386,10 @@ components: type: array items: type: object - value: - type: object displayValue: type: string + value: + type: object LocalizedString: type: object properties: @@ -3397,13 +3397,13 @@ components: type: object additionalProperties: type: string - value: - type: string languageTags: uniqueItems: true type: array items: type: string + value: + type: string AppConfiguration: type: object properties: @@ -4092,6 +4092,15 @@ components: $ref: '#/components/schemas/SsaConfiguration' fapi: type: boolean + allResponseTypesSupported: + uniqueItems: true + type: array + items: + type: string + enum: + - code + - token + - id_token enabledFeatureFlags: uniqueItems: true type: array @@ -4119,15 +4128,6 @@ components: - STAT - PAR - SSA - allResponseTypesSupported: - uniqueItems: true - type: array - items: - type: string - enum: - - code - - token - - id_token AuthenticationFilter: required: - baseDn @@ -4384,13 +4384,13 @@ components: type: boolean internal: type: boolean - locationPath: - type: string locationType: type: string enum: - ldap - file + locationPath: + type: string baseDn: type: string ScriptError: diff --git a/jans-config-api/plugins/docs/user-mgt-plugin-swagger.yaml b/jans-config-api/plugins/docs/user-mgt-plugin-swagger.yaml index c5debdf81ec..4b47382ece4 100644 --- a/jans-config-api/plugins/docs/user-mgt-plugin-swagger.yaml +++ b/jans-config-api/plugins/docs/user-mgt-plugin-swagger.yaml @@ -225,10 +225,10 @@ components: type: array items: type: object - value: - type: object displayValue: type: string + value: + type: object CustomUser: type: object properties: diff --git a/jans-config-api/server/src/main/java/io/jans/configapi/service/auth/ClientService.java b/jans-config-api/server/src/main/java/io/jans/configapi/service/auth/ClientService.java index b711a51d838..cc4525e2fe9 100644 --- a/jans-config-api/server/src/main/java/io/jans/configapi/service/auth/ClientService.java +++ b/jans-config-api/server/src/main/java/io/jans/configapi/service/auth/ClientService.java @@ -6,12 +6,17 @@ package io.jans.configapi.service.auth; -import static io.jans.as.model.util.Util.escapeLog; +import static org.apache.commons.lang3.BooleanUtils.isTrue; + import io.jans.as.common.model.registration.Client; import io.jans.as.common.service.OrganizationService; import io.jans.as.common.service.common.InumService; import io.jans.as.common.util.AttributeConstants; +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.configuration.AppConfiguration; import io.jans.as.model.crypto.signature.SignatureAlgorithm; import io.jans.as.model.register.ApplicationType; import io.jans.configapi.core.model.SearchRequest; @@ -24,11 +29,17 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import java.io.Serializable; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; +import com.google.common.collect.Lists; + /** * @author Mougang T.Gasmyr * @@ -50,11 +61,21 @@ public class ClientService implements Serializable { @Inject private transient InumService inumService; + @Inject + AttributeService attributeService; + + @Inject + transient ScopeService scopeService; + + @Inject + transient AppConfiguration appConfiguration; + public boolean contains(String clientDn) { return persistenceEntryManager.contains(clientDn, Client.class); } public void addClient(Client client) { + setClientDefaultAttributes(client, false); persistenceEntryManager.persist(client); } @@ -77,9 +98,9 @@ public Client getClientByInum(String inum) { } public List searchClients(String pattern, int sizeLimit) { - if (logger.isDebugEnabled()) { - logger.debug("Search Clients with pattern:{}, sizeLimit:{}", escapeLog(pattern), escapeLog(sizeLimit)); - } + + logger.debug("Search Clients with pattern:{}, sizeLimit:{}", pattern, sizeLimit); + String[] targetArray = new String[] { pattern }; Filter displayNameFilter = Filter.createSubstringFilter(AttributeConstants.DISPLAY_NAME, null, targetArray, null); @@ -101,9 +122,8 @@ public List getAllClients() { } public PagedResult getClients(SearchRequest searchRequest) { - if (logger.isDebugEnabled()) { - logger.debug("Search Clients with searchRequest:{}", escapeLog(searchRequest)); - } + logger.debug("Search Clients with searchRequest:{}", searchRequest); + Filter searchFilter = null; if (StringUtils.isNotEmpty(searchRequest.getFilter())) { String[] targetArray = new String[] { searchRequest.getFilter() }; @@ -165,4 +185,203 @@ public String generateInumForNewClient() { } while (persistenceEntryManager.contains(newDn, Client.class)); return newInum; } + + public Client setClientDefaultAttributes(Client client, boolean update) { + logger.debug("Client data - client:{}", client); + if (client == null) { + return client; + } + + logger.debug("client.getApplicationType:{}, client.getRedirectUris():{}, client.getClaimRedirectUris():{}", + client.getApplicationType(), client.getRedirectUris(), client.getClaimRedirectUris()); + + List redirectUris = client.getRedirectUris() != null ? Arrays.asList(client.getRedirectUris()) : null; + if (redirectUris != null && !redirectUris.isEmpty()) { + redirectUris = new ArrayList<>(new HashSet<>(redirectUris)); // Remove repeated elements + client.setRedirectUris(redirectUris.toArray(new String[0])); + } + List claimsRedirectUris = client.getClaimRedirectUris() != null + ? Arrays.asList(client.getClaimRedirectUris()) + : null; + if (claimsRedirectUris != null && !claimsRedirectUris.isEmpty()) { + claimsRedirectUris = new ArrayList<>(new HashSet<>(claimsRedirectUris)); // Remove repeated elements + client.setClaimRedirectUris(claimsRedirectUris.toArray(new String[0])); + } + + client.setApplicationType( + client.getApplicationType() != null ? client.getApplicationType() : ApplicationType.WEB); + + if (StringUtils.isNotBlank(client.getSectorIdentifierUri())) { + client.setSectorIdentifierUri(client.getSectorIdentifierUri()); + } + + logger.debug("client.getResponseTypes():{}, client.getGrantTypes():{}", client.getResponseTypes(), + client.getGrantTypes()); + Set responseTypeSet = client.getResponseTypes() != null + ? new HashSet<>(Arrays.asList(client.getResponseTypes())) + : null; + Set grantTypeSet = client.getGrantTypes() != null + ? new HashSet<>(Arrays.asList(client.getGrantTypes())) + : null; + + if (isTrue(appConfiguration.getGrantTypesAndResponseTypesAutofixEnabled())) { + if (isTrue(appConfiguration.getClientRegDefaultToCodeFlowWithRefresh())) { + if (responseTypeSet.isEmpty() && grantTypeSet.isEmpty()) { + responseTypeSet.add(ResponseType.CODE); + } + if (responseTypeSet.contains(ResponseType.CODE)) { + grantTypeSet.add(GrantType.AUTHORIZATION_CODE); + grantTypeSet.add(GrantType.REFRESH_TOKEN); + } + if (grantTypeSet.contains(GrantType.AUTHORIZATION_CODE)) { + responseTypeSet.add(ResponseType.CODE); + grantTypeSet.add(GrantType.REFRESH_TOKEN); + } + } + if (responseTypeSet.contains(ResponseType.TOKEN) || responseTypeSet.contains(ResponseType.ID_TOKEN)) { + grantTypeSet.add(GrantType.IMPLICIT); + } + if (grantTypeSet.contains(GrantType.IMPLICIT)) { + responseTypeSet.add(ResponseType.TOKEN); + } + } + + responseTypeSet.retainAll(appConfiguration.getAllResponseTypesSupported()); + grantTypeSet.retainAll(appConfiguration.getGrantTypesSupported()); + + Set dynamicGrantTypeDefault = appConfiguration.getDynamicGrantTypeDefault(); + grantTypeSet.retainAll(dynamicGrantTypeDefault); + + if (!update || !responseTypeSet.isEmpty()) { + client.setResponseTypes(responseTypeSet.toArray(new ResponseType[0])); + } + if (!update || (isTrue(appConfiguration.getEnableClientGrantTypeUpdate())) + && (client.getGrantTypes() != null && client.getGrantTypes().length > 0)) { + client.setGrantTypes(grantTypeSet.toArray(new GrantType[0])); + } + + logger.debug("Set client.getResponseTypes():{}, client.getGrantTypes():{}", client.getResponseTypes(), + client.getGrantTypes()); + List contacts = client.getContacts() != null ? Arrays.asList(client.getContacts()) : null; + if (contacts != null && !contacts.isEmpty()) { + contacts = new ArrayList<>(new HashSet<>(contacts)); // Remove repeated elements + client.setContacts(contacts.toArray(new String[0])); + } + + logger.debug("client.getTokenEndpointAuthMethod():{}", client.getTokenEndpointAuthMethod()); + if (StringUtils.isBlank(client.getTokenEndpointAuthMethod())) { + // If omitted, the default is client_secret_basic + client.setTokenEndpointAuthMethod(AuthenticationMethod.CLIENT_SECRET_BASIC.toString()); + } + + logger.debug("client.getDefaultAcrValues():{}", client.getDefaultAcrValues()); + List defaultAcrValues = client.getDefaultAcrValues() != null + ? Arrays.asList(client.getDefaultAcrValues()) + : null; + if (defaultAcrValues != null && !defaultAcrValues.isEmpty()) { + defaultAcrValues = new ArrayList<>(new HashSet<>(defaultAcrValues)); // Remove repeated elements + client.setDefaultAcrValues(defaultAcrValues.toArray(new String[defaultAcrValues.size()])); + } + + logger.debug("client.getGroups():{}", client.getGroups()); + final List groups = client.getGroups() != null ? Arrays.asList(client.getGroups()) : null; + if (groups != null && !groups.isEmpty()) { + client.setGroups(new HashSet<>(groups).toArray(new String[0])); // remove duplicates + } + + logger.debug("client.getGroups():{}, client.getPostLogoutRedirectUris():{}", client.getGroups(), client.getPostLogoutRedirectUris()); + List postLogoutRedirectUris = client.getPostLogoutRedirectUris() != null + ? Arrays.asList(client.getPostLogoutRedirectUris()) + : null; + if (postLogoutRedirectUris != null && !postLogoutRedirectUris.isEmpty()) { + postLogoutRedirectUris = new ArrayList<>(new HashSet<>(postLogoutRedirectUris)); // Remove repeated elements + client.setPostLogoutRedirectUris(postLogoutRedirectUris.toArray(new String[postLogoutRedirectUris.size()])); + } + + List requestUris = client.getRequestUris() != null ? Arrays.asList(client.getRequestUris()) : null; + if (requestUris != null && !requestUris.isEmpty()) { + requestUris = new ArrayList<>(new HashSet<>(requestUris)); // Remove repeated elements + client.setRequestUris(requestUris.toArray(new String[requestUris.size()])); + } + + List authorizedOrigins = client.getAuthorizedOrigins() != null + ? Arrays.asList(client.getAuthorizedOrigins()) + : null; + if (authorizedOrigins != null && !authorizedOrigins.isEmpty()) { + authorizedOrigins = new ArrayList<>(new HashSet<>(authorizedOrigins)); // Remove repeated elements + client.setAuthorizedOrigins(authorizedOrigins.toArray(new String[authorizedOrigins.size()])); + } + + List scopes = client.getScopes() != null ? Arrays.asList(client.getScopes()) : null; + if (grantTypeSet.contains(GrantType.RESOURCE_OWNER_PASSWORD_CREDENTIALS) + && !appConfiguration.getDynamicRegistrationAllowedPasswordGrantScopes().isEmpty()) { + scopes = Lists.newArrayList(scopes); + scopes.retainAll(appConfiguration.getDynamicRegistrationAllowedPasswordGrantScopes()); + } + List scopesDn; + if (scopes != null && !scopes.isEmpty() + && isTrue(appConfiguration.getDynamicRegistrationScopesParamEnabled())) { + List defaultScopes = scopeService.getDefaultScopesDn(); + List requestedScopes = scopeService.getScopesDn(scopes); + Set allowedScopes = new HashSet<>(); + + for (String requestedScope : requestedScopes) { + if (defaultScopes.contains(requestedScope)) { + allowedScopes.add(requestedScope); + } + } + + scopesDn = new ArrayList<>(allowedScopes); + client.setScopes(scopesDn.toArray(new String[scopesDn.size()])); + } else { + scopesDn = scopeService.getDefaultScopesDn(); + client.setScopes(scopesDn.toArray(new String[scopesDn.size()])); + } + + List claims = client.getClaims() != null ? Arrays.asList(client.getClaims()) : null; + if (claims != null && !claims.isEmpty()) { + List claimsDn = attributeService.getAttributesDn(claims); + client.setClaims(claimsDn.toArray(new String[claimsDn.size()])); + } + + List authorizedAcrValues = client.getAttributes().getAuthorizedAcrValues(); + if (authorizedAcrValues != null && !authorizedAcrValues.isEmpty()) { + authorizedAcrValues = new ArrayList<>(new HashSet<>(authorizedAcrValues)); // Remove repeated elements + client.getAttributes().setAuthorizedAcrValues(authorizedAcrValues); + } + logger.debug("Final client.getAttributes().getAuthorizedAcrValues():{}", + client.getAttributes().getAuthorizedAcrValues()); + + // Custom params + updateCustomAttributes(client); + + return client; + } + + private void updateCustomAttributes(Client client) { + logger.debug( + "ClientService::updateCustomAttributes() - client:{}, appConfiguration.getDynamicRegistrationCustomObjectClass():{},appConfiguration.getDynamicRegistrationCustomAttributes():{} ", + client, appConfiguration.getDynamicRegistrationCustomObjectClass(), + appConfiguration.getDynamicRegistrationCustomAttributes()); + + // custom object class + final String customOC = appConfiguration.getDynamicRegistrationCustomObjectClass(); + if (StringUtils.isNotBlank(customOC)) { + client.setCustomObjectClasses(new String[] { customOC }); + } + + // custom attributes (custom attributes must be in custom object class) + final List attrList = appConfiguration.getDynamicRegistrationCustomAttributes(); + if (attrList == null || attrList.isEmpty()) { + return; + } + + logger.debug("ClientService::updateCustomAttributes() - client.getCustomAttributes():{}, attrList:{}", + client.getCustomAttributes(), attrList); + for (String attr : attrList) { + logger.debug( + "ClientService::updateCustomAttributes() - attr:{}", attr); + + } + } }