diff --git a/en/identity-server/6.1.0/docs/deploy/change-to-oracle.md b/en/identity-server/6.1.0/docs/deploy/change-to-oracle.md index 3ced44e3c9..ad5d7b8a4d 100644 --- a/en/identity-server/6.1.0/docs/deploy/change-to-oracle.md +++ b/en/identity-server/6.1.0/docs/deploy/change-to-oracle.md @@ -109,6 +109,18 @@ Apart from the basic configurations specified above, WSO2 Identity Server suppor {!./includes/db-config-table.md !} +### Using an alternate user to connect to database + +When the database owner is not the user used to connect to the database, specify the parent schema in the datasource declarion. + + ``` toml + [database.identity_db.db_props] + parentSchema = "" + + [database.shared_db.db_props] + parentSchema = "" + ``` + --- ## Configure the connection pool behavior on return diff --git a/en/identity-server/7.0.0/docs/deploy/configure/databases/carbon-database/change-to-oracle.md b/en/identity-server/7.0.0/docs/deploy/configure/databases/carbon-database/change-to-oracle.md index e9d2ffac6b..291581a961 100644 --- a/en/identity-server/7.0.0/docs/deploy/configure/databases/carbon-database/change-to-oracle.md +++ b/en/identity-server/7.0.0/docs/deploy/configure/databases/carbon-database/change-to-oracle.md @@ -106,8 +106,19 @@ Apart from the basic configurations specified above, WSO2 Identity Server suppor {% include "../../../../includes/db-config-table.md" %} +### Using an alternate user to connect to database + +When the database owner is not the user used to connect to the database, specify the parent schema in the datasource declarion. + + ``` toml + [database.identity_db.db_props] + parentSchema = "" + + [database.shared_db.db_props] + parentSchema = "" + ``` + --- - ## Configure the connection pool behavior on return {% include "../../../../includes/connection-pool-behavior.md" %} diff --git a/en/identity-server/7.0.0/docs/guides/users/outbound-provisioning/outbound-connectors/custom-outbound-connectors.md b/en/identity-server/7.0.0/docs/guides/users/outbound-provisioning/outbound-connectors/custom-outbound-connectors.md new file mode 100644 index 0000000000..d2a2b05dbd --- /dev/null +++ b/en/identity-server/7.0.0/docs/guides/users/outbound-provisioning/outbound-connectors/custom-outbound-connectors.md @@ -0,0 +1 @@ +{% include "../../../../../../../includes/guides/users/outbound-provisioning/outbound-connectors/custom-outbound-connectors.md" %} \ No newline at end of file diff --git a/en/identity-server/7.0.0/mkdocs.yml b/en/identity-server/7.0.0/mkdocs.yml index 1868a1eab5..0b9af075cb 100644 --- a/en/identity-server/7.0.0/mkdocs.yml +++ b/en/identity-server/7.0.0/mkdocs.yml @@ -507,6 +507,7 @@ nav: - Google: guides/users/outbound-provisioning/outbound-connectors/google.md - Salesforce: guides/users/outbound-provisioning/outbound-connectors/salesforce.md - SCIM2: guides/users/outbound-provisioning/outbound-connectors/scim2.md + - Custom Outbound Connector: guides/users/outbound-provisioning/outbound-connectors/custom-outbound-connectors.md - Provisioning patterns: guides/users/outbound-provisioning/provisioning-patterns.md - Sync User Accounts: - Overview: guides/users/sync-user-accounts/sync-account-overview.md diff --git a/en/identity-server/next/docs/deploy/configure/databases/carbon-database/change-to-oracle.md b/en/identity-server/next/docs/deploy/configure/databases/carbon-database/change-to-oracle.md index e9d2ffac6b..0c08cb2e6a 100644 --- a/en/identity-server/next/docs/deploy/configure/databases/carbon-database/change-to-oracle.md +++ b/en/identity-server/next/docs/deploy/configure/databases/carbon-database/change-to-oracle.md @@ -106,6 +106,18 @@ Apart from the basic configurations specified above, WSO2 Identity Server suppor {% include "../../../../includes/db-config-table.md" %} +### Using an alternate user to connect to database + +When the database owner is not the user used to connect to the database, specify the parent schema in the datasource declarion. + + ``` toml + [database.identity_db.db_props] + parentSchema = "" + + [database.shared_db.db_props] + parentSchema = "" + ``` + --- ## Configure the connection pool behavior on return diff --git a/en/identity-server/next/docs/guides/users/outbound-provisioning/outbound-connectors/custom-outbound-connectors.md b/en/identity-server/next/docs/guides/users/outbound-provisioning/outbound-connectors/custom-outbound-connectors.md new file mode 100644 index 0000000000..d2a2b05dbd --- /dev/null +++ b/en/identity-server/next/docs/guides/users/outbound-provisioning/outbound-connectors/custom-outbound-connectors.md @@ -0,0 +1 @@ +{% include "../../../../../../../includes/guides/users/outbound-provisioning/outbound-connectors/custom-outbound-connectors.md" %} \ No newline at end of file diff --git a/en/identity-server/next/mkdocs.yml b/en/identity-server/next/mkdocs.yml index 1461a95bf6..d2e41e18a6 100644 --- a/en/identity-server/next/mkdocs.yml +++ b/en/identity-server/next/mkdocs.yml @@ -509,6 +509,7 @@ nav: - Google: guides/users/outbound-provisioning/outbound-connectors/google.md - Salesforce: guides/users/outbound-provisioning/outbound-connectors/salesforce.md - SCIM2: guides/users/outbound-provisioning/outbound-connectors/scim2.md + - Custom Outbound Connector: guides/users/outbound-provisioning/outbound-connectors/custom-outbound-connectors.md - Provisioning patterns: guides/users/outbound-provisioning/provisioning-patterns.md - Sync User Accounts: - Overview: guides/users/sync-user-accounts/sync-account-overview.md diff --git a/en/includes/guides/users/outbound-provisioning/outbound-connectors/custom-outbound-connectors.md b/en/includes/guides/users/outbound-provisioning/outbound-connectors/custom-outbound-connectors.md new file mode 100644 index 0000000000..fee0e4cf7c --- /dev/null +++ b/en/includes/guides/users/outbound-provisioning/outbound-connectors/custom-outbound-connectors.md @@ -0,0 +1,278 @@ +# Write a custom Outbound Provisioning Connector + +In addition to Google, Salesforce, SCIM, and SPML, it is possible to create custom connectors. + +Follow the steps given below to write an outbound provisioning connector. + +1. The following API is used to configure a custom connector. The connector API can be obtained [here](https://github.com/wso2/carbon-identity-framework/blob/v7.0.78/components/provisioning/). + + ??? info "Click here to view the API source code" + ```java + /* + * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.provisioning; + + import org.apache.commons.collections.CollectionUtils; + import org.apache.commons.lang.StringUtils; + import org.wso2.carbon.CarbonConstants; + import org.wso2.carbon.context.PrivilegedCarbonContext; + import org.wso2.carbon.identity.application.common.model.ClaimMapping; + import org.wso2.carbon.identity.application.common.model.Property; + import org.wso2.carbon.user.core.UserCoreConstants; + + import java.io.Serializable; + import java.util.HashMap; + import java.util.List; + import java.util.Map; + import java.util.UUID; + + public abstract class AbstractOutboundProvisioningConnector implements Serializable { + + private static final long serialVersionUID = 8619915839101228583L; + + private static final String PROVISIONING_IDP = "IDP"; + private static final String PROVISIONING_TENANT = "TD"; + private static final String PROVISIONING_DOMAIN = "UD"; + private static final String PROVISIONING_USER = "UN"; + protected boolean jitProvisioningEnabled; + + /** + * @param provisioningProperties + * @throws IdentityProvisioningException + */ + public abstract void init(Property[] provisioningProperties) + throws IdentityProvisioningException; + + /** + * @param provisioningEntity + * @throws IdentityProvisioningException + */ + public abstract ProvisionedIdentifier provision(ProvisioningEntity provisioningEntity) + throws IdentityProvisioningException; + + /** + * override only if needed - if claims are controlled by the identity provider, this will return + * null. If it is connector specific this must return the corresponding claim dialect. + * + * @return + * @throws IdentityProvisioningException + */ + public String getClaimDialectUri() throws IdentityProvisioningException { + return null; + } + + /** + * @return + * @throws IdentityProvisioningException + */ + protected boolean isJitProvisioningEnabled() throws IdentityProvisioningException { + return jitProvisioningEnabled; + } + + /** + * @param attributeMap + * @return + */ + protected List getUserNames(Map> attributeMap) { + return ProvisioningUtil.getClaimValues(attributeMap, + IdentityProvisioningConstants.USERNAME_CLAIM_URI, getUserStoreDomainName()); + } + + /** + * @param attributeMap + * @return + */ + protected List getGroupNames(Map> attributeMap) { + return ProvisioningUtil.getClaimValues(attributeMap, + IdentityProvisioningConstants.GROUP_CLAIM_URI, getUserStoreDomainName()); + } + + /** + * @param attributeMap + * @return + */ + protected String getPassword(Map> attributeMap) { + List claimValue = ProvisioningUtil.getClaimValues(attributeMap, + IdentityProvisioningConstants.PASSWORD_CLAIM_URI, null); + + if (CollectionUtils.isNotEmpty(claimValue) && claimValue.get(0) != null) { + return claimValue.get(0); + } + + return UUID.randomUUID().toString(); + + } + + /** + * @param attributeMap + * @return claimValues + */ + protected Map getSingleValuedClaims(Map> attributeMap) { + + Map claimValues = new HashMap<>(); + + for (Map.Entry> entry : attributeMap.entrySet()) { + ClaimMapping mapping = entry.getKey(); + if (mapping.getRemoteClaim() != null && mapping.getRemoteClaim().getClaimUri() != null) { + String claimUri = mapping.getRemoteClaim().getClaimUri(); + + if (!(IdentityProvisioningConstants.GROUP_CLAIM_URI.equals(claimUri) + || IdentityProvisioningConstants.PASSWORD_CLAIM_URI.equals(claimUri) || IdentityProvisioningConstants.USERNAME_CLAIM_URI + .equals(claimUri))) { + if (CollectionUtils.isNotEmpty(entry.getValue()) && entry.getValue().get(0) != null) { + claimValues.put(claimUri, entry.getValue().get(0)); + } else { + claimValues.put(claimUri, mapping.getDefaultValue()); + } + } + } + } + + return claimValues; + } + + /** + * @return + */ + protected String getUserStoreDomainName() { + // return null by default. concrete implementations can override this value whenever + // required. + return null; + } + + protected String buildUserId(ProvisioningEntity provisioningEntity, String provisioningPattern, + String separator, String idpName) throws IdentityProvisioningException { + + Map provValues = new HashMap<>(); + String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); + String username = provisioningEntity.getEntityName(); + String userStoreDomain = getDomainFromUserName(username); + + if (separator == null) { + separator = ""; + } + + String provIdentifier = ""; + provValues.put(PROVISIONING_TENANT, tenantDomain.replaceAll(separator, "")); + + if (username != null) { + provValues.put(PROVISIONING_USER, removeDomainFromUserName(username)); + } + provValues.put(PROVISIONING_IDP, idpName.replaceAll(separator, "")); + + if (userStoreDomain != null) { + provValues.put(PROVISIONING_DOMAIN, userStoreDomain.replaceAll(separator, "")); + } + + String[] provisioningEntries = buildProvisioningEntries(provisioningPattern); + + for (int i = 0; i < provisioningEntries.length; i++) { + if (StringUtils.isNotBlank(provisioningEntries[i])) { + if (StringUtils.isBlank(provIdentifier)) { + provIdentifier = provValues.get(provisioningEntries[i].trim()); + } else { + provIdentifier = provIdentifier.concat(separator).concat(provValues.get(provisioningEntries[i].trim())); + } + } + } + + return provIdentifier.toLowerCase(); + } + + private String[] buildProvisioningEntries(String provisioningPattern) throws IdentityProvisioningException { + + if (!provisioningPattern.contains("{") || !provisioningPattern.contains("}")) { + throw new IdentityProvisioningException("Invalid Provisioning Pattern"); + } + + String provisioningPatternWithoutCurlBrace = provisioningPattern.replaceAll("\\{", "").replaceAll("\\}", ""); + return provisioningPatternWithoutCurlBrace.split(","); + } + + private String getDomainFromUserName(String username) { + int index; + String domain = UserCoreConstants.PRIMARY_DEFAULT_DOMAIN_NAME; + if (StringUtils.isNotBlank(username)) { + if ((index = username.indexOf("/")) > 0) { + domain = username.substring(0, index); + } + return domain; + } + return domain; + } + + private String removeDomainFromUserName(String username) { + int index; + if ((index = username.indexOf(CarbonConstants.DOMAIN_SEPARATOR)) >= 0) { + // remove domain name if exist + username = username.substring(index + 1); + } + return username; + } + + } + ``` + +2. Refer the source code of the [sample outbound connector](https://github.com/wso2/samples-is/tree/master/sample-outbound-connector) to get an understanding about the structure of the connector. The Google provisioning connector that uses the API given above can be found [here](https://github.com/wso2-extensions/identity-outbound-provisioning-google/blob/v5.1.9/components/org.wso2.carbon.identity.provisioning.connector.google/src/main/java/org/wso2/carbon/identity/provisioning/connector/google/GoogleProvisioningConnector.java). Using these connectors as examples, you can customize this code according to your requirements. + + !!! note + To ensure that the connector works, check whether the following configurations are added in the **pom.xml** file (found in the above connector API link) inside the `` tags. + + ``` xml + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.provisioning + 5.20.25 + + ``` + + ``` xml + + + wso2-nexus + WSO2 internal Repository + http://maven.wso2.org/nexus/content/groups/wso2-public/ + + true + daily + ignore + + + + wso2.releases + WSO2 internal Repository + http://maven.wso2.org/nexus/content/repositories/releases/ + + true + daily + ignore + + + + ``` + +3. Additionally, the custom connector metadata such as description, display name can be added as follows to supplement the UI. + + ```toml + [[console.extensions.outboundProvisioningConnectors]] + connectorId="U2FtcGxl" + description="Sample outbound provisioning connector" + displayName="Sample outbound" + icon="" + ```