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 0aedf42836a..d2a2972f1c2 100644 --- a/jans-config-api/docs/jans-config-api-swagger-auto.yaml +++ b/jans-config-api/docs/jans-config-api-swagger-auto.yaml @@ -7178,20 +7178,20 @@ components: $ref: '#/components/schemas/AttributeValidation' tooltip: type: string - whitePagesCanView: - type: boolean adminCanEdit: type: boolean - adminCanView: - type: boolean userCanView: type: boolean - userCanAccess: - type: boolean adminCanAccess: type: boolean userCanEdit: type: boolean + adminCanView: + type: boolean + userCanAccess: + type: boolean + whitePagesCanView: + type: boolean baseDn: type: string PatchRequest: @@ -7355,6 +7355,7 @@ components: - client_credentials - refresh_token - urn:ietf:params:oauth:grant-type:uma-ticket + - urn:ietf:params:oauth:grant-type:token-exchange - urn:openid:params:grant-type:ciba - urn:ietf:params:oauth:grant-type:device_code applicationType: @@ -7517,8 +7518,6 @@ components: format: int32 displayName: type: string - tokenBindingSupported: - type: boolean authenticationMethod: type: string enum: @@ -7530,6 +7529,8 @@ components: - tls_client_auth - self_signed_tls_client_auth - none + tokenBindingSupported: + type: boolean baseDn: type: string inum: @@ -7796,6 +7797,7 @@ components: - client_credentials - refresh_token - urn:ietf:params:oauth:grant-type:uma-ticket + - urn:ietf:params:oauth:grant-type:token-exchange - urn:openid:params:grant-type:ciba - urn:ietf:params:oauth:grant-type:device_code subjectTypesSupported: @@ -8056,6 +8058,7 @@ components: - client_credentials - refresh_token - urn:ietf:params:oauth:grant-type:uma-ticket + - urn:ietf:params:oauth:grant-type:token-exchange - urn:openid:params:grant-type:ciba - urn:ietf:params:oauth:grant-type:device_code cssLocation: @@ -8171,6 +8174,10 @@ components: type: boolean disableU2fEndpoint: type: boolean + rotateDeviceSecret: + type: boolean + returnDeviceSecretFromAuthzEndpoint: + type: boolean dcrSignatureValidationEnabled: type: boolean dcrSignatureValidationSharedSecret: @@ -8328,15 +8335,6 @@ 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 @@ -8364,6 +8362,15 @@ components: - STAT - PAR - SSA + allResponseTypesSupported: + uniqueItems: true + type: array + items: + type: string + enum: + - code + - token + - id_token AuthenticationFilter: required: - baseDn @@ -8620,13 +8627,13 @@ components: type: boolean internal: type: boolean - locationPath: - type: string locationType: type: string enum: - ldap - file + locationPath: + type: string baseDn: type: string ScriptError: @@ -9028,6 +9035,10 @@ components: type: object additionalProperties: type: string + deviceSecrets: + type: array + items: + type: string expirationDate: type: string format: date-time diff --git a/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/service/user/UserManagementService.java b/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/service/user/UserManagementService.java index 2e16445202b..c3ba34978c1 100644 --- a/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/service/user/UserManagementService.java +++ b/jans-config-api/plugins/admin-ui-plugin/src/main/java/io/jans/ca/plugin/adminui/service/user/UserManagementService.java @@ -45,7 +45,7 @@ public AdminRole getRoleObjByName(String role) throws ApplicationException { List roles = adminConf.getDynamic().getRoles().stream().filter(ele -> ele.getRole().equals(role)).collect(Collectors.toList()); if (roles.isEmpty()) { log.error(ErrorResponse.ROLE_NOT_FOUND.getDescription()); - throw new ApplicationException(Response.Status.BAD_REQUEST.getStatusCode(), ErrorResponse.ROLE_NOT_FOUND.getDescription()); + throw new ApplicationException(Response.Status.NOT_FOUND.getStatusCode(), ErrorResponse.ROLE_NOT_FOUND.getDescription()); } return roles.stream().findFirst().get(); } catch (ApplicationException e) { @@ -160,7 +160,7 @@ public AdminPermission getPermissionObjByName(String permission) throws Applicat List permissions = adminConf.getDynamic().getPermissions().stream().filter(ele -> ele.getPermission().equals(permission)).collect(Collectors.toList()); if (permissions.isEmpty()) { log.error(ErrorResponse.ROLE_NOT_FOUND.getDescription()); - throw new ApplicationException(Response.Status.BAD_REQUEST.getStatusCode(), ErrorResponse.ROLE_NOT_FOUND.getDescription()); + throw new ApplicationException(Response.Status.NOT_FOUND.getStatusCode(), ErrorResponse.PERMISSION_NOT_FOUND.getDescription()); } return permissions.stream().findFirst().get(); } catch (ApplicationException e) { @@ -336,9 +336,12 @@ public RolePermissionMapping getAdminUIRolePermissionsMapping(String role) throw if (roleScopeMapping.isEmpty()) { log.error(ErrorResponse.ROLE_PERMISSION_MAP_NOT_FOUND.getDescription()); - throw new ApplicationException(Response.Status.BAD_REQUEST.getStatusCode(), ErrorResponse.ROLE_PERMISSION_MAP_NOT_FOUND.getDescription()); + throw new ApplicationException(Response.Status.NOT_FOUND.getStatusCode(), ErrorResponse.ROLE_PERMISSION_MAP_NOT_FOUND.getDescription()); } return roleScopeMapping.stream().findFirst().get(); + } catch (ApplicationException e) { + log.error(ErrorResponse.GET_ADMIUI_PERMISSIONS_ERROR.getDescription()); + throw e; } catch (Exception e) { log.error(ErrorResponse.ERROR_READING_ROLE_PERMISSION_MAP.getDescription(), e); throw new ApplicationException(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), ErrorResponse.ERROR_READING_ROLE_PERMISSION_MAP.getDescription()); diff --git a/jans-config-api/plugins/docs/fido2-plugin-swagger.yaml b/jans-config-api/plugins/docs/fido2-plugin-swagger.yaml index 000410e1bc7..4061cb382ff 100644 --- a/jans-config-api/plugins/docs/fido2-plugin-swagger.yaml +++ b/jans-config-api/plugins/docs/fido2-plugin-swagger.yaml @@ -29,7 +29,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/DbApplicationConfiguration' + $ref: '#/components/schemas/AppConfiguration' "401": description: Unauthorized "500": @@ -48,14 +48,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/DbApplicationConfiguration' + $ref: '#/components/schemas/AppConfiguration' responses: "200": description: Fido2Config content: application/json: schema: - type: string + $ref: '#/components/schemas/AppConfiguration' "401": description: Unauthorized "500": @@ -94,16 +94,83 @@ paths: - https://jans.io/oauth/config/fido2.readonly components: schemas: - DbApplicationConfiguration: + AppConfiguration: type: object properties: - dn: + issuer: type: string - dynamicConf: + baseEndpoint: type: string - revision: + cleanServiceInterval: + type: integer + format: int32 + cleanServiceBatchChunkSize: type: integer - format: int64 + format: int32 + useLocalCache: + type: boolean + disableJdkLogger: + type: boolean + loggingLevel: + type: string + loggingLayout: + type: string + externalLoggerConfiguration: + type: string + metricReporterInterval: + type: integer + format: int32 + metricReporterKeepDataDays: + type: integer + format: int32 + metricReporterEnabled: + type: boolean + personCustomObjectClassList: + type: array + items: + type: string + fido2Configuration: + $ref: '#/components/schemas/Fido2Configuration' + Fido2Configuration: + type: object + properties: + authenticatorCertsFolder: + type: string + mdsAccessToken: + type: string + mdsCertsFolder: + type: string + mdsTocsFolder: + type: string + checkU2fAttestations: + type: boolean + userAutoEnrollment: + type: boolean + unfinishedRequestExpiration: + type: integer + format: int32 + authenticationHistoryExpiration: + type: integer + format: int32 + serverMetadataFolder: + type: string + requestedCredentialTypes: + type: array + items: + type: string + requestedParties: + type: array + items: + $ref: '#/components/schemas/RequestedParty' + RequestedParty: + type: object + properties: + name: + type: string + domains: + type: array + items: + type: string Fido2RegistrationData: type: object properties: diff --git a/jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2ConfigResource.java b/jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2ConfigResource.java index 3f912622df1..0a10e1c770b 100644 --- a/jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2ConfigResource.java +++ b/jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2ConfigResource.java @@ -6,16 +6,13 @@ package io.jans.configapi.plugin.fido2.rest; -import com.fasterxml.jackson.core.JsonProcessingException; - -import io.jans.config.oxtrust.DbApplicationConfiguration; import io.jans.configapi.core.rest.BaseResource; import io.jans.configapi.core.rest.ProtectedApi; import io.jans.configapi.plugin.fido2.service.Fido2Service; import io.jans.configapi.plugin.fido2.util.Fido2Util; import io.jans.configapi.util.ApiAccessConstants; import io.jans.configapi.plugin.fido2.util.Constants; -import io.jans.configapi.core.util.Jackson; +import io.jans.fido2.model.conf.AppConfiguration; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.parameters.RequestBody; @@ -53,33 +50,33 @@ public class Fido2ConfigResource extends BaseResource { "Fido2 - Configuration" }, security = @SecurityRequirement(name = "oauth2", scopes = { ApiAccessConstants.FIDO2_CONFIG_READ_ACCESS })) @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = DbApplicationConfiguration.class))), + @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = AppConfiguration.class))), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "500", description = "InternalServerError") }) @GET @ProtectedApi(scopes = { ApiAccessConstants.FIDO2_CONFIG_READ_ACCESS }) - public Response getFido2Configuration() throws JsonProcessingException { - DbApplicationConfiguration dbApplicationConfiguration = this.fido2Service.find(); - logger.debug("FIDO2 details dbApplicationConfiguration.getDynamicConf():{}", - dbApplicationConfiguration.getDynamicConf()); - return Response.ok(Jackson.asJsonNode(dbApplicationConfiguration.getDynamicConf())).build(); + public Response getFido2Configuration() { + AppConfiguration appConfiguration = this.fido2Service.find(); + logger.debug("FIDO2 details appConfiguration():{}", appConfiguration); + return Response.ok(appConfiguration).build(); } @Operation(summary = "Updates Fido2 configuration properties", description = "Updates Fido2 configuration properties", operationId = "put-properties-fido2", tags = { "Fido2 - Configuration" }, security = @SecurityRequirement(name = "oauth2", scopes = { ApiAccessConstants.FIDO2_CONFIG_WRITE_ACCESS })) - @RequestBody(description = "Fido2Config", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = DbApplicationConfiguration.class))) + @RequestBody(description = "Fido2Config", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = AppConfiguration.class))) @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Fido2Config", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = String.class))), + @ApiResponse(responseCode = "200", description = "Fido2Config", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = AppConfiguration.class))), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "500", description = "InternalServerError") }) @PUT @ProtectedApi(scopes = { ApiAccessConstants.FIDO2_CONFIG_WRITE_ACCESS }) - public Response updateFido2Configuration(@NotNull String fido2ConfigJson) { - logger.debug("FIDO2 details to be updated - fido2ConfigJson:{} ", fido2ConfigJson); - checkResourceNotNull(fido2ConfigJson, FIDO2_CONFIGURATION); - this.fido2Service.merge(fido2ConfigJson); - return Response.ok(fido2ConfigJson).build(); + public Response updateFido2Configuration(@NotNull AppConfiguration appConfiguration) { + logger.debug("FIDO2 details to be updated - appConfiguration:{} ", appConfiguration); + checkResourceNotNull(appConfiguration, FIDO2_CONFIGURATION); + this.fido2Service.merge(appConfiguration); + appConfiguration = this.fido2Service.find(); + return Response.ok(appConfiguration).build(); } } \ No newline at end of file diff --git a/jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/service/Fido2Service.java b/jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/service/Fido2Service.java index 5f76582357f..4d0aec3d671 100644 --- a/jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/service/Fido2Service.java +++ b/jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/service/Fido2Service.java @@ -6,8 +6,9 @@ package io.jans.configapi.plugin.fido2.service; -import io.jans.config.oxtrust.DbApplicationConfiguration; import io.jans.configapi.configuration.ConfigurationFactory; +import io.jans.fido2.model.conf.Conf; +import io.jans.fido2.model.conf.AppConfiguration; import io.jans.orm.PersistenceEntryManager; import io.jans.orm.exception.BasePersistenceException; import org.slf4j.Logger; @@ -27,21 +28,30 @@ public class Fido2Service { @Inject ConfigurationFactory configurationFactory; - public DbApplicationConfiguration find() { + public Conf findConf() { try { String configurationDn = configurationFactory.getBaseConfiguration() .getString("fido2_ConfigurationEntryDN"); - return persistenceManager.find(DbApplicationConfiguration.class, configurationDn); + return persistenceManager.find(Conf.class, configurationDn); } catch (BasePersistenceException var3) { logger.error("Failed to load Fido2 configuration from LDAP"); return null; } } - public void merge(String fido2ConfigJson) { - DbApplicationConfiguration fido2Configuration = this.find(); - fido2Configuration.setDynamicConf(fido2ConfigJson); - fido2Configuration.setRevision(fido2Configuration.getRevision() + 1L); - persistenceManager.merge(fido2Configuration); + public AppConfiguration find() { + final Conf conf = findConf(); + return conf.getDynamicConf(); + } + + public void mergeConf(Conf conf) { + conf.setRevision(conf.getRevision() + 1); + persistenceManager.merge(conf); + } + + public void merge(AppConfiguration fido2ConfigJson) { + Conf conf = this.findConf(); + conf.setDynamicConf(fido2ConfigJson); + mergeConf(conf); } } diff --git a/jans-config-api/server/src/main/java/io/jans/configapi/configuration/ObjectMapperContextResolver.java b/jans-config-api/server/src/main/java/io/jans/configapi/configuration/ObjectMapperContextResolver.java index 5957e167cb4..e350a5fa4cd 100644 --- a/jans-config-api/server/src/main/java/io/jans/configapi/configuration/ObjectMapperContextResolver.java +++ b/jans-config-api/server/src/main/java/io/jans/configapi/configuration/ObjectMapperContextResolver.java @@ -10,6 +10,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.MapperFeature; import java.text.SimpleDateFormat; import jakarta.inject.Singleton; @@ -29,8 +30,10 @@ public ObjectMapper getContext(Class type) { return defaultObjectMapper; } + @SuppressWarnings("deprecation") public static ObjectMapper createDefaultMapper() { ObjectMapper mapper = new ObjectMapper(); + mapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT); 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 58f6f5702de..bba5890f4fc 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 @@ -212,9 +212,10 @@ public Client setClientDefaultAttributes(Client client, boolean update) { claimsRedirectUris = new ArrayList<>(new HashSet<>(claimsRedirectUris)); // Remove repeated elements client.setClaimRedirectUris(claimsRedirectUris.toArray(new String[0])); } - logger.trace("After setting client.getApplicationType:{}, client.getRedirectUris():{}, client.getClaimRedirectUris():{}", + logger.trace( + "After setting client.getApplicationType:{}, client.getRedirectUris():{}, client.getClaimRedirectUris():{}", client.getApplicationType(), client.getRedirectUris(), client.getClaimRedirectUris()); - + client.setApplicationType( client.getApplicationType() != null ? client.getApplicationType() : ApplicationType.WEB); @@ -222,14 +223,14 @@ public Client setClientDefaultAttributes(Client client, boolean update) { client.setSectorIdentifierUri(client.getSectorIdentifierUri()); } - logger.trace("client.getApplicationType():{}, client.getResponseTypes():{}, client.getGrantTypes():{}",client.getApplicationType(), client.getResponseTypes(), - client.getGrantTypes()); + logger.trace("client.getApplicationType():{}, client.getResponseTypes():{}, client.getGrantTypes():{}", + client.getApplicationType(), client.getResponseTypes(), client.getGrantTypes()); Set responseTypeSet = client.getResponseTypes() != null ? new HashSet<>(Arrays.asList(client.getResponseTypes())) - : null; + : new HashSet<>(); Set grantTypeSet = client.getGrantTypes() != null ? new HashSet<>(Arrays.asList(client.getGrantTypes())) - : null; + : new HashSet<>(); if (isTrue(appConfiguration.getGrantTypesAndResponseTypesAutofixEnabled())) { if (isTrue(appConfiguration.getClientRegDefaultToCodeFlowWithRefresh())) { @@ -255,13 +256,13 @@ public Client setClientDefaultAttributes(Client client, boolean update) { responseTypeSet.retainAll(appConfiguration.getAllResponseTypesSupported()); grantTypeSet.retainAll(appConfiguration.getGrantTypesSupported()); - logger.trace("After setting - client.getResponseTypes():{}, client.getGrantTypes():{}", client.getResponseTypes(), - client.getGrantTypes()); - + logger.trace("After setting - client.getResponseTypes():{}, client.getGrantTypes():{}", + client.getResponseTypes(), client.getGrantTypes()); + Set dynamicGrantTypeDefault = appConfiguration.getDynamicGrantTypeDefault(); grantTypeSet.retainAll(dynamicGrantTypeDefault); - if (!update || !responseTypeSet.isEmpty()) { + if (!update || (responseTypeSet != null && !responseTypeSet.isEmpty())) { client.setResponseTypes(responseTypeSet.toArray(new ResponseType[0])); } if (!update || (isTrue(appConfiguration.getEnableClientGrantTypeUpdate())) @@ -298,7 +299,8 @@ public Client setClientDefaultAttributes(Client client, boolean update) { client.setGroups(new HashSet<>(groups).toArray(new String[0])); // remove duplicates } - logger.debug("client.getGroups():{}, client.getPostLogoutRedirectUris():{}", client.getGroups(), client.getPostLogoutRedirectUris()); + logger.debug("client.getGroups():{}, client.getPostLogoutRedirectUris():{}", client.getGroups(), + client.getPostLogoutRedirectUris()); List postLogoutRedirectUris = client.getPostLogoutRedirectUris() != null ? Arrays.asList(client.getPostLogoutRedirectUris()) : null; @@ -320,16 +322,18 @@ public Client setClientDefaultAttributes(Client client, boolean update) { authorizedOrigins = new ArrayList<>(new HashSet<>(authorizedOrigins)); // Remove repeated elements client.setAuthorizedOrigins(authorizedOrigins.toArray(new String[authorizedOrigins.size()])); } - - logger.debug("client.getScopes():{}, appConfiguration.getDynamicRegistrationScopesParamEnabled():{}",client.getScopes(), appConfiguration.getDynamicRegistrationScopesParamEnabled()); - + + logger.debug("client.getScopes():{}, appConfiguration.getDynamicRegistrationScopesParamEnabled():{}", + client.getScopes(), appConfiguration.getDynamicRegistrationScopesParamEnabled()); + 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()])); } - logger.debug("client.getClaims():{}, client.getAttributes().getAuthorizedAcrValues():{}",client.getClaims(), client.getAttributes().getAuthorizedAcrValues()); - + logger.debug("client.getClaims():{}, client.getAttributes().getAuthorizedAcrValues():{}", client.getClaims(), + client.getAttributes().getAuthorizedAcrValues()); + List authorizedAcrValues = client.getAttributes().getAuthorizedAcrValues(); if (authorizedAcrValues != null && !authorizedAcrValues.isEmpty()) { authorizedAcrValues = new ArrayList<>(new HashSet<>(authorizedAcrValues)); // Remove repeated elements @@ -365,8 +369,7 @@ private void updateCustomAttributes(Client client) { logger.debug("ClientService::updateCustomAttributes() - client.getCustomAttributes():{}, attrList:{}", client.getCustomAttributes(), attrList); for (String attr : attrList) { - logger.debug( - "ClientService::updateCustomAttributes() - attr:{}", attr); + logger.debug("ClientService::updateCustomAttributes() - attr:{}", attr); } }