diff --git a/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.common/src/main/java/org/wso2/carbon/identity/api/server/idp/common/Constants.java b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.common/src/main/java/org/wso2/carbon/identity/api/server/idp/common/Constants.java index c431dfad97..7b96242696 100644 --- a/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.common/src/main/java/org/wso2/carbon/identity/api/server/idp/common/Constants.java +++ b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.common/src/main/java/org/wso2/carbon/identity/api/server/idp/common/Constants.java @@ -146,18 +146,18 @@ public enum ErrorMessage { ERROR_CODE_ERROR_LISTING_TRUSTED_TOKEN_ISSUERS("60021", "Unable to list existing trusted token issuers.", "Server encountered an error while listing the trusted token issuers."), - ERROR_CODE_ENDPOINT_PROVIDED_FOR_SYSTEM_AUTH("60039", "Endpoint provided for the system " + - "defined federated authenticator", "No endpoint configuration must be provided for " + - "the system defined federated authenticators %s."), - ERROR_CODE_PROPERTIES_PROVIDED_FOR_USER_AUTH("60040", "Properties provided for the user " + - "defined federated authenticator", "No properties must be provided for the user defined " + + ERROR_CODE_ENDPOINT_PROVIDED_FOR_SYSTEM_AUTH("60039", "No endpoint configuration is allowed " + + "for system defined authenticators.", "No endpoint configuration must be " + + "provided for the system defined federated authenticators %s."), + ERROR_CODE_PROPERTIES_PROVIDED_FOR_USER_AUTH("60040", "No properties are allowed for " + + "user defined authenticators.", "No properties must be provided for the user defined " + "federated authenticators %s."), ERROR_CODE_NO_ENDPOINT_PROVIDED("60041", "No endpoint provided.", "Endpoint " + "configuration must be provided for the user defined federated authenticators %s."), ERROR_CODE_NON_DECODABLE_AUTH_ID("60042", "Non-decodable authenticator ID.", "Unable to decode the provided authenticator ID %s."), ERROR_CODE_NO_SYSTEM_AUTHENTICATOR_FOUND("60043", "No system authenticator found.", - "No system authenticator found for the provided authenticator Id."), + "No system authenticator found for the provided authenticator Id %s."), // Server Error starting from 650xx. ERROR_CODE_ERROR_ADDING_IDP("65002", 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..34602068d7 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,38 @@ 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 { + private DefinedByType resolveDefinedByTypeForCreateFederatedAuthenticator(String definedByType) { - 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)); - } - - 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); } - - 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; + return DefinedByType.SYSTEM; } - 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 +3110,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 +3883,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 +3893,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..f769c1e31b --- /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,175 @@ +/* + * 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(), + String.format(error.getDescription(), builder.authenticatorName)); + } + } + + 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); + } + } +}