From ebc5f5de31482f25ee50e1fdc8d3cdc600d00a01 Mon Sep 17 00:00:00 2001 From: Vigneshan Date: Wed, 8 Jun 2022 14:55:53 +0530 Subject: [PATCH] Implement multi-tenant api publishing --- .../pom.xml | 5 +- .../publisher/APIPublisherServiceImpl.java | 312 ++++++++++-------- .../webapp/publisher/config/Tenants.java | 45 +++ .../config/WebappPublisherConfig.java | 10 + .../conf/webapp-publisher-config.xml | 7 +- .../conf/etc/webapp-publisher-config.xml.j2 | 11 +- 6 files changed, 256 insertions(+), 134 deletions(-) create mode 100644 components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/config/Tenants.java diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/pom.xml b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/pom.xml index 25ebf6fa063..5a5f021dcae 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/pom.xml +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/pom.xml @@ -149,8 +149,8 @@ org.wso2.carbon.apimgt.webapp.publisher.* - com.google.gson;version="2.3", - com.google.gson.reflect;version="2.3", + com.google.gson;version="[2.3,2.8.6)", + com.google.gson.reflect;version="[2.3,2.8.6)", io.swagger.annotations, javax.servlet;version="2.6", javax.xml, @@ -169,6 +169,7 @@ org.wso2.carbon.apimgt.api, org.wso2.carbon.apimgt.api.model, org.wso2.carbon.apimgt.impl, + org.wso2.carbon.apimgt.impl.utils, org.wso2.carbon.apimgt.webapp.publisher, org.wso2.carbon.apimgt.webapp.publisher.config, org.wso2.carbon.apimgt.webapp.publisher.dto, diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherServiceImpl.java b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherServiceImpl.java index 34c9348e240..9791e654998 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherServiceImpl.java +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherServiceImpl.java @@ -18,6 +18,9 @@ */ package org.wso2.carbon.apimgt.webapp.publisher; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.apimgt.impl.utils.APIUtil; import org.wso2.carbon.apimgt.webapp.publisher.dto.ApiScope; import org.wso2.carbon.apimgt.webapp.publisher.dto.ApiUriTemplate; import org.wso2.carbon.apimgt.api.APIManagementException; @@ -36,10 +39,15 @@ import org.wso2.carbon.apimgt.impl.APIManagerFactory; import org.wso2.carbon.apimgt.webapp.publisher.config.WebappPublisherConfig; import org.wso2.carbon.apimgt.webapp.publisher.exception.APIManagerPublisherException; import org.wso2.carbon.context.PrivilegedCarbonContext; -import org.wso2.carbon.utils.multitenancy.MultitenantUtils; +import org.wso2.carbon.user.api.UserStoreException; +import org.wso2.carbon.user.core.service.RealmService; +import org.wso2.carbon.user.core.tenant.Tenant; +import org.wso2.carbon.user.core.tenant.TenantSearchResult; +import org.wso2.carbon.utils.multitenancy.MultitenantConstants; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -55,146 +63,190 @@ public class APIPublisherServiceImpl implements APIPublisherService { private static final String CREATED_STATUS = "CREATED"; private static final String PUBLISH_ACTION = "Publish"; public static final APIManagerFactory API_MANAGER_FACTORY = APIManagerFactory.getInstance(); + private static final Log log = LogFactory.getLog(APIPublisherServiceImpl.class); @Override public void publishAPI(APIConfig apiConfig) throws APIManagerPublisherException { - String tenantDomain = MultitenantUtils.getTenantDomain(apiConfig.getOwner()); - PrivilegedCarbonContext.startTenantFlow(); - PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain, true); - PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername(apiConfig.getOwner()); - int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); + WebappPublisherConfig config = WebappPublisherConfig.getInstance(); + List tenants = new ArrayList<>(Collections.singletonList(APIConstants.SUPER_TENANT_DOMAIN)); + tenants.addAll(config.getTenants().getTenant()); + RealmService realmService = (RealmService) PrivilegedCarbonContext.getThreadLocalCarbonContext() + .getOSGiService(RealmService.class, null); try { - APIProvider apiProvider = API_MANAGER_FACTORY.getAPIProvider(apiConfig.getOwner()); - APIIdentifier apiIdentifier = new APIIdentifier(apiConfig.getOwner(), apiConfig.getName(), apiConfig.getVersion()); - - if (!apiProvider.isAPIAvailable(apiIdentifier)) { - - // add new scopes as shared scopes - Set allSharedScopeKeys = apiProvider.getAllSharedScopeKeys(tenantDomain); - for (ApiScope apiScope : apiConfig.getScopes()) { - if (!allSharedScopeKeys.contains(apiScope.getKey())) { - Scope scope = new Scope(); - scope.setName(apiScope.getName()); - scope.setDescription(apiScope.getDescription()); - scope.setKey(apiScope.getKey()); - scope.setRoles(apiScope.getRoles()); - apiProvider.addSharedScope(scope, tenantDomain); - } - } - API api = getAPI(apiConfig, true); - API createdAPI = apiProvider.addAPI(api); - if (CREATED_STATUS.equals(createdAPI.getStatus())) { - apiProvider.changeLifeCycleStatus(tenantDomain, createdAPI.getUuid(), PUBLISH_ACTION, null); - APIRevision apiRevision = new APIRevision(); - apiRevision.setApiUUID(createdAPI.getUuid()); - apiRevision.setDescription("Initial Revision"); - String apiRevisionId = apiProvider.addAPIRevision(apiRevision, tenantDomain); - - APIRevisionDeployment apiRevisionDeployment = new APIRevisionDeployment(); - apiRevisionDeployment.setDeployment(API_PUBLISH_ENVIRONMENT); - apiRevisionDeployment.setVhost(System.getProperty("iot.gateway.host")); - apiRevisionDeployment.setDisplayOnDevportal(true); - - List apiRevisionDeploymentList = new ArrayList<>(); - apiRevisionDeploymentList.add(apiRevisionDeployment); - apiProvider.deployAPIRevision(createdAPI.getUuid(), apiRevisionId, apiRevisionDeploymentList); - + boolean tenantFound = false; + boolean tenantsLoaded = false; + TenantSearchResult tenantSearchResult = null; + for (String tenantDomain : tenants) { + PrivilegedCarbonContext.startTenantFlow(); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain, true); + if (!tenantsLoaded) { + tenantSearchResult = realmService.getTenantManager() + .listTenants(Integer.MAX_VALUE, 0, "asc", "UM_ID", null); + tenantsLoaded = true; } - } else { - if (WebappPublisherConfig.getInstance().isEnabledUpdateApi()) { - - // With 4.x to 5.x upgrade - // - there cannot be same local scope assigned in 2 different APIs - // - local scopes will be deprecated in the future, so need to move all scopes as shared scopes - - // if an api scope is not available as shared scope, but already assigned as local scope -> that means, the scopes available for this API has not moved as shared scopes - // in order to do that : - // 1. update the same API removing scopes from URI templates - // 2. add scopes as shared scopes - // 3. update the API again adding scopes for the URI Templates - - // if an api scope is not available as shared scope, and not assigned as local scope -> that means, there are new scopes - // 1. add new scopes as shared scopes - // 2. update the API adding scopes for the URI Templates - - Set allSharedScopeKeys = apiProvider.getAllSharedScopeKeys(tenantDomain); - Set scopesToMoveAsSharedScopes = new HashSet<>(); - for (ApiScope apiScope : apiConfig.getScopes()) { - // if the scope is not available as shared scope and it is assigned to an API as a local scope - // need remove the local scope and add as a shared scope - if (!allSharedScopeKeys.contains(apiScope.getKey())) { - if (apiProvider.isScopeKeyAssignedLocally(apiIdentifier, apiScope.getKey(), tenantId)) { - // collect scope to move as shared scopes - scopesToMoveAsSharedScopes.add(apiScope); - } else { - // if new scope add as shared scope - Scope scope = new Scope(); - scope.setName(apiScope.getName()); - scope.setDescription(apiScope.getDescription()); - scope.setKey(apiScope.getKey()); - scope.setRoles(apiScope.getRoles()); - apiProvider.addSharedScope(scope, tenantDomain); - } + if (tenantDomain.equals(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME)) { + tenantFound = true; + realmService.getTenantUserRealm(MultitenantConstants.SUPER_TENANT_ID) + .getRealmConfiguration().getAdminUserName(); + } else { + List allTenants = tenantSearchResult.getTenantList(); + for (Tenant tenant : allTenants) { + if (tenant.getDomain().equals(tenantDomain)) { + tenantFound = true; + tenant.getAdminName(); + break; + } else { + tenantFound = false; } } + } - // Get existing API - API existingAPI = apiProvider.getAPI(apiIdentifier); - - if (scopesToMoveAsSharedScopes.size() > 0) { - // update API to remove local scopes - API api = getAPI(apiConfig, false); - api.setStatus(existingAPI.getStatus()); - apiProvider.updateAPI(api); - - for (ApiScope apiScope : scopesToMoveAsSharedScopes) { - Scope scope = new Scope(); - scope.setName(apiScope.getName()); - scope.setDescription(apiScope.getDescription()); - scope.setKey(apiScope.getKey()); - scope.setRoles(apiScope.getRoles()); - apiProvider.addSharedScope(scope, tenantDomain); + if (tenantFound) { + PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername(apiConfig.getOwner()); + int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); + + try { + apiConfig.setOwner(APIUtil.getTenantAdminUserName(tenantDomain)); + apiConfig.setTenantDomain(tenantDomain); + APIProvider apiProvider = API_MANAGER_FACTORY.getAPIProvider(apiConfig.getOwner()); + APIIdentifier apiIdentifier = new APIIdentifier(APIUtil.replaceEmailDomain(apiConfig.getOwner()), + apiConfig.getName(), apiConfig.getVersion()); + + if (!apiProvider.isAPIAvailable(apiIdentifier)) { + + // add new scopes as shared scopes + Set allSharedScopeKeys = apiProvider.getAllSharedScopeKeys(tenantDomain); + for (ApiScope apiScope : apiConfig.getScopes()) { + if (!allSharedScopeKeys.contains(apiScope.getKey())) { + Scope scope = new Scope(); + scope.setName(apiScope.getName()); + scope.setDescription(apiScope.getDescription()); + scope.setKey(apiScope.getKey()); + scope.setRoles(apiScope.getRoles()); + apiProvider.addSharedScope(scope, tenantDomain); + } + } + API api = getAPI(apiConfig, true); + api.setId(apiIdentifier); + API createdAPI = apiProvider.addAPI(api); + if (CREATED_STATUS.equals(createdAPI.getStatus())) { + apiProvider.changeLifeCycleStatus(tenantDomain, createdAPI.getUuid(), PUBLISH_ACTION, null); + APIRevision apiRevision = new APIRevision(); + apiRevision.setApiUUID(createdAPI.getUuid()); + apiRevision.setDescription("Initial Revision"); + String apiRevisionId = apiProvider.addAPIRevision(apiRevision, tenantDomain); + + APIRevisionDeployment apiRevisionDeployment = new APIRevisionDeployment(); + apiRevisionDeployment.setDeployment(API_PUBLISH_ENVIRONMENT); + apiRevisionDeployment.setVhost(System.getProperty("iot.gateway.host")); + apiRevisionDeployment.setDisplayOnDevportal(true); + + List apiRevisionDeploymentList = new ArrayList<>(); + apiRevisionDeploymentList.add(apiRevisionDeployment); + apiProvider.deployAPIRevision(createdAPI.getUuid(), apiRevisionId, apiRevisionDeploymentList); + } + } else { + if (WebappPublisherConfig.getInstance().isEnabledUpdateApi()) { + + // With 4.x to 5.x upgrade + // - there cannot be same local scope assigned in 2 different APIs + // - local scopes will be deprecated in the future, so need to move all scopes as shared scopes + + // if an api scope is not available as shared scope, but already assigned as local scope -> that means, the scopes available for this API has not moved as shared scopes + // in order to do that : + // 1. update the same API removing scopes from URI templates + // 2. add scopes as shared scopes + // 3. update the API again adding scopes for the URI Templates + + // if an api scope is not available as shared scope, and not assigned as local scope -> that means, there are new scopes + // 1. add new scopes as shared scopes + // 2. update the API adding scopes for the URI Templates + + Set allSharedScopeKeys = apiProvider.getAllSharedScopeKeys(tenantDomain); + Set scopesToMoveAsSharedScopes = new HashSet<>(); + for (ApiScope apiScope : apiConfig.getScopes()) { + // if the scope is not available as shared scope and it is assigned to an API as a local scope + // need remove the local scope and add as a shared scope + if (!allSharedScopeKeys.contains(apiScope.getKey())) { + if (apiProvider.isScopeKeyAssignedLocally(apiIdentifier, apiScope.getKey(), tenantId)) { + // collect scope to move as shared scopes + scopesToMoveAsSharedScopes.add(apiScope); + } else { + // if new scope add as shared scope + Scope scope = new Scope(); + scope.setName(apiScope.getName()); + scope.setDescription(apiScope.getDescription()); + scope.setKey(apiScope.getKey()); + scope.setRoles(apiScope.getRoles()); + apiProvider.addSharedScope(scope, tenantDomain); + } + } + } + + // Get existing API + API existingAPI = apiProvider.getAPI(apiIdentifier); + + if (scopesToMoveAsSharedScopes.size() > 0) { + // update API to remove local scopes + API api = getAPI(apiConfig, false); + api.setStatus(existingAPI.getStatus()); + apiProvider.updateAPI(api); + + for (ApiScope apiScope : scopesToMoveAsSharedScopes) { + Scope scope = new Scope(); + scope.setName(apiScope.getName()); + scope.setDescription(apiScope.getDescription()); + scope.setKey(apiScope.getKey()); + scope.setRoles(apiScope.getRoles()); + apiProvider.addSharedScope(scope, tenantDomain); + } + } + + existingAPI = apiProvider.getAPI(apiIdentifier); + API api = getAPI(apiConfig, true); + api.setStatus(existingAPI.getStatus()); + apiProvider.updateAPI(api); + + // Assumption: Assume the latest revision is the published one + String latestRevisionUUID = apiProvider.getLatestRevisionUUID(existingAPI.getUuid()); + List latestRevisionDeploymentList = + apiProvider.getAPIRevisionDeploymentList(latestRevisionUUID); + + List apiRevisionList = apiProvider.getAPIRevisions(existingAPI.getUuid()); + if (apiRevisionList.size() >= 5) { + String earliestRevisionUUID = apiProvider.getEarliestRevisionUUID(existingAPI.getUuid()); + List earliestRevisionDeploymentList = + apiProvider.getAPIRevisionDeploymentList(earliestRevisionUUID); + apiProvider.undeployAPIRevisionDeployment(existingAPI.getUuid(), earliestRevisionUUID, earliestRevisionDeploymentList); + apiProvider.deleteAPIRevision(existingAPI.getUuid(), earliestRevisionUUID, tenantDomain); + } + + // create new revision + APIRevision apiRevision = new APIRevision(); + apiRevision.setApiUUID(existingAPI.getUuid()); + apiRevision.setDescription("Updated Revision"); + String apiRevisionId = apiProvider.addAPIRevision(apiRevision, tenantDomain); + + apiProvider.deployAPIRevision(existingAPI.getUuid(), apiRevisionId, latestRevisionDeploymentList); + + if (CREATED_STATUS.equals(existingAPI.getStatus())) { + apiProvider.changeLifeCycleStatus(tenantDomain, existingAPI.getUuid(), PUBLISH_ACTION, null); + } + } } - } - - existingAPI = apiProvider.getAPI(apiIdentifier); - API api = getAPI(apiConfig, true); - api.setStatus(existingAPI.getStatus()); - apiProvider.updateAPI(api); - - // Assumption: Assume the latest revision is the published one - String latestRevisionUUID = apiProvider.getLatestRevisionUUID(existingAPI.getUuid()); - List latestRevisionDeploymentList = - apiProvider.getAPIRevisionDeploymentList(latestRevisionUUID); - - List apiRevisionList = apiProvider.getAPIRevisions(existingAPI.getUuid()); - if (apiRevisionList.size() >= 5) { - String earliestRevisionUUID = apiProvider.getEarliestRevisionUUID(existingAPI.getUuid()); - List earliestRevisionDeploymentList = - apiProvider.getAPIRevisionDeploymentList(earliestRevisionUUID); - apiProvider.undeployAPIRevisionDeployment(existingAPI.getUuid(), earliestRevisionUUID, earliestRevisionDeploymentList); - apiProvider.deleteAPIRevision(existingAPI.getUuid(), earliestRevisionUUID, tenantDomain); - } - - // create new revision - APIRevision apiRevision = new APIRevision(); - apiRevision.setApiUUID(existingAPI.getUuid()); - apiRevision.setDescription("Updated Revision"); - String apiRevisionId = apiProvider.addAPIRevision(apiRevision, tenantDomain); - - apiProvider.deployAPIRevision(existingAPI.getUuid(), apiRevisionId, latestRevisionDeploymentList); - - if (CREATED_STATUS.equals(existingAPI.getStatus())) { - apiProvider.changeLifeCycleStatus(tenantDomain, existingAPI.getUuid(), PUBLISH_ACTION, null); + } catch (FaultGatewaysException | APIManagementException e) { + String msg = "Error occurred while publishing api"; + log.error(msg, e); + throw new APIManagerPublisherException(e); + } finally { + PrivilegedCarbonContext.endTenantFlow(); } } } - - - } catch (FaultGatewaysException | APIManagementException e) { + } catch (UserStoreException e) { + String msg = "Error occurred while retrieving admin user from tenant user realm"; + log.error(msg, e); throw new APIManagerPublisherException(e); - } finally { - PrivilegedCarbonContext.endTenantFlow(); } } @@ -209,7 +261,7 @@ public class APIPublisherServiceImpl implements APIPublisherService { api.setStatus(CREATED_STATUS); api.setWsdlUrl(null); api.setResponseCache("Disabled"); - api.setContextTemplate(context + "/{version}" ); + api.setContextTemplate(context + "/{version}"); api.setSwaggerDefinition(APIPublisherUtil.getSwaggerDefinition(config)); api.setType("HTTP"); @@ -262,7 +314,7 @@ public class APIPublisherServiceImpl implements APIPublisherService { api.setVisibility(APIConstants.API_PRIVATE_VISIBILITY); } String endpointConfig = "{ \"endpoint_type\": \"http\", \"sandbox_endpoints\": { \"url\": \" " + - config.getEndpoint() + "\" }, \"production_endpoints\": { \"url\": \" "+ config.getEndpoint()+"\" } }"; + config.getEndpoint() + "\" }, \"production_endpoints\": { \"url\": \" " + config.getEndpoint() + "\" } }"; api.setEndpointConfig(endpointConfig); List accessControlAllowOrigins = new ArrayList<>(); diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/config/Tenants.java b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/config/Tenants.java new file mode 100644 index 00000000000..2738a79447c --- /dev/null +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/config/Tenants.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. 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.apimgt.webapp.publisher.config; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlType; +import java.util.ArrayList; +import java.util.List; + + +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "Tenants", propOrder = { + "tenant" +}) +public class Tenants { + + @XmlElement(name = "Tenant") + protected List tenant = new ArrayList<>();; + + /** + * Gets the value of the profile property. + * + */ + public List getTenant() { + return this.tenant; + } + +} diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/config/WebappPublisherConfig.java b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/config/WebappPublisherConfig.java index a54d370e5cf..38cdef154e2 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/config/WebappPublisherConfig.java +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/config/WebappPublisherConfig.java @@ -43,6 +43,7 @@ public class WebappPublisherConfig { private boolean isEnabledUpdateApi; private Profiles profiles; private static boolean isInitialized = false; + private Tenants tenants; private static WebappPublisherConfig config; @@ -100,6 +101,15 @@ public class WebappPublisherConfig { this.profiles = profiles; } + @XmlElement(name = "Tenants", required = true) + public Tenants getTenants() { + return tenants; + } + + public void setTenants(Tenants tenants) { + this.tenants = tenants; + } + public synchronized static void init() throws WebappPublisherConfigurationFailedException { if (isInitialized) { return; diff --git a/features/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher.feature/src/main/resources/conf/webapp-publisher-config.xml b/features/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher.feature/src/main/resources/conf/webapp-publisher-config.xml index 067a6af7f8e..63869352a54 100644 --- a/features/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher.feature/src/main/resources/conf/webapp-publisher-config.xml +++ b/features/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher.feature/src/main/resources/conf/webapp-publisher-config.xml @@ -36,4 +36,9 @@ default - \ No newline at end of file + + + + + + diff --git a/features/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher.feature/src/main/resources/conf_templates/templates/repository/conf/etc/webapp-publisher-config.xml.j2 b/features/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher.feature/src/main/resources/conf_templates/templates/repository/conf/etc/webapp-publisher-config.xml.j2 index d8a0413ec8a..3b48b73ae87 100644 --- a/features/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher.feature/src/main/resources/conf_templates/templates/repository/conf/etc/webapp-publisher-config.xml.j2 +++ b/features/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher.feature/src/main/resources/conf_templates/templates/repository/conf/etc/webapp-publisher-config.xml.j2 @@ -53,4 +53,13 @@ {% endfor %} {% endif %} - \ No newline at end of file + + + + {% if webapp_publisher_configs.tenants is defined %} + {%- for tenant in webapp_publisher_configs.tenants -%} + {{tenant}} + {% endfor %} + {% endif %} + +