From 65d508caa7fb824199a3283b0e8ae8f7756fe94a Mon Sep 17 00:00:00 2001 From: Thisara-Welmilla Date: Tue, 12 Nov 2024 12:57:50 +0530 Subject: [PATCH] Update APIs to support custom authentication management. --- .../v1/core/ServerIdpManagementService.java | 181 +++++++----------- ...atedAuthenticatorConfigBuilderFactory.java | 174 +++++++++++++++++ 2 files changed, 238 insertions(+), 117 deletions(-) create mode 100644 components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/main/java/org/wso2/carbon/identity/api/server/idp/v1/impl/FederatedAuthenticatorConfigBuilderFactory.java diff --git a/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/main/java/org/wso2/carbon/identity/api/server/idp/v1/core/ServerIdpManagementService.java b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/main/java/org/wso2/carbon/identity/api/server/idp/v1/core/ServerIdpManagementService.java index 1e9b62d92a..e3ab6f8d36 100644 --- a/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/main/java/org/wso2/carbon/identity/api/server/idp/v1/core/ServerIdpManagementService.java +++ b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/main/java/org/wso2/carbon/identity/api/server/idp/v1/core/ServerIdpManagementService.java @@ -41,6 +41,7 @@ import org.wso2.carbon.identity.api.server.common.error.ErrorResponse; import org.wso2.carbon.identity.api.server.idp.common.Constants; import org.wso2.carbon.identity.api.server.idp.common.IdentityProviderServiceHolder; +import org.wso2.carbon.identity.api.server.idp.v1.impl.FederatedAuthenticatorConfigBuilderFactory; import org.wso2.carbon.identity.api.server.idp.v1.model.AssociationRequest; import org.wso2.carbon.identity.api.server.idp.v1.model.AssociationResponse; import org.wso2.carbon.identity.api.server.idp.v1.model.AuthenticationType; @@ -1773,43 +1774,31 @@ private void updateFederatedAuthenticatorConfig(IdentityProvider idp, FederatedA FederatedAuthenticatorConfig defaultAuthConfig = null; List fedAuthConfigs = new ArrayList<>(); for (FederatedAuthenticator authenticator : federatedAuthenticators) { - String authenticatorName = getDecodedAuthName(authenticator.getAuthenticatorId()); - FederatedAuthenticatorConfig authConfig; - String definedByType = null; - if (authenticator.getDefinedBy() != null) { - definedByType = authenticator.getDefinedBy().toString(); + String authenticatorName = getDecodedAuthenticatorName(authenticator.getAuthenticatorId()); + String definedByType; + if (isNewFederatedAuthenticator) { + definedByType = resolveDefinedByTypeForCreateFederatedAuthenticator( + authenticator.getDefinedBy().toString()).toString(); + } else { + definedByType = resolveDefinedByTypeForUpdateFederatedAuthenticator(authenticatorName).toString(); } - definedByType = resolveDefinedByType(authenticatorName, definedByType, isNewFederatedAuthenticator) - .toString(); if (DefinedByType.SYSTEM.toString().equals(definedByType)) { - authConfig = createSystemDefinedFederatedAuthenticator(authenticatorName, - authenticator.getEndpoint()); - List authProperties = - authenticator.getProperties(); - if (IdentityApplicationConstants.Authenticator.SAML2SSO.FED_AUTH_NAME - .equals(authConfig.getName())) { - validateSamlMetadata(authProperties); - } - if (authProperties != null) { - if (!areAllDistinct(authProperties)) { - throw handleException(Response.Status.BAD_REQUEST, - Constants.ErrorMessage.ERROR_CODE_INVALID_INPUT, " Duplicate properties are found" + - " in the request."); - } - List properties = authProperties.stream() - .map(propertyToInternal) - .collect(Collectors.toList()); - authConfig.setProperties(properties.toArray(new Property[0])); - } - } else { - authConfig = createUserDefinedFederatedAuthenticator(authenticatorName, authenticator.getEndpoint(), - authenticator.getProperties()); + validateAuthenticatorProperties(authenticatorName, authenticator.getProperties()); } - authConfig.setName(authenticatorName); - authConfig.setDisplayName(getDisplayNameOfAuthenticator(authConfig.getName())); - authConfig.setEnabled(authenticator.getIsEnabled()); + FederatedAuthenticatorConfigBuilderFactory.Builder builder = + new FederatedAuthenticatorConfigBuilderFactory.Builder(); + builder.authenticatorName(authenticatorName); + builder.definedByType(definedByType); + builder.enabled(authenticator.getIsEnabled()); + builder.displayName(getDisplayNameOfAuthenticator(authenticatorName)); + builder.endpoint(authenticator.getEndpoint()); + List properties = authenticator.getProperties().stream().map(propertyToInternal) + .collect(Collectors.toList()); + builder.properties(properties); + FederatedAuthenticatorConfig authConfig = builder.build(); + fedAuthConfigs.add(authConfig); if (StringUtils.equals(defaultAuthenticator, authenticator.getAuthenticatorId())) { @@ -2858,99 +2847,39 @@ private ProvisioningConnectorConfig createProvisioningConnectorConfig(String out private FederatedAuthenticatorConfig updateFederatedAuthenticatorConfig(String federatedAuthenticatorId, FederatedAuthenticatorPUTRequest authenticator) throws IdentityProviderManagementClientException { - String authenticatorName = getDecodedAuthName(federatedAuthenticatorId); - FederatedAuthenticatorConfig authConfig; - String definedByType = null; - if (authenticator.getDefinedBy() != null) { - definedByType = authenticator.getDefinedBy().toString(); - } - definedByType = resolveDefinedByType(authenticatorName, definedByType, false).toString(); + String authenticatorName = getDecodedAuthenticatorName(federatedAuthenticatorId); + String definedByType = resolveDefinedByTypeForUpdateFederatedAuthenticator(authenticatorName).toString(); if (DefinedByType.SYSTEM.toString().equals(definedByType)) { - authConfig = createSystemDefinedFederatedAuthenticator(authenticatorName, authenticator.getEndpoint()); - if (IdentityApplicationConstants.Authenticator.SAML2SSO.FED_AUTH_NAME.equals(authenticatorName)) { - validateSamlMetadata(authenticator.getProperties()); - } - if (IdentityApplicationConstants.Authenticator.OIDC.FED_AUTH_NAME.equals(authenticatorName)) { - validateDuplicateOpenIDConnectScopes(authenticator.getProperties()); - validateDefaultOpenIDConnectScopes(authenticator.getProperties()); - } - List properties = authenticator.getProperties().stream().map(propertyToInternal) - .collect(Collectors.toList()); - authConfig.setProperties(properties.toArray(new Property[0])); - } else { - authConfig = createUserDefinedFederatedAuthenticator(authenticatorName, authenticator.getEndpoint(), - authenticator.getProperties()); + validateAuthenticatorProperties(authenticatorName, authenticator.getProperties()); } - authConfig.setName(authenticatorName); - authConfig.setDisplayName(getDisplayNameOfAuthenticator(authenticatorName)); - authConfig.setEnabled(authenticator.getIsEnabled()); - return authConfig; - } - - private FederatedAuthenticatorConfig createSystemDefinedFederatedAuthenticator( - String authenticatorName, Endpoint endpoint) throws IdentityProviderManagementClientException { - - if (endpoint != null) { - Constants.ErrorMessage error = Constants.ErrorMessage.ERROR_CODE_ENDPOINT_PROVIDED_FOR_SYSTEM_AUTH; - throw new IdentityProviderManagementClientException(error.getCode(), - String.format(error.getDescription(), authenticatorName)); - } - - if (ApplicationAuthenticatorService.getInstance() - .getFederatedAuthenticatorByName((authenticatorName)) == null) { - Constants.ErrorMessage error = Constants.ErrorMessage.ERROR_CODE_NO_SYSTEM_AUTHENTICATOR_FOUND; - throw new IdentityProviderManagementClientException(error.getCode(), error.getDescription()); - } - - FederatedAuthenticatorConfig authConfig = new FederatedAuthenticatorConfig(); - authConfig.setDefinedByType(DefinedByType.SYSTEM); + FederatedAuthenticatorConfigBuilderFactory.Builder builder = new FederatedAuthenticatorConfigBuilderFactory + .Builder(); + builder.authenticatorName(authenticatorName); + builder.definedByType(definedByType); + builder.enabled(authenticator.getIsEnabled()); + builder.displayName(getDisplayNameOfAuthenticator(authenticatorName)); + builder.endpoint(authenticator.getEndpoint()); + List properties = authenticator.getProperties().stream().map(propertyToInternal) + .collect(Collectors.toList()); + builder.properties(properties); - return authConfig; + return builder.build(); } - private UserDefinedFederatedAuthenticatorConfig createUserDefinedFederatedAuthenticator( - String authenticatorName, Endpoint endpoint, List - properties) throws IdentityProviderManagementClientException { - - if (properties == null || !properties.isEmpty()) { - Constants.ErrorMessage error = Constants.ErrorMessage.ERROR_CODE_PROPERTIES_PROVIDED_FOR_USER_AUTH; - throw new IdentityProviderManagementClientException(error.getCode(), - String.format(error.getDescription(), authenticatorName)); - } + private DefinedByType resolveDefinedByTypeForCreateFederatedAuthenticator(String definedByType) { - if (endpoint == null || endpoint.getUri() == null || endpoint.getAuthentication() == null) { - Constants.ErrorMessage error = Constants.ErrorMessage.ERROR_CODE_NO_ENDPOINT_PROVIDED; - throw new IdentityProviderManagementClientException(error.getCode(), - String.format(error.getDescription(), authenticatorName)); + /* For new federated authenticators: + If 'definedByType' is not null, use the value provided in the request payload. If not, default to SYSTEM. */ + if (definedByType != null) { + return DefinedByType.valueOf(definedByType); + } else { + return DefinedByType.SYSTEM; } - - UserDefinedFederatedAuthenticatorConfig userDefinedAuthConfig = new UserDefinedFederatedAuthenticatorConfig(); - userDefinedAuthConfig.setDefinedByType(DefinedByType.USER); - - UserDefinedAuthenticatorEndpointConfig.UserDefinedAuthenticatorEndpointConfigBuilder endpointConfigBuilder = - new UserDefinedAuthenticatorEndpointConfig.UserDefinedAuthenticatorEndpointConfigBuilder(); - endpointConfigBuilder.uri(endpoint.getUri()); - endpointConfigBuilder.authenticationType(endpoint.getAuthentication().getType().toString()); - endpointConfigBuilder.authenticationProperties(endpoint.getAuthentication().getProperties() - .entrySet().stream().collect(Collectors.toMap( - Map.Entry::getKey, entry -> entry.getValue().toString()))); - userDefinedAuthConfig.setEndpointConfig(endpointConfigBuilder.build()); - return userDefinedAuthConfig; } - private DefinedByType resolveDefinedByType( - String authenticatorName, String definedByType, boolean isNewFederatedAuthenticator) { + private DefinedByType resolveDefinedByTypeForUpdateFederatedAuthenticator(String authenticatorName) { - /* For new federated authenticators: - If 'definedByType' is not null, use the value provided in the request payload. If not, default to SYSTEM. */ - if (isNewFederatedAuthenticator) { - if (definedByType != null) { - return DefinedByType.valueOf(definedByType); - } else { - return DefinedByType.SYSTEM; - } - } /* For existing federated authenticators, disregard any value provided in the request payload. Instead, resolve and retrieve the 'definedBy' type of the corresponding existing authenticator. If the authenticator config is present in the ApplicationAuthenticatorService list, return its type, @@ -3182,8 +3111,8 @@ private void resolveEndpointConfiguration(FederatedAuthenticator authenticator, endpoint.setUri(endpointConfig.getEndpointConfig().getUri()); authenticator.setEndpoint(endpoint); } catch (ClassCastException e) { - throw new IdentityProviderManagementServerException("Error occurred while resolving endpoint " + - "configuration of the authenticator.", e); + throw new IdentityProviderManagementServerException(String.format("Error occurred while resolving" + + " endpoint configuration of the authenticator %s.", authenticator.getName()), e); } } @@ -3955,7 +3884,7 @@ private void validateSystemReservedIDP(String idpName) throws IdentityProviderMa } } - private String getDecodedAuthName(String authId) throws IdentityProviderManagementClientException { + private String getDecodedAuthenticatorName(String authId) throws IdentityProviderManagementClientException { try { return base64URLDecode(authId); @@ -3965,4 +3894,22 @@ private String getDecodedAuthName(String authId) throws IdentityProviderManageme String.format(error.getDescription(), authId)); } } + + private void validateAuthenticatorProperties(String authenticatorName, + List properties) + throws IdentityProviderManagementClientException { + + if (IdentityApplicationConstants.Authenticator.SAML2SSO.FED_AUTH_NAME.equals(authenticatorName)) { + validateSamlMetadata(properties); + } + if (IdentityApplicationConstants.Authenticator.OIDC.FED_AUTH_NAME.equals(authenticatorName)) { + validateDuplicateOpenIDConnectScopes(properties); + validateDefaultOpenIDConnectScopes(properties); + } + + if (!areAllDistinct(properties)) { + Constants.ErrorMessage error = Constants.ErrorMessage.ERROR_CODE_INVALID_INPUT; + throw new IdentityProviderManagementClientException(error.getCode(), error.getDescription()); + } + } } diff --git a/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/main/java/org/wso2/carbon/identity/api/server/idp/v1/impl/FederatedAuthenticatorConfigBuilderFactory.java b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/main/java/org/wso2/carbon/identity/api/server/idp/v1/impl/FederatedAuthenticatorConfigBuilderFactory.java new file mode 100644 index 0000000000..73c7ea2aab --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/main/java/org/wso2/carbon/identity/api/server/idp/v1/impl/FederatedAuthenticatorConfigBuilderFactory.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.idp.v1.impl; + +import org.wso2.carbon.identity.api.server.idp.common.Constants; +import org.wso2.carbon.identity.api.server.idp.v1.model.Endpoint; +import org.wso2.carbon.identity.application.common.ApplicationAuthenticatorService; +import org.wso2.carbon.identity.application.common.model.FederatedAuthenticatorConfig; +import org.wso2.carbon.identity.application.common.model.Property; +import org.wso2.carbon.identity.application.common.model.UserDefinedAuthenticatorEndpointConfig; +import org.wso2.carbon.identity.application.common.model.UserDefinedFederatedAuthenticatorConfig; +import org.wso2.carbon.identity.base.AuthenticatorPropertyConstants.DefinedByType; +import org.wso2.carbon.idp.mgt.IdentityProviderManagementClientException; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * The factory class for creating instances of FederatedAuthenticatorConfig depending on the definedBy type. + * Returns FederatedAuthenticatorConfig for SYSTEM types and UserDefinedFederatedAuthenticatorConfig for USER types. + */ +public class FederatedAuthenticatorConfigBuilderFactory { + + private static FederatedAuthenticatorConfig createFederatedAuthenticatorConfig(Builder builder) + throws IdentityProviderManagementClientException { + + FederatedAuthenticatorConfig config; + if (DefinedByType.SYSTEM.toString().equals(builder.definedByType)) { + config = createSystemDefinedFederatedAuthenticator(builder); + } else { + config = createUserDefinedFederatedAuthenticator(builder); + } + + config.setName(builder.authenticatorName); + config.setDisplayName(builder.displayName); + config.setEnabled(builder.isEnabled); + + return config; + } + + private static FederatedAuthenticatorConfig createSystemDefinedFederatedAuthenticator( + Builder builder) throws IdentityProviderManagementClientException { + + validateSystemDefinedFederatedAuthenticatorModel(builder); + FederatedAuthenticatorConfig authConfig = new FederatedAuthenticatorConfig(); + authConfig.setDefinedByType(DefinedByType.SYSTEM); + authConfig.setProperties(builder.properties.toArray(new Property[0])); + return authConfig; + } + + private static void validateSystemDefinedFederatedAuthenticatorModel(Builder builder) + throws IdentityProviderManagementClientException { + + // The System-defined authenticator configs must not have endpoint configurations; throw an error if they do. + if (builder.endpoint != null) { + Constants.ErrorMessage error = Constants.ErrorMessage.ERROR_CODE_ENDPOINT_PROVIDED_FOR_SYSTEM_AUTH; + throw new IdentityProviderManagementClientException(error.getCode(), String.format(error.getDescription(), + builder.authenticatorName)); + } + + // Check if there is an authenticator registered in the system for the given authenticator ID. + if (ApplicationAuthenticatorService.getInstance() + .getFederatedAuthenticatorByName(builder.authenticatorName) == null) { + Constants.ErrorMessage error = Constants.ErrorMessage.ERROR_CODE_NO_SYSTEM_AUTHENTICATOR_FOUND; + throw new IdentityProviderManagementClientException(error.getCode(), error.getDescription()); + } + } + + private static UserDefinedFederatedAuthenticatorConfig createUserDefinedFederatedAuthenticator(Builder builder) + throws IdentityProviderManagementClientException { + + validateUserDefinedFederatedAuthenticatorModel(builder); + + UserDefinedFederatedAuthenticatorConfig authConfig = new UserDefinedFederatedAuthenticatorConfig(); + UserDefinedAuthenticatorEndpointConfig.UserDefinedAuthenticatorEndpointConfigBuilder endpointConfigBuilder = + new UserDefinedAuthenticatorEndpointConfig.UserDefinedAuthenticatorEndpointConfigBuilder(); + endpointConfigBuilder.uri(builder.endpoint.getUri()); + endpointConfigBuilder.authenticationType(builder.endpoint.getAuthentication().getType().toString()); + endpointConfigBuilder.authenticationProperties(builder.endpoint.getAuthentication().getProperties() + .entrySet().stream().collect(Collectors.toMap( + Map.Entry::getKey, entry -> entry.getValue().toString()))); + authConfig.setEndpointConfig(endpointConfigBuilder.build()); + + return authConfig; + } + + private static void validateUserDefinedFederatedAuthenticatorModel(Builder builder) + throws IdentityProviderManagementClientException { + + // The User-defined authenticator configs must not have properties configurations; throw an error if they do. + if (builder.properties == null || !builder.properties.isEmpty()) { + Constants.ErrorMessage error = Constants.ErrorMessage.ERROR_CODE_PROPERTIES_PROVIDED_FOR_USER_AUTH; + throw new IdentityProviderManagementClientException(error.getCode(), + String.format(error.getDescription(), builder.authenticatorName)); + } + + // The User-defined authenticator configs must have endpoint configurations; throw an error if they don't. + if (builder.endpoint == null) { + Constants.ErrorMessage error = Constants.ErrorMessage.ERROR_CODE_NO_ENDPOINT_PROVIDED; + throw new IdentityProviderManagementClientException(error.getCode(), + String.format(error.getDescription(), builder.authenticatorName)); + } + } + + /** + * Builder class to build FederatedAuthenticatorConfig. + */ + public static class Builder { + private String definedByType; + private String authenticatorName; + private String displayName; + private Endpoint endpoint; + private List properties; + private Boolean isEnabled; + + public Builder definedByType(String definedByType) { + + this.definedByType = definedByType; + return this; + } + + public Builder authenticatorName(String authenticatorName) { + + this.authenticatorName = authenticatorName; + return this; + } + + public Builder displayName(String displayName) { + + this.displayName = displayName; + return this; + } + + public Builder endpoint(Endpoint endpoint) { + + this.endpoint = endpoint; + return this; + } + + public Builder properties(List properties) { + + this.properties = properties; + return this; + } + + public Builder enabled(Boolean enabled) { + + isEnabled = enabled; + return this; + } + + public FederatedAuthenticatorConfig build() throws IdentityProviderManagementClientException { + + return FederatedAuthenticatorConfigBuilderFactory.createFederatedAuthenticatorConfig(this); + } + } +}