diff --git a/components/org.wso2.carbon.identity.api.server.authenticators/org.wso2.carbon.identity.api.server.authenticators.common/src/main/java/org/wso2/carbon/identity/api/server/authenticators/common/Constants.java b/components/org.wso2.carbon.identity.api.server.authenticators/org.wso2.carbon.identity.api.server.authenticators.common/src/main/java/org/wso2/carbon/identity/api/server/authenticators/common/Constants.java index b2c546f4f3..72f0b0c811 100644 --- a/components/org.wso2.carbon.identity.api.server.authenticators/org.wso2.carbon.identity.api.server.authenticators.common/src/main/java/org/wso2/carbon/identity/api/server/authenticators/common/Constants.java +++ b/components/org.wso2.carbon.identity.api.server.authenticators/org.wso2.carbon.identity.api.server.authenticators.common/src/main/java/org/wso2/carbon/identity/api/server/authenticators/common/Constants.java @@ -27,6 +27,8 @@ private Constants() { public static final String AUTHENTICATOR_ERROR_PREFIX = "AUT-"; public static final String FEDERATED_AUTHENTICATORS = "federatedAuthenticators"; + // IdP property marking a shadow (shared) connection resolved from a parent organization. + public static final String IS_SHARED_PROPERTY = "isShared"; public static final String AUTHENTICATOR_PATH_COMPONENT = "/authenticators"; public static final String CONFIGS_AUTHENTICATOR_PATH_COMPONENT = "/configs/authenticators/%s"; public static final String PATH_SEPERATOR = "/"; diff --git a/components/org.wso2.carbon.identity.api.server.authenticators/org.wso2.carbon.identity.api.server.authenticators.v1/src/gen/java/org/wso2/carbon/identity/api/server/authenticators/v1/model/Authenticator.java b/components/org.wso2.carbon.identity.api.server.authenticators/org.wso2.carbon.identity.api.server.authenticators.v1/src/gen/java/org/wso2/carbon/identity/api/server/authenticators/v1/model/Authenticator.java index 76fd698e31..62ac3f7fcd 100644 --- a/components/org.wso2.carbon.identity.api.server.authenticators/org.wso2.carbon.identity.api.server.authenticators.v1/src/gen/java/org/wso2/carbon/identity/api/server/authenticators/v1/model/Authenticator.java +++ b/components/org.wso2.carbon.identity.api.server.authenticators/org.wso2.carbon.identity.api.server.authenticators.v1/src/gen/java/org/wso2/carbon/identity/api/server/authenticators/v1/model/Authenticator.java @@ -36,6 +36,7 @@ public class Authenticator { private String name; private String displayName; private Boolean isEnabled; + private Boolean isShared; @XmlType(name="DefinedByEnum") @XmlEnum(String.class) @@ -182,6 +183,25 @@ public void setIsEnabled(Boolean isEnabled) { this.isEnabled = isEnabled; } + /** + * Whether this authenticator belongs to a shared connection resolved from a parent organization. + **/ + public Authenticator isShared(Boolean isShared) { + + this.isShared = isShared; + return this; + } + + @ApiModelProperty(example = "false", value = "Whether this authenticator belongs to a shared connection resolved from a parent organization.") + @JsonProperty("isShared") + @Valid + public Boolean getIsShared() { + return isShared; + } + public void setIsShared(Boolean isShared) { + this.isShared = isShared; + } + /** **/ public Authenticator definedBy(DefinedByEnum definedBy) { @@ -314,6 +334,7 @@ public boolean equals(java.lang.Object o) { Objects.equals(this.name, authenticator.name) && Objects.equals(this.displayName, authenticator.displayName) && Objects.equals(this.isEnabled, authenticator.isEnabled) && + Objects.equals(this.isShared, authenticator.isShared) && Objects.equals(this.definedBy, authenticator.definedBy) && Objects.equals(this.type, authenticator.type) && Objects.equals(this.image, authenticator.image) && @@ -324,7 +345,7 @@ public boolean equals(java.lang.Object o) { @Override public int hashCode() { - return Objects.hash(id, name, displayName, isEnabled, definedBy, type, image, description, tags, self); + return Objects.hash(id, name, displayName, isEnabled, isShared, definedBy, type, image, description, tags, self); } @Override @@ -337,6 +358,7 @@ public String toString() { sb.append(" name: ").append(toIndentedString(name)).append("\n"); sb.append(" displayName: ").append(toIndentedString(displayName)).append("\n"); sb.append(" isEnabled: ").append(toIndentedString(isEnabled)).append("\n"); + sb.append(" isShared: ").append(toIndentedString(isShared)).append("\n"); sb.append(" definedBy: ").append(toIndentedString(definedBy)).append("\n"); sb.append(" type: ").append(toIndentedString(type)).append("\n"); sb.append(" image: ").append(toIndentedString(image)).append("\n"); diff --git a/components/org.wso2.carbon.identity.api.server.authenticators/org.wso2.carbon.identity.api.server.authenticators.v1/src/main/java/org/wso2/carbon/identity/api/server/authenticators/v1/core/ServerAuthenticatorManagementService.java b/components/org.wso2.carbon.identity.api.server.authenticators/org.wso2.carbon.identity.api.server.authenticators.v1/src/main/java/org/wso2/carbon/identity/api/server/authenticators/v1/core/ServerAuthenticatorManagementService.java index 20f452387e..a9ad261ad9 100644 --- a/components/org.wso2.carbon.identity.api.server.authenticators/org.wso2.carbon.identity.api.server.authenticators.v1/src/main/java/org/wso2/carbon/identity/api/server/authenticators/v1/core/ServerAuthenticatorManagementService.java +++ b/components/org.wso2.carbon.identity.api.server.authenticators/org.wso2.carbon.identity.api.server.authenticators.v1/src/main/java/org/wso2/carbon/identity/api/server/authenticators/v1/core/ServerAuthenticatorManagementService.java @@ -45,6 +45,7 @@ import org.wso2.carbon.identity.application.common.exception.AuthenticatorMgtServerException; import org.wso2.carbon.identity.application.common.model.FederatedAuthenticatorConfig; import org.wso2.carbon.identity.application.common.model.IdentityProvider; +import org.wso2.carbon.identity.application.common.model.IdentityProviderProperty; import org.wso2.carbon.identity.application.common.model.LocalAuthenticatorConfig; import org.wso2.carbon.identity.application.common.model.RequestPathAuthenticatorConfig; import org.wso2.carbon.identity.application.common.model.UserDefinedLocalAuthenticatorConfig; @@ -63,6 +64,7 @@ import org.wso2.carbon.idp.mgt.IdpManager; import org.wso2.carbon.idp.mgt.model.ConnectedAppsResult; import org.wso2.carbon.idp.mgt.model.IdpSearchResult; +import org.wso2.carbon.idp.mgt.model.SharedIdPResolveType; import java.io.IOException; import java.io.UnsupportedEncodingException; @@ -155,7 +157,8 @@ public List getAuthenticators(String filter, Integer limit, Integ at the maximum items per page count. */ if (idPCountToBeRetrieved > 0 && StringUtils.isBlank(filter)) { IdpSearchResult idpSearchResult = idpManager.getIdPs(idPCountToBeRetrieved, null, null, - null, null, ContextLoader.getTenantDomainFromContext(), requestedAttributeList); + null, null, ContextLoader.getTenantDomainFromContext(), requestedAttributeList, + SharedIdPResolveType.BASE_PARENT); identityProviders = idpSearchResult.getIdPs(); } @@ -401,7 +404,7 @@ private List buildAuthenticatorsListResponse(String filter, List< try { idpSearchResult = idpManager.getIdPs(idPCountToBeRetrieved, null, null, null, ContextLoader.getTenantDomainFromContext(), requestedAttributeList, - expressionNodesForIdp); + expressionNodesForIdp, SharedIdPResolveType.BASE_PARENT); identityProviders = idpSearchResult.getIdPs(); if (identityProviders != null) { addIdPsToAuthenticatorList(maximumItemsPerPage, identityProviders, authenticators, @@ -504,6 +507,7 @@ private void addIdp(IdentityProvider identityProvider, List authe authenticator.setDisplayName(identityProvider.getIdentityProviderName()); } authenticator.setIsEnabled(identityProvider.isEnable()); + authenticator.setIsShared(resolveIsSharedConnection(identityProvider)); authenticator.setType(Authenticator.TypeEnum.FEDERATED); authenticator.setImage(identityProvider.getImageUrl()); authenticator.setDescription(identityProvider.getIdentityProviderDescription()); @@ -531,6 +535,28 @@ private void addIdp(IdentityProvider identityProvider, List authe String.format("/v1/identity-providers/%s", identityProvider.getResourceId())).toString()); } + /** + * Resolves the value of the {@code isShared} IdP property when it is present on the identity provider (i.e. it + * belongs to a shared/shadow connection resolved from a parent organization), or {@code null} when the property + * is absent — so the {@code isShared} attribute is emitted only for shared connections and omitted otherwise. + * + * @param identityProvider The identity provider. + * @return The {@code isShared} property value if present; {@code null} otherwise. + */ + private Boolean resolveIsSharedConnection(IdentityProvider identityProvider) { + + IdentityProviderProperty[] idpProperties = identityProvider.getIdpProperties(); + if (idpProperties == null) { + return null; + } + for (IdentityProviderProperty property : idpProperties) { + if (Constants.IS_SHARED_PROPERTY.equals(property.getName())) { + return Boolean.parseBoolean(property.getValue()); + } + } + return null; + } + private FederatedAuthenticatorConfig resolveFederatedAuthenticatorConfig(IdentityProvider identityProvider) { try { @@ -892,7 +918,8 @@ private void getFilteredIdPs(int limit, int offSet, List requestedAttrib try { IdpSearchResult idpSearchResult = idpManager.getIdPs(limit, (offSet + limit), null, null, - ContextLoader.getTenantDomainFromContext(), requestedAttributeList, expressionNodes); + ContextLoader.getTenantDomainFromContext(), requestedAttributeList, expressionNodes, + SharedIdPResolveType.BASE_PARENT); identityProviders.addAll(idpSearchResult.getIdPs()); } catch (IdentityProviderManagementException e) { throw handleIdPException(e, Constants.ErrorMessage.ERROR_CODE_ERROR_LISTING_IDPS, null); diff --git a/components/org.wso2.carbon.identity.api.server.authenticators/org.wso2.carbon.identity.api.server.authenticators.v1/src/main/resources/authenticators.yaml b/components/org.wso2.carbon.identity.api.server.authenticators/org.wso2.carbon.identity.api.server.authenticators.v1/src/main/resources/authenticators.yaml index 2b312e43a2..75a1c962f1 100644 --- a/components/org.wso2.carbon.identity.api.server.authenticators/org.wso2.carbon.identity.api.server.authenticators.v1/src/main/resources/authenticators.yaml +++ b/components/org.wso2.carbon.identity.api.server.authenticators/org.wso2.carbon.identity.api.server.authenticators.v1/src/main/resources/authenticators.yaml @@ -333,6 +333,12 @@ components: isEnabled: type: boolean example: true + isShared: + type: boolean + example: true + description: Whether this authenticator belongs to a shared connection resolved from a parent organization. + Present (and true) only for shared connections; omitted otherwise. + readOnly: true definedBy: type: string enum: diff --git a/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.common/pom.xml b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.common/pom.xml index be1c9033d8..89761539f3 100644 --- a/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.common/pom.xml +++ b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.common/pom.xml @@ -58,6 +58,11 @@ org.wso2.carbon.identity.template.mgt provided + + org.wso2.carbon.identity.organization.management + org.wso2.carbon.identity.organization.management.organization.connection.sharing + provided + 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 8799630aa1..5bf8a322cc 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 @@ -34,6 +34,8 @@ public class Constants { public static final String JWKS_URI = "jwksUri"; public static final String SAML_METADATA_URI = "samlMetadataUri"; public static final String IDP_ISSUER_NAME = "idpIssuerName"; + // IdP property marking a shadow (shared) connection resolved from a parent organization. + public static final String IS_SHARED_PROPERTY = "isShared"; public static final String TEMPLATE_ID = "templateId"; public static final String META_DATA_SAML = "meta_data_saml"; public static final String SELECT_MODE = "SelectMode"; @@ -63,6 +65,19 @@ public class Constants { public static final String PAGE_LINK_REL_NEXT = "next"; public static final String PAGE_LINK_REL_PREVIOUS = "previous"; + // IdP sharing constants. + public static final String IDP_SHARE_PATH_COMPONENT = "/share"; + public static final String LIMIT_PARAM = "limit"; + public static final String RECURSIVE_PARAM = "recursive"; + public static final String FILTER_PARAM = "filter"; + public static final String EXCLUDED_ATTRIBUTES_PARAM = "excludedAttributes"; + public static final String INCLUDED_ATTRIBUTES_PARAM = "attributes"; + public static final String ASYNC_OPERATION_RESPONSE_STATUS = "Processing"; + public static final String IDP_SHARE_TRIGGER_SUCCESS = + "Identity provider sharing process triggered successfully."; + public static final String IDP_UNSHARE_TRIGGER_SUCCESS = + "Identity provider unsharing process triggered successfully."; + // IdP attributes constants. public static final String IS_PRIMARY = "isPrimary"; public static final String IS_FEDERATION_HUB = "isFederationHub"; @@ -170,6 +185,18 @@ public enum ErrorMessage { "Maximum number of authenticator properties exceeded.", "Maximum number of allowed properties for a federated authenticator have been exceeded. " + "Max allowed: %s."), + ERROR_CODE_INVALID_SHARE_REQUEST_BODY("60046", + "Invalid identity provider share request body.", + "The request body of the identity provider sharing request is empty or invalid."), + ERROR_CODE_SHARE_REQUEST_NO_ORGANIZATIONS("60047", + "No organizations provided.", + "At least one organization must be provided for the identity provider sharing/unsharing operation."), + ERROR_CODE_UNSUPPORTED_SHARE_POLICY("60048", + "Unsupported sharing policy.", + "Provided sharing policy %s is not supported for this operation."), + ERROR_CODE_INVALID_PAGINATION_CURSOR("60049", + "Invalid pagination cursor.", + "Provided pagination cursor is invalid."), // Server Error starting from 650xx. ERROR_CODE_ERROR_ADDING_IDP("65002", @@ -288,7 +315,15 @@ public enum ErrorMessage { ERROR_CODE_ERROR_INVALID_SEARCH_FILTER("65055", "Search request validation failed.", "Invalid search filter."), ERROR_CODE_VALIDATING_LOCAL_CLAIM_URIS("65056", "Error while validation local claim URIs", - "Error while validating claim URIs against local claims"); + "Error while validating claim URIs against local claims"), + ERROR_CODE_ERROR_SHARING_IDP("65057", "Unable to share the identity provider.", + "Server encountered an error while sharing the identity provider."), + ERROR_CODE_ERROR_UNSHARING_IDP("65058", "Unable to unshare the identity provider.", + "Server encountered an error while unsharing the identity provider."), + ERROR_CODE_ERROR_RETRIEVING_SHARED_ORGS("65059", + "Unable to retrieve the organizations the identity provider is shared with.", + "Server encountered an error while retrieving the organizations the identity provider " + + "is shared with."); private final String code; private final String message; 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/IdentityProviderServiceHolder.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/IdentityProviderServiceHolder.java index eacdf4944e..a980d0720b 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/IdentityProviderServiceHolder.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/IdentityProviderServiceHolder.java @@ -20,6 +20,7 @@ import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.identity.claim.metadata.mgt.ClaimMetadataManagementService; +import org.wso2.carbon.identity.organization.management.organization.connection.sharing.ConnectionSharingPolicyHandlerService; import org.wso2.carbon.identity.template.mgt.TemplateManager; import org.wso2.carbon.idp.mgt.IdentityProviderManager; @@ -49,6 +50,13 @@ private static class TemplateManagerHolder { .getThreadLocalCarbonContext().getOSGiService(TemplateManager.class, null); } + private static class ConnectionSharingPolicyHandlerServiceHolder { + + static final ConnectionSharingPolicyHandlerService SERVICE = + (ConnectionSharingPolicyHandlerService) PrivilegedCarbonContext.getThreadLocalCarbonContext() + .getOSGiService(ConnectionSharingPolicyHandlerService.class, null); + } + /** * Get IdentityProviderManager osgi service. * @@ -78,4 +86,14 @@ public static TemplateManager getTemplateManager() { return TemplateManagerHolder.SERVICE; } + + /** + * Get ConnectionSharingPolicyHandlerService osgi service. + * + * @return ConnectionSharingPolicyHandlerService. + */ + public static ConnectionSharingPolicyHandlerService getConnectionSharingPolicyHandlerService() { + + return ConnectionSharingPolicyHandlerServiceHolder.SERVICE; + } } diff --git a/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/pom.xml b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/pom.xml index 1f66db21df..37c061ed5c 100644 --- a/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/pom.xml +++ b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/pom.xml @@ -191,6 +191,21 @@ cxf-rt-rs-extension-search provided + + org.wso2.carbon.identity.organization.management.core + org.wso2.carbon.identity.organization.management.service + provided + + + org.wso2.carbon.identity.organization.management + org.wso2.carbon.identity.organization.management.organization.connection.sharing + provided + + + org.wso2.carbon.identity.organization.management + org.wso2.carbon.identity.organization.resource.sharing.policy.management + provided + org.testng testng diff --git a/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/IdentityProvidersApi.java b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/IdentityProvidersApi.java index 95749e65a3..d89d32cd13 100644 --- a/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/IdentityProvidersApi.java +++ b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/IdentityProvidersApi.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2023-2026, 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 @@ -38,8 +38,12 @@ import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderListResponse; import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderPOSTRequest; import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderResponse; +import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderShareAllRequestBody; +import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderShareSelectedRequestBody; import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderTemplate; import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderTemplateListResponse; +import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderUnshareAllRequestBody; +import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderUnshareSelectedRequestBody; import org.wso2.carbon.identity.api.server.idp.v1.model.JustInTimeProvisioning; import java.util.List; import org.wso2.carbon.identity.api.server.idp.v1.model.MetaFederatedAuthenticator; @@ -51,8 +55,10 @@ import org.wso2.carbon.identity.api.server.idp.v1.model.OutboundConnectorPUTRequest; import org.wso2.carbon.identity.api.server.idp.v1.model.OutboundProvisioningRequest; import org.wso2.carbon.identity.api.server.idp.v1.model.Patch; +import org.wso2.carbon.identity.api.server.idp.v1.model.ProcessSuccessResponse; import org.wso2.carbon.identity.api.server.idp.v1.model.ProvisioningResponse; import org.wso2.carbon.identity.api.server.idp.v1.model.Roles; +import org.wso2.carbon.identity.api.server.idp.v1.model.SharedOrganizationsResponse; import javax.validation.Valid; import javax.ws.rs.*; @@ -437,6 +443,30 @@ public Response getIDPs( @Valid@ApiParam(value = "Maximum number of records t return delegate.getIDPs(limit, offset, filter, sortOrder, sortBy, requiredAttributes ); } + @Valid + @GET + @Path("/{identity-provider-id}/share") + + @Produces({ "application/json" }) + @ApiOperation(value = "List of organizations that the identity provider is shared with. ", notes = "This API returns the list of organizations that the identity provider is shared with.
Scope required: `internal_idp_shared_access_view` ", response = SharedOrganizationsResponse.class, authorizations = { + @Authorization(value = "BasicAuth"), + @Authorization(value = "OAuth2", scopes = { + + }) + }, tags={ "Identity Provider Sharing", }) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Successful response", response = SharedOrganizationsResponse.class), + @ApiResponse(code = 400, message = "Bad Request", response = Error.class), + @ApiResponse(code = 401, message = "Unauthorized", response = Void.class), + @ApiResponse(code = 403, message = "Forbidden", response = Void.class), + @ApiResponse(code = 404, message = "Not Found", response = Error.class), + @ApiResponse(code = 500, message = "Server Error", response = Error.class) + }) + public Response getIdentityProviderSharedOrganizations(@ApiParam(value = "ID of the identity provider which is shared with organizations.",required=true) @PathParam("identity-provider-id") String identityProviderId, @Valid@ApiParam(value = "Base64 encoded cursor value for backward pagination. ") @QueryParam("before") String before, @Valid@ApiParam(value = "Base64 encoded cursor value for forward pagination. ") @QueryParam("after") String after, @Valid@ApiParam(value = "Condition to filter the retrieval of records. Supports 'sw', 'co', 'ew' and 'eq' operations. ") @QueryParam("filter") String filter, @Valid@ApiParam(value = "Maximum number of records to return. ") @QueryParam("limit") Integer limit, @Valid@ApiParam(value = "Determines whether a recursive search should happen. If set to true, will include shared organizations in all levels of the hierarchy; If set to false, includes only shared organizations in the next level of the hierarchy. ") @QueryParam("recursive") Boolean recursive, @Valid@ApiParam(value = "excludedAttribute parameter. ") @QueryParam("excludedAttributes") String excludedAttributes, @Valid@ApiParam(value = "Specifies the required parameters in the response. Only 'sharingMode' attribute is currently supported. /identity-providers/{identity-provider-id}/share?attributes=sharingMode ") @QueryParam("attributes") String attributes) { + + return delegate.getIdentityProviderSharedOrganizations(identityProviderId, before, after, filter, limit, recursive, excludedAttributes, attributes ); + } + @Valid @GET @Path("/{identity-provider-id}/provisioning/jit") @@ -701,6 +731,102 @@ public Response patchIDP(@ApiParam(value = "ID of the identity provider.",requir return delegate.patchIDP(identityProviderId, patch ); } + @Valid + @POST + @Path("/share-with-all") + @Consumes({ "application/json" }) + @Produces({ "application/json" }) + @ApiOperation(value = "Share a single identity provider with all organizations", notes = "This API provides the capability to share an identity provider with all organizations.
Scope required: `internal_idp_share` ", response = ProcessSuccessResponse.class, authorizations = { + @Authorization(value = "BasicAuth"), + @Authorization(value = "OAuth2", scopes = { + + }) + }, tags={ "Identity Provider Sharing", }) + @ApiResponses(value = { + @ApiResponse(code = 202, message = "Sharing process triggered successfully", response = ProcessSuccessResponse.class), + @ApiResponse(code = 400, message = "Bad Request", response = Error.class), + @ApiResponse(code = 401, message = "Unauthorized", response = Void.class), + @ApiResponse(code = 403, message = "Forbidden", response = Void.class), + @ApiResponse(code = 404, message = "Not Found", response = Error.class), + @ApiResponse(code = 500, message = "Server Error", response = Error.class) + }) + public Response shareIdentityProviderWithAll(@ApiParam(value = "" ,required=true) @Valid IdentityProviderShareAllRequestBody identityProviderShareAllRequestBody) { + + return delegate.shareIdentityProviderWithAll(identityProviderShareAllRequestBody ); + } + + @Valid + @POST + @Path("/share") + @Consumes({ "application/json" }) + @Produces({ "application/json" }) + @ApiOperation(value = "Share a single identity provider with specific organizations", notes = "This API provides the capability to share an identity provider with specific organizations.
Scope required: `internal_idp_share` ", response = ProcessSuccessResponse.class, authorizations = { + @Authorization(value = "BasicAuth"), + @Authorization(value = "OAuth2", scopes = { + + }) + }, tags={ "Identity Provider Sharing", }) + @ApiResponses(value = { + @ApiResponse(code = 202, message = "Sharing process triggered successfully", response = ProcessSuccessResponse.class), + @ApiResponse(code = 400, message = "Bad Request", response = Error.class), + @ApiResponse(code = 401, message = "Unauthorized", response = Void.class), + @ApiResponse(code = 403, message = "Forbidden", response = Void.class), + @ApiResponse(code = 404, message = "Not Found", response = Error.class), + @ApiResponse(code = 500, message = "Server Error", response = Error.class) + }) + public Response shareIdentityProviderWithSelected(@ApiParam(value = "" ,required=true) @Valid IdentityProviderShareSelectedRequestBody identityProviderShareSelectedRequestBody) { + + return delegate.shareIdentityProviderWithSelected(identityProviderShareSelectedRequestBody ); + } + + @Valid + @POST + @Path("/unshare-with-all") + @Consumes({ "application/json" }) + @Produces({ "application/json" }) + @ApiOperation(value = "Unshare an identity provider from all organizations", notes = "This API provides the capability to unshare an identity provider from all organizations.
Scope required: `internal_idp_unshare` ", response = ProcessSuccessResponse.class, authorizations = { + @Authorization(value = "BasicAuth"), + @Authorization(value = "OAuth2", scopes = { + + }) + }, tags={ "Identity Provider Sharing", }) + @ApiResponses(value = { + @ApiResponse(code = 202, message = "Unsharing process triggered successfully", response = ProcessSuccessResponse.class), + @ApiResponse(code = 400, message = "Bad Request", response = Error.class), + @ApiResponse(code = 401, message = "Unauthorized", response = Void.class), + @ApiResponse(code = 403, message = "Forbidden", response = Void.class), + @ApiResponse(code = 404, message = "Not Found", response = Error.class), + @ApiResponse(code = 500, message = "Server Error", response = Error.class) + }) + public Response unshareIdentityProviderFromAll(@ApiParam(value = "" ,required=true) @Valid IdentityProviderUnshareAllRequestBody identityProviderUnshareAllRequestBody) { + + return delegate.unshareIdentityProviderFromAll(identityProviderUnshareAllRequestBody ); + } + + @Valid + @POST + @Path("/unshare") + @Consumes({ "application/json" }) + @Produces({ "application/json" }) + @ApiOperation(value = "Unshare an identity provider from specific organizations", notes = "This API provides the capability to unshare an identity provider from specific organizations.
Scope required: `internal_idp_unshare` ", response = ProcessSuccessResponse.class, authorizations = { + @Authorization(value = "BasicAuth"), + @Authorization(value = "OAuth2", scopes = { + + }) + }, tags={ "Identity Provider Sharing", }) + @ApiResponses(value = { + @ApiResponse(code = 202, message = "Unsharing process triggered successfully", response = ProcessSuccessResponse.class), + @ApiResponse(code = 400, message = "Bad Request", response = Error.class), + @ApiResponse(code = 401, message = "Unauthorized", response = Void.class), + @ApiResponse(code = 403, message = "Forbidden", response = Void.class), + @ApiResponse(code = 404, message = "Not Found", response = Error.class), + @ApiResponse(code = 500, message = "Server Error", response = Error.class) + }) + public Response unshareIdentityProviderFromSelected(@ApiParam(value = "" ,required=true) @Valid IdentityProviderUnshareSelectedRequestBody identityProviderUnshareSelectedRequestBody) { + + return delegate.unshareIdentityProviderFromSelected(identityProviderUnshareSelectedRequestBody ); + } + @Valid @PUT @Path("/{identity-provider-id}/claims") diff --git a/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/IdentityProvidersApiService.java b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/IdentityProvidersApiService.java index 33d9d0edb5..5fc34588f0 100644 --- a/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/IdentityProvidersApiService.java +++ b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/IdentityProvidersApiService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2023-2026, 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 @@ -38,8 +38,12 @@ import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderListResponse; import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderPOSTRequest; import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderResponse; +import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderShareAllRequestBody; +import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderShareSelectedRequestBody; import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderTemplate; import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderTemplateListResponse; +import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderUnshareAllRequestBody; +import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderUnshareSelectedRequestBody; import org.wso2.carbon.identity.api.server.idp.v1.model.JustInTimeProvisioning; import java.util.List; import org.wso2.carbon.identity.api.server.idp.v1.model.MetaFederatedAuthenticator; @@ -53,6 +57,7 @@ import org.wso2.carbon.identity.api.server.idp.v1.model.Patch; import org.wso2.carbon.identity.api.server.idp.v1.model.ProvisioningResponse; import org.wso2.carbon.identity.api.server.idp.v1.model.Roles; +import org.wso2.carbon.identity.api.server.idp.v1.model.SharedOrganizationsResponse; import javax.ws.rs.core.Response; @@ -88,6 +93,8 @@ public interface IdentityProvidersApiService { public Response getIDPs(Integer limit, Integer offset, String filter, String sortOrder, String sortBy, String requiredAttributes); + public Response getIdentityProviderSharedOrganizations(String identityProviderId, String before, String after, String filter, Integer limit, Boolean recursive, String excludedAttributes, String attributes); + public Response getJITConfig(String identityProviderId); public Response getMetaFederatedAuthenticator(String federatedAuthenticatorId); @@ -110,6 +117,14 @@ public interface IdentityProvidersApiService { public Response patchIDP(String identityProviderId, List patch); + public Response shareIdentityProviderWithAll(IdentityProviderShareAllRequestBody identityProviderShareAllRequestBody); + + public Response shareIdentityProviderWithSelected(IdentityProviderShareSelectedRequestBody identityProviderShareSelectedRequestBody); + + public Response unshareIdentityProviderFromAll(IdentityProviderUnshareAllRequestBody identityProviderUnshareAllRequestBody); + + public Response unshareIdentityProviderFromSelected(IdentityProviderUnshareSelectedRequestBody identityProviderUnshareSelectedRequestBody); + public Response updateClaimConfig(String identityProviderId, Claims claims); public Response updateFederatedAssociationConfig(String identityProviderId, AssociationRequest associationRequest); diff --git a/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/BasicOrganizationResponse.java b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/BasicOrganizationResponse.java new file mode 100644 index 0000000000..07132c2617 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/BasicOrganizationResponse.java @@ -0,0 +1,317 @@ +/* + * Copyright (c) 2026, 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.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import org.wso2.carbon.identity.api.server.idp.v1.model.SharingMode; +import javax.validation.constraints.*; + + +import io.swagger.annotations.*; +import java.util.Objects; +import javax.validation.Valid; +import javax.xml.bind.annotation.*; + +public class BasicOrganizationResponse { + + private String id; + private String name; + private String orgHandle; + private String parentId; + +@XmlType(name="StatusEnum") +@XmlEnum(String.class) +public enum StatusEnum { + + @XmlEnumValue("ACTIVE") ACTIVE(String.valueOf("ACTIVE")), @XmlEnumValue("DISABLED") DISABLED(String.valueOf("DISABLED")); + + + private String value; + + StatusEnum(String v) { + value = v; + } + + public String value() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static StatusEnum fromValue(String value) { + for (StatusEnum b : StatusEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } +} + + private StatusEnum status; + private String ref; + private Boolean hasChildren; + private Integer depthFromRoot; + private SharingMode sharingMode; + + /** + **/ + public BasicOrganizationResponse id(String id) { + + this.id = id; + return this; + } + + @ApiModelProperty(example = "b4526d91-a8bf-43d2-8b14-c548cf73065b", required = true, value = "") + @JsonProperty("id") + @Valid + @NotNull(message = "Property id cannot be null.") + + public String getId() { + return id; + } + public void setId(String id) { + this.id = id; + } + + /** + **/ + public BasicOrganizationResponse name(String name) { + + this.name = name; + return this; + } + + @ApiModelProperty(example = "ABC Builders", required = true, value = "") + @JsonProperty("name") + @Valid + @NotNull(message = "Property name cannot be null.") + + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + + /** + **/ + public BasicOrganizationResponse orgHandle(String orgHandle) { + + this.orgHandle = orgHandle; + return this; + } + + @ApiModelProperty(example = "abcbuilders", required = true, value = "") + @JsonProperty("orgHandle") + @Valid + @NotNull(message = "Property orgHandle cannot be null.") + + public String getOrgHandle() { + return orgHandle; + } + public void setOrgHandle(String orgHandle) { + this.orgHandle = orgHandle; + } + + /** + * The parent organization ID. + **/ + public BasicOrganizationResponse parentId(String parentId) { + + this.parentId = parentId; + return this; + } + + @ApiModelProperty(example = "08f8c1d2-4b3e-4c5a-9f6b-7d8e9f0a1b2c", required = true, value = "The parent organization ID. ") + @JsonProperty("parentId") + @Valid + @NotNull(message = "Property parentId cannot be null.") + + public String getParentId() { + return parentId; + } + public void setParentId(String parentId) { + this.parentId = parentId; + } + + /** + **/ + public BasicOrganizationResponse status(StatusEnum status) { + + this.status = status; + return this; + } + + @ApiModelProperty(example = "ACTIVE", required = true, value = "") + @JsonProperty("status") + @Valid + @NotNull(message = "Property status cannot be null.") + + public StatusEnum getStatus() { + return status; + } + public void setStatus(StatusEnum status) { + this.status = status; + } + + /** + **/ + public BasicOrganizationResponse ref(String ref) { + + this.ref = ref; + return this; + } + + @ApiModelProperty(example = "/t/wso2.com/api/server/v1/organizations/b4526d91-a8bf-43d2-8b14-c548cf73065b", required = true, value = "") + @JsonProperty("ref") + @Valid + @NotNull(message = "Property ref cannot be null.") + + public String getRef() { + return ref; + } + public void setRef(String ref) { + this.ref = ref; + } + + /** + **/ + public BasicOrganizationResponse hasChildren(Boolean hasChildren) { + + this.hasChildren = hasChildren; + return this; + } + + @ApiModelProperty(example = "true", required = true, value = "") + @JsonProperty("hasChildren") + @Valid + @NotNull(message = "Property hasChildren cannot be null.") + + public Boolean getHasChildren() { + return hasChildren; + } + public void setHasChildren(Boolean hasChildren) { + this.hasChildren = hasChildren; + } + + /** + **/ + public BasicOrganizationResponse depthFromRoot(Integer depthFromRoot) { + + this.depthFromRoot = depthFromRoot; + return this; + } + + @ApiModelProperty(example = "1", required = true, value = "") + @JsonProperty("depthFromRoot") + @Valid + @NotNull(message = "Property depthFromRoot cannot be null.") + + public Integer getDepthFromRoot() { + return depthFromRoot; + } + public void setDepthFromRoot(Integer depthFromRoot) { + this.depthFromRoot = depthFromRoot; + } + + /** + **/ + public BasicOrganizationResponse sharingMode(SharingMode sharingMode) { + + this.sharingMode = sharingMode; + return this; + } + + @ApiModelProperty(value = "") + @JsonProperty("sharingMode") + @Valid + public SharingMode getSharingMode() { + return sharingMode; + } + public void setSharingMode(SharingMode sharingMode) { + this.sharingMode = sharingMode; + } + + + + @Override + public boolean equals(java.lang.Object o) { + + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + BasicOrganizationResponse basicOrganizationResponse = (BasicOrganizationResponse) o; + return Objects.equals(this.id, basicOrganizationResponse.id) && + Objects.equals(this.name, basicOrganizationResponse.name) && + Objects.equals(this.orgHandle, basicOrganizationResponse.orgHandle) && + Objects.equals(this.parentId, basicOrganizationResponse.parentId) && + Objects.equals(this.status, basicOrganizationResponse.status) && + Objects.equals(this.ref, basicOrganizationResponse.ref) && + Objects.equals(this.hasChildren, basicOrganizationResponse.hasChildren) && + Objects.equals(this.depthFromRoot, basicOrganizationResponse.depthFromRoot) && + Objects.equals(this.sharingMode, basicOrganizationResponse.sharingMode); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, orgHandle, parentId, status, ref, hasChildren, depthFromRoot, sharingMode); + } + + @Override + public String toString() { + + StringBuilder sb = new StringBuilder(); + sb.append("class BasicOrganizationResponse {\n"); + + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" orgHandle: ").append(toIndentedString(orgHandle)).append("\n"); + sb.append(" parentId: ").append(toIndentedString(parentId)).append("\n"); + sb.append(" status: ").append(toIndentedString(status)).append("\n"); + sb.append(" ref: ").append(toIndentedString(ref)).append("\n"); + sb.append(" hasChildren: ").append(toIndentedString(hasChildren)).append("\n"); + sb.append(" depthFromRoot: ").append(toIndentedString(depthFromRoot)).append("\n"); + sb.append(" sharingMode: ").append(toIndentedString(sharingMode)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n"); + } +} + diff --git a/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/IdentityProviderResponse.java b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/IdentityProviderResponse.java index 21930c295d..e64e172e9e 100644 --- a/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/IdentityProviderResponse.java +++ b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/IdentityProviderResponse.java @@ -47,6 +47,7 @@ public class IdentityProviderResponse { private String templateId; private Boolean isEnabled = true; private Boolean isPrimary = false; + private Boolean isShared; private String image; private Boolean isFederationHub; private String homeRealmIdentifier; @@ -169,6 +170,25 @@ public void setIsPrimary(Boolean isPrimary) { this.isPrimary = isPrimary; } + /** + * Whether this connection is a shared connection resolved from a parent organization. + **/ + public IdentityProviderResponse isShared(Boolean isShared) { + + this.isShared = isShared; + return this; + } + + @ApiModelProperty(example = "false", value = "Whether this connection is a shared connection resolved from a parent organization.") + @JsonProperty("isShared") + @Valid + public Boolean getIsShared() { + return isShared; + } + public void setIsShared(Boolean isShared) { + this.isShared = isShared; + } + /** **/ public IdentityProviderResponse image(String image) { @@ -412,6 +432,7 @@ public boolean equals(java.lang.Object o) { Objects.equals(this.templateId, identityProviderResponse.templateId) && Objects.equals(this.isEnabled, identityProviderResponse.isEnabled) && Objects.equals(this.isPrimary, identityProviderResponse.isPrimary) && + Objects.equals(this.isShared, identityProviderResponse.isShared) && Objects.equals(this.image, identityProviderResponse.image) && Objects.equals(this.isFederationHub, identityProviderResponse.isFederationHub) && Objects.equals(this.homeRealmIdentifier, identityProviderResponse.homeRealmIdentifier) && @@ -428,7 +449,7 @@ public boolean equals(java.lang.Object o) { @Override public int hashCode() { - return Objects.hash(id, name, description, templateId, isEnabled, isPrimary, image, isFederationHub, homeRealmIdentifier, certificate, alias, idpIssuerName, claims, roles, groups, federatedAuthenticators, provisioning, implicitAssociation); + return Objects.hash(id, name, description, templateId, isEnabled, isPrimary, isShared, image, isFederationHub, homeRealmIdentifier, certificate, alias, idpIssuerName, claims, roles, groups, federatedAuthenticators, provisioning, implicitAssociation); } @Override @@ -443,6 +464,7 @@ public String toString() { sb.append(" templateId: ").append(toIndentedString(templateId)).append("\n"); sb.append(" isEnabled: ").append(toIndentedString(isEnabled)).append("\n"); sb.append(" isPrimary: ").append(toIndentedString(isPrimary)).append("\n"); + sb.append(" isShared: ").append(toIndentedString(isShared)).append("\n"); sb.append(" image: ").append(toIndentedString(image)).append("\n"); sb.append(" isFederationHub: ").append(toIndentedString(isFederationHub)).append("\n"); sb.append(" homeRealmIdentifier: ").append(toIndentedString(homeRealmIdentifier)).append("\n"); diff --git a/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/IdentityProviderShareAllRequestBody.java b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/IdentityProviderShareAllRequestBody.java new file mode 100644 index 0000000000..e1563f6ded --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/IdentityProviderShareAllRequestBody.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2026, 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.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import javax.validation.constraints.*; + + +import io.swagger.annotations.*; +import java.util.Objects; +import javax.validation.Valid; +import javax.xml.bind.annotation.*; + +public class IdentityProviderShareAllRequestBody { + + private String identityProviderId; + private String policy; + + /** + **/ + public IdentityProviderShareAllRequestBody identityProviderId(String identityProviderId) { + + this.identityProviderId = identityProviderId; + return this; + } + + @ApiModelProperty(example = "c75e27f9-98c7-4518-a968-c6cd59f0ac6b", required = true, value = "") + @JsonProperty("identityProviderId") + @Valid + @NotNull(message = "Property identityProviderId cannot be null.") + + public String getIdentityProviderId() { + return identityProviderId; + } + public void setIdentityProviderId(String identityProviderId) { + this.identityProviderId = identityProviderId; + } + + /** + * Sharing scope for this organization. Possible values: - `SELECTED_ORG_ONLY` - `SELECTED_ORG_WITH_ALL_EXISTING_AND_FUTURE_CHILDREN` - `ALL_EXISTING_AND_FUTURE_ORGS` + **/ + public IdentityProviderShareAllRequestBody policy(String policy) { + + this.policy = policy; + return this; + } + + @ApiModelProperty(required = true, value = "Sharing scope for this organization. Possible values: - `SELECTED_ORG_ONLY` - `SELECTED_ORG_WITH_ALL_EXISTING_AND_FUTURE_CHILDREN` - `ALL_EXISTING_AND_FUTURE_ORGS`") + @JsonProperty("policy") + @Valid + @NotNull(message = "Property policy cannot be null.") + + public String getPolicy() { + return policy; + } + public void setPolicy(String policy) { + this.policy = policy; + } + + + + @Override + public boolean equals(java.lang.Object o) { + + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + IdentityProviderShareAllRequestBody identityProviderShareAllRequestBody = (IdentityProviderShareAllRequestBody) o; + return Objects.equals(this.identityProviderId, identityProviderShareAllRequestBody.identityProviderId) && + Objects.equals(this.policy, identityProviderShareAllRequestBody.policy); + } + + @Override + public int hashCode() { + return Objects.hash(identityProviderId, policy); + } + + @Override + public String toString() { + + StringBuilder sb = new StringBuilder(); + sb.append("class IdentityProviderShareAllRequestBody {\n"); + + sb.append(" identityProviderId: ").append(toIndentedString(identityProviderId)).append("\n"); + sb.append(" policy: ").append(toIndentedString(policy)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n"); + } +} + diff --git a/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/IdentityProviderShareSelectedRequestBody.java b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/IdentityProviderShareSelectedRequestBody.java new file mode 100644 index 0000000000..43aee93b4a --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/IdentityProviderShareSelectedRequestBody.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2026, 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.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.util.ArrayList; +import java.util.List; +import org.wso2.carbon.identity.api.server.idp.v1.model.OrgShareConfig; +import javax.validation.constraints.*; + + +import io.swagger.annotations.*; +import java.util.Objects; +import javax.validation.Valid; +import javax.xml.bind.annotation.*; + +public class IdentityProviderShareSelectedRequestBody { + + private String identityProviderId; + private List organizations = new ArrayList<>(); + + + /** + **/ + public IdentityProviderShareSelectedRequestBody identityProviderId(String identityProviderId) { + + this.identityProviderId = identityProviderId; + return this; + } + + @ApiModelProperty(example = "c75e27f9-98c7-4518-a968-c6cd59f0ac6b", required = true, value = "") + @JsonProperty("identityProviderId") + @Valid + @NotNull(message = "Property identityProviderId cannot be null.") + + public String getIdentityProviderId() { + return identityProviderId; + } + public void setIdentityProviderId(String identityProviderId) { + this.identityProviderId = identityProviderId; + } + + /** + **/ + public IdentityProviderShareSelectedRequestBody organizations(List organizations) { + + this.organizations = organizations; + return this; + } + + @ApiModelProperty(required = true, value = "") + @JsonProperty("organizations") + @Valid + @NotNull(message = "Property organizations cannot be null.") + + public List getOrganizations() { + return organizations; + } + public void setOrganizations(List organizations) { + this.organizations = organizations; + } + + public IdentityProviderShareSelectedRequestBody addOrganizationsItem(OrgShareConfig organizationsItem) { + this.organizations.add(organizationsItem); + return this; + } + + + + @Override + public boolean equals(java.lang.Object o) { + + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + IdentityProviderShareSelectedRequestBody identityProviderShareSelectedRequestBody = (IdentityProviderShareSelectedRequestBody) o; + return Objects.equals(this.identityProviderId, identityProviderShareSelectedRequestBody.identityProviderId) && + Objects.equals(this.organizations, identityProviderShareSelectedRequestBody.organizations); + } + + @Override + public int hashCode() { + return Objects.hash(identityProviderId, organizations); + } + + @Override + public String toString() { + + StringBuilder sb = new StringBuilder(); + sb.append("class IdentityProviderShareSelectedRequestBody {\n"); + + sb.append(" identityProviderId: ").append(toIndentedString(identityProviderId)).append("\n"); + sb.append(" organizations: ").append(toIndentedString(organizations)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n"); + } +} + diff --git a/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/IdentityProviderUnshareAllRequestBody.java b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/IdentityProviderUnshareAllRequestBody.java new file mode 100644 index 0000000000..9798ee4923 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/IdentityProviderUnshareAllRequestBody.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2026, 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.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import javax.validation.constraints.*; + + +import io.swagger.annotations.*; +import java.util.Objects; +import javax.validation.Valid; +import javax.xml.bind.annotation.*; + +public class IdentityProviderUnshareAllRequestBody { + + private String identityProviderId; + + /** + **/ + public IdentityProviderUnshareAllRequestBody identityProviderId(String identityProviderId) { + + this.identityProviderId = identityProviderId; + return this; + } + + @ApiModelProperty(example = "c75e27f9-98c7-4518-a968-c6cd59f0ac6b", required = true, value = "") + @JsonProperty("identityProviderId") + @Valid + @NotNull(message = "Property identityProviderId cannot be null.") + + public String getIdentityProviderId() { + return identityProviderId; + } + public void setIdentityProviderId(String identityProviderId) { + this.identityProviderId = identityProviderId; + } + + + + @Override + public boolean equals(java.lang.Object o) { + + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + IdentityProviderUnshareAllRequestBody identityProviderUnshareAllRequestBody = (IdentityProviderUnshareAllRequestBody) o; + return Objects.equals(this.identityProviderId, identityProviderUnshareAllRequestBody.identityProviderId); + } + + @Override + public int hashCode() { + return Objects.hash(identityProviderId); + } + + @Override + public String toString() { + + StringBuilder sb = new StringBuilder(); + sb.append("class IdentityProviderUnshareAllRequestBody {\n"); + + sb.append(" identityProviderId: ").append(toIndentedString(identityProviderId)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n"); + } +} + diff --git a/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/IdentityProviderUnshareSelectedRequestBody.java b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/IdentityProviderUnshareSelectedRequestBody.java new file mode 100644 index 0000000000..7a364083cf --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/IdentityProviderUnshareSelectedRequestBody.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2026, 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.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.util.ArrayList; +import java.util.List; +import javax.validation.constraints.*; + + +import io.swagger.annotations.*; +import java.util.Objects; +import javax.validation.Valid; +import javax.xml.bind.annotation.*; + +public class IdentityProviderUnshareSelectedRequestBody { + + private String identityProviderId; + private List orgIds = new ArrayList<>(); + + + /** + **/ + public IdentityProviderUnshareSelectedRequestBody identityProviderId(String identityProviderId) { + + this.identityProviderId = identityProviderId; + return this; + } + + @ApiModelProperty(example = "c75e27f9-98c7-4518-a968-c6cd59f0ac6b", required = true, value = "") + @JsonProperty("identityProviderId") + @Valid + @NotNull(message = "Property identityProviderId cannot be null.") + + public String getIdentityProviderId() { + return identityProviderId; + } + public void setIdentityProviderId(String identityProviderId) { + this.identityProviderId = identityProviderId; + } + + /** + **/ + public IdentityProviderUnshareSelectedRequestBody orgIds(List orgIds) { + + this.orgIds = orgIds; + return this; + } + + @ApiModelProperty(example = "[\"682edf68-4835-4bb8-961f-0a16bc6cc866\",\"ghfbctgf-4318-46d4-8ee1-7t3s38e23098\"]", required = true, value = "") + @JsonProperty("orgIds") + @Valid + @NotNull(message = "Property orgIds cannot be null.") + + public List getOrgIds() { + return orgIds; + } + public void setOrgIds(List orgIds) { + this.orgIds = orgIds; + } + + public IdentityProviderUnshareSelectedRequestBody addOrgIdsItem(String orgIdsItem) { + this.orgIds.add(orgIdsItem); + return this; + } + + + + @Override + public boolean equals(java.lang.Object o) { + + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + IdentityProviderUnshareSelectedRequestBody identityProviderUnshareSelectedRequestBody = (IdentityProviderUnshareSelectedRequestBody) o; + return Objects.equals(this.identityProviderId, identityProviderUnshareSelectedRequestBody.identityProviderId) && + Objects.equals(this.orgIds, identityProviderUnshareSelectedRequestBody.orgIds); + } + + @Override + public int hashCode() { + return Objects.hash(identityProviderId, orgIds); + } + + @Override + public String toString() { + + StringBuilder sb = new StringBuilder(); + sb.append("class IdentityProviderUnshareSelectedRequestBody {\n"); + + sb.append(" identityProviderId: ").append(toIndentedString(identityProviderId)).append("\n"); + sb.append(" orgIds: ").append(toIndentedString(orgIds)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n"); + } +} + diff --git a/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/OrgShareConfig.java b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/OrgShareConfig.java new file mode 100644 index 0000000000..7c3d1759a3 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/OrgShareConfig.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2026, 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.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import javax.validation.constraints.*; + + +import io.swagger.annotations.*; +import java.util.Objects; +import javax.validation.Valid; +import javax.xml.bind.annotation.*; + +public class OrgShareConfig { + + private String orgId; + private String policy; + + /** + **/ + public OrgShareConfig orgId(String orgId) { + + this.orgId = orgId; + return this; + } + + @ApiModelProperty(example = "682edf68-4835-4bb8-961f-0a16bc6cc866", required = true, value = "") + @JsonProperty("orgId") + @Valid + @NotNull(message = "Property orgId cannot be null.") + + public String getOrgId() { + return orgId; + } + public void setOrgId(String orgId) { + this.orgId = orgId; + } + + /** + * Sharing scope for this organization. Possible values: - `SELECTED_ORG_ONLY` - `SELECTED_ORG_WITH_ALL_EXISTING_AND_FUTURE_CHILDREN` - `ALL_EXISTING_AND_FUTURE_ORGS` + **/ + public OrgShareConfig policy(String policy) { + + this.policy = policy; + return this; + } + + @ApiModelProperty(required = true, value = "Sharing scope for this organization. Possible values: - `SELECTED_ORG_ONLY` - `SELECTED_ORG_WITH_ALL_EXISTING_AND_FUTURE_CHILDREN` - `ALL_EXISTING_AND_FUTURE_ORGS`") + @JsonProperty("policy") + @Valid + @NotNull(message = "Property policy cannot be null.") + + public String getPolicy() { + return policy; + } + public void setPolicy(String policy) { + this.policy = policy; + } + + + + @Override + public boolean equals(java.lang.Object o) { + + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + OrgShareConfig orgShareConfig = (OrgShareConfig) o; + return Objects.equals(this.orgId, orgShareConfig.orgId) && + Objects.equals(this.policy, orgShareConfig.policy); + } + + @Override + public int hashCode() { + return Objects.hash(orgId, policy); + } + + @Override + public String toString() { + + StringBuilder sb = new StringBuilder(); + sb.append("class OrgShareConfig {\n"); + + sb.append(" orgId: ").append(toIndentedString(orgId)).append("\n"); + sb.append(" policy: ").append(toIndentedString(policy)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n"); + } +} + diff --git a/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/ProcessSuccessResponse.java b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/ProcessSuccessResponse.java new file mode 100644 index 0000000000..d484647c30 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/ProcessSuccessResponse.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2026, 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.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import javax.validation.constraints.*; + + +import io.swagger.annotations.*; +import java.util.Objects; +import javax.validation.Valid; +import javax.xml.bind.annotation.*; + +public class ProcessSuccessResponse { + + private String status; + private String details; + + /** + **/ + public ProcessSuccessResponse status(String status) { + + this.status = status; + return this; + } + + @ApiModelProperty(example = "Processing", value = "") + @JsonProperty("status") + @Valid + public String getStatus() { + return status; + } + public void setStatus(String status) { + this.status = status; + } + + /** + **/ + public ProcessSuccessResponse details(String details) { + + this.details = details; + return this; + } + + @ApiModelProperty(example = "Identity provider sharing process triggered successfully.", value = "") + @JsonProperty("details") + @Valid + public String getDetails() { + return details; + } + public void setDetails(String details) { + this.details = details; + } + + + + @Override + public boolean equals(java.lang.Object o) { + + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ProcessSuccessResponse processSuccessResponse = (ProcessSuccessResponse) o; + return Objects.equals(this.status, processSuccessResponse.status) && + Objects.equals(this.details, processSuccessResponse.details); + } + + @Override + public int hashCode() { + return Objects.hash(status, details); + } + + @Override + public String toString() { + + StringBuilder sb = new StringBuilder(); + sb.append("class ProcessSuccessResponse {\n"); + + sb.append(" status: ").append(toIndentedString(status)).append("\n"); + sb.append(" details: ").append(toIndentedString(details)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n"); + } +} + diff --git a/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/SharedOrganizationsResponse.java b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/SharedOrganizationsResponse.java new file mode 100644 index 0000000000..4d2e886c7c --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/SharedOrganizationsResponse.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2026, 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.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.util.ArrayList; +import java.util.List; +import org.wso2.carbon.identity.api.server.idp.v1.model.BasicOrganizationResponse; +import org.wso2.carbon.identity.api.server.idp.v1.model.Link; +import org.wso2.carbon.identity.api.server.idp.v1.model.SharingMode; +import javax.validation.constraints.*; + + +import io.swagger.annotations.*; +import java.util.Objects; +import javax.validation.Valid; +import javax.xml.bind.annotation.*; + +public class SharedOrganizationsResponse { + + private List links = null; + + private SharingMode sharingMode; + private List organizations = null; + + + /** + **/ + public SharedOrganizationsResponse links(List links) { + + this.links = links; + return this; + } + + @ApiModelProperty(example = "[{\"href\":\"/api/server/v1/identity-providers/67f5a202-48c4-4313-9327-016da5f08f17/share?limit=10&recursive=false&next=MTA=\",\"rel\":\"next\"},{\"href\":\"/api/server/v1/identity-providers/67f5a202-48c4-4313-9327-016da5f08f17/share?limit=10&recursive=false&before=MTA=\",\"rel\":\"previous\"}]", value = "") + @JsonProperty("links") + @Valid + public List getLinks() { + return links; + } + public void setLinks(List links) { + this.links = links; + } + + public SharedOrganizationsResponse addLinksItem(Link linksItem) { + if (this.links == null) { + this.links = new ArrayList<>(); + } + this.links.add(linksItem); + return this; + } + + /** + **/ + public SharedOrganizationsResponse sharingMode(SharingMode sharingMode) { + + this.sharingMode = sharingMode; + return this; + } + + @ApiModelProperty(value = "") + @JsonProperty("sharingMode") + @Valid + public SharingMode getSharingMode() { + return sharingMode; + } + public void setSharingMode(SharingMode sharingMode) { + this.sharingMode = sharingMode; + } + + /** + **/ + public SharedOrganizationsResponse organizations(List organizations) { + + this.organizations = organizations; + return this; + } + + @ApiModelProperty(value = "") + @JsonProperty("organizations") + @Valid + public List getOrganizations() { + return organizations; + } + public void setOrganizations(List organizations) { + this.organizations = organizations; + } + + public SharedOrganizationsResponse addOrganizationsItem(BasicOrganizationResponse organizationsItem) { + if (this.organizations == null) { + this.organizations = new ArrayList<>(); + } + this.organizations.add(organizationsItem); + return this; + } + + + + @Override + public boolean equals(java.lang.Object o) { + + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SharedOrganizationsResponse sharedOrganizationsResponse = (SharedOrganizationsResponse) o; + return Objects.equals(this.links, sharedOrganizationsResponse.links) && + Objects.equals(this.sharingMode, sharedOrganizationsResponse.sharingMode) && + Objects.equals(this.organizations, sharedOrganizationsResponse.organizations); + } + + @Override + public int hashCode() { + return Objects.hash(links, sharingMode, organizations); + } + + @Override + public String toString() { + + StringBuilder sb = new StringBuilder(); + sb.append("class SharedOrganizationsResponse {\n"); + + sb.append(" links: ").append(toIndentedString(links)).append("\n"); + sb.append(" sharingMode: ").append(toIndentedString(sharingMode)).append("\n"); + sb.append(" organizations: ").append(toIndentedString(organizations)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n"); + } +} + diff --git a/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/SharingMode.java b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/SharingMode.java new file mode 100644 index 0000000000..b5d98891eb --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/gen/java/org/wso2/carbon/identity/api/server/idp/v1/model/SharingMode.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2026, 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.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import javax.validation.constraints.*; + + +import io.swagger.annotations.*; +import java.util.Objects; +import javax.validation.Valid; +import javax.xml.bind.annotation.*; + +public class SharingMode { + + private String policy; + + /** + * Sharing scope for this organization. Possible values: - `SELECTED_ORG_ONLY` - `SELECTED_ORG_WITH_ALL_EXISTING_AND_FUTURE_CHILDREN` - `ALL_EXISTING_AND_FUTURE_ORGS` + **/ + public SharingMode policy(String policy) { + + this.policy = policy; + return this; + } + + @ApiModelProperty(value = "Sharing scope for this organization. Possible values: - `SELECTED_ORG_ONLY` - `SELECTED_ORG_WITH_ALL_EXISTING_AND_FUTURE_CHILDREN` - `ALL_EXISTING_AND_FUTURE_ORGS`") + @JsonProperty("policy") + @Valid + public String getPolicy() { + return policy; + } + public void setPolicy(String policy) { + this.policy = policy; + } + + + + @Override + public boolean equals(java.lang.Object o) { + + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SharingMode sharingMode = (SharingMode) o; + return Objects.equals(this.policy, sharingMode.policy); + } + + @Override + public int hashCode() { + return Objects.hash(policy); + } + + @Override + public String toString() { + + StringBuilder sb = new StringBuilder(); + sb.append("class SharingMode {\n"); + + sb.append(" policy: ").append(toIndentedString(policy)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n"); + } +} + 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 4474f7f00b..d810617624 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 @@ -129,6 +129,7 @@ import org.wso2.carbon.idp.mgt.dao.IdPManagementDAO; import org.wso2.carbon.idp.mgt.model.ConnectedAppsResult; import org.wso2.carbon.idp.mgt.model.IdpSearchResult; +import org.wso2.carbon.idp.mgt.model.SharedIdPResolveType; import org.wso2.carbon.idp.mgt.util.IdPManagementConstants; import org.wso2.carbon.idp.mgt.util.IdPManagementUtil; import org.yaml.snakeyaml.TypeDescription; @@ -213,7 +214,8 @@ public IdentityProviderListResponse getIDPs(String requiredAttributes, Integer l requestedAttributeList = new ArrayList<>(Arrays.asList(requiredAttributes.split(","))); } return createIDPListResponse(identityProviderManager.getIdPs(limit, offset, filter, sortOrder, sortBy, - ContextLoader.getTenantDomainFromContext(), requestedAttributeList), + ContextLoader.getTenantDomainFromContext(), requestedAttributeList, + SharedIdPResolveType.BASE_PARENT), requestedAttributeList); } catch (IdentityProviderManagementException e) { throw handleIdPException(e, Constants.ErrorMessage.ERROR_CODE_ERROR_LISTING_IDPS, null); @@ -277,7 +279,7 @@ public IdentityProviderResponse getIDP(String idpId) { try { IdentityProvider identityProvider = identityProviderManager.getIdPByResourceId(idpId, - ContextLoader.getTenantDomainFromContext(), true); + ContextLoader.getTenantDomainFromContext(), true, SharedIdPResolveType.BASE_PARENT); if (identityProvider == null) { throw handleException(Response.Status.NOT_FOUND, Constants.ErrorMessage.ERROR_CODE_IDP_NOT_FOUND, idpId); @@ -302,7 +304,7 @@ public IdentityProviderResponse patchIDP(String identityProviderId, List try { IdentityProvider identityProvider = identityProviderManager.getIdPByResourceId(identityProviderId, - ContextLoader.getTenantDomainFromContext(), true); + ContextLoader.getTenantDomainFromContext(), true, SharedIdPResolveType.RAW); if (identityProvider == null) { throw handleException(Response.Status.NOT_FOUND, Constants.ErrorMessage.ERROR_CODE_IDP_NOT_FOUND, identityProviderId); @@ -374,7 +376,8 @@ public FileContent exportIDP(String idpId, boolean excludeSecrets, String fileTy String tenantDomain = ContextLoader.getTenantDomainFromContext(); identityProvider = RESIDENT_IDP_RESERVED_NAME.equals(idpId) ? dao.getIdPByName(null, RESIDENT_IDP_RESERVED_NAME, IdentityTenantUtil.getTenantId(tenantDomain), tenantDomain) : - identityProviderManager.getIdPByResourceId(idpId, tenantDomain, true); + identityProviderManager.getIdPByResourceId(idpId, tenantDomain, true, + SharedIdPResolveType.RAW); idpToExport = createIdPClone(identityProvider); if (idpToExport == null) { throw handleException(Response.Status.NOT_FOUND, @@ -719,7 +722,7 @@ public FederatedAuthenticatorListResponse getFederatedAuthenticators(String idpI try { IdentityProvider idP = identityProviderManager.getIdPByResourceId(idpId, ContextLoader - .getTenantDomainFromContext(), true); + .getTenantDomainFromContext(), true, SharedIdPResolveType.BASE_PARENT); if (idP == null) { throw handleException(Response.Status.NOT_FOUND, Constants.ErrorMessage.ERROR_CODE_IDP_NOT_FOUND, @@ -752,7 +755,7 @@ public FederatedAuthenticator getFederatedAuthenticator(String idpId, String aut try { IdentityProvider idp = identityProviderManager.getIdPByResourceId(idpId, ContextLoader - .getTenantDomainFromContext(), true); + .getTenantDomainFromContext(), true, SharedIdPResolveType.BASE_PARENT); if (idp == null) { throw handleException(Response.Status.NOT_FOUND, Constants.ErrorMessage.ERROR_CODE_IDP_NOT_FOUND, idpId); @@ -785,7 +788,7 @@ public FederatedAuthenticatorListResponse updateFederatedAuthenticators(String i try { IdentityProvider idp = identityProviderManager.getIdPByResourceId(idpId, ContextLoader - .getTenantDomainFromContext(), true); + .getTenantDomainFromContext(), true, SharedIdPResolveType.RAW); if (idp == null) { throw handleException(Response.Status.NOT_FOUND, Constants.ErrorMessage.ERROR_CODE_IDP_NOT_FOUND, idpId); @@ -814,7 +817,8 @@ public FederatedAuthenticator updateFederatedAuthenticator(String idpId, String try { String tenantDomain = ContextLoader.getTenantDomainFromContext(); - IdentityProvider idp = identityProviderManager.getIdPByResourceId(idpId, tenantDomain, true); + IdentityProvider idp = identityProviderManager.getIdPByResourceId(idpId, tenantDomain, true, + SharedIdPResolveType.RAW); if (idp == null) { throw handleException(Response.Status.NOT_FOUND, Constants.ErrorMessage.ERROR_CODE_IDP_NOT_FOUND, idpId); @@ -871,7 +875,7 @@ public OutboundConnectorListResponse getOutboundConnectors(String idpId) { try { IdentityProvider idp = identityProviderManager.getIdPByResourceId(idpId, ContextLoader - .getTenantDomainFromContext(), true); + .getTenantDomainFromContext(), true, SharedIdPResolveType.BASE_PARENT); if (idp == null) { throw handleException(Response.Status.NOT_FOUND, Constants.ErrorMessage.ERROR_CODE_IDP_NOT_FOUND, idpId); @@ -911,7 +915,7 @@ public OutboundConnector getOutboundConnector(String idpId, String connectorId) try { IdentityProvider idp = identityProviderManager.getIdPByResourceId(idpId, ContextLoader - .getTenantDomainFromContext(), true); + .getTenantDomainFromContext(), true, SharedIdPResolveType.BASE_PARENT); if (idp == null) { throw handleException(Response.Status.NOT_FOUND, Constants.ErrorMessage.ERROR_CODE_IDP_NOT_FOUND, idpId); @@ -943,7 +947,7 @@ public OutboundConnectorListResponse updateOutboundConnectors(String idpId, Outb try { IdentityProvider idp = identityProviderManager.getIdPByResourceId(idpId, ContextLoader - .getTenantDomainFromContext(), true); + .getTenantDomainFromContext(), true, SharedIdPResolveType.RAW); if (idp == null) { throw handleException(Response.Status.NOT_FOUND, Constants.ErrorMessage.ERROR_CODE_IDP_NOT_FOUND, idpId); @@ -975,7 +979,7 @@ public OutboundConnector updateOutboundConnector(String idpId, String connectorI try { IdentityProvider idp = identityProviderManager.getIdPByResourceId(idpId, ContextLoader - .getTenantDomainFromContext(), true); + .getTenantDomainFromContext(), true, SharedIdPResolveType.RAW); if (idp == null) { throw handleException(Response.Status.NOT_FOUND, Constants.ErrorMessage.ERROR_CODE_IDP_NOT_FOUND, idpId); @@ -1033,7 +1037,7 @@ public Claims getClaimConfig(String idpId) { try { IdentityProvider identityProvider = identityProviderManager.getIdPByResourceId(idpId, ContextLoader - .getTenantDomainFromContext(), true); + .getTenantDomainFromContext(), true, SharedIdPResolveType.BASE_PARENT); if (identityProvider == null) { throw handleException(Response.Status.NOT_FOUND, Constants.ErrorMessage.ERROR_CODE_IDP_NOT_FOUND, idpId); @@ -1057,8 +1061,8 @@ public Claims updateClaimConfig(String idpId, Claims claims) { try { String tenantDomain = ContextLoader.getTenantDomainFromContext(); validateClaims(tenantDomain, claims); - IdentityProvider idP = - createIdPClone(identityProviderManager.getIdPByResourceId(idpId, tenantDomain, true)); + IdentityProvider idP = createIdPClone(identityProviderManager.getIdPByResourceId(idpId, tenantDomain, + true, SharedIdPResolveType.RAW)); if (idP == null) { throw handleException(Response.Status.NOT_FOUND, Constants.ErrorMessage.ERROR_CODE_IDP_NOT_FOUND, idpId); @@ -1081,7 +1085,7 @@ public Roles getRoleConfig(String idpId) { try { IdentityProvider identityProvider = identityProviderManager.getIdPByResourceId(idpId, ContextLoader - .getTenantDomainFromContext(), true); + .getTenantDomainFromContext(), true, SharedIdPResolveType.BASE_PARENT); if (identityProvider == null) { throw handleException(Response.Status.NOT_FOUND, Constants.ErrorMessage.ERROR_CODE_IDP_NOT_FOUND, idpId); @@ -1103,7 +1107,7 @@ public Roles updateRoleConfig(String idpId, Roles roles) { try { IdentityProvider idP = identityProviderManager.getIdPByResourceId(idpId, ContextLoader - .getTenantDomainFromContext(), true); + .getTenantDomainFromContext(), true, SharedIdPResolveType.RAW); if (idP == null) { throw handleException(Response.Status.NOT_FOUND, Constants.ErrorMessage.ERROR_CODE_IDP_NOT_FOUND, idpId); @@ -1128,7 +1132,7 @@ public List getGroupConfig(String idpId) { try { IdentityProvider identityProvider = identityProviderManager.getIdPByResourceId(idpId, - ContextLoader.getTenantDomainFromContext(), true); + ContextLoader.getTenantDomainFromContext(), true, SharedIdPResolveType.BASE_PARENT); if (identityProvider == null) { throw handleException(Response.Status.NOT_FOUND, Constants.ErrorMessage.ERROR_CODE_IDP_NOT_FOUND, idpId); @@ -1150,14 +1154,15 @@ public List updateGroupConfig(String idpId, List groups) { try { IdentityProvider idP = identityProviderManager.getIdPByResourceId(idpId, ContextLoader - .getTenantDomainFromContext(), true); + .getTenantDomainFromContext(), true, SharedIdPResolveType.RAW); if (idP == null) { throw handleException(Response.Status.NOT_FOUND, Constants.ErrorMessage.ERROR_CODE_IDP_NOT_FOUND, idpId); } - updateGroups(idP, groups); + IdentityProvider idpToUpdate = createIdPClone(idP); + updateGroups(idpToUpdate, groups); - IdentityProvider updatedIdP = identityProviderManager.updateIdPByResourceId(idpId, idP, + IdentityProvider updatedIdP = identityProviderManager.updateIdPByResourceId(idpId, idpToUpdate, ContextLoader.getTenantDomainFromContext()); return createGroupResponse(updatedIdP); } catch (IdentityProviderManagementException e) { @@ -1175,7 +1180,7 @@ public ProvisioningResponse getProvisioningConfig(String idpId) { try { IdentityProvider identityProvider = identityProviderManager.getIdPByResourceId(idpId, ContextLoader - .getTenantDomainFromContext(), true); + .getTenantDomainFromContext(), true, SharedIdPResolveType.BASE_PARENT); if (identityProvider == null) { throw handleException(Response.Status.NOT_FOUND, Constants.ErrorMessage.ERROR_CODE_IDP_NOT_FOUND, idpId); @@ -1190,7 +1195,7 @@ public AssociationResponse getFederatedAssociationConfig(String idpId) { try { IdentityProvider identityProvider = identityProviderManager.getIdPByResourceId(idpId, ContextLoader - .getTenantDomainFromContext(), true); + .getTenantDomainFromContext(), true, SharedIdPResolveType.BASE_PARENT); if (identityProvider == null) { throw handleException(Response.Status.NOT_FOUND, Constants.ErrorMessage.ERROR_CODE_IDP_NOT_FOUND, idpId); @@ -1206,7 +1211,7 @@ public AssociationResponse updateFederatedAssociationConfig(String idpId, Associ try { IdentityProvider idP = identityProviderManager.getIdPByResourceId(idpId, ContextLoader - .getTenantDomainFromContext(), true); + .getTenantDomainFromContext(), true, SharedIdPResolveType.RAW); if (idP == null) { throw handleException(Response.Status.NOT_FOUND, Constants.ErrorMessage.ERROR_CODE_IDP_NOT_FOUND, @@ -1234,7 +1239,7 @@ public JustInTimeProvisioning getJITConfig(String idpId) { try { IdentityProvider identityProvider = identityProviderManager.getIdPByResourceId(idpId, ContextLoader - .getTenantDomainFromContext(), true); + .getTenantDomainFromContext(), true, SharedIdPResolveType.BASE_PARENT); if (identityProvider == null) { throw handleException(Response.Status.NOT_FOUND, Constants.ErrorMessage.ERROR_CODE_IDP_NOT_FOUND, idpId); @@ -1256,7 +1261,7 @@ public JustInTimeProvisioning updateJITConfig(String idpId, JustInTimeProvisioni try { IdentityProvider idP = identityProviderManager.getIdPByResourceId(idpId, ContextLoader - .getTenantDomainFromContext(), true); + .getTenantDomainFromContext(), true, SharedIdPResolveType.RAW); if (idP == null) { throw handleException(Response.Status.NOT_FOUND, Constants.ErrorMessage.ERROR_CODE_IDP_NOT_FOUND, idpId); @@ -2410,6 +2415,7 @@ private void populateIDPBasicInfo(IdentityProviderResponse idpResponse, Identity idpResponse.setId(identityProvider.getResourceId()); idpResponse.setIsEnabled(identityProvider.isEnable()); idpResponse.setIsPrimary(identityProvider.isPrimary()); + idpResponse.setIsShared(resolveIsSharedConnection(identityProvider)); idpResponse.setName(identityProvider.getIdentityProviderName()); idpResponse.setDescription(identityProvider.getIdentityProviderDescription()); idpResponse.setAlias(identityProvider.getAlias()); @@ -2430,6 +2436,28 @@ private String getIDPIssuerName(IdentityProvider identityProvider) { return null; } + /** + * Resolves the value of the {@code isShared} IdP property when it is present on the identity provider (i.e. it + * is a shared/shadow connection resolved from a parent organization), or {@code null} when the property is + * absent — so the {@code isShared} attribute is emitted only for shared connections and omitted otherwise. + * + * @param identityProvider The identity provider. + * @return The {@code isShared} property value if present; {@code null} otherwise. + */ + private Boolean resolveIsSharedConnection(IdentityProvider identityProvider) { + + IdentityProviderProperty[] idpProperties = identityProvider.getIdpProperties(); + if (idpProperties == null) { + return null; + } + for (IdentityProviderProperty property : idpProperties) { + if (Constants.IS_SHARED_PROPERTY.equals(property.getName())) { + return Boolean.parseBoolean(property.getValue()); + } + } + return null; + } + private Certificate createIDPCertificate(IdentityProvider identityProvider) { Certificate certificate = null; 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/ServerIdpSharingService.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/ServerIdpSharingService.java new file mode 100644 index 0000000000..b8d89d2c0f --- /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/core/ServerIdpSharingService.java @@ -0,0 +1,474 @@ +/* + * Copyright (c) 2026, 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.core; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.identity.api.server.common.error.APIError; +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.v1.model.BasicOrganizationResponse; +import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderShareAllRequestBody; +import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderShareSelectedRequestBody; +import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderUnshareAllRequestBody; +import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderUnshareSelectedRequestBody; +import org.wso2.carbon.identity.api.server.idp.v1.model.Link; +import org.wso2.carbon.identity.api.server.idp.v1.model.OrgShareConfig; +import org.wso2.carbon.identity.api.server.idp.v1.model.ProcessSuccessResponse; +import org.wso2.carbon.identity.api.server.idp.v1.model.SharedOrganizationsResponse; +import org.wso2.carbon.identity.api.server.idp.v1.model.SharingMode; +import org.wso2.carbon.identity.organization.management.organization.connection.sharing.ConnectionSharingPolicyHandlerService; +import org.wso2.carbon.identity.organization.management.organization.connection.sharing.constant.ConnectionType; +import org.wso2.carbon.identity.organization.management.organization.connection.sharing.dto.ConnectionSharingModeDTO; +import org.wso2.carbon.identity.organization.management.organization.connection.sharing.dto.GeneralConnectionShareDTO; +import org.wso2.carbon.identity.organization.management.organization.connection.sharing.dto.GeneralConnectionUnshareDTO; +import org.wso2.carbon.identity.organization.management.organization.connection.sharing.dto.GetConnectionSharedOrgsDTO; +import org.wso2.carbon.identity.organization.management.organization.connection.sharing.dto.ResponseConnectionOrgDetailsDTO; +import org.wso2.carbon.identity.organization.management.organization.connection.sharing.dto.ResponseSharedConnectionOrgsDTO; +import org.wso2.carbon.identity.organization.management.organization.connection.sharing.dto.SelectiveConnectionShareDTO; +import org.wso2.carbon.identity.organization.management.organization.connection.sharing.dto.SelectiveConnectionShareOrgConfigDTO; +import org.wso2.carbon.identity.organization.management.organization.connection.sharing.dto.SelectiveConnectionUnshareDTO; +import org.wso2.carbon.identity.organization.management.organization.connection.sharing.exception.ConnectionSharingMgtClientException; +import org.wso2.carbon.identity.organization.management.organization.connection.sharing.exception.ConnectionSharingMgtException; +import org.wso2.carbon.identity.organization.management.service.constant.OrganizationManagementConstants; +import org.wso2.carbon.identity.organization.resource.sharing.policy.management.constant.PolicyEnum; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.Collections; +import java.util.List; + +import javax.ws.rs.core.Response; + +import static org.wso2.carbon.identity.api.server.common.Constants.ERROR_CODE_DELIMITER; +import static org.wso2.carbon.identity.api.server.common.Constants.V1_API_PATH_COMPONENT; +import static org.wso2.carbon.identity.api.server.common.ContextLoader.buildURIForBody; +import static org.wso2.carbon.identity.api.server.idp.common.Constants.ASYNC_OPERATION_RESPONSE_STATUS; +import static org.wso2.carbon.identity.api.server.idp.common.Constants.EXCLUDED_ATTRIBUTES_PARAM; +import static org.wso2.carbon.identity.api.server.idp.common.Constants.FILTER_PARAM; +import static org.wso2.carbon.identity.api.server.idp.common.Constants.IDP_PATH_COMPONENT; +import static org.wso2.carbon.identity.api.server.idp.common.Constants.IDP_SHARE_PATH_COMPONENT; +import static org.wso2.carbon.identity.api.server.idp.common.Constants.IDP_SHARE_TRIGGER_SUCCESS; +import static org.wso2.carbon.identity.api.server.idp.common.Constants.IDP_UNSHARE_TRIGGER_SUCCESS; +import static org.wso2.carbon.identity.api.server.idp.common.Constants.INCLUDED_ATTRIBUTES_PARAM; +import static org.wso2.carbon.identity.api.server.idp.common.Constants.LIMIT_PARAM; +import static org.wso2.carbon.identity.api.server.idp.common.Constants.PAGE_LINK_REL_NEXT; +import static org.wso2.carbon.identity.api.server.idp.common.Constants.PAGE_LINK_REL_PREVIOUS; +import static org.wso2.carbon.identity.api.server.idp.common.Constants.PATH_SEPERATOR; +import static org.wso2.carbon.identity.api.server.idp.common.Constants.RECURSIVE_PARAM; +import static org.wso2.carbon.identity.organization.management.service.constant.OrganizationManagementConstants.ORGANIZATION_PATH; +import static org.wso2.carbon.identity.organization.management.service.constant.OrganizationManagementConstants.PAGINATION_AFTER; +import static org.wso2.carbon.identity.organization.management.service.constant.OrganizationManagementConstants.PAGINATION_BEFORE; +import static org.wso2.carbon.identity.organization.management.service.util.Utils.getOrganizationId; + +/** + * Calls internal osgi services to perform identity provider sharing related operations. + */ +public class ServerIdpSharingService { + + private static final Log log = LogFactory.getLog(ServerIdpSharingService.class); + + private final ConnectionSharingPolicyHandlerService connectionSharingPolicyHandlerService; + + public ServerIdpSharingService(ConnectionSharingPolicyHandlerService connectionSharingPolicyHandlerService) { + + this.connectionSharingPolicyHandlerService = connectionSharingPolicyHandlerService; + } + + /** + * Share an identity provider with selected organizations. + * + * @param requestBody Request body of the share request. + * @return The status of the operation. + */ + public Response shareIdentityProviderWithSelected(IdentityProviderShareSelectedRequestBody requestBody) { + + if (requestBody == null || StringUtils.isBlank(requestBody.getIdentityProviderId())) { + throw handleException(Response.Status.BAD_REQUEST, + Constants.ErrorMessage.ERROR_CODE_INVALID_SHARE_REQUEST_BODY, null); + } + if (CollectionUtils.isEmpty(requestBody.getOrganizations())) { + throw handleException(Response.Status.BAD_REQUEST, + Constants.ErrorMessage.ERROR_CODE_SHARE_REQUEST_NO_ORGANIZATIONS, null); + } + List organizations = new ArrayList<>(); + for (OrgShareConfig orgShareConfig : requestBody.getOrganizations()) { + SelectiveConnectionShareOrgConfigDTO orgConfig = new SelectiveConnectionShareOrgConfigDTO(); + orgConfig.setOrgId(orgShareConfig.getOrgId()); + if (orgShareConfig.getPolicy() != null) { + orgConfig.setPolicy(getPolicyEnum(orgShareConfig.getPolicy())); + } + organizations.add(orgConfig); + } + SelectiveConnectionShareDTO selectiveConnectionShareDTO = new SelectiveConnectionShareDTO(); + selectiveConnectionShareDTO.setConnectionId(requestBody.getIdentityProviderId()); + selectiveConnectionShareDTO.setConnectionType(ConnectionType.IDP); + selectiveConnectionShareDTO.setOrganizations(organizations); + try { + connectionSharingPolicyHandlerService.populateSelectiveConnectionShare(selectiveConnectionShareDTO); + return Response.accepted() + .entity(createProcessSuccessResponse(IDP_SHARE_TRIGGER_SUCCESS)).build(); + } catch (ConnectionSharingMgtException e) { + throw handleConnectionSharingException(e, Constants.ErrorMessage.ERROR_CODE_ERROR_SHARING_IDP); + } + } + + /** + * Share an identity provider with all organizations. + * + * @param requestBody Request body of the share request. + * @return The status of the operation. + */ + public Response shareIdentityProviderWithAll(IdentityProviderShareAllRequestBody requestBody) { + + if (requestBody == null || StringUtils.isBlank(requestBody.getIdentityProviderId()) + || requestBody.getPolicy() == null) { + throw handleException(Response.Status.BAD_REQUEST, + Constants.ErrorMessage.ERROR_CODE_INVALID_SHARE_REQUEST_BODY, null); + } + PolicyEnum policy = getPolicyEnum(requestBody.getPolicy()); + if (PolicyEnum.ALL_EXISTING_ORGS_ONLY != policy && PolicyEnum.ALL_EXISTING_AND_FUTURE_ORGS != policy) { + throw handleException(Response.Status.BAD_REQUEST, + Constants.ErrorMessage.ERROR_CODE_UNSUPPORTED_SHARE_POLICY, String.valueOf(policy)); + } + GeneralConnectionShareDTO generalConnectionShareDTO = new GeneralConnectionShareDTO(); + generalConnectionShareDTO.setConnectionId(requestBody.getIdentityProviderId()); + generalConnectionShareDTO.setConnectionType(ConnectionType.IDP); + generalConnectionShareDTO.setPolicy(policy); + try { + connectionSharingPolicyHandlerService.populateGeneralConnectionShare(generalConnectionShareDTO); + return Response.accepted() + .entity(createProcessSuccessResponse(IDP_SHARE_TRIGGER_SUCCESS)).build(); + } catch (ConnectionSharingMgtException e) { + throw handleConnectionSharingException(e, Constants.ErrorMessage.ERROR_CODE_ERROR_SHARING_IDP); + } + } + + /** + * Unshare an identity provider from selected organizations. + * + * @param requestBody Request body of the unshare request. + * @return The status of the operation. + */ + public Response unshareIdentityProviderFromSelected(IdentityProviderUnshareSelectedRequestBody requestBody) { + + if (requestBody == null || StringUtils.isBlank(requestBody.getIdentityProviderId())) { + throw handleException(Response.Status.BAD_REQUEST, + Constants.ErrorMessage.ERROR_CODE_INVALID_SHARE_REQUEST_BODY, null); + } + if (CollectionUtils.isEmpty(requestBody.getOrgIds())) { + throw handleException(Response.Status.BAD_REQUEST, + Constants.ErrorMessage.ERROR_CODE_SHARE_REQUEST_NO_ORGANIZATIONS, null); + } + SelectiveConnectionUnshareDTO selectiveConnectionUnshareDTO = new SelectiveConnectionUnshareDTO(); + selectiveConnectionUnshareDTO.setConnectionId(requestBody.getIdentityProviderId()); + selectiveConnectionUnshareDTO.setConnectionType(ConnectionType.IDP); + selectiveConnectionUnshareDTO.setOrgIds(requestBody.getOrgIds()); + try { + connectionSharingPolicyHandlerService.populateSelectiveConnectionUnshare(selectiveConnectionUnshareDTO); + return Response.accepted() + .entity(createProcessSuccessResponse(IDP_UNSHARE_TRIGGER_SUCCESS)).build(); + } catch (ConnectionSharingMgtException e) { + throw handleConnectionSharingException(e, Constants.ErrorMessage.ERROR_CODE_ERROR_UNSHARING_IDP); + } + } + + /** + * Unshare an identity provider from all organizations. + * + * @param requestBody Request body of the unshare request. + * @return The status of the operation. + */ + public Response unshareIdentityProviderFromAll(IdentityProviderUnshareAllRequestBody requestBody) { + + if (requestBody == null || StringUtils.isBlank(requestBody.getIdentityProviderId())) { + throw handleException(Response.Status.BAD_REQUEST, + Constants.ErrorMessage.ERROR_CODE_INVALID_SHARE_REQUEST_BODY, null); + } + GeneralConnectionUnshareDTO generalConnectionUnshareDTO = new GeneralConnectionUnshareDTO(); + generalConnectionUnshareDTO.setConnectionId(requestBody.getIdentityProviderId()); + generalConnectionUnshareDTO.setConnectionType(ConnectionType.IDP); + try { + connectionSharingPolicyHandlerService.populateGeneralConnectionUnshare(generalConnectionUnshareDTO); + return Response.accepted() + .entity(createProcessSuccessResponse(IDP_UNSHARE_TRIGGER_SUCCESS)).build(); + } catch (ConnectionSharingMgtException e) { + throw handleConnectionSharingException(e, Constants.ErrorMessage.ERROR_CODE_ERROR_UNSHARING_IDP); + } + } + + /** + * Returns the organizations that the given identity provider is shared with. + * + * @param identityProviderId ID of the identity provider. + * @param before Base64 encoded cursor for backward pagination. + * @param after Base64 encoded cursor for forward pagination. + * @param filter Filter to apply on the organizations. + * @param limit Maximum number of organizations to return. + * @param recursive Whether to return shared organizations in all levels of the hierarchy. + * @param excludedAttributes Attributes to exclude from the response. + * @param attributes Attributes to include in the response. + * @return The organizations that the identity provider is shared with. + */ + public Response getSharedOrganizations(String identityProviderId, String before, String after, String filter, + Integer limit, Boolean recursive, String excludedAttributes, + String attributes) { + + int limitValue = (limit == null) ? 0 : limit; + // To return all shared organizations if the recursive flag was not provided. + boolean recursiveFlag = (recursive == null) || recursive; + GetConnectionSharedOrgsDTO getConnectionSharedOrgsDTO = new GetConnectionSharedOrgsDTO(); + getConnectionSharedOrgsDTO.setConnectionId(identityProviderId); + getConnectionSharedOrgsDTO.setConnectionType(ConnectionType.IDP); + getConnectionSharedOrgsDTO.setInitiatingOrgId(getOrganizationId()); + getConnectionSharedOrgsDTO.setBefore(decodeCursor(before)); + getConnectionSharedOrgsDTO.setAfter(decodeCursor(after)); + getConnectionSharedOrgsDTO.setFilter(filter); + getConnectionSharedOrgsDTO.setLimit(limitValue); + getConnectionSharedOrgsDTO.setRecursive(recursiveFlag); + getConnectionSharedOrgsDTO.setAttributes(resolveAttributeList(attributes)); + try { + ResponseSharedConnectionOrgsDTO sharedOrgsResponse = + connectionSharingPolicyHandlerService.getConnectionSharedOrganizations( + getConnectionSharedOrgsDTO); + String paginationQueryParams = buildPaginationQueryParams(filter, limitValue, recursive, + excludedAttributes, attributes); + return Response.ok() + .entity(createSharedOrgsResponse(sharedOrgsResponse, paginationQueryParams, identityProviderId)) + .build(); + } catch (ConnectionSharingMgtException e) { + throw handleConnectionSharingException(e, Constants.ErrorMessage.ERROR_CODE_ERROR_RETRIEVING_SHARED_ORGS); + } + } + + private SharedOrganizationsResponse createSharedOrgsResponse(ResponseSharedConnectionOrgsDTO sharedOrgsResponse, + String paginationQueryParams, + String identityProviderId) { + + SharedOrganizationsResponse response = new SharedOrganizationsResponse(); + if (sharedOrgsResponse == null) { + return response; + } + if (sharedOrgsResponse.getSharedOrgs() != null) { + for (ResponseConnectionOrgDetailsDTO orgDetails : sharedOrgsResponse.getSharedOrgs()) { + BasicOrganizationResponse basicOrganizationResponse = new BasicOrganizationResponse() + .id(orgDetails.getOrgId()) + .name(orgDetails.getOrgName()) + .orgHandle(orgDetails.getOrgHandle()) + .parentId(orgDetails.getParentOrgId()) + .depthFromRoot(orgDetails.getDepthFromRoot()) + .ref(buildOrganizationURL(orgDetails.getOrgId()).toString()) + .hasChildren(orgDetails.getHasChildren()) + .sharingMode(convertSharingModeToResponseType(orgDetails.getSharingMode())); + if (StringUtils.isNotBlank(orgDetails.getStatus())) { + basicOrganizationResponse.setStatus( + BasicOrganizationResponse.StatusEnum.valueOf(orgDetails.getStatus())); + } + response.addOrganizationsItem(basicOrganizationResponse); + } + } + response.setSharingMode(convertSharingModeToResponseType(sharedOrgsResponse.getSharingMode())); + if (sharedOrgsResponse.getNextPageCursor() != 0) { + Link nextLink = new Link(); + String base64EncodedCursor = Base64.getEncoder().encodeToString( + String.valueOf(sharedOrgsResponse.getNextPageCursor()).getBytes(StandardCharsets.UTF_8)); + nextLink.setHref(URI.create(buildURIForPagination(paginationQueryParams, identityProviderId) + "&" + + PAGINATION_AFTER + "=" + base64EncodedCursor).toString()); + nextLink.setRel(PAGE_LINK_REL_NEXT); + response.addLinksItem(nextLink); + } + if (sharedOrgsResponse.getPreviousPageCursor() != 0) { + Link previousLink = new Link(); + String base64EncodedCursor = Base64.getEncoder().encodeToString( + String.valueOf(sharedOrgsResponse.getPreviousPageCursor()).getBytes(StandardCharsets.UTF_8)); + previousLink.setHref(URI.create(buildURIForPagination(paginationQueryParams, identityProviderId) + "&" + + PAGINATION_BEFORE + "=" + base64EncodedCursor).toString()); + previousLink.setRel(PAGE_LINK_REL_PREVIOUS); + response.addLinksItem(previousLink); + } + return response; + } + + private SharingMode convertSharingModeToResponseType(ConnectionSharingModeDTO sharingModeDTO) { + + if (sharingModeDTO == null || sharingModeDTO.getPolicy() == null) { + return null; + } + SharingMode sharingMode = new SharingMode(); + sharingMode.setPolicy(sharingModeDTO.getPolicy().name()); + return sharingMode; + } + + private List resolveAttributeList(String attributes) { + + if (StringUtils.isBlank(attributes)) { + return Collections.emptyList(); + } + List attributeList = new ArrayList<>(); + for (String attribute : Arrays.asList(attributes.split(","))) { + if (StringUtils.isNotBlank(attribute)) { + attributeList.add(attribute.trim()); + } + } + return attributeList; + } + + private int decodeCursor(String encodedCursor) { + + if (StringUtils.isBlank(encodedCursor)) { + return 0; + } + try { + return Integer.parseInt( + new String(Base64.getDecoder().decode(encodedCursor), StandardCharsets.UTF_8)); + } catch (IllegalArgumentException e) { + throw handleException(Response.Status.BAD_REQUEST, + Constants.ErrorMessage.ERROR_CODE_INVALID_PAGINATION_CURSOR, null); + } + } + + private String buildPaginationQueryParams(String filter, int limit, Boolean recursive, String excludedAttributes, + String attributes) { + + StringBuilder urlStringBuilder = new StringBuilder("?"); + if (limit != 0) { + urlStringBuilder.append(LIMIT_PARAM).append("=").append(limit); + } + if (recursive != null) { + urlStringBuilder.append("&").append(RECURSIVE_PARAM).append("=").append(recursive); + } + if (StringUtils.isNotBlank(filter)) { + try { + urlStringBuilder.append("&").append(FILTER_PARAM).append("=") + .append(URLEncoder.encode(filter, StandardCharsets.UTF_8.name())); + } catch (UnsupportedEncodingException e) { + throw handleException(Response.Status.INTERNAL_SERVER_ERROR, + Constants.ErrorMessage.ERROR_CODE_BUILDING_LINKS, e.getMessage()); + } + } + if (StringUtils.isNotBlank(excludedAttributes)) { + urlStringBuilder.append("&").append(EXCLUDED_ATTRIBUTES_PARAM).append("=").append(excludedAttributes); + } + if (StringUtils.isNotBlank(attributes)) { + urlStringBuilder.append("&").append(INCLUDED_ATTRIBUTES_PARAM).append("=").append(attributes); + } + return urlStringBuilder.toString(); + } + + private static String buildURIForPagination(String paginationQueryParams, String identityProviderId) { + + return buildURIForBody(V1_API_PATH_COMPONENT + IDP_PATH_COMPONENT + PATH_SEPERATOR + identityProviderId + + IDP_SHARE_PATH_COMPONENT + paginationQueryParams).toString(); + } + + private static URI buildOrganizationURL(String organizationId) { + + return buildURIForBody(PATH_SEPERATOR + OrganizationManagementConstants.V1_API_PATH_COMPONENT + + PATH_SEPERATOR + ORGANIZATION_PATH + PATH_SEPERATOR + organizationId); + } + + private ProcessSuccessResponse createProcessSuccessResponse(String details) { + + return new ProcessSuccessResponse() + .status(ASYNC_OPERATION_RESPONSE_STATUS) + .details(details); + } + + private PolicyEnum getPolicyEnum(String policy) { + + if (policy == null) { + return null; + } + if (PolicyEnum.ALL_EXISTING_ORGS_ONLY.getValue().equals(policy)) { + return PolicyEnum.ALL_EXISTING_ORGS_ONLY; + } else if (PolicyEnum.ALL_EXISTING_AND_FUTURE_ORGS.getValue().equals(policy)) { + return PolicyEnum.ALL_EXISTING_AND_FUTURE_ORGS; + } else if (PolicyEnum.SELECTED_ORG_ONLY.getValue().equals(policy)) { + return PolicyEnum.SELECTED_ORG_ONLY; + } else if (PolicyEnum.SELECTED_ORG_WITH_ALL_EXISTING_AND_FUTURE_CHILDREN.getValue().equals(policy)) { + return PolicyEnum.SELECTED_ORG_WITH_ALL_EXISTING_AND_FUTURE_CHILDREN; + } + throw handleException(Response.Status.BAD_REQUEST, + Constants.ErrorMessage.ERROR_CODE_UNSUPPORTED_SHARE_POLICY, policy); + } + + private APIError handleConnectionSharingException(ConnectionSharingMgtException e, + Constants.ErrorMessage errorEnum) { + + ErrorResponse errorResponse; + Response.Status status; + if (e instanceof ConnectionSharingMgtClientException) { + if (e.getErrorCode() != null) { + String errorCode = e.getErrorCode(); + errorCode = errorCode.contains(ERROR_CODE_DELIMITER) ? errorCode : + Constants.IDP_MANAGEMENT_PREFIX + errorCode; + errorResponse = getErrorBuilder(errorCode, e.getMessage(), e.getDescription()) + .build(log, e.getMessage()); + } else { + errorResponse = getErrorBuilder(errorEnum, null).build(log, e.getMessage()); + errorResponse.setDescription(e.getMessage()); + } + status = Response.Status.BAD_REQUEST; + } else { + if (e.getErrorCode() != null) { + String errorCode = e.getErrorCode(); + errorCode = errorCode.contains(ERROR_CODE_DELIMITER) ? errorCode : + Constants.IDP_MANAGEMENT_PREFIX + errorCode; + errorResponse = getErrorBuilder(errorCode, e.getMessage(), e.getDescription()) + .build(log, e, e.getMessage()); + } else { + errorResponse = getErrorBuilder(errorEnum, null).build(log, e, e.getMessage()); + } + status = Response.Status.INTERNAL_SERVER_ERROR; + } + return new APIError(status, errorResponse); + } + + private APIError handleException(Response.Status status, Constants.ErrorMessage error, String data) { + + return new APIError(status, getErrorBuilder(error, data).build()); + } + + private ErrorResponse.Builder getErrorBuilder(Constants.ErrorMessage errorMsg, String data) { + + return new ErrorResponse.Builder().withCode(errorMsg.getCode()).withMessage(errorMsg.getMessage()) + .withDescription(includeData(errorMsg, data)); + } + + private ErrorResponse.Builder getErrorBuilder(String errorCode, String errorMsg, String description) { + + return new ErrorResponse.Builder().withCode(errorCode).withMessage(errorMsg) + .withDescription(description); + } + + private static String includeData(Constants.ErrorMessage error, String data) { + + if (StringUtils.isNotBlank(data)) { + return String.format(error.getDescription(), data); + } + return String.format(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/factories/ServerIdpSharingServiceFactory.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/factories/ServerIdpSharingServiceFactory.java new file mode 100644 index 0000000000..f69c0a5551 --- /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/factories/ServerIdpSharingServiceFactory.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2026, 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.factories; + +import org.wso2.carbon.identity.api.server.idp.common.IdentityProviderServiceHolder; +import org.wso2.carbon.identity.api.server.idp.v1.core.ServerIdpSharingService; +import org.wso2.carbon.identity.organization.management.organization.connection.sharing.ConnectionSharingPolicyHandlerService; + +/** + * Factory class for Server Idp Sharing Service. + */ +public class ServerIdpSharingServiceFactory { + + private static final ServerIdpSharingService SERVICE; + + static { + ConnectionSharingPolicyHandlerService connectionSharingPolicyHandlerService = + IdentityProviderServiceHolder.getConnectionSharingPolicyHandlerService(); + + if (connectionSharingPolicyHandlerService == null) { + throw new IllegalStateException( + "ConnectionSharingPolicyHandlerService is not available from OSGi context."); + } + + SERVICE = new ServerIdpSharingService(connectionSharingPolicyHandlerService); + } + + /** + * Get ServerIdpSharingService. + * + * @return ServerIdpSharingService. + */ + public static ServerIdpSharingService getServerIdpSharingService() { + + return SERVICE; + } +} 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/IdentityProvidersApiServiceImpl.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/IdentityProvidersApiServiceImpl.java index 73bd3dcaf3..05a6cda904 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/impl/IdentityProvidersApiServiceImpl.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/IdentityProvidersApiServiceImpl.java @@ -25,7 +25,9 @@ import org.wso2.carbon.identity.api.server.common.file.FileContent; import org.wso2.carbon.identity.api.server.idp.v1.IdentityProvidersApiService; import org.wso2.carbon.identity.api.server.idp.v1.core.ServerIdpManagementService; +import org.wso2.carbon.identity.api.server.idp.v1.core.ServerIdpSharingService; import org.wso2.carbon.identity.api.server.idp.v1.factories.ServerIdpManagementServiceFactory; +import org.wso2.carbon.identity.api.server.idp.v1.factories.ServerIdpSharingServiceFactory; import org.wso2.carbon.identity.api.server.idp.v1.model.AssociationRequest; import org.wso2.carbon.identity.api.server.idp.v1.model.Claims; import org.wso2.carbon.identity.api.server.idp.v1.model.FederatedAuthenticatorPUTRequest; @@ -33,7 +35,11 @@ import org.wso2.carbon.identity.api.server.idp.v1.model.IdPGroup; import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderPOSTRequest; import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderResponse; +import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderShareAllRequestBody; +import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderShareSelectedRequestBody; import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderTemplate; +import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderUnshareAllRequestBody; +import org.wso2.carbon.identity.api.server.idp.v1.model.IdentityProviderUnshareSelectedRequestBody; import org.wso2.carbon.identity.api.server.idp.v1.model.JustInTimeProvisioning; import org.wso2.carbon.identity.api.server.idp.v1.model.OutboundConnectorPUTRequest; import org.wso2.carbon.identity.api.server.idp.v1.model.OutboundProvisioningRequest; @@ -56,6 +62,7 @@ public class IdentityProvidersApiServiceImpl implements IdentityProvidersApiService { private final ServerIdpManagementService idpManagementService; + private final ServerIdpSharingService idpSharingService; public IdentityProvidersApiServiceImpl() { @@ -64,6 +71,11 @@ public IdentityProvidersApiServiceImpl() { } catch (IllegalStateException e) { throw new RuntimeException("Error occurred while initiating ServerIdpManagementService.", e); } + try { + this.idpSharingService = ServerIdpSharingServiceFactory.getServerIdpSharingService(); + } catch (IllegalStateException e) { + throw new RuntimeException("Error occurred while initiating ServerIdpSharingService.", e); + } } @Override @@ -185,6 +197,15 @@ public Response getIDPs(Integer limit, Integer offset, String filter, String sor sortOrder)).build(); } + @Override + public Response getIdentityProviderSharedOrganizations(String identityProviderId, String before, String after, + String filter, Integer limit, Boolean recursive, + String excludedAttributes, String attributes) { + + return idpSharingService.getSharedOrganizations(identityProviderId, before, after, filter, limit, recursive, + excludedAttributes, attributes); + } + @Override public Response getJITConfig(String identityProviderId) { @@ -257,6 +278,30 @@ public Response patchIDP(String identityProviderId, List patchRequest) { return Response.ok().entity(idpManagementService.patchIDP(identityProviderId, patchRequest)).build(); } + @Override + public Response shareIdentityProviderWithAll(IdentityProviderShareAllRequestBody requestBody) { + + return idpSharingService.shareIdentityProviderWithAll(requestBody); + } + + @Override + public Response shareIdentityProviderWithSelected(IdentityProviderShareSelectedRequestBody requestBody) { + + return idpSharingService.shareIdentityProviderWithSelected(requestBody); + } + + @Override + public Response unshareIdentityProviderFromAll(IdentityProviderUnshareAllRequestBody requestBody) { + + return idpSharingService.unshareIdentityProviderFromAll(requestBody); + } + + @Override + public Response unshareIdentityProviderFromSelected(IdentityProviderUnshareSelectedRequestBody requestBody) { + + return idpSharingService.unshareIdentityProviderFromSelected(requestBody); + } + @Override public Response updateClaimConfig(String identityProviderId, Claims claims) { diff --git a/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/main/resources/idp.yaml b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/main/resources/idp.yaml index 7bbc996e6d..b166d4396b 100644 --- a/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/main/resources/idp.yaml +++ b/components/org.wso2.carbon.identity.api.server.idp/org.wso2.carbon.identity.api.server.idp.v1/src/main/resources/idp.yaml @@ -1813,6 +1813,231 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + '/identity-providers/{identity-provider-id}/share': + get: + tags: + - Identity Provider Sharing + summary: | + List of organizations that the identity provider is shared with. + description: > + This API returns the list of organizations that the identity provider is shared with.
+ Scope required: `internal_idp_shared_access_view` + operationId: getIdentityProviderSharedOrganizations + parameters: + - name: identity-provider-id + in: path + description: ID of the identity provider which is shared with organizations. + required: true + schema: + type: string + - $ref: '#/components/parameters/beforeQueryParam' + - $ref: '#/components/parameters/afterQueryParam' + - $ref: '#/components/parameters/sharedOrganizationsFilterQueryParam' + - $ref: '#/components/parameters/sharedOrganizationsLimitQueryParam' + - $ref: '#/components/parameters/recursiveQueryParam' + - $ref: '#/components/parameters/idpShareExcludedAttributesQueryParam' + - $ref: '#/components/parameters/idpShareAttributesQueryParam' + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/SharedOrganizationsResponse' + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /identity-providers/share: + post: + tags: [ Identity Provider Sharing ] + summary: Share a single identity provider with specific organizations + description: > + This API provides the capability to share an identity provider with specific organizations.
+ Scope required: `internal_idp_share` + operationId: shareIdentityProviderWithSelected + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/IdentityProviderShareSelectedRequestBody' + responses: + '202': + description: Sharing process triggered successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ProcessSuccessResponse' + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /identity-providers/share-with-all: + post: + tags: [ Identity Provider Sharing ] + summary: Share a single identity provider with all organizations + description: > + This API provides the capability to share an identity provider with all organizations.
+ Scope required: `internal_idp_share` + operationId: shareIdentityProviderWithAll + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/IdentityProviderShareAllRequestBody' + responses: + '202': + description: Sharing process triggered successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ProcessSuccessResponse' + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /identity-providers/unshare: + post: + tags: [ Identity Provider Sharing ] + summary: Unshare an identity provider from specific organizations + description: > + This API provides the capability to unshare an identity provider from specific organizations.
+ Scope required: `internal_idp_unshare` + operationId: unshareIdentityProviderFromSelected + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/IdentityProviderUnshareSelectedRequestBody' + responses: + '202': + description: Unsharing process triggered successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ProcessSuccessResponse' + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /identity-providers/unshare-with-all: + post: + tags: [ Identity Provider Sharing ] + summary: Unshare an identity provider from all organizations + description: > + This API provides the capability to unshare an identity provider from all organizations.
+ Scope required: `internal_idp_unshare` + operationId: unshareIdentityProviderFromAll + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/IdentityProviderUnshareAllRequestBody' + responses: + '202': + description: Unsharing process triggered successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ProcessSuccessResponse' + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' /identity-providers/templates: get: tags: @@ -2469,6 +2694,70 @@ components: supported. schema: type: string + beforeQueryParam: + name: before + in: query + required: false + description: | + Base64 encoded cursor value for backward pagination. + schema: + type: string + example: Ng== + afterQueryParam: + name: after + in: query + required: false + description: | + Base64 encoded cursor value for forward pagination. + schema: + type: string + example: Ng== + sharedOrganizationsFilterQueryParam: + name: filter + in: query + required: false + description: | + Condition to filter the retrieval of records. Supports 'sw', 'co', 'ew' and 'eq' operations. + schema: + type: string + example: id+eq+088fb49c-46fa-48c1-a0a8-5538ee4b7ec5 + sharedOrganizationsLimitQueryParam: + name: limit + in: query + required: false + description: | + Maximum number of records to return. + schema: + type: integer + example: 10 + recursiveQueryParam: + in: query + name: recursive + required: false + description: | + Determines whether a recursive search should happen. + If set to true, will include shared organizations in all levels of the hierarchy; If set to false, includes only shared organizations in the next level of the hierarchy. + schema: + type: boolean + idpShareExcludedAttributesQueryParam: + in: query + name: excludedAttributes + description: | + excludedAttribute parameter. + required: false + schema: + type: string + idpShareAttributesQueryParam: + in: query + name: attributes + required: false + description: | + Specifies the required parameters in the response. + Only 'sharingMode' attribute is currently supported. + + /identity-providers/{identity-provider-id}/share?attributes=sharingMode + schema: + type: string securitySchemes: BasicAuth: type: http @@ -2673,6 +2962,12 @@ components: isPrimary: type: boolean default: false + isShared: + type: boolean + example: true + description: Whether this connection is a shared connection resolved from a parent organization. + Present (and true) only for shared connections; omitted otherwise. + readOnly: true image: type: string example: "google-logo-url" @@ -3277,9 +3572,9 @@ components: default: false example: true skipJITForLookupFailure: - type: boolean - default: false - example: true + type: boolean + default: false + example: true accountLookupAttributeMappings: type: array items: @@ -3524,3 +3819,153 @@ components: type: string format: binary description: file to upload + SharedOrganizationsResponse: + type: object + properties: + links: + type: array + items: + $ref: '#/components/schemas/Link' + example: + [ + { + "href": "/api/server/v1/identity-providers/67f5a202-48c4-4313-9327-016da5f08f17/share?limit=10&recursive=false&next=MTA=", + "rel": "next", + }, + { + "href": "/api/server/v1/identity-providers/67f5a202-48c4-4313-9327-016da5f08f17/share?limit=10&recursive=false&before=MTA=", + "rel": "previous", + } + ] + sharingMode: + $ref: '#/components/schemas/SharingMode' + organizations: + type: array + items: + $ref: '#/components/schemas/BasicOrganizationResponse' + SharingMode: + type: object + properties: + policy: + type: string + description: |- + Sharing scope for this organization. + + Possible values: + - `SELECTED_ORG_ONLY` + - `SELECTED_ORG_WITH_ALL_EXISTING_AND_FUTURE_CHILDREN` + - `ALL_EXISTING_AND_FUTURE_ORGS` + BasicOrganizationResponse: + type: object + required: + - id + - name + - orgHandle + - parentId + - status + - ref + - hasChildren + - depthFromRoot + properties: + id: + type: string + example: 'b4526d91-a8bf-43d2-8b14-c548cf73065b' + name: + type: string + example: 'ABC Builders' + orgHandle: + type: string + example: 'abcbuilders' + parentId: + type: string + example: '08f8c1d2-4b3e-4c5a-9f6b-7d8e9f0a1b2c' + description: | + The parent organization ID. + status: + type: string + enum: [ ACTIVE, DISABLED ] + example: ACTIVE + ref: + type: string + example: '/t/wso2.com/api/server/v1/organizations/b4526d91-a8bf-43d2-8b14-c548cf73065b' + hasChildren: + type: boolean + example: true + depthFromRoot: + type: integer + example: 1 + sharingMode: + $ref: '#/components/schemas/SharingMode' + IdentityProviderShareSelectedRequestBody: + type: object + required: [ identityProviderId, organizations ] + properties: + identityProviderId: + type: string + example: 'c75e27f9-98c7-4518-a968-c6cd59f0ac6b' + organizations: + type: array + items: + $ref: '#/components/schemas/OrgShareConfig' + OrgShareConfig: + type: object + required: [ orgId, policy ] + properties: + orgId: + type: string + example: '682edf68-4835-4bb8-961f-0a16bc6cc866' + policy: + type: string + description: |- + Sharing scope for this organization. + + Possible values: + - `SELECTED_ORG_ONLY` + - `SELECTED_ORG_WITH_ALL_EXISTING_AND_FUTURE_CHILDREN` + - `ALL_EXISTING_AND_FUTURE_ORGS` + IdentityProviderShareAllRequestBody: + type: object + required: [ identityProviderId, policy ] + properties: + identityProviderId: + type: string + example: 'c75e27f9-98c7-4518-a968-c6cd59f0ac6b' + policy: + type: string + description: |- + Sharing scope for this organization. + + Possible values: + - `SELECTED_ORG_ONLY` + - `SELECTED_ORG_WITH_ALL_EXISTING_AND_FUTURE_CHILDREN` + - `ALL_EXISTING_AND_FUTURE_ORGS` + IdentityProviderUnshareSelectedRequestBody: + type: object + required: [ identityProviderId, orgIds ] + properties: + identityProviderId: + type: string + example: 'c75e27f9-98c7-4518-a968-c6cd59f0ac6b' + orgIds: + type: array + items: + type: string + example: + - 682edf68-4835-4bb8-961f-0a16bc6cc866 + - ghfbctgf-4318-46d4-8ee1-7t3s38e23098 + IdentityProviderUnshareAllRequestBody: + type: object + required: [ identityProviderId ] + properties: + identityProviderId: + type: string + example: 'c75e27f9-98c7-4518-a968-c6cd59f0ac6b' + ProcessSuccessResponse: + type: object + properties: + status: + type: string + example: "Processing" + details: + type: string + example: "Identity provider sharing process triggered successfully." diff --git a/pom.xml b/pom.xml index fa4371323f..6becc2e76b 100644 --- a/pom.xml +++ b/pom.xml @@ -788,6 +788,12 @@ ${org.wso2.carbon.identity.organization.management.version} provided
+ + org.wso2.carbon.identity.organization.management + org.wso2.carbon.identity.organization.management.organization.connection.sharing + ${org.wso2.carbon.identity.organization.management.version} + provided + org.wso2.carbon.identity.server.api org.wso2.carbon.identity.api.server.organization.agent.sharing.management.common @@ -1151,7 +1157,7 @@ 1.0.0 - 2.5.10 + 2.5.14-SNAPSHOT 3.4.146