From 8a2cbb2c7fee6fb9cb2d3f39345288fb993c2b8d Mon Sep 17 00:00:00 2001 From: Ashan Thamara Palihakkara <75057725+ashanthamara@users.noreply.github.com> Date: Sun, 3 Nov 2024 11:04:14 +0530 Subject: [PATCH] Refactor application-mgt component to utilize certificate-mgt component for certificate management (#6087) * Add certificate-mgt dependency to application-mgt component * Refactor application-mgt to untilize certificate-mgt component for application certificate management * Add sonarcloud suggestions * Remove unused imports * Add unit tests for application certificate management * Modify unit tests * Refactor application-certificate unit tests * Improve line coverage * Add sonarCloud suggestion to reduce method complexity * Change CERTIFICATE property name to the defined constant variable * address comments * Remove schema change * Add comments to the test methods * Improve assertions * Minor improvement --- .../pom.xml | 7 + .../mgt/ApplicationManagementServiceImpl.java | 13 - .../mgt/dao/impl/ApplicationDAOImpl.java | 423 ++++++++---------- .../mgt/dao/impl/ApplicationMgtDBQueries.java | 28 ++ ...ApplicationManagementServiceComponent.java | 23 + ...ationManagementServiceComponentHolder.java | 22 + .../ApplicationManagementServiceImplTest.java | 319 +++++++++++++ 7 files changed, 581 insertions(+), 254 deletions(-) diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/pom.xml b/components/application-mgt/org.wso2.carbon.identity.application.mgt/pom.xml index 3cfed6bb713d..cb58488e74a9 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/pom.xml +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/pom.xml @@ -191,6 +191,10 @@ org.wso2.carbon.identity.framework org.wso2.carbon.identity.secret.mgt.core + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.certificate.management + @@ -260,6 +264,9 @@ version="${org.wso2.carbon.identity.organization.management.core.version.range}", org.wso2.carbon.identity.api.resource.mgt.model; version="${carbon.identity.package.import.version.range}", org.wso2.carbon.identity.api.resource.mgt.util; version="${carbon.identity.package.import.version.range}", + org.wso2.carbon.identity.certificate.management.service; version="${carbon.identity.package.import.version.range}", + org.wso2.carbon.identity.certificate.management.exception; version="${carbon.identity.package.import.version.range}", + org.wso2.carbon.identity.certificate.management.model; version="${carbon.identity.package.import.version.range}", !org.wso2.carbon.identity.application.mgt.internal, diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImpl.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImpl.java index a9e845f64e23..15cfd3acc428 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImpl.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImpl.java @@ -173,7 +173,6 @@ import static org.wso2.carbon.identity.application.mgt.inbound.InboundFunctions.updateOrInsertInbound; import static org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils.triggerAuditLogEvent; import static org.wso2.carbon.identity.core.util.IdentityUtil.getInitiatorId; -import static org.wso2.carbon.identity.core.util.IdentityUtil.isValidPEMCertificate; import static org.wso2.carbon.identity.role.v2.mgt.core.RoleConstants.Error.ROLE_MANAGEMENT_ERROR_CODE_PREFIX; import static org.wso2.carbon.identity.role.v2.mgt.core.RoleConstants.Error.ROLE_NOT_FOUND; import static org.wso2.carbon.utils.multitenancy.MultitenantConstants.SUPER_TENANT_DOMAIN_NAME; @@ -3036,7 +3035,6 @@ private void doPreUpdateChecks(String storedAppName, ServiceProvider updatedApp, validateAuthorization(updatedAppName, storedAppName, username, tenantDomain); validateAppName(storedAppName, updatedApp, tenantDomain); - validateApplicationCertificate(updatedApp, tenantDomain); boolean isValid = isAssociatedRolesConfigValid(updatedApp, tenantDomain); if (!isValid) { throw new IdentityApplicationManagementClientException( @@ -3058,17 +3056,6 @@ private void updateApplicationPermissions(ServiceProvider updatedApp, String upd } } - private void validateApplicationCertificate(ServiceProvider updatedApp, - String tenantDomain) throws IdentityApplicationManagementException { - - if (!isValidPEMCertificate(updatedApp.getCertificateContent())) { - String error = "Provided application certificate for application with name: %s in tenantDomain: %s " + - "is malformed."; - throw buildClientException(INVALID_REQUEST, - String.format(error, updatedApp.getApplicationName(), tenantDomain)); - } - } - private void validateApplicationConfigurations(ServiceProvider application, String tenantDomain, String username) throws IdentityApplicationManagementException { diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java index 063bb4bd6a96..8579289fd2f3 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java @@ -80,6 +80,9 @@ import org.wso2.carbon.identity.base.AuthenticatorPropertyConstants.DefinedByType; import org.wso2.carbon.identity.base.IdentityException; import org.wso2.carbon.identity.base.IdentityRuntimeException; +import org.wso2.carbon.identity.certificate.management.exception.CertificateMgtClientException; +import org.wso2.carbon.identity.certificate.management.exception.CertificateMgtException; +import org.wso2.carbon.identity.certificate.management.model.Certificate; import org.wso2.carbon.identity.core.CertificateRetrievingException; import org.wso2.carbon.identity.core.URLBuilderException; import org.wso2.carbon.identity.core.model.ExpressionNode; @@ -105,11 +108,9 @@ import org.wso2.carbon.utils.DBUtils; import org.wso2.carbon.utils.multitenancy.MultitenantConstants; -import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.sql.Connection; import java.sql.PreparedStatement; @@ -126,9 +127,11 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.OptionalInt; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.IntStream; import java.util.stream.Stream; import static java.util.Objects.isNull; @@ -150,6 +153,7 @@ import static org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants.Error.INVALID_FILTER; import static org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants.Error.INVALID_LIMIT; import static org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants.Error.INVALID_OFFSET; +import static org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants.Error.INVALID_REQUEST; import static org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants.Error.SORTING_NOT_IMPLEMENTED; import static org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants.ISSUER_SP_PROPERTY_NAME; import static org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants.IS_API_BASED_AUTHENTICATION_ENABLED_DISPLAY_NAME; @@ -367,7 +371,7 @@ public int createApplication(ServiceProvider application, String tenantDomain) IdentityDatabaseUtil.commitTransaction(connection); return result.getApplicationId(); } catch (SQLException e) { - IdentityDatabaseUtil.rollbackTransaction(connection); + rollbackAddApplicationTransaction(connection, application, tenantDomain); if (isApplicationConflict(e)) { throw new IdentityApplicationManagementClientException(APPLICATION_ALREADY_EXISTS.getCode(), "Application already exists with name: " + application.getApplicationName() @@ -569,6 +573,9 @@ public void updateApplication(ServiceProvider serviceProvider, String tenantDoma addApplicationConfigurations(connection, serviceProvider, tenantDomain); IdentityDatabaseUtil.commitTransaction(connection); + } catch (IdentityApplicationManagementClientException e) { + IdentityDatabaseUtil.rollbackTransaction(connection); + throw e; } catch (SQLException | UserStoreException | IdentityApplicationManagementException e) { IdentityDatabaseUtil.rollbackTransaction(connection); throw new IdentityApplicationManagementException("Failed to update application id: " + applicationId, e); @@ -594,7 +601,7 @@ private void addApplicationConfigurations(Connection connection, ServiceProvider // you can change application name, description, isSasApp... updateBasicApplicationData(serviceProvider, connection); - updateApplicationCertificate(serviceProvider, tenantID, connection); + updateApplicationCertificate(serviceProvider, tenantID); updateInboundProvisioningConfiguration(applicationId, serviceProvider.getInboundProvisioningConfig(), connection); @@ -754,85 +761,101 @@ private void updateConsentPurposeConfiguration(Connection connection, int applic } /** - * Updates the application certificate record in the database, with the certificate in the given service provider - * object. If the certificate content is available in the given service provider and a reference is not available, - * create a new database record for the certificate and add the reference to the given service provider object. + * Updates the application certificate record in the database using ApplicationCertificateMgtService, + * with the certificate in the given service provider object. If the certificate content is available in the given + * service provider and a reference is not available, create a new database record for the certificate and add the + * reference to the given service provider object. * - * @param serviceProvider - * @param tenantID - * @param connection - * @throws SQLException + * @param serviceProvider Service provider object. + * @param tenantID Tenant ID. + * @throws IdentityApplicationManagementException If an error occurs while updating the certificate. */ - private void updateApplicationCertificate(ServiceProvider serviceProvider, int tenantID, - Connection connection) - throws SQLException, IdentityApplicationManagementException { + private void updateApplicationCertificate(ServiceProvider serviceProvider, int tenantID) + throws IdentityApplicationManagementException { - // If the certificate content is empty, remove the certificate reference property if exists. - // And remove the certificate. if (StringUtils.isBlank(serviceProvider.getCertificateContent())) { + // Remove the certificate reference property if exists and remove the certificate. + removeCertificateReferenceAndDelete(serviceProvider, tenantID); + } else { + String certificateReferenceIdString = getCertificateReferenceID(serviceProvider.getSpProperties()); + if (certificateReferenceIdString != null) { + // If there is a reference, update the relevant existing certificate record. + updateCertificate(certificateReferenceIdString, serviceProvider.getCertificateContent(), tenantID); + } else { + // There is no existing reference. Persisting the certificate as a new record. + persistApplicationCertificate(serviceProvider, tenantID); + } + } + } - ServiceProviderProperty[] serviceProviderProperties = serviceProvider.getSpProperties(); + /** + * Removes the certificate reference property from the given service provider object and deletes the certificate + * record from the database. + * + * @param serviceProvider Service provider object. + * @param tenantID Tenant ID. + * @throws IdentityApplicationManagementException If an error occurs while removing the certificate reference. + */ + private void removeCertificateReferenceAndDelete(ServiceProvider serviceProvider, int tenantID) + throws IdentityApplicationManagementException { - if (serviceProviderProperties != null) { + ServiceProviderProperty[] spProperties = serviceProvider.getSpProperties(); - // Get the index of the certificate reference property index in the properties array. - int certificateReferenceIdIndex = -1; - String certificateReferenceId = null; - for (int i = 0; i < serviceProviderProperties.length; i++) { - if ("CERTIFICATE".equals(serviceProviderProperties[i].getName())) { - certificateReferenceIdIndex = i; - certificateReferenceId = serviceProviderProperties[i].getValue(); - break; - } - } + if (spProperties == null) { + return; + } - // If there is a certificate reference, remove it from the properties array. - // Removing will be done by creating a new array and copying the elements other than the - // certificate reference from the existing array, - if (certificateReferenceIdIndex > -1) { + OptionalInt certificateReferenceIdIndex = IntStream.range(0, spProperties.length) + .filter(index -> SP_PROPERTY_NAME_CERTIFICATE.equals(spProperties[index].getName())) + .findFirst(); - ServiceProviderProperty[] propertiesWithoutCertificateReference = - new ServiceProviderProperty[serviceProviderProperties.length - 1]; + if (certificateReferenceIdIndex.isPresent()) { + int certificateReferenceIndex = certificateReferenceIdIndex.getAsInt(); + int certificateID = Integer.parseInt(spProperties[certificateReferenceIndex].getValue()); - System.arraycopy(serviceProviderProperties, 0, propertiesWithoutCertificateReference, - 0, certificateReferenceIdIndex); - System.arraycopy(serviceProviderProperties, certificateReferenceIdIndex + 1, - propertiesWithoutCertificateReference, certificateReferenceIdIndex, - propertiesWithoutCertificateReference.length - certificateReferenceIdIndex); + serviceProvider.setSpProperties(getFilteredSpProperties(spProperties, certificateReferenceIndex)); + deleteCertificate(certificateID, IdentityTenantUtil.getTenantDomain(tenantID)); + } + } - serviceProvider.setSpProperties(propertiesWithoutCertificateReference); - deleteCertificate(connection, Integer.parseInt(certificateReferenceId)); - } - } - } else { - // First get the certificate reference from the application properties. - ServiceProviderProperty[] serviceProviderProperties = serviceProvider.getSpProperties(); + /** + * Returns a new array of service provider properties with the property at the specified index removed. + * This method creates a copy of the original `spProperties` array, omitting the element at `indexToRemove`. + * + * @param spProperties An array of service provider properties. + * @param indexToRemove The index of the property to be removed from the array. + * @return A new array of service provider properties, excluding the property at the specified index. + */ + private ServiceProviderProperty[] getFilteredSpProperties(ServiceProviderProperty[] spProperties, + int indexToRemove) { - String certificateReferenceIdString = getCertificateReferenceID(serviceProviderProperties); + ServiceProviderProperty[] updatedSpProperties = new ServiceProviderProperty[spProperties.length - 1]; + System.arraycopy(spProperties, 0, updatedSpProperties, 0, indexToRemove); + System.arraycopy(spProperties, indexToRemove + 1, updatedSpProperties, indexToRemove, + updatedSpProperties.length - indexToRemove); - // If there is a reference, update the relevant certificate record. - if (certificateReferenceIdString != null) { // Update the existing record. - PreparedStatement statementToUpdateCertificate = null; - try { - statementToUpdateCertificate = connection.prepareStatement( - ApplicationMgtDBQueries.UPDATE_CERTIFICATE); - setBlobValue(serviceProvider.getCertificateContent(), statementToUpdateCertificate, 1); - statementToUpdateCertificate.setInt(2, Integer.parseInt(certificateReferenceIdString)); - - statementToUpdateCertificate.executeUpdate(); - } catch (IOException e) { - throw new IdentityApplicationManagementException("An error occurred while processing content " + - "stream of certificate.", e); - } finally { - IdentityApplicationManagementUtil.closeStatement(statementToUpdateCertificate); - } - } else { - /* - There is no existing reference. - Persisting the certificate in the given service provider as a new record. - */ - persistApplicationCertificate(serviceProvider, tenantID, connection); - } + return updatedSpProperties; + } + + /** + * Update the existing certificate record with the given certificate ID. + * + * @param certificateId Certificate ID. + * @param certificateContent Certificate content to be updated. + * @param tenantID Tenant ID. + * @throws IdentityApplicationManagementException If an error occurs while updating the certificate. + */ + private void updateCertificate(String certificateId, String certificateContent, int tenantID) + throws IdentityApplicationManagementException { + + try { + ApplicationManagementServiceComponentHolder.getInstance().getApplicationCertificateMgtService() + .updateCertificateContent(Integer.parseInt(certificateId), certificateContent, + IdentityTenantUtil.getTenantDomain(tenantID)); + } catch (CertificateMgtClientException e) { + throw new IdentityApplicationManagementClientException(INVALID_REQUEST.getCode(), e.getDescription(), e); + } catch (CertificateMgtException e) { + throw new IdentityApplicationManagementException("Error while updating certificate", e); } } @@ -859,35 +882,21 @@ private String getCertificateReferenceID(ServiceProviderProperty[] serviceProvid * Persists the certificate content of the given service provider object, * and adds ID of the newly added certificate as a property of the service provider object. * - * @param serviceProvider - * @param tenantID - * @param connection - * @throws SQLException + * @param serviceProvider Service provider object. + * @param tenantID Tenant ID. + * @throws IdentityApplicationManagementException If an error occurs while adding the certificate. */ - private void persistApplicationCertificate(ServiceProvider serviceProvider, int tenantID, - Connection connection) - throws SQLException, IdentityApplicationManagementException { + private void persistApplicationCertificate(ServiceProvider serviceProvider, int tenantID) + throws IdentityApplicationManagementException { - // Configure the prepared statement to collect the auto generated id of the database record. - PreparedStatement statementToAddCertificate = null; - ResultSet results = null; try { - - String dbProductName = connection.getMetaData().getDatabaseProductName(); - statementToAddCertificate = connection.prepareStatement(ApplicationMgtDBQueries.ADD_CERTIFICATE, - new String[] {DBUtils.getConvertedAutoGeneratedColumnName(dbProductName, "ID")}); - - statementToAddCertificate.setString(1, serviceProvider.getApplicationName()); - setBlobValue(serviceProvider.getCertificateContent(), statementToAddCertificate, 2); - statementToAddCertificate.setInt(3, tenantID); - statementToAddCertificate.execute(); - - results = statementToAddCertificate.getGeneratedKeys(); - - int newlyAddedCertificateID = 0; - if (results.next()) { - newlyAddedCertificateID = results.getInt(1); - } + Certificate certificate = new Certificate.Builder() + .name(serviceProvider.getApplicationName()) + .certificateContent(serviceProvider.getCertificateContent()) + .build(); + int newlyAddedCertificateID = ApplicationManagementServiceComponentHolder.getInstance() + .getApplicationCertificateMgtService().addCertificate(certificate, + IdentityTenantUtil.getTenantDomain(tenantID)); // Not all JDBC drivers support getting the auto generated database ID. // So if the ID is not returned, get the ID by querying the database passing the certificate name. @@ -895,16 +904,14 @@ private void persistApplicationCertificate(ServiceProvider serviceProvider, int if (log.isDebugEnabled()) { log.debug("JDBC Driver did not return the application id, executing Select operation"); } - newlyAddedCertificateID = getCertificateIDByName(serviceProvider.getApplicationName(), - tenantID, connection); + newlyAddedCertificateID = getCertificateIDByName(serviceProvider.getApplicationName(), tenantID); } addApplicationCertificateReferenceAsServiceProviderProperty(serviceProvider, newlyAddedCertificateID); - } catch (IOException e) { - throw new IdentityApplicationManagementException("An error occurred while processing content stream " + - "of certificate.", e); - } finally { - IdentityApplicationManagementUtil.closeResultSet(results); - IdentityApplicationManagementUtil.closeStatement(statementToAddCertificate); + } catch (CertificateMgtClientException e) { + throw new IdentityApplicationManagementClientException(INVALID_REQUEST.getCode(), e.getDescription(), e); + } catch (CertificateMgtException e) { + throw new IdentityApplicationManagementServerException("Error while adding certificate for application: " + + serviceProvider.getApplicationName(), e); } } @@ -931,8 +938,8 @@ private void addApplicationCertificateReferenceAsServiceProviderProperty(Service } ServiceProviderProperty propertyForCertificate = new ServiceProviderProperty(); - propertyForCertificate.setDisplayName("CERTIFICATE"); - propertyForCertificate.setName("CERTIFICATE"); + propertyForCertificate.setDisplayName(SP_PROPERTY_NAME_CERTIFICATE); + propertyForCertificate.setName(SP_PROPERTY_NAME_CERTIFICATE); propertyForCertificate.setValue(String.valueOf(newlyAddedCertificateID)); newServiceProviderProperties[newServiceProviderProperties.length - 1] = propertyForCertificate; @@ -943,34 +950,28 @@ private void addApplicationCertificateReferenceAsServiceProviderProperty(Service /** * Returns the database ID of the certificate with the given certificate name and the tenant ID. * - * @param applicationName - * @param tenantID - * @param connection - * @return - * @throws SQLException + * @param applicationName Name of the application. + * @param tenantID Tenant ID. + * @return Certificate ID. + * @throws IdentityApplicationManagementException If an error occurs while retrieving the certificate ID. */ - private int getCertificateIDByName(String applicationName, int tenantID, Connection connection) - throws SQLException { + private int getCertificateIDByName(String applicationName, int tenantID) + throws IdentityApplicationManagementException { - PreparedStatement statementToGetCertificateId = null; - ResultSet results = null; try { - statementToGetCertificateId = connection.prepareStatement( - ApplicationMgtDBQueries.GET_CERTIFICATE_ID_BY_NAME); - statementToGetCertificateId.setString(1, applicationName); - statementToGetCertificateId.setInt(2, tenantID); - - results = statementToGetCertificateId.executeQuery(); + Certificate certificate = ApplicationManagementServiceComponentHolder.getInstance() + .getApplicationCertificateMgtService().getCertificateByName(applicationName, + IdentityTenantUtil.getTenantDomain(tenantID)); - int applicationId = -1; - while (results.next()) { - applicationId = results.getInt(1); + int certificateId = -1; + if (certificate != null) { + certificateId = Integer.parseInt(certificate.getId()); } - return applicationId; - } finally { - IdentityApplicationManagementUtil.closeResultSet(results); - IdentityApplicationManagementUtil.closeStatement(statementToGetCertificateId); + return certificateId; + } catch (CertificateMgtException e) { + throw new IdentityApplicationManagementException("Error while retrieving certificate ID by name " + + "for application: " + applicationName, e); } } @@ -1904,48 +1905,34 @@ private ConsentPurposeConfigs getConsentPurposeConfigs(Connection connection, in * Retrieves the certificate content from the database using the certificate reference id property of a * service provider. * - * @param serviceProviderProperties - * @param connection - * @return - * @throws CertificateRetrievingException + * @param serviceProviderProperties List of service provider properties. + * @return Certificate content. + * @throws CertificateRetrievingException If an error occurs while retrieving the certificate. */ - private String getCertificateContent(List serviceProviderProperties, Connection connection) + private String getCertificateContent(List serviceProviderProperties) throws CertificateRetrievingException { String certificateReferenceId = null; for (ServiceProviderProperty property : serviceProviderProperties) { - if ("CERTIFICATE".equals(property.getName())) { + if (SP_PROPERTY_NAME_CERTIFICATE.equals(property.getName())) { certificateReferenceId = property.getValue(); } } if (certificateReferenceId != null) { - - PreparedStatement statementForFetchingCertificate = null; - ResultSet results = null; + Certificate certificate; try { - statementForFetchingCertificate = connection.prepareStatement( - ApplicationMgtDBQueries.GET_CERTIFICATE_BY_ID); - statementForFetchingCertificate.setInt(1, Integer.parseInt(certificateReferenceId)); - - results = statementForFetchingCertificate.executeQuery(); - - String certificateContent = null; - while (results.next()) { - certificateContent = getBlobValue(results.getBinaryStream("CERTIFICATE_IN_PEM")); - } + certificate = ApplicationManagementServiceComponentHolder.getInstance() + .getApplicationCertificateMgtService().getCertificate(Integer.parseInt(certificateReferenceId), + CarbonContext.getThreadLocalCarbonContext().getTenantDomain()); - if (certificateContent != null) { - return certificateContent; + if (certificate != null) { + return certificate.getCertificateContent(); } - } catch (SQLException | IOException e) { - String errorMessage = "An error occurred while retrieving the certificate for the " + - "application."; + } catch (CertificateMgtException e) { + String errorMessage = "An error occurred while retrieving the certificate for the application."; log.error(errorMessage); throw new CertificateRetrievingException(errorMessage, e); - } finally { - IdentityApplicationManagementUtil.closeResultSet(results); - IdentityApplicationManagementUtil.closeStatement(statementForFetchingCertificate); } } return null; @@ -2288,7 +2275,7 @@ public ServiceProvider getApplication(int applicationId) throws IdentityApplicat serviceProvider.setRequestPathAuthenticatorConfigs(requestPathAuthenticators); serviceProvider.setSpProperties(propertyList.toArray(new ServiceProviderProperty[0])); - serviceProvider.setCertificateContent(getCertificateContent(propertyList, connection)); + serviceProvider.setCertificateContent(getCertificateContent(propertyList)); // Set role associations. serviceProvider.setAssociatedRolesConfig( @@ -2445,7 +2432,7 @@ public ServiceProvider getApplicationWithRequiredAttributes(int applicationId, L readAndSetConfigurationsFromProperties(propertyList, serviceProvider.getLocalAndOutBoundAuthenticationConfig()); serviceProvider.setSpProperties(propertyList.toArray(new ServiceProviderProperty[0])); - serviceProvider.setCertificateContent(getCertificateContent(propertyList, connection)); + serviceProvider.setCertificateContent(getCertificateContent(propertyList)); } if (TEMPLATE_ID_SP_PROPERTY_NAME.equals(requiredAttribute)) { serviceProvider.setTemplateId(getTemplateId(propertyList)); @@ -4296,7 +4283,7 @@ public void deleteApplication(String appName) throws IdentityApplicationManageme try { // Delete the application certificate if there is any. - deleteCertificate(connection, appName, tenantID); + deleteCertificate(appName, tenantID); // First, delete all the clients of the application int applicationID = getApplicationIDByName(appName, tenantID, connection); @@ -4431,9 +4418,6 @@ public void deleteApplications(int tenantId) throws IdentityApplicationManagemen try (Connection connection = IdentityDatabaseUtil.getDBConnection(true)) { - // Delete the application certificates of the tenant. - deleteCertificatesByTenantId(connection, tenantId); - try (PreparedStatement deleteClientPrepStmt = connection .prepareStatement(ApplicationMgtDBQueries.REMOVE_APPS_FROM_APPMGT_APP_BY_TENANT_ID)) { deleteClientPrepStmt.setInt(1, tenantId); @@ -4664,15 +4648,14 @@ public void deletePermissionAndRoleConfiguration(int applicationID, Connection c /** * Delete the certificate of the given application if there is one. * - * @param connection - * @param appName - * @param tenantID - * @throws UserStoreException - * @throws IdentityApplicationManagementException - * @throws SQLException + * @param appName Application name. + * @param tenantID Tenant ID. + * @throws UserStoreException If an error occurred while resolving tenant. + * @throws IdentityApplicationManagementException If an error occurred while retrieving the application or + * deleting the certificate. */ - private void deleteCertificate(Connection connection, String appName, int tenantID) - throws UserStoreException, IdentityApplicationManagementException, SQLException { + private void deleteCertificate(String appName, int tenantID) throws UserStoreException, + IdentityApplicationManagementException { String tenantDomain = MultitenantConstants.SUPER_TENANT_DOMAIN_NAME; @@ -4686,50 +4669,24 @@ private void deleteCertificate(Connection connection, String appName, int tenant String certificateReferenceID = getCertificateReferenceID(application.getSpProperties()); if (certificateReferenceID != null) { - deleteCertificate(connection, Integer.parseInt(certificateReferenceID)); + deleteCertificate(Integer.parseInt(certificateReferenceID), IdentityTenantUtil.getTenantDomain(tenantID)); } } /** * Deletes the certificate for given ID from the database. * - * @param connection - * @param id + * @param id Certificate ID. + * @param tenantDomain Tenant domain. + * @throws IdentityApplicationManagementException If an error occurred while deleting the certificate. */ - private void deleteCertificate(Connection connection, int id) throws SQLException { + private void deleteCertificate(int id, String tenantDomain) throws IdentityApplicationManagementException { - PreparedStatement statementToRemoveCertificate = null; try { - - statementToRemoveCertificate = connection.prepareStatement(ApplicationMgtDBQueries.REMOVE_CERTIFICATE); - statementToRemoveCertificate.setInt(1, id); - statementToRemoveCertificate.execute(); - } finally { - IdentityApplicationManagementUtil.closeStatement(statementToRemoveCertificate); - } - } - - /** - * Deletes all certificates of a given tenant id from the database. - * - * @param connection The database connection. - * @param tenantId The id of the tenant. - */ - private void deleteCertificatesByTenantId(Connection connection, int tenantId) throws SQLException { - - if (log.isDebugEnabled()) { - log.debug("Deleting all application certificates of tenant: " + tenantId); - } - - PreparedStatement deleteCertificatesStmt = null; - - try { - deleteCertificatesStmt = connection.prepareStatement( - ApplicationMgtDBQueries.REMOVE_CERTIFICATES_BY_TENANT_ID); - deleteCertificatesStmt.setInt(1, tenantId); - deleteCertificatesStmt.execute(); - } finally { - IdentityApplicationManagementUtil.closeStatement(deleteCertificatesStmt); + ApplicationManagementServiceComponentHolder.getInstance().getApplicationCertificateMgtService() + .deleteCertificate(id, tenantDomain); + } catch (CertificateMgtException e) { + throw new IdentityApplicationManagementException("Error while deleting certificate", e); } } @@ -5356,39 +5313,6 @@ private void setBlobValue(String value, PreparedStatement prepStmt, int index) t } } - /** - * Get string from inputStream of a blob - * - * @param is input stream - * @return - * @throws IOException - */ - private String getBlobValue(InputStream is) throws IOException { - - if (is != null) { - BufferedReader br = null; - StringBuilder sb = new StringBuilder(); - String line; - try { - br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); - while ((line = br.readLine()) != null) { - sb.append(line); - } - } finally { - if (br != null) { - try { - br.close(); - } catch (IOException e) { - log.error("Error in retrieving the Blob value", e); - } - } - } - - return sb.toString(); - } - return null; - } - private void updateConfigurationsAsServiceProperties(ServiceProvider sp) throws IdentityApplicationManagementException { @@ -5852,10 +5776,13 @@ public String addApplication(ServiceProvider application, addApplicationConfigurations(connection, application, tenantDomain); IdentityDatabaseUtil.commitTransaction(connection); return resourceId; + } catch (IdentityApplicationManagementClientException e) { + rollbackAddApplicationTransaction(connection, application, tenantDomain); + throw e; } catch (SQLException | UserStoreException | IdentityApplicationManagementException e) { log.error("Error while creating the application with name: " + application.getApplicationName() + " in tenantDomain: " + tenantDomain + ". Rolling back created application information."); - IdentityDatabaseUtil.rollbackTransaction(connection); + rollbackAddApplicationTransaction(connection, application, tenantDomain); if (isApplicationConflict(e)) { throw new IdentityApplicationManagementClientException(APPLICATION_ALREADY_EXISTS.getCode(), "Application already exists with name: " + application.getApplicationName() @@ -5879,6 +5806,8 @@ public void updateApplicationByResourceId(String resourceId, updatedApp.setApplicationID(appIdUsingResourceId); updateApplication(updatedApp, tenantDomain); + } catch (IdentityApplicationManagementClientException e) { + throw e; } catch (IdentityApplicationManagementException ex) { // Send error code. throw new IdentityApplicationManagementServerException("Error while updating application with resourceId: " @@ -5922,7 +5851,7 @@ public void deleteApplicationByResourceId(String resourceId, if (application != null) { // Delete the application certificate if there is any - deleteApplicationCertificate(connection, application); + deleteApplicationCertificate(application, tenantDomain); // Delete android attestation service credentials if there is any deleteAndroidAttestationCredentials(application); @@ -6551,11 +6480,12 @@ private int getAppIdUsingResourceId(String resourceId) return applicationId; } - private void deleteApplicationCertificate(Connection connection, ServiceProvider application) throws SQLException { + private void deleteApplicationCertificate(ServiceProvider application, String tenantDomain) + throws IdentityApplicationManagementException { String certificateReferenceID = getCertificateReferenceID(application.getSpProperties()); if (certificateReferenceID != null) { - deleteCertificate(connection, Integer.parseInt(certificateReferenceID)); + deleteCertificate(Integer.parseInt(certificateReferenceID), tenantDomain); } } @@ -6674,4 +6604,15 @@ public List getTrustedApps(PlatformType platformType) throws Identit } return trustedApps; } + + private void rollbackAddApplicationTransaction(Connection connection, ServiceProvider application, + String tenantDomain) throws IdentityApplicationManagementException { + + try { + IdentityDatabaseUtil.rollbackTransaction(connection); + deleteApplicationCertificate(application, tenantDomain); + } catch (Exception e) { + throw new IdentityApplicationManagementException("Error while rolling back the transaction.", e); + } + } } diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationMgtDBQueries.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationMgtDBQueries.java index b6ee788f9ccc..fc65dc521468 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationMgtDBQueries.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationMgtDBQueries.java @@ -46,8 +46,16 @@ public class ApplicationMgtDBQueries { public static final String UPDATE_BASIC_APPINFO_WITH_LOCAL_AND_OUTBOUND_CONFIGURATION = "UPDATE SP_APP SET " + "IS_SEND_AUTH_LIST_OF_IDPS=?, IS_USE_TENANT_DOMAIN_SUBJECT=?, IS_USE_USER_DOMAIN_SUBJECT=?, " + "ENABLE_AUTHORIZATION=?, SUBJECT_CLAIM_URI=?, AUTH_TYPE=? WHERE TENANT_ID= ? AND ID = ?"; + /** + * @deprecated Use ApplicationCertificateMgtService instead. + */ + @Deprecated public static final String UPDATE_CERTIFICATE = "UPDATE IDN_CERTIFICATE SET CERTIFICATE_IN_PEM = ? WHERE " + "ID = ?"; + /** + * @deprecated Use ApplicationCertificateMgtService instead. + */ + @Deprecated public static final String ADD_CERTIFICATE = "INSERT INTO IDN_CERTIFICATE(NAME, CERTIFICATE_IN_PEM, TENANT_ID) " + "VALUES(?, ?, ?)"; public static final String UPDATE_BASIC_APPINFO_WITH_PRO_PROPERTIES = "UPDATE SP_APP SET PROVISIONING_USERSTORE_" + @@ -325,11 +333,31 @@ public class ApplicationMgtDBQueries { public static final String DELETE_SP_METADATA = "DELETE FROM SP_METADATA WHERE SP_ID = ?"; + /** + * @deprecated Use ApplicationCertificateMgtService instead. + */ + @Deprecated public static final String GET_CERTIFICATE_BY_ID = "SELECT CERTIFICATE_IN_PEM FROM IDN_CERTIFICATE WHERE ID = ?"; + /** + * @deprecated Use ApplicationCertificateMgtService instead. + */ + @Deprecated public static final String GET_CERTIFICATE_ID_BY_NAME = "SELECT ID FROM IDN_CERTIFICATE WHERE NAME = ? AND " + "TENANT_ID = ?"; + /** + * @deprecated Use ApplicationCertificateMgtService instead. + */ + @Deprecated public static final String REMOVE_CERTIFICATE = "DELETE FROM IDN_CERTIFICATE WHERE ID = ?"; + /** + * This query was previously used to delete all certificates associated with a tenant as part of tenant deletion. + * However, there is no product entry point for invoking this method directly. Since tenant deletion is managed + * as an administrative process, it is now recommended to handle certificate removal for a tenant through such + * process. + * @deprecated + */ + @Deprecated public static final String REMOVE_CERTIFICATES_BY_TENANT_ID = "DELETE FROM IDN_CERTIFICATE WHERE TENANT_ID = ?"; public static final String CHECK_AVAILABILITY_OF_IDN_CERTIFICATE_TABLE_MYSQL = "SELECT ID FROM IDN_CERTIFICATE " + "LIMIT 1"; diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponent.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponent.java index bca4581dc47d..986b2374dad9 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponent.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponent.java @@ -66,6 +66,7 @@ import org.wso2.carbon.identity.application.mgt.provider.RegistryBasedApplicationPermissionProvider; import org.wso2.carbon.identity.application.mgt.validator.ApplicationValidator; import org.wso2.carbon.identity.application.mgt.validator.DefaultApplicationValidator; +import org.wso2.carbon.identity.certificate.management.service.ApplicationCertificateManagementService; import org.wso2.carbon.identity.claim.metadata.mgt.ClaimMetadataManagementService; import org.wso2.carbon.identity.claim.metadata.mgt.listener.ClaimMetadataMgtListener; import org.wso2.carbon.identity.core.SAMLSSOServiceProviderManager; @@ -635,4 +636,26 @@ private void unsetSecretResolveManagerService(SecretResolveManager secretResolve ApplicationManagementServiceComponentHolder.getInstance().setSecretResolveManager(null); } + + @Reference( + name = "org.wso2.carbon.identity.certificate.management.service.ApplicationCertificateManagementService", + service = ApplicationCertificateManagementService.class, + cardinality = ReferenceCardinality.MANDATORY, + policy = ReferencePolicy.DYNAMIC, + unbind = "unsetApplicationCertificateManagementService" + ) + protected void setApplicationCertificateManagementService(ApplicationCertificateManagementService + applicationCertificateManagementService) { + + ApplicationManagementServiceComponentHolder.getInstance() + .setApplicationCertificateMgtService(applicationCertificateManagementService); + log.debug("ApplicationCertificateManagementService set in ApplicationManagementServiceComponent bundle."); + } + + protected void unsetApplicationCertificateManagementService(ApplicationCertificateManagementService + applicationCertificateManagementService) { + + ApplicationManagementServiceComponentHolder.getInstance().setApplicationCertificateMgtService(null); + log.debug("ApplicationCertificateManagementService unset in ApplicationManagementServiceComponent bundle."); + } } diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponentHolder.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponentHolder.java index 758a552e24da..289cde9c84f8 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponentHolder.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponentHolder.java @@ -22,6 +22,7 @@ import org.wso2.carbon.identity.application.mgt.AbstractInboundAuthenticatorConfig; import org.wso2.carbon.identity.application.mgt.inbound.protocol.ApplicationInboundAuthConfigHandler; import org.wso2.carbon.identity.application.mgt.provider.ApplicationPermissionProvider; +import org.wso2.carbon.identity.certificate.management.service.ApplicationCertificateManagementService; import org.wso2.carbon.identity.claim.metadata.mgt.ClaimMetadataManagementService; import org.wso2.carbon.identity.core.SAMLSSOServiceProviderManager; import org.wso2.carbon.identity.event.services.IdentityEventService; @@ -79,6 +80,8 @@ public class ApplicationManagementServiceComponentHolder { private SecretManager secretManager; private SecretResolveManager secretResolveManager; + private ApplicationCertificateManagementService applicationCertificateMgtService; + private ApplicationManagementServiceComponentHolder() { } @@ -418,4 +421,23 @@ public List getApplicationInboundAuthConfig return applicationInboundAuthConfigHandlers; } + + /** + * Gets the ApplicationCertificateManagementService instance. + * @return The ApplicationCertificateManagementService instance. + */ + public ApplicationCertificateManagementService getApplicationCertificateMgtService() { + + return applicationCertificateMgtService; + } + + /** + * Sets the ApplicationCertificateManagementService instance. + * @param applicationCertificateMgtService The ApplicationCertificateManagementService instance to be set. + */ + public void setApplicationCertificateMgtService( + ApplicationCertificateManagementService applicationCertificateMgtService) { + + this.applicationCertificateMgtService = applicationCertificateMgtService; + } } diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImplTest.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImplTest.java index ec71ddfe0732..38c6097fe252 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImplTest.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImplTest.java @@ -18,6 +18,7 @@ package org.wso2.carbon.identity.application.mgt; +import org.apache.commons.lang.StringUtils; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.testng.Assert; @@ -34,6 +35,7 @@ import org.wso2.carbon.identity.application.common.ApplicationAuthenticatorService; import org.wso2.carbon.identity.application.common.IdentityApplicationManagementClientException; import org.wso2.carbon.identity.application.common.IdentityApplicationManagementException; +import org.wso2.carbon.identity.application.common.IdentityApplicationManagementServerException; import org.wso2.carbon.identity.application.common.model.ApplicationBasicInfo; import org.wso2.carbon.identity.application.common.model.AuthenticationStep; import org.wso2.carbon.identity.application.common.model.Claim; @@ -54,8 +56,10 @@ import org.wso2.carbon.identity.application.common.model.RequestPathAuthenticatorConfig; import org.wso2.carbon.identity.application.common.model.RoleMapping; import org.wso2.carbon.identity.application.common.model.ServiceProvider; +import org.wso2.carbon.identity.application.common.model.ServiceProviderProperty; import org.wso2.carbon.identity.application.common.model.SpTrustedAppMetadata; import org.wso2.carbon.identity.application.common.model.TrustedApp; +import org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants.PlatformType; import org.wso2.carbon.identity.application.mgt.inbound.dto.ApplicationDTO; import org.wso2.carbon.identity.application.mgt.inbound.dto.InboundProtocolConfigurationDTO; import org.wso2.carbon.identity.application.mgt.inbound.dto.InboundProtocolsDTO; @@ -64,6 +68,11 @@ import org.wso2.carbon.identity.application.mgt.provider.ApplicationPermissionProvider; import org.wso2.carbon.identity.application.mgt.provider.RegistryBasedApplicationPermissionProvider; import org.wso2.carbon.identity.base.AuthenticatorPropertyConstants.DefinedByType; +import org.wso2.carbon.identity.certificate.management.exception.CertificateMgtClientException; +import org.wso2.carbon.identity.certificate.management.exception.CertificateMgtException; +import org.wso2.carbon.identity.certificate.management.exception.CertificateMgtServerException; +import org.wso2.carbon.identity.certificate.management.model.Certificate; +import org.wso2.carbon.identity.certificate.management.service.ApplicationCertificateManagementService; import org.wso2.carbon.identity.common.testng.WithH2Database; import org.wso2.carbon.identity.common.testng.realm.InMemoryRealmService; import org.wso2.carbon.identity.common.testng.realm.MockUserStoreManager; @@ -99,14 +108,19 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Optional; import static java.lang.Boolean.FALSE; import static java.lang.Boolean.TRUE; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.when; import static org.wso2.carbon.CarbonConstants.REGISTRY_SYSTEM_USERNAME; import static org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants.PlatformType; @@ -114,6 +128,7 @@ import static org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants.TEMPLATE_VERSION_SP_PROPERTY_NAME; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.PORTAL_NAMES_CONFIG_ELEMENT; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.TRUSTED_APP_CONSENT_REQUIRED_PROPERTY; +import static org.wso2.carbon.identity.certificate.management.constant.CertificateMgtErrors.ERROR_INVALID_CERTIFICATE_CONTENT; import static org.wso2.carbon.utils.multitenancy.MultitenantConstants.SUPER_TENANT_DOMAIN_NAME; import static org.wso2.carbon.utils.multitenancy.MultitenantConstants.SUPER_TENANT_ID; @@ -154,9 +169,19 @@ public class ApplicationManagementServiceImplTest { private static final String ANDROID_PACKAGE_NAME_1 = "com.wso2.sample.mobile.application"; private static final String ANDROID_PACKAGE_NAME_2 = "com.wso2.sample.mobile.application2"; private static final String APPLE_APP_ID = "APPLETEAMID.com.wso2.mobile.sample"; + private static final String CERTIFICATE = "dummy_application_certificate"; + private static final String UPDATED_CERTIFICATE = "updated_dummy_application_certificate"; + private static final int CERTIFICATE_ID = 1; private IdPManagementDAO idPManagementDAO; private ApplicationManagementServiceImpl applicationManagementService; + private ApplicationCertificateManagementService applicationCertificateManagementService; + + private String appIdWithCert; + private ServiceProvider appWithCert; + private Certificate certificate; + private CertificateMgtServerException serverException; + private CertificateMgtClientException clientException; @BeforeClass public void setup() throws RegistryException, UserStoreException, SecretManagementException { @@ -190,6 +215,19 @@ public void setup() throws RegistryException, UserStoreException, SecretManageme CarbonConstants.ENABLE_LEGACY_AUTHZ_RUNTIME = false; MockedStatic definedByType = mockStatic(DefinedByType.class); definedByType.when(() -> DefinedByType.valueOf(anyString())).thenReturn(DefinedByType.SYSTEM); + + applicationCertificateManagementService = mock(ApplicationCertificateManagementService.class); + ApplicationManagementServiceComponentHolder.getInstance() + .setApplicationCertificateMgtService(applicationCertificateManagementService); + certificate = new Certificate.Builder() + .id(String.valueOf(CERTIFICATE_ID)) + .name(APPLICATION_NAME_1) + .certificateContent(CERTIFICATE) + .build(); + serverException = new CertificateMgtServerException("server_error_message", "server_error_description", "65010", + new Throwable()); + clientException = new CertificateMgtClientException(ERROR_INVALID_CERTIFICATE_CONTENT.getMessage(), + ERROR_INVALID_CERTIFICATE_CONTENT.getDescription(), ERROR_INVALID_CERTIFICATE_CONTENT.getCode()); } @DataProvider(name = "addApplicationDataProvider") @@ -1328,6 +1366,287 @@ public void addApplicationWithTemplateIdAndTemplateVersionData(String templateId REGISTRY_SYSTEM_USERNAME); } + @Test(groups = "certificate", priority = 1) + public void testAddApplicationWithCertificate() throws CertificateMgtException { + + ServiceProvider inputSP = new ServiceProvider(); + inputSP.setApplicationName(APPLICATION_NAME_1); + inputSP.setCertificateContent(certificate.getCertificateContent()); + addApplicationConfigurations(inputSP); + + doReturn(0).when(applicationCertificateManagementService).addCertificate(any(), anyString()); + doReturn(certificate).when(applicationCertificateManagementService).getCertificateByName(any(), anyString()); + try { + appIdWithCert = applicationManagementService.createApplication(inputSP, SUPER_TENANT_DOMAIN_NAME, + REGISTRY_SYSTEM_USERNAME); + } catch (Exception e) { + Assert.fail("Application addition with certificate addition should be successful without any exceptions"); + } + } + + @Test(groups = "certificate", priority = 2, dependsOnMethods = "testAddApplicationWithCertificate") + public void testGetApplicationWithServerErrorWhenRetrievingCert() throws CertificateMgtException { + + doThrow(serverException).when(applicationCertificateManagementService).getCertificate(anyInt(), anyString()); + try { + applicationManagementService.getApplicationByResourceId(appIdWithCert, SUPER_TENANT_DOMAIN_NAME); + Assert.fail("Successful retrieval of the application without an exception is considered as a failure"); + } catch (IdentityApplicationManagementException e) { + Assert.assertEquals(e.getClass(), IdentityApplicationManagementServerException.class); + Assert.assertTrue(e.getMessage().contains("Error while retrieving application with resourceId: " + + appIdWithCert + " in tenantDomain: " + SUPER_TENANT_DOMAIN_NAME)); + for (Throwable cause = e.getCause(); cause != null; cause = cause.getCause()) { + if (cause instanceof CertificateMgtServerException) { + return; + } + } + Assert.fail("Expected cause of type CertificateMgtServerException was not found in the exception chain"); + } + } + + @Test(groups = "certificate", priority = 3, dependsOnMethods = "testAddApplicationWithCertificate") + public void testGetApplicationWithCertificate() throws IdentityApplicationManagementException, + CertificateMgtException { + + reset(applicationCertificateManagementService); + when(applicationCertificateManagementService.getCertificate(anyInt(), anyString())).thenReturn(certificate); + appWithCert = applicationManagementService.getApplicationByResourceId(appIdWithCert, SUPER_TENANT_DOMAIN_NAME); + + Optional certificateValue = Arrays.stream(appWithCert.getSpProperties()) + .filter(prop -> "CERTIFICATE".equals(prop.getName())) + .map(ServiceProviderProperty::getValue) + .findFirst(); + Assert.assertTrue(certificateValue.isPresent(), "Certificate property not found"); + Assert.assertEquals(Integer.parseInt(certificateValue.get()), CERTIFICATE_ID); + Assert.assertEquals(appWithCert.getCertificateContent(), certificate.getCertificateContent()); + } + + @Test(groups = "certificate", priority = 4, dependsOnMethods = {"testAddApplicationWithCertificate", + "testGetApplicationWithCertificate"}) + public void testUpdateApplicationWithCertificate() throws CertificateMgtException { + + doNothing().when(applicationCertificateManagementService).updateCertificateContent(anyInt(), anyString(), + anyString()); + try { + appWithCert.setCertificateContent(UPDATED_CERTIFICATE); + applicationManagementService.updateApplicationByResourceId(appIdWithCert, appWithCert, + SUPER_TENANT_DOMAIN_NAME, REGISTRY_SYSTEM_USERNAME); + } catch (Exception e) { + Assert.fail("Application update with certificate update should be successful without any exceptions"); + } + } + + @Test(groups = "certificate", priority = 5, dependsOnMethods = {"testAddApplicationWithCertificate", + "testGetApplicationWithCertificate"}) + public void testUpdateApplicationWithServerErrorWhenUpdatingCert() throws CertificateMgtException { + + reset(applicationCertificateManagementService); + doThrow(serverException).when(applicationCertificateManagementService).updateCertificateContent(anyInt(), + anyString(), anyString()); + try { + appWithCert.setCertificateContent(UPDATED_CERTIFICATE); + applicationManagementService.updateApplicationByResourceId(appIdWithCert, appWithCert, + SUPER_TENANT_DOMAIN_NAME, REGISTRY_SYSTEM_USERNAME); + Assert.fail("Successful update of the application without an exception is considered as a failure"); + } catch (IdentityApplicationManagementException e) { + Assert.assertEquals(e.getClass(), IdentityApplicationManagementServerException.class); + Assert.assertTrue(e.getMessage().contains("Error while updating application with resourceId: " + + appIdWithCert + " in tenantDomain: " + SUPER_TENANT_DOMAIN_NAME)); + for (Throwable cause = e.getCause(); cause != null; cause = cause.getCause()) { + if (cause instanceof CertificateMgtServerException) { + return; + } + } + Assert.fail("Expected cause of type CertificateMgtServerException was not found in the exception chain"); + } + } + + @Test(groups = "certificate", priority = 6, dependsOnMethods = {"testAddApplicationWithCertificate", + "testGetApplicationWithCertificate"}) + public void testUpdateApplicationWithClientErrorWhenUpdatingCert() throws CertificateMgtException { + + reset(applicationCertificateManagementService); + doThrow(clientException).when(applicationCertificateManagementService).updateCertificateContent(anyInt(), + anyString(), anyString()); + try { + appWithCert.setCertificateContent(UPDATED_CERTIFICATE); + applicationManagementService.updateApplicationByResourceId(appIdWithCert, appWithCert, + SUPER_TENANT_DOMAIN_NAME, REGISTRY_SYSTEM_USERNAME); + Assert.fail("Successful update of the application without an exception is considered as a failure"); + } catch (IdentityApplicationManagementException e) { + Assert.assertEquals(e.getClass(), IdentityApplicationManagementClientException.class); + Assert.assertEquals(e.getMessage(), clientException.getDescription()); + for (Throwable cause = e.getCause(); cause != null; cause = cause.getCause()) { + if (cause instanceof CertificateMgtClientException) { + return; + } + } + Assert.fail("Expected cause of type CertificateMgtClientException was not found in the exception chain"); + } + } + + @Test(groups = "certificate", priority = 7, dependsOnMethods = {"testAddApplicationWithCertificate", + "testGetApplicationWithCertificate"}) + public void testUpdateApplicationWithServerErrorWhenDeletingCert() throws CertificateMgtException { + + doThrow(serverException).when(applicationCertificateManagementService).deleteCertificate(anyInt(), anyString()); + try { + appWithCert.setCertificateContent(StringUtils.EMPTY); + applicationManagementService.updateApplicationByResourceId(appIdWithCert, appWithCert, + SUPER_TENANT_DOMAIN_NAME, REGISTRY_SYSTEM_USERNAME); + Assert.fail("Successful update of the application without an exception is considered as a failure"); + } catch (IdentityApplicationManagementException e) { + Assert.assertEquals(e.getClass(), IdentityApplicationManagementServerException.class); + Assert.assertTrue(e.getMessage().contains("Error while updating application with resourceId: " + + appIdWithCert + " in tenantDomain: " + SUPER_TENANT_DOMAIN_NAME)); + for (Throwable cause = e.getCause(); cause != null; cause = cause.getCause()) { + if (cause instanceof CertificateMgtServerException) { + return; + } + } + Assert.fail("Expected cause of type CertificateMgtServerException was not found in the exception chain"); + } + } + + @Test(groups = "certificate", priority = 8, dependsOnMethods = {"testAddApplicationWithCertificate", + "testGetApplicationWithCertificate"}) + public void testDeleteApplicationWithCertificate() throws IdentityApplicationManagementException, + CertificateMgtException { + + reset(applicationCertificateManagementService); + doNothing().when(applicationCertificateManagementService).deleteCertificate(anyInt(), anyString()); + try { + appWithCert.setCertificateContent(StringUtils.EMPTY); + applicationManagementService.updateApplicationByResourceId(appIdWithCert, appWithCert, + SUPER_TENANT_DOMAIN_NAME, REGISTRY_SYSTEM_USERNAME); + } catch (Exception e) { + Assert.fail("Application update with certificate deletion should be successful without any exceptions"); + } + + appWithCert = applicationManagementService.getApplicationByResourceId(appIdWithCert, SUPER_TENANT_DOMAIN_NAME); + Optional certificateValue = Arrays.stream(appWithCert.getSpProperties()) + .filter(prop -> "CERTIFICATE".equals(prop.getName())) + .map(ServiceProviderProperty::getValue) + .findFirst(); + Assert.assertFalse(certificateValue.isPresent(), "Certificate property should be removed"); + Assert.assertNull(appWithCert.getCertificateContent()); + + // Deleting added application. + doNothing().when(applicationCertificateManagementService).deleteCertificate(anyInt(), anyString()); + applicationManagementService.deleteApplication(appWithCert.getApplicationName(), + SUPER_TENANT_DOMAIN_NAME, REGISTRY_SYSTEM_USERNAME); + } + + @Test(groups = "certificate", priority = 9) + public void testAddApplicationWithServerErrorWhenAddingCert() throws CertificateMgtException { + + ServiceProvider inputSP = new ServiceProvider(); + inputSP.setApplicationName(APPLICATION_NAME_1); + inputSP.setCertificateContent(certificate.getCertificateContent()); + addApplicationConfigurations(inputSP); + + reset(applicationCertificateManagementService); + doThrow(serverException).when(applicationCertificateManagementService).addCertificate(any(), anyString()); + try { + appIdWithCert = applicationManagementService.createApplication(inputSP, SUPER_TENANT_DOMAIN_NAME, + REGISTRY_SYSTEM_USERNAME); + Assert.fail("Successful creation of the application without an exception is considered as a failure"); + } catch (IdentityApplicationManagementException e) { + Assert.assertTrue(e.getMessage().contains("Error while creating an application: " + + inputSP.getApplicationName() + " in tenantDomain: " + SUPER_TENANT_DOMAIN_NAME)); + for (Throwable cause = e.getCause(); cause != null; cause = cause.getCause()) { + if (cause instanceof CertificateMgtServerException) { + return; + } + } + Assert.fail("Expected cause of type CertificateMgtServerException was not found in the exception chain"); + } + } + + @Test(groups = "certificate", priority = 10) + public void testAddApplicationWithClientErrorWhenAddingCert() throws CertificateMgtException { + + ServiceProvider inputSP = new ServiceProvider(); + inputSP.setApplicationName(APPLICATION_NAME_1); + inputSP.setCertificateContent(certificate.getCertificateContent()); + addApplicationConfigurations(inputSP); + + reset(applicationCertificateManagementService); + doThrow(clientException).when(applicationCertificateManagementService).addCertificate(any(), anyString()); + try { + appIdWithCert = applicationManagementService.createApplication(inputSP, SUPER_TENANT_DOMAIN_NAME, + REGISTRY_SYSTEM_USERNAME); + Assert.fail("Successful creation of the application without an exception is considered as a failure"); + } catch (IdentityApplicationManagementException e) { + Assert.assertEquals(e.getClass(), IdentityApplicationManagementClientException.class); + Assert.assertEquals(e.getMessage(), clientException.getDescription()); + for (Throwable cause = e.getCause(); cause != null; cause = cause.getCause()) { + if (cause instanceof CertificateMgtClientException) { + return; + } + } + Assert.fail("Expected cause of type CertificateMgtClientException was not found in the exception chain"); + } + } + + @Test(groups = "certificate", priority = 11) + public void testAddApplicationWithServerErrorWhenRetrievingCertByName() throws CertificateMgtException { + + ServiceProvider inputSP = new ServiceProvider(); + inputSP.setApplicationName(APPLICATION_NAME_1); + inputSP.setCertificateContent(certificate.getCertificateContent()); + addApplicationConfigurations(inputSP); + + reset(applicationCertificateManagementService); + doReturn(0).when(applicationCertificateManagementService).addCertificate(any(), anyString()); + doThrow(serverException).when(applicationCertificateManagementService).getCertificateByName(any(), + anyString()); + try { + appIdWithCert = applicationManagementService.createApplication(inputSP, SUPER_TENANT_DOMAIN_NAME, + REGISTRY_SYSTEM_USERNAME); + Assert.fail("Successful creation of the application without an exception is considered as a failure"); + } catch (IdentityApplicationManagementException e) { + Assert.assertTrue(e.getMessage().contains("Error while creating an application: " + + inputSP.getApplicationName() + " in tenantDomain: " + SUPER_TENANT_DOMAIN_NAME)); + for (Throwable cause = e.getCause(); cause != null; cause = cause.getCause()) { + if (cause instanceof CertificateMgtServerException) { + return; + } + } + Assert.fail("Expected cause of type CertificateMgtServerException was not found in the exception chain"); + } + } + + @Test(groups = "certificate", priority = 12) + public void testAddCertificateToExistingApplication() throws IdentityApplicationManagementException, + CertificateMgtException { + + ServiceProvider inputSP = new ServiceProvider(); + inputSP.setApplicationName(APPLICATION_NAME_2); + addApplicationConfigurations(inputSP); + + String appIdWithoutCert = applicationManagementService.createApplication(inputSP, SUPER_TENANT_DOMAIN_NAME, + REGISTRY_SYSTEM_USERNAME); + ServiceProvider appWithoutCert = applicationManagementService.getApplicationByResourceId(appIdWithoutCert, + SUPER_TENANT_DOMAIN_NAME); + + reset(applicationCertificateManagementService); + doReturn(2).when(applicationCertificateManagementService).addCertificate(any(), anyString()); + + try { + appWithoutCert.setCertificateContent(certificate.getCertificateContent()); + applicationManagementService.updateApplicationByResourceId(appIdWithoutCert, appWithoutCert, + SUPER_TENANT_DOMAIN_NAME, REGISTRY_SYSTEM_USERNAME); + } catch (Exception e) { + Assert.fail("Application update with certificate addition should be successful without any exceptions"); + } + + // Deleting added application. + doNothing().when(applicationCertificateManagementService).deleteCertificate(anyInt(), anyString()); + applicationManagementService.deleteApplication(appWithoutCert.getApplicationName(), + SUPER_TENANT_DOMAIN_NAME, REGISTRY_SYSTEM_USERNAME); + } + private void addApplicationConfigurations(ServiceProvider serviceProvider) { serviceProvider.setDescription("Created for testing");