diff --git a/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/java/org/wso2/carbon/identity/api/server/application/management/v1/core/ServerApplicationManagementService.java b/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/java/org/wso2/carbon/identity/api/server/application/management/v1/core/ServerApplicationManagementService.java index 059ec7e99d..f8f50cc71f 100644 --- a/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/java/org/wso2/carbon/identity/api/server/application/management/v1/core/ServerApplicationManagementService.java +++ b/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/java/org/wso2/carbon/identity/api/server/application/management/v1/core/ServerApplicationManagementService.java @@ -227,7 +227,8 @@ import static org.wso2.carbon.identity.organization.management.service.util.Utils.isLegacyAuthzRuntime; /** - * Calls internal osgi services to perform server application management related operations. + * Calls internal osgi services to perform server application management related + * operations. */ public class ServerApplicationManagementService { @@ -237,8 +238,8 @@ public class ServerApplicationManagementService { private final ServerApplicationMetadataService applicationMetadataService; public ServerApplicationManagementService(ApplicationManagementService applicationManagementService, - AuthorizedAPIManagementService authorizedAPIManagementService, - TemplateManager templateManager) { + AuthorizedAPIManagementService authorizedAPIManagementService, + TemplateManager templateManager) { this.applicationManagementService = applicationManagementService; this.authorizedAPIManagementService = authorizedAPIManagementService; @@ -262,11 +263,11 @@ public ServerApplicationManagementService(ApplicationManagementService applicati private static final String YML_FILE_EXTENSION = ".yml"; private static final String JSON_FILE_EXTENSION = ".json"; private static final String XML_FILE_EXTENSION = ".xml"; - private static final String[] VALID_MEDIA_TYPES_XML = {"application/xml", "text/xml"}; - private static final String[] VALID_MEDIA_TYPES_YAML = {"application/yaml", "text/yaml", "application/x-yaml"}; - private static final String[] VALID_MEDIA_TYPES_JSON = {"application/json", "text/json"}; - private static final Class[] INBOUND_CONFIG_PROTOCOLS = new Class[]{ServiceProvider.class, - SAMLSSOServiceProviderDTO.class, OAuthAppDO.class}; + private static final String[] VALID_MEDIA_TYPES_XML = { "application/xml", "text/xml" }; + private static final String[] VALID_MEDIA_TYPES_YAML = { "application/yaml", "text/yaml", "application/x-yaml" }; + private static final String[] VALID_MEDIA_TYPES_JSON = { "application/json", "text/json" }; + private static final Class[] INBOUND_CONFIG_PROTOCOLS = new Class[] { ServiceProvider.class, + SAMLSSOServiceProviderDTO.class, OAuthAppDO.class }; static { SUPPORTED_FILTER_ATTRIBUTES.add(NAME); @@ -284,14 +285,14 @@ public ServerApplicationManagementService(ApplicationManagementService applicati @Deprecated public ApplicationListResponse getAllApplications(Integer limit, Integer offset, String filter, String sortOrder, - String sortBy, String requiredAttributes) { + String sortBy, String requiredAttributes) { return getAllApplications(limit, offset, filter, sortOrder, sortBy, requiredAttributes, false); } public ApplicationListResponse getAllApplications(Integer limit, Integer offset, String filter, String sortOrder, - String sortBy, String requiredAttributes, - Boolean excludeSystemPortals) { + String sortBy, String requiredAttributes, + Boolean excludeSystemPortals) { handleNotImplementedCapabilities(sortOrder, sortBy); String tenantDomain = ContextLoader.getTenantDomainFromContext(); @@ -319,7 +320,7 @@ public ApplicationListResponse getAllApplications(Integer limit, Integer offset, excludeSystemPortals); ApplicationBasicInfo[] filteredAppList = applicationManagementService.getApplicationBasicInfo(tenantDomain, - username, filter, offset, limit, excludeSystemPortals); + username, filter, offset, limit, excludeSystemPortals); int resultsInCurrentPage = filteredAppList.length; List requestedAttributeList = new ArrayList<>(); @@ -347,7 +348,7 @@ public ApplicationListResponse getAllApplications(Integer limit, Integer offset, .count(resultsInCurrentPage) .applications(getApplicationListItems(serviceProviderList, requestedAttributeList)) .links(Util.buildPaginationLinks(limit, offset, totalResults, - APPLICATION_MANAGEMENT_PATH_COMPONENT, requiredAttributes, filter) + APPLICATION_MANAGEMENT_PATH_COMPONENT, requiredAttributes, filter) .entrySet() .stream() .map(link -> new Link().rel(link.getKey()).href(link.getValue())) @@ -359,7 +360,7 @@ public ApplicationListResponse getAllApplications(Integer limit, Integer offset, .count(resultsInCurrentPage) .applications(getApplicationListItems(filteredAppList)) .links(Util.buildPaginationLinks(limit, offset, totalResults, - APPLICATION_MANAGEMENT_PATH_COMPONENT, requiredAttributes, filter) + APPLICATION_MANAGEMENT_PATH_COMPONENT, requiredAttributes, filter) .entrySet() .stream() .map(link -> new Link().rel(link.getKey()).href(link.getValue())) @@ -411,7 +412,7 @@ private void validateRequiredAttributes(List requestedAttributeList) { } private List getSpWithRequiredAttributes(ApplicationBasicInfo[] filteredAppList, - List requestedAttributeList) + List requestedAttributeList) throws IdentityApplicationManagementException { List serviceProviderList = new ArrayList<>(); @@ -510,7 +511,8 @@ public ArrayList getConfiguredAuthenticators(Stri * Export an application identified by the applicationId, as an XML string. * * @param applicationId ID of the application to be exported. - * @param exportSecrets If True, all hashed or encrypted secrets will also be exported. + * @param exportSecrets If True, all hashed or encrypted secrets will also be + * exported. * @return XML string of the application. */ public String exportApplication(String applicationId, Boolean exportSecrets) { @@ -545,7 +547,8 @@ private boolean hasOperationScopeForSecretExport(boolean exportSecrets) { * Export an application identified by the applicationId, in the given format. * * @param applicationId ID of the application to be exported. - * @param exportSecrets If True, all hashed or encrypted secrets will also be exported. + * @param exportSecrets If True, all hashed or encrypted secrets will also be + * exported. * @param fileType The format of the exported string. * @return string of the application in the given format. */ @@ -608,8 +611,7 @@ private TransferResource generateFileFromModel(String fileType, ServiceProvider return new TransferResource( fileNameSB.toString(), fileContent.getBytes(StandardCharsets.UTF_8), - new MimeType("application/octet-stream") - ); + new MimeType("application/octet-stream")); } catch (MimeTypeParseException e) { throw new RuntimeException("Failed to parse MIME type", e); } @@ -627,8 +629,8 @@ private String parseXmlFromServiceProvider(ServiceProvider serviceProvider) { public void beforeMarshal(Object source) { if (source instanceof InboundAuthenticationConfig) { InboundAuthenticationConfig config = (InboundAuthenticationConfig) source; - for (InboundAuthenticationRequestConfig requestConfig - : config.getInboundAuthenticationRequestConfigs()) { + for (InboundAuthenticationRequestConfig requestConfig : config + .getInboundAuthenticationRequestConfigs()) { requestConfig.setInboundConfiguration(null); } } @@ -706,23 +708,27 @@ private String doImportApplication(InputStream fileInputStream, Attachment fileD String fileType = fileDetail.getDataHandler().getContentType(); ServiceProvider serviceProvider = parseSP(spFileContent, fileType, tenantDomain); - + + if (serviceProvider.getAccessUrl() != null) { + serviceProvider.setAccessUrl(StringUtils.trimToNull(serviceProvider.getAccessUrl())); + } /* - * internal_application_script_update scope is required when, performing adaptive script related operations. + * internal_application_script_update scope is required when, performing + * adaptive script related operations. * Validate the permission before allowing the operation. */ - ServiceProvider existingApp = - applicationManagementService.getServiceProvider(serviceProvider.getApplicationName(), tenantDomain); + ServiceProvider existingApp = applicationManagementService + .getServiceProvider(serviceProvider.getApplicationName(), tenantDomain); if (isAppUpdate && existingApp != null) { - LocalAndOutboundAuthenticationConfig existingAuthConfig = - existingApp.getLocalAndOutBoundAuthenticationConfig(); + LocalAndOutboundAuthenticationConfig existingAuthConfig = existingApp + .getLocalAndOutBoundAuthenticationConfig(); if (existingAuthConfig != null && existingAuthConfig.getAuthenticationScriptConfig() != null && existingAuthConfig.getAuthenticationScriptConfig().getContent() != null) { validateAuthenticationScriptUpdatePermission(); } } - LocalAndOutboundAuthenticationConfig updatingAuthConfig = - serviceProvider.getLocalAndOutBoundAuthenticationConfig(); + LocalAndOutboundAuthenticationConfig updatingAuthConfig = serviceProvider + .getLocalAndOutBoundAuthenticationConfig(); if (updatingAuthConfig != null && updatingAuthConfig.getAuthenticationScriptConfig() != null && updatingAuthConfig.getAuthenticationScriptConfig().getContent() != null) { validateAuthenticationScriptUpdatePermission(); @@ -838,8 +844,8 @@ private SpFileContent buildSpFileContent(InputStream fileInputStream, Attachment private APIError handleErrorResponse(ImportResponse importResponse) { - String errorCode = importResponse.getErrorCode() != null ? - importResponse.getErrorCode() : INVALID_REQUEST.getCode(); + String errorCode = importResponse.getErrorCode() != null ? importResponse.getErrorCode() + : INVALID_REQUEST.getCode(); String msg = "Error importing application from XML file."; String description = null; if (ArrayUtils.isNotEmpty(importResponse.getErrors())) { @@ -852,7 +858,8 @@ private APIError handleErrorResponse(ImportResponse importResponse) { public String createApplication(ApplicationModel applicationModel, String template) { /* - * Create application with the adaptive script requires the internal_application_script_update scope. + * Create application with the adaptive script requires the + * internal_application_script_update scope. * Validate the permission before allowing the update. */ AuthenticationSequence authSequence = applicationModel.getAuthenticationSequence(); @@ -866,8 +873,10 @@ public String createApplication(ApplicationModel applicationModel, String templa } /* - * CORS adding step should be moved to the service layer and the validation also should happen there. - * But for now we are handling the validation here. (This should move when the CORS management is + * CORS adding step should be moved to the service layer and the validation also + * should happen there. + * But for now we are handling the validation here. (This should move when the + * CORS management is * moved to the service layer). */ if (applicationModel.getInboundProtocolConfiguration() != null && @@ -907,6 +916,11 @@ public String createApplication(ApplicationModel applicationModel, String templa String applicationId = null; try { + // Trim access URL before persistence + if (applicationModel.getAccessUrl() != null) { + applicationModel.setAccessUrl(StringUtils.trimToNull(applicationModel.getAccessUrl())); + } + ApplicationDTO applicationDTO = new ApiModelToServiceProvider().apply(applicationModel); applicationId = applicationManagementService.createApplication(applicationDTO, tenantDomain, username); @@ -943,7 +957,8 @@ public String createApplication(ApplicationModel applicationModel, String templa throw buildClientError(e, "Error creating application. Allow CORS origins update failed."); } catch (Throwable e) { /* - * For more information read https://github.com/wso2/product-is/issues/12579. This is to overcome the + * For more information read https://github.com/wso2/product-is/issues/12579. + * This is to overcome the * above issue. */ if (applicationId != null) { @@ -959,7 +974,8 @@ public void patchApplication(String applicationId, ApplicationPatchModel applica ServiceProvider appToUpdate = cloneApplication(applicationId); /* - * Updating the adaptive script requires the internal_application_script_update scope. + * Updating the adaptive script requires the internal_application_script_update + * scope. * Validate the permission before allowing the update. */ if (isScriptUpdating(appToUpdate, applicationPatchModel)) { @@ -982,6 +998,14 @@ public void patchApplication(String applicationId, ApplicationPatchModel applica } if (applicationPatchModel != null) { + // Trim access URL before persistence + if (applicationPatchModel.getAccessUrl() != null) { + applicationModel.setAccessUrl(StringUtils.trimToNull(applicationModel.getAccessUrl())); + + } + if (log.isDebugEnabled()) { + log.debug("Patching application with id: {}.", applicationId); + } new UpdateServiceProvider().apply(appToUpdate, applicationPatchModel); } @@ -1016,12 +1040,13 @@ private boolean isScriptUpdating(ServiceProvider existingApp, ApplicationPatchMo return true; } - // Check whether the authentication sequence is reverting to default when current authentication sequence + // Check whether the authentication sequence is reverting to default when + // current authentication sequence // has a script configured. LocalAndOutboundAuthenticationConfig existingAuthConfig = existingApp.getLocalAndOutBoundAuthenticationConfig(); String currentAuthenticationType = existingAuthConfig.getAuthenticationType(); - boolean isRevertToDefault = - appPatchModel.getAuthenticationSequence().getType() == AuthenticationSequence.TypeEnum.DEFAULT && + boolean isRevertToDefault = appPatchModel.getAuthenticationSequence() + .getType() == AuthenticationSequence.TypeEnum.DEFAULT && StringUtils.isNotBlank(currentAuthenticationType) && !AuthenticationSequence.TypeEnum.DEFAULT.name().equals(currentAuthenticationType); @@ -1045,8 +1070,8 @@ private void validateAuthenticationScriptUpdatePermission() { private boolean isSkippedEnforcingScriptUpdatePermission() { - String skipEnforceScriptUpdatePermissionValue = - IdentityUtil.getProperty("ApplicationMgt.SkipEnforceScriptUpdatePermission"); + String skipEnforceScriptUpdatePermissionValue = IdentityUtil + .getProperty("ApplicationMgt.SkipEnforceScriptUpdatePermission"); if (StringUtils.isBlank(skipEnforceScriptUpdatePermissionValue)) { return false; } @@ -1055,7 +1080,7 @@ private boolean isSkippedEnforcingScriptUpdatePermission() { } private void restrictRoleAssociationUpdateInOrgAudience(String applicationId, - ApplicationPatchModel applicationPatchModel) { + ApplicationPatchModel applicationPatchModel) { ServiceProvider application = getServiceProvider(applicationId); String allowedAudience = application.getAssociatedRolesConfig().getAllowedAudience(); @@ -1133,8 +1158,8 @@ public void deleteApplication(String applicationId) { CORSManagementService corsManagementService = ApplicationManagementServiceHolder.getCorsManagementService(); try { // Delete CORS origins for OIDC Apps. - List existingCORSOrigins = - corsManagementService.getApplicationCORSOrigins(applicationId, tenantDomain); + List existingCORSOrigins = corsManagementService.getApplicationCORSOrigins(applicationId, + tenantDomain); if (!CollectionUtils.isEmpty(existingCORSOrigins)) { ApplicationManagementServiceHolder.getCorsManagementService() .deleteCORSOrigins(applicationId, @@ -1149,8 +1174,9 @@ public void deleteApplication(String applicationId) { throw handleIdentityApplicationManagementException(e, msg); } catch (CORSManagementServiceClientException e) { /* - For not existing application scenarios the following error code will be returned. To preserve the behaviour - we need to return 204. + * For not existing application scenarios the following error code will be + * returned. To preserve the behaviour + * we need to return 204. */ if (ERROR_CODE_INVALID_APP_ID.getCode().equals(e.getErrorCode())) { if (log.isDebugEnabled()) { @@ -1286,7 +1312,8 @@ public List getInboundProtocols(String applicationId) { public void putInboundOAuthConfiguration(String applicationId, OpenIDConnectConfiguration oidcConfigModel) { /* - * CORS updating step should be moved to the service layer and the validation also should happen there. + * CORS updating step should be moved to the service layer and the validation + * also should happen there. * But for now we are handling the validation here. */ validateCORSOrigins(oidcConfigModel.getAllowedOrigins()); @@ -1299,7 +1326,7 @@ public void putInboundSAMLConfiguration(String applicationId, SAML2Configuration } public void putInboundPassiveSTSConfiguration(String applicationId, - PassiveStsConfiguration passiveStsConfiguration) { + PassiveStsConfiguration passiveStsConfiguration) { putInbound(applicationId, passiveStsConfiguration, PassiveSTSInboundFunctions::putPassiveSTSInbound); } @@ -1310,7 +1337,7 @@ public void putInboundWSTrustConfiguration(String applicationId, WSTrustConfigur } public void updateCustomInbound(String applicationId, String inboundType, - CustomInboundProtocolConfiguration customInbound) { + CustomInboundProtocolConfiguration customInbound) { if (isUnknownInboundType(inboundType)) { throw buildBadRequestError("Unknown inbound type: " + inboundType); @@ -1323,7 +1350,8 @@ public void updateCustomInbound(String applicationId, String inboundType, /** * Create a new template object. * - * @param applicationTemplateModel applicationTemplateModel from which the template is created. + * @param applicationTemplateModel applicationTemplateModel from which the + * template is created. * @return unique id of the newly created template. */ public String createApplicationTemplate(ApplicationTemplateModel applicationTemplateModel) { @@ -1344,17 +1372,17 @@ public String createApplicationTemplate(ApplicationTemplateModel applicationTemp * @param offset number of records to skip for pagination. * @return ApplicationTemplatesList containing the list of templates. */ - public ApplicationTemplatesList listApplicationTemplates(Integer limit, Integer offset, SearchContext - searchContext) { + public ApplicationTemplatesList listApplicationTemplates(Integer limit, Integer offset, + SearchContext searchContext) { validatePaginationSupport(limit, offset); try { - List