diff --git a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.extension.rest.api/pom.xml b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.extension.rest.api/pom.xml
index 00e8d5c08c..632150f8fe 100644
--- a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.extension.rest.api/pom.xml
+++ b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.extension.rest.api/pom.xml
@@ -84,6 +84,10 @@
okhttp
compile
+
+ org.wso2.carbon
+ org.wso2.carbon.user.api
+
@@ -121,7 +125,10 @@
org.wso2.carbon.apimgt.impl;version="${carbon.api.mgt.version.range}",
org.wso2.carbon.apimgt.impl.utils;version="${carbon.api.mgt.version.range}",
org.wso2.carbon.apimgt.impl.internal;version="${carbon.api.mgt.version.range}",
- org.json
+ org.json,
+ org.wso2.carbon.user.api,
+ org.wso2.carbon.context;version="4.6",
+ org.wso2.carbon.utils.*
diff --git a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.extension.rest.api/src/main/java/io/entgra/device/mgt/core/apimgt/extension/rest/api/APIApplicationServicesImpl.java b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.extension.rest.api/src/main/java/io/entgra/device/mgt/core/apimgt/extension/rest/api/APIApplicationServicesImpl.java
index 05ecf1fd9e..c412d189c0 100644
--- a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.extension.rest.api/src/main/java/io/entgra/device/mgt/core/apimgt/extension/rest/api/APIApplicationServicesImpl.java
+++ b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.extension.rest.api/src/main/java/io/entgra/device/mgt/core/apimgt/extension/rest/api/APIApplicationServicesImpl.java
@@ -33,8 +33,11 @@ import okhttp3.RequestBody;
import okhttp3.Credentials;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.wso2.carbon.apimgt.impl.APIConstants;
import org.wso2.carbon.apimgt.impl.APIManagerConfiguration;
import org.wso2.carbon.apimgt.impl.internal.ServiceReferenceHolder;
+import org.wso2.carbon.context.PrivilegedCarbonContext;
+
import java.io.IOException;
public class APIApplicationServicesImpl implements APIApplicationServices {
@@ -48,12 +51,12 @@ public class APIApplicationServicesImpl implements APIApplicationServices {
getAPIManagerConfigurationService().getAPIManagerConfiguration();
@Override
- public APIApplicationKey createAndRetrieveApplicationCredentials()
- throws APIServicesException {
+ public APIApplicationKey createAndRetrieveApplicationCredentials() throws APIServicesException {
+ String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();
+ String serverUser = getScopePublishUserName(tenantDomain);
+ String serverPassword = getScopePublishUserPassword(tenantDomain);
String applicationEndpoint = config.getFirstProperty(Constants.DCR_END_POINT);
- String serverUser = config.getFirstProperty(Constants.SERVER_USER);
- String serverPassword = config.getFirstProperty(Constants.SERVER_PASSWORD);
JSONObject jsonObject = new JSONObject();
jsonObject.put("callbackUrl", Constants.EMPTY_STRING);
@@ -68,9 +71,11 @@ public class APIApplicationServicesImpl implements APIApplicationServices {
.addHeader(Constants.AUTHORIZATION_HEADER_NAME, Credentials.basic(serverUser, serverPassword))
.post(requestBody)
.build();
+
try {
- Response response = client.newCall(request).execute();
- return gson.fromJson(response.body().string(), APIApplicationKey.class);
+ try (Response response = client.newCall(request).execute()) {
+ return gson.fromJson(response.body().string(), APIApplicationKey.class);
+ }
} catch (IOException e) {
msg = "Error occurred while processing the response";
log.error(msg, e);
@@ -82,8 +87,9 @@ public class APIApplicationServicesImpl implements APIApplicationServices {
public AccessTokenInfo generateAccessTokenFromRegisteredApplication(String consumerKey, String consumerSecret)
throws APIServicesException {
- String userName = config.getFirstProperty(Constants.SERVER_USER);
- String userPassword = config.getFirstProperty(Constants.SERVER_PASSWORD);
+ String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();
+ String userName = getScopePublishUserName(tenantDomain);
+ String userPassword = getScopePublishUserPassword(tenantDomain);
JSONObject params = new JSONObject();
params.put(Constants.GRANT_TYPE_PARAM_NAME, Constants.PASSWORD_GRANT_TYPE);
@@ -94,9 +100,8 @@ public class APIApplicationServicesImpl implements APIApplicationServices {
}
@Override
- public AccessTokenInfo generateAccessTokenFromRefreshToken(String refreshToken, String consumerKey, String consumerSecret)
- throws APIServicesException {
-
+ public AccessTokenInfo generateAccessTokenFromRefreshToken(String refreshToken, String consumerKey,
+ String consumerSecret) throws APIServicesException {
JSONObject params = new JSONObject();
params.put(Constants.GRANT_TYPE_PARAM_NAME, Constants.REFRESH_TOKEN_GRANT_TYPE);
params.put(Constants.REFRESH_TOKEN_GRANT_TYPE_PARAM_NAME, refreshToken);
@@ -125,4 +130,20 @@ public class APIApplicationServicesImpl implements APIApplicationServices {
throw new APIServicesException(e);
}
}
+
+ private String getScopePublishUserName(String tenantDomain) {
+ if(APIConstants.SUPER_TENANT_DOMAIN.equals(tenantDomain)) {
+ return config.getFirstProperty(Constants.SERVER_USER);
+ } else {
+ return Constants.SCOPE_PUBLISH_RESERVED_USER_NAME + "@" + tenantDomain;
+ }
+ }
+
+ private String getScopePublishUserPassword(String tenantDomain) {
+ if(APIConstants.SUPER_TENANT_DOMAIN.equals(tenantDomain)) {
+ return config.getFirstProperty(Constants.SERVER_PASSWORD);
+ } else {
+ return Constants.SCOPE_PUBLISH_RESERVED_USER_PASSWORD;
+ }
+ }
}
diff --git a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.extension.rest.api/src/main/java/io/entgra/device/mgt/core/apimgt/extension/rest/api/PublisherRESTAPIServices.java b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.extension.rest.api/src/main/java/io/entgra/device/mgt/core/apimgt/extension/rest/api/PublisherRESTAPIServices.java
index b86437b6f3..a263ad2d63 100644
--- a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.extension.rest.api/src/main/java/io/entgra/device/mgt/core/apimgt/extension/rest/api/PublisherRESTAPIServices.java
+++ b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.extension.rest.api/src/main/java/io/entgra/device/mgt/core/apimgt/extension/rest/api/PublisherRESTAPIServices.java
@@ -42,6 +42,9 @@ public interface PublisherRESTAPIServices {
boolean updateSharedScope(APIApplicationKey apiApplicationKey, AccessTokenInfo accessTokenInfo, Scope scope)
throws APIServicesException, BadRequestException, UnexpectedResponseException;
+ boolean deleteSharedScope(APIApplicationKey apiApplicationKey, AccessTokenInfo accessTokenInfo, Scope scope)
+ throws APIServicesException, BadRequestException, UnexpectedResponseException;
+
APIInfo getApi(APIApplicationKey apiApplicationKey, AccessTokenInfo accessTokenInfo, String apiUuid)
throws APIServicesException, BadRequestException, UnexpectedResponseException;
diff --git a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.extension.rest.api/src/main/java/io/entgra/device/mgt/core/apimgt/extension/rest/api/PublisherRESTAPIServicesImpl.java b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.extension.rest.api/src/main/java/io/entgra/device/mgt/core/apimgt/extension/rest/api/PublisherRESTAPIServicesImpl.java
index 8ba8bb12f7..8db730cc7b 100644
--- a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.extension.rest.api/src/main/java/io/entgra/device/mgt/core/apimgt/extension/rest/api/PublisherRESTAPIServicesImpl.java
+++ b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.extension.rest.api/src/main/java/io/entgra/device/mgt/core/apimgt/extension/rest/api/PublisherRESTAPIServicesImpl.java
@@ -79,6 +79,7 @@ public class PublisherRESTAPIServicesImpl implements PublisherRESTAPIServices {
throw new BadRequestException(msg);
} else {
String msg = "Response : " + response.code() + response.body();
+ log.error(msg);
throw new UnexpectedResponseException(msg);
}
} catch (IOException e) {
@@ -123,6 +124,7 @@ public class PublisherRESTAPIServicesImpl implements PublisherRESTAPIServices {
return false;
} else {
String msg = "Response : " + response.code() + response.body();
+ log.error(msg);
throw new UnexpectedResponseException(msg);
}
} catch (IOException e) {
@@ -177,6 +179,7 @@ public class PublisherRESTAPIServicesImpl implements PublisherRESTAPIServices {
throw new BadRequestException(msg);
} else {
String msg = "Response : " + response.code() + response.message();
+ log.error(msg);
throw new UnexpectedResponseException(msg);
}
} catch (IOException e) {
@@ -231,6 +234,61 @@ public class PublisherRESTAPIServicesImpl implements PublisherRESTAPIServices {
throw new BadRequestException(msg);
} else {
String msg = "Response : " + response.code() + response.body();
+ log.error(msg);
+ throw new UnexpectedResponseException(msg);
+ }
+ } catch (IOException e) {
+ String msg = "Error occurred while processing the response";
+ log.error(msg, e);
+ throw new APIServicesException(msg, e);
+ }
+ }
+
+ @Override
+ public boolean deleteSharedScope(APIApplicationKey apiApplicationKey, AccessTokenInfo accessTokenInfo, Scope scope)
+ throws APIServicesException, BadRequestException, UnexpectedResponseException {
+ String updateScopeUrl = endPointPrefix + Constants.SCOPE_API_ENDPOINT + scope.getId();
+
+ JSONArray bindings = new JSONArray();
+ if (scope.getBindings() != null) {
+ for (String str : scope.getBindings()) {
+ bindings.put(str);
+ }
+ }
+
+ JSONObject payload = new JSONObject();
+ payload.put("name", (scope.getName() != null ? scope.getName() : ""));
+ payload.put("displayName", (scope.getDisplayName() != null ? scope.getDisplayName() : ""));
+ payload.put("description", (scope.getDescription() != null ? scope.getDescription() : ""));
+ payload.put("bindings", (bindings != null ? bindings : ""));
+ payload.put("usageCount", (scope.getUsageCount() != 0 ? scope.getUsageCount() : 0));
+
+ RequestBody requestBody = RequestBody.create(JSON, payload.toString());
+ Request request = new Request.Builder()
+ .url(updateScopeUrl)
+ .addHeader(Constants.AUTHORIZATION_HEADER_NAME, Constants.AUTHORIZATION_HEADER_PREFIX_BEARER
+ + accessTokenInfo.getAccess_token())
+ .delete(requestBody)
+ .build();
+
+ try {
+ Response response = client.newCall(request).execute();
+ if (HttpStatus.SC_OK == response.code()) {
+ return true;
+ } else if (HttpStatus.SC_UNAUTHORIZED == response.code()) {
+ APIApplicationServices apiApplicationServices = new APIApplicationServicesImpl();
+ AccessTokenInfo refreshedAccessToken = apiApplicationServices.
+ generateAccessTokenFromRefreshToken(accessTokenInfo.getRefresh_token(),
+ apiApplicationKey.getClientId(), apiApplicationKey.getClientSecret());
+ //TODO: max attempt count
+ return deleteSharedScope(apiApplicationKey, refreshedAccessToken, scope);
+ } else if (HttpStatus.SC_BAD_REQUEST == response.code()) {
+ String msg = "Bad Request, Invalid scope object";
+ log.error(msg);
+ throw new BadRequestException(msg);
+ } else {
+ String msg = "Response : " + response.code() + response.body();
+ log.error(msg);
throw new UnexpectedResponseException(msg);
}
} catch (IOException e) {
@@ -269,6 +327,7 @@ public class PublisherRESTAPIServicesImpl implements PublisherRESTAPIServices {
throw new BadRequestException(msg);
} else {
String msg = "Response : " + response.code() + response.body();
+ log.error(msg);
throw new UnexpectedResponseException(msg);
}
} catch (IOException e) {
@@ -308,6 +367,7 @@ public class PublisherRESTAPIServicesImpl implements PublisherRESTAPIServices {
throw new BadRequestException(msg);
} else {
String msg = "Response : " + response.code() + response.body();
+ log.error(msg);
throw new UnexpectedResponseException(msg);
}
} catch (IOException e) {
@@ -447,6 +507,7 @@ public class PublisherRESTAPIServicesImpl implements PublisherRESTAPIServices {
throw new BadRequestException(msg);
} else {
String msg = "Response status : " + response.code() + " Response message : " + response.message();
+ log.error(msg);
throw new UnexpectedResponseException(msg);
}
} catch (IOException e) {
@@ -586,6 +647,7 @@ public class PublisherRESTAPIServicesImpl implements PublisherRESTAPIServices {
throw new BadRequestException(msg);
} else {
String msg = "Response : " + response.code() + response.body();
+ log.error(msg);
throw new UnexpectedResponseException(msg);
}
} catch (IOException e) {
@@ -632,6 +694,7 @@ public class PublisherRESTAPIServicesImpl implements PublisherRESTAPIServices {
throw new BadRequestException(msg);
} else {
String msg = "Response : " + response.code() + response.body();
+ log.error(msg);
throw new UnexpectedResponseException(msg);
}
} catch (IOException e) {
@@ -673,6 +736,7 @@ public class PublisherRESTAPIServicesImpl implements PublisherRESTAPIServices {
throw new BadRequestException(msg);
} else {
String msg = "Response : " + response.code() + response.body();
+ log.error(msg);
throw new UnexpectedResponseException(msg);
}
} catch (IOException e) {
@@ -720,6 +784,7 @@ public class PublisherRESTAPIServicesImpl implements PublisherRESTAPIServices {
throw new BadRequestException(msg);
} else {
String msg = "Response : " + response.code() + response.body();
+ log.error(msg);
throw new UnexpectedResponseException(msg);
}
} catch (IOException e) {
@@ -761,6 +826,7 @@ public class PublisherRESTAPIServicesImpl implements PublisherRESTAPIServices {
throw new BadRequestException(msg);
} else {
String msg = "Response : " + response.code() + response.body();
+ log.error(msg);
throw new UnexpectedResponseException(msg);
}
} catch (IOException e) {
@@ -804,6 +870,7 @@ public class PublisherRESTAPIServicesImpl implements PublisherRESTAPIServices {
throw new BadRequestException(msg);
} else {
String msg = "Response : " + response.code() + response.body();
+ log.error(msg);
throw new UnexpectedResponseException(msg);
}
} catch (IOException e) {
@@ -846,6 +913,7 @@ public class PublisherRESTAPIServicesImpl implements PublisherRESTAPIServices {
throw new BadRequestException(msg);
} else {
String msg = "Response : " + response.code() + response.body();
+ log.error(msg);
throw new UnexpectedResponseException(msg);
}
} catch (IOException e) {
@@ -889,6 +957,7 @@ public class PublisherRESTAPIServicesImpl implements PublisherRESTAPIServices {
throw new BadRequestException(msg);
} else {
String msg = "Response : " + response.code() + response.body();
+ log.error(msg);
throw new UnexpectedResponseException(msg);
}
} catch (IOException e) {
@@ -939,6 +1008,7 @@ public class PublisherRESTAPIServicesImpl implements PublisherRESTAPIServices {
throw new BadRequestException(msg);
} else {
String msg = "Response : " + response.code() + response.body();
+ log.error(msg);
throw new UnexpectedResponseException(msg);
}
} catch (IOException e) {
@@ -990,6 +1060,7 @@ public class PublisherRESTAPIServicesImpl implements PublisherRESTAPIServices {
throw new BadRequestException(msg);
} else {
String msg = "Response : " + response.code() + response.body();
+ log.error(msg);
throw new UnexpectedResponseException(msg);
}
} catch (IOException e) {
@@ -1031,6 +1102,7 @@ public class PublisherRESTAPIServicesImpl implements PublisherRESTAPIServices {
throw new BadRequestException(msg);
} else {
String msg = "Response : " + response.code() + response.body();
+ log.error(msg);
throw new UnexpectedResponseException(msg);
}
} catch (IOException e) {
@@ -1071,6 +1143,7 @@ public class PublisherRESTAPIServicesImpl implements PublisherRESTAPIServices {
throw new BadRequestException(msg);
} else {
String msg = "Response : " + response.code() + response.body();
+ log.error(msg);
throw new UnexpectedResponseException(msg);
}
} catch (IOException e) {
@@ -1111,6 +1184,7 @@ public class PublisherRESTAPIServicesImpl implements PublisherRESTAPIServices {
throw new BadRequestException(msg);
} else {
String msg = "Response : " + response.code() + response.body();
+ log.error(msg);
throw new UnexpectedResponseException(msg);
}
} catch (IOException e) {
@@ -1162,6 +1236,7 @@ public class PublisherRESTAPIServicesImpl implements PublisherRESTAPIServices {
throw new BadRequestException(msg);
} else {
String msg = "Response : " + response.code() + response.body();
+ log.error(msg);
throw new UnexpectedResponseException(msg);
}
} catch (IOException e) {
@@ -1208,6 +1283,7 @@ public class PublisherRESTAPIServicesImpl implements PublisherRESTAPIServices {
throw new BadRequestException(msg);
} else {
String msg = "Response : " + response.code() + response.body();
+ log.error(msg);
throw new UnexpectedResponseException(msg);
}
} catch (IOException e) {
diff --git a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.extension.rest.api/src/main/java/io/entgra/device/mgt/core/apimgt/extension/rest/api/constants/Constants.java b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.extension.rest.api/src/main/java/io/entgra/device/mgt/core/apimgt/extension/rest/api/constants/Constants.java
index 5a577e3eb1..90312d093d 100644
--- a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.extension.rest.api/src/main/java/io/entgra/device/mgt/core/apimgt/extension/rest/api/constants/Constants.java
+++ b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.extension.rest.api/src/main/java/io/entgra/device/mgt/core/apimgt/extension/rest/api/constants/Constants.java
@@ -65,6 +65,10 @@ public final class Constants {
public static final String SCOPE_API_ENDPOINT = "/api/am/publisher/v2/scopes/";
public static final String API_ENDPOINT = "/api/am/publisher/v2/apis/";
public static final String GET_ALL_APIS = "/api/am/publisher/v2/apis?limit=1000";
+ public static final String SCOPE_PUBLISH_RESERVED_USER_NAME = "scope_publish_reserved_user";
+ public static final String SCOPE_PUBLISH_RESERVED_USER_PASSWORD = "&gKfyE8E4rUY4Q";
+ public static final String ADMIN_ROLE_KEY = "admin";
+ public static final String PERM_SCOPE_MAPPING_META_KEY = "perm-scope-mapping";
}
diff --git a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.extension.rest.api/src/main/java/io/entgra/device/mgt/core/apimgt/extension/rest/api/internal/APIManagerServiceDataHolder.java b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.extension.rest.api/src/main/java/io/entgra/device/mgt/core/apimgt/extension/rest/api/internal/APIManagerServiceDataHolder.java
index 2779366b9d..deeeaa2a06 100644
--- a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.extension.rest.api/src/main/java/io/entgra/device/mgt/core/apimgt/extension/rest/api/internal/APIManagerServiceDataHolder.java
+++ b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.extension.rest.api/src/main/java/io/entgra/device/mgt/core/apimgt/extension/rest/api/internal/APIManagerServiceDataHolder.java
@@ -21,12 +21,16 @@ package io.entgra.device.mgt.core.apimgt.extension.rest.api.internal;
import io.entgra.device.mgt.core.apimgt.extension.rest.api.APIApplicationServices;
import io.entgra.device.mgt.core.apimgt.extension.rest.api.PublisherRESTAPIServices;
import org.wso2.carbon.apimgt.impl.APIManagerConfigurationService;
+import org.wso2.carbon.user.core.service.RealmService;
+import org.wso2.carbon.user.core.tenant.TenantManager;
public class APIManagerServiceDataHolder {
private APIApplicationServices apiApplicationServices;
private APIManagerConfigurationService apiManagerConfigurationService;
private PublisherRESTAPIServices publisherRESTAPIServices;
+ private RealmService realmService;
+ private TenantManager tenantManager;
private static APIManagerServiceDataHolder thisInstance = new APIManagerServiceDataHolder();
@@ -63,4 +67,27 @@ public class APIManagerServiceDataHolder {
public void setPublisherRESTAPIServices(PublisherRESTAPIServices publisherRESTAPIServices) {
this.publisherRESTAPIServices = publisherRESTAPIServices;
}
+
+ public RealmService getRealmService() {
+ if (realmService == null) {
+ throw new IllegalStateException("Realm service is not initialized properly");
+ }
+ return realmService;
+ }
+
+ public void setRealmService(RealmService realmService) {
+ this.realmService = realmService;
+ this.setTenantManager(realmService);
+ }
+
+ public TenantManager getTenantManager() {
+ return tenantManager;
+ }
+
+ private void setTenantManager(RealmService realmService) {
+ if (realmService == null) {
+ throw new IllegalStateException("Realm service is not initialized properly");
+ }
+ this.tenantManager = realmService.getTenantManager();
+ }
}
diff --git a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.extension.rest.api/src/main/java/io/entgra/device/mgt/core/apimgt/extension/rest/api/util/APIPublisherUtils.java b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.extension.rest.api/src/main/java/io/entgra/device/mgt/core/apimgt/extension/rest/api/util/APIPublisherUtils.java
new file mode 100644
index 0000000000..6de3f50a32
--- /dev/null
+++ b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.extension.rest.api/src/main/java/io/entgra/device/mgt/core/apimgt/extension/rest/api/util/APIPublisherUtils.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2018 - 2024, 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 io.entgra.device.mgt.core.apimgt.extension.rest.api.util;
+
+import io.entgra.device.mgt.core.apimgt.extension.rest.api.constants.Constants;
+import io.entgra.device.mgt.core.apimgt.extension.rest.api.exceptions.APIServicesException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.wso2.carbon.context.PrivilegedCarbonContext;
+import org.wso2.carbon.user.api.UserStoreException;
+import org.wso2.carbon.user.api.UserStoreManager;
+import org.wso2.carbon.utils.multitenancy.MultitenantConstants;
+import org.wso2.carbon.utils.multitenancy.MultitenantUtils;
+
+/**
+ * This class contains utility methods needed for API publishing
+ */
+public class APIPublisherUtils {
+ private static final Log log = LogFactory.getLog(APIPublisherUtils.class);
+
+ /**
+ * This method will create the temporary user created to publish scopes to the sub tenant space.
+ * @param tenantDomain sub tenant domain from which the user will be created
+ * @throws APIServicesException if the user was unable to be created
+ */
+ public static void createScopePublishUserIfNotExists(String tenantDomain) throws APIServicesException {
+ if (!MultitenantConstants.SUPER_TENANT_DOMAIN_NAME.equals(tenantDomain)) {
+ try {
+ UserStoreManager userStoreManager =
+ PrivilegedCarbonContext.getThreadLocalCarbonContext().getUserRealm().getUserStoreManager();
+ if (!userStoreManager.isExistingUser(MultitenantUtils.getTenantAwareUsername(Constants.SCOPE_PUBLISH_RESERVED_USER_NAME))) {
+ if (log.isDebugEnabled()) {
+ log.debug("Creating scope publish user '" + Constants.SCOPE_PUBLISH_RESERVED_USER_NAME + "' in '" +
+ tenantDomain + "' tenant domain.");
+ }
+ String[] roles = {Constants.ADMIN_ROLE_KEY};
+ userStoreManager.addUser(
+ MultitenantUtils.getTenantAwareUsername(Constants.SCOPE_PUBLISH_RESERVED_USER_NAME),
+ Constants.SCOPE_PUBLISH_RESERVED_USER_PASSWORD,
+ roles,
+ null,
+ ""
+ );
+ }
+ } catch (UserStoreException e) {
+ String msg = "Error occurred while creating scope publishing user in tenant: '" + tenantDomain + "'.";
+ log.error(msg);
+ throw new APIServicesException(msg, e);
+ }
+ }
+ }
+
+ /**
+ * This method will delete the temporary user created to publish scopes to the sub tenant space.
+ * @param tenantDomain sub tenant domain from which the scope publish user will be removed from
+ */
+ public static void removeScopePublishUserIfExists(String tenantDomain) {
+ if (!MultitenantConstants.SUPER_TENANT_DOMAIN_NAME.equals(tenantDomain)) {
+ try {
+ UserStoreManager userStoreManager =
+ PrivilegedCarbonContext.getThreadLocalCarbonContext().getUserRealm().getUserStoreManager();
+ if (userStoreManager.isExistingUser(MultitenantUtils.getTenantAwareUsername(Constants.SCOPE_PUBLISH_RESERVED_USER_NAME))) {
+ if (log.isDebugEnabled()) {
+ log.debug("Deleting scope publish user '" + Constants.SCOPE_PUBLISH_RESERVED_USER_NAME + "' from '" +
+ tenantDomain + "' tenant domain.");
+ }
+ userStoreManager.deleteUser(MultitenantUtils.getTenantAwareUsername(Constants.SCOPE_PUBLISH_RESERVED_USER_NAME));
+ }
+ } catch(UserStoreException e){
+ String msg = "Error occurred while deleting scope publishing user from tenant: '" + tenantDomain + "'.";
+ log.error(msg);
+ }
+ }
+ }
+}
diff --git a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/APIPublisherService.java b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/APIPublisherService.java
index eec6cfcab7..b66d723a1a 100644
--- a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/APIPublisherService.java
+++ b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/APIPublisherService.java
@@ -39,7 +39,7 @@ public interface APIPublisherService {
/**
* Add default scopes defined in the cdm-config.xml
*/
- void addDefaultScopesIfNotExist();
+ void addDefaultScopesIfNotExist() throws APIManagerPublisherException;
/**
* If the permissions are in the permission list, identify the relevant scopes of the supplied permission list
diff --git a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/APIPublisherServiceImpl.java b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/APIPublisherServiceImpl.java
index 8630a8ecb8..e9a58e556c 100644
--- a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/APIPublisherServiceImpl.java
+++ b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/APIPublisherServiceImpl.java
@@ -19,16 +19,23 @@ package io.entgra.device.mgt.core.apimgt.webapp.publisher;
import com.google.gson.Gson;
import io.entgra.device.mgt.core.apimgt.extension.rest.api.APIApplicationServices;
-import io.entgra.device.mgt.core.apimgt.extension.rest.api.APIApplicationServicesImpl;
import io.entgra.device.mgt.core.apimgt.extension.rest.api.PublisherRESTAPIServices;
-import io.entgra.device.mgt.core.apimgt.extension.rest.api.PublisherRESTAPIServicesImpl;
import io.entgra.device.mgt.core.apimgt.extension.rest.api.constants.Constants;
import io.entgra.device.mgt.core.apimgt.extension.rest.api.dto.APIApplicationKey;
-import io.entgra.device.mgt.core.apimgt.extension.rest.api.dto.APIInfo.*;
+import io.entgra.device.mgt.core.apimgt.extension.rest.api.dto.APIInfo.APIInfo;
+import io.entgra.device.mgt.core.apimgt.extension.rest.api.dto.APIInfo.APIRevision;
+import io.entgra.device.mgt.core.apimgt.extension.rest.api.dto.APIInfo.APIRevisionDeployment;
+import io.entgra.device.mgt.core.apimgt.extension.rest.api.dto.APIInfo.CORSConfiguration;
+import io.entgra.device.mgt.core.apimgt.extension.rest.api.dto.APIInfo.Documentation;
+import io.entgra.device.mgt.core.apimgt.extension.rest.api.dto.APIInfo.Mediation;
+import io.entgra.device.mgt.core.apimgt.extension.rest.api.dto.APIInfo.MediationPolicy;
+import io.entgra.device.mgt.core.apimgt.extension.rest.api.dto.APIInfo.Operations;
+import io.entgra.device.mgt.core.apimgt.extension.rest.api.dto.APIInfo.Scope;
import io.entgra.device.mgt.core.apimgt.extension.rest.api.dto.AccessTokenInfo;
import io.entgra.device.mgt.core.apimgt.extension.rest.api.exceptions.APIServicesException;
import io.entgra.device.mgt.core.apimgt.extension.rest.api.exceptions.BadRequestException;
import io.entgra.device.mgt.core.apimgt.extension.rest.api.exceptions.UnexpectedResponseException;
+import io.entgra.device.mgt.core.apimgt.extension.rest.api.util.APIPublisherUtils;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.config.WebappPublisherConfig;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.dto.ApiScope;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.dto.ApiUriTemplate;
@@ -60,6 +67,7 @@ import org.wso2.carbon.user.core.tenant.Tenant;
import org.wso2.carbon.user.core.tenant.TenantSearchResult;
import org.wso2.carbon.utils.CarbonUtils;
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;
+import org.wso2.carbon.utils.multitenancy.MultitenantUtils;
import java.io.BufferedReader;
import java.io.File;
@@ -111,15 +119,6 @@ public class APIPublisherServiceImpl implements APIPublisherService {
PublisherRESTAPIServices publisherRESTAPIServices = APIPublisherDataHolder.getInstance().getPublisherRESTAPIServices();
APIApplicationKey apiApplicationKey;
AccessTokenInfo accessTokenInfo;
- try {
- apiApplicationKey = apiApplicationServices.createAndRetrieveApplicationCredentials();
- accessTokenInfo = apiApplicationServices.generateAccessTokenFromRegisteredApplication(
- apiApplicationKey.getClientId(), apiApplicationKey.getClientSecret());
- } catch (APIServicesException e) {
- String errorMsg = "Error occurred while generating the API application";
- log.error(errorMsg, e);
- throw new APIManagerPublisherException(e);
- }
try {
boolean tenantFound = false;
@@ -154,6 +153,17 @@ public class APIPublisherServiceImpl implements APIPublisherService {
PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername(apiConfig.getOwner());
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
+ try {
+ APIPublisherUtils.createScopePublishUserIfNotExists(tenantDomain);
+ apiApplicationKey = apiApplicationServices.createAndRetrieveApplicationCredentials();
+ accessTokenInfo = apiApplicationServices.generateAccessTokenFromRegisteredApplication(
+ apiApplicationKey.getClientId(), apiApplicationKey.getClientSecret());
+ } catch (APIServicesException e) {
+ String errorMsg = "Error occurred while generating the API application";
+ log.error(errorMsg, e);
+ throw new APIManagerPublisherException(e);
+ }
+
try {
apiConfig.setOwner(APIUtil.getTenantAdminUserName(tenantDomain));
apiConfig.setTenantDomain(tenantDomain);
@@ -428,6 +438,7 @@ public class APIPublisherServiceImpl implements APIPublisherService {
log.error(msg, e);
throw new APIManagerPublisherException(e);
} finally {
+ APIPublisherUtils.removeScopePublishUserIfExists(tenantDomain);
PrivilegedCarbonContext.endTenantFlow();
}
}
@@ -439,37 +450,53 @@ public class APIPublisherServiceImpl implements APIPublisherService {
}
}
- public void addDefaultScopesIfNotExist() {
+ @Override
+ public void addDefaultScopesIfNotExist() throws APIManagerPublisherException {
+ WebappPublisherConfig config = WebappPublisherConfig.getInstance();
+ List tenants = new ArrayList<>(Collections.singletonList(APIConstants.SUPER_TENANT_DOMAIN));
+ tenants.addAll(config.getTenants().getTenant());
+
DeviceManagementConfig deviceManagementConfig = DeviceConfigurationManager.getInstance().getDeviceManagementConfig();
DefaultPermissions defaultPermissions = deviceManagementConfig.getDefaultPermissions();
APIApplicationServices apiApplicationServices = APIPublisherDataHolder.getInstance().getApiApplicationServices();
PublisherRESTAPIServices publisherRESTAPIServices = APIPublisherDataHolder.getInstance().getPublisherRESTAPIServices();
- try {
- APIApplicationKey apiApplicationKey =
- apiApplicationServices.createAndRetrieveApplicationCredentials();
- AccessTokenInfo accessTokenInfo =
- apiApplicationServices.generateAccessTokenFromRegisteredApplication(
- apiApplicationKey.getClientId(), apiApplicationKey.getClientSecret());
+ for (String tenantDomain : tenants) {
+ try {
+ PrivilegedCarbonContext.startTenantFlow();
+ PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain, true);
- Scope scope = new Scope();
- for (DefaultPermission defaultPermission: defaultPermissions.getDefaultPermissions()) {
- if (!publisherRESTAPIServices.isSharedScopeNameExists(apiApplicationKey, accessTokenInfo,
- defaultPermission.getScopeMapping().getKey())) {
- ScopeMapping scopeMapping = defaultPermission.getScopeMapping();
-
- List bindings = new ArrayList<>(
- Arrays.asList(scopeMapping.getDefaultRoles().split(",")));
- bindings.add(ADMIN_ROLE_KEY);
- scope.setName(scopeMapping.getKey());
- scope.setDescription(scopeMapping.getName());
- scope.setDisplayName(scopeMapping.getName());
- scope.setBindings(bindings);
- publisherRESTAPIServices.addNewSharedScope(apiApplicationKey, accessTokenInfo, scope);
+ APIPublisherUtils.createScopePublishUserIfNotExists(tenantDomain);
+ APIApplicationKey apiApplicationKey =
+ apiApplicationServices.createAndRetrieveApplicationCredentials();
+ AccessTokenInfo accessTokenInfo =
+ apiApplicationServices.generateAccessTokenFromRegisteredApplication(
+ apiApplicationKey.getClientId(), apiApplicationKey.getClientSecret());
+
+ Scope scope = new Scope();
+ for (DefaultPermission defaultPermission : defaultPermissions.getDefaultPermissions()) {
+ if (!publisherRESTAPIServices.isSharedScopeNameExists(apiApplicationKey, accessTokenInfo,
+ defaultPermission.getScopeMapping().getKey())) {
+ ScopeMapping scopeMapping = defaultPermission.getScopeMapping();
+
+ List bindings = new ArrayList<>(
+ Arrays.asList(scopeMapping.getDefaultRoles().split(",")));
+ bindings.add(ADMIN_ROLE_KEY);
+ scope.setName(scopeMapping.getKey());
+ scope.setDescription(scopeMapping.getName());
+ scope.setDisplayName(scopeMapping.getName());
+ scope.setBindings(bindings);
+ publisherRESTAPIServices.addNewSharedScope(apiApplicationKey, accessTokenInfo, scope);
+ }
}
+ } catch (BadRequestException | UnexpectedResponseException | APIServicesException e) {
+ String errorMsg = "Error occurred while adding default scopes";
+ log.error(errorMsg, e);
+ throw new APIManagerPublisherException(e);
+ } finally {
+ APIPublisherUtils.removeScopePublishUserIfExists(tenantDomain);
+ PrivilegedCarbonContext.endTenantFlow();
}
- } catch (BadRequestException | UnexpectedResponseException | APIServicesException e) {
- log.error("Error occurred while adding default scopes");
}
}
@@ -487,24 +514,26 @@ public class APIPublisherServiceImpl implements APIPublisherService {
APIApplicationKey apiApplicationKey;
AccessTokenInfo accessTokenInfo;
- try {
- apiApplicationKey = apiApplicationServices.createAndRetrieveApplicationCredentials();
- accessTokenInfo = apiApplicationServices.generateAccessTokenFromRegisteredApplication(
- apiApplicationKey.getClientId(), apiApplicationKey.getClientSecret());
- } catch (APIServicesException e) {
- String errorMsg = "Error occurred while generating the API application";
- log.error(errorMsg, e);
- throw new APIManagerPublisherException(e);
- }
UserStoreManager userStoreManager;
+ String fileName = null;
- try {
- for (String tenantDomain : tenants) {
+ for (String tenantDomain : tenants) {
+ try {
PrivilegedCarbonContext.startTenantFlow();
PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain, true);
+ try {
+ APIPublisherUtils.createScopePublishUserIfNotExists(tenantDomain);
+ apiApplicationKey = apiApplicationServices.createAndRetrieveApplicationCredentials();
+ accessTokenInfo = apiApplicationServices.generateAccessTokenFromRegisteredApplication(
+ apiApplicationKey.getClientId(), apiApplicationKey.getClientSecret());
+ } catch (APIServicesException e) {
+ String errorMsg = "Error occurred while generating the API application";
+ log.error(errorMsg, e);
+ throw new APIManagerPublisherException(e);
+ }
try {
- String fileName =
+ fileName =
CarbonUtils.getCarbonConfigDirPath() + File.separator + "etc"
+ File.separator + tenantDomain + ".csv";
try {
@@ -594,34 +623,39 @@ public class APIPublisherServiceImpl implements APIPublisherService {
}
}
}
- } catch (IOException | DirectoryIteratorException ex) {
- log.error("failed to read scopes from file.", ex);
+ } catch (IOException | DirectoryIteratorException e) {
+ String errorMsg = "Failed to read scopes from file: '" + fileName + "'.";
+ log.error(errorMsg, e);
+ throw new APIManagerPublisherException(e);
}
+ } catch (APIServicesException e) {
+ String errorMsg = "Error while processing Publisher REST API response";
+ log.error(errorMsg, e);
+ throw new APIManagerPublisherException(e);
+ } catch (BadRequestException e) {
+ String errorMsg = "Error while calling Publisher REST APIs";
+ log.error(errorMsg, e);
+ throw new APIManagerPublisherException(e);
+ } catch (UnexpectedResponseException e) {
+ String errorMsg = "Unexpected response from the server";
+ log.error(errorMsg, e);
+ throw new APIManagerPublisherException(e);
+ } finally {
+ APIPublisherUtils.removeScopePublishUserIfExists(tenantDomain);
+ PrivilegedCarbonContext.endTenantFlow();
}
- } catch (APIServicesException e) {
- String errorMsg = "Error while processing Publisher REST API response";
- log.error(errorMsg, e);
- throw new APIManagerPublisherException(e);
- } catch (BadRequestException e) {
- String errorMsg = "Error while calling Publisher REST APIs";
- log.error(errorMsg, e);
- throw new APIManagerPublisherException(e);
- } catch (UnexpectedResponseException e) {
- String errorMsg = "Unexpected response from the server";
- log.error(errorMsg, e);
- throw new APIManagerPublisherException(e);
- } finally {
- PrivilegedCarbonContext.endTenantFlow();
}
}
@Override
public void updateScopeRoleMapping(String roleName, String[] permissions, String[] removedPermissions) throws APIManagerPublisherException {
+ String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();
APIApplicationServices apiApplicationServices = APIPublisherDataHolder.getInstance().getApiApplicationServices();
PublisherRESTAPIServices publisherRESTAPIServices = APIPublisherDataHolder.getInstance().getPublisherRESTAPIServices();
APIApplicationKey apiApplicationKey;
AccessTokenInfo accessTokenInfo;
try {
+ APIPublisherUtils.createScopePublishUserIfNotExists(tenantDomain);
apiApplicationKey = apiApplicationServices.createAndRetrieveApplicationCredentials();
accessTokenInfo = apiApplicationServices.generateAccessTokenFromRegisteredApplication(
apiApplicationKey.getClientId(), apiApplicationKey.getClientSecret());
@@ -662,6 +696,8 @@ public class APIPublisherServiceImpl implements APIPublisherService {
String errorMsg = "Unexpected response from the server";
log.error(errorMsg, e);
throw new APIManagerPublisherException(errorMsg, e);
+ } finally {
+ APIPublisherUtils.removeScopePublishUserIfExists(tenantDomain);
}
}
diff --git a/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/service/impl/UserManagementServiceImpl.java b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/service/impl/UserManagementServiceImpl.java
index bc3a26b88d..34ea482e1b 100644
--- a/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/service/impl/UserManagementServiceImpl.java
+++ b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/service/impl/UserManagementServiceImpl.java
@@ -419,7 +419,8 @@ public class UserManagementServiceImpl implements UserManagementService {
userList = new ArrayList<>(users.size());
BasicUserInfo user;
for (String username : users) {
- if (Constants.APIM_RESERVED_USER.equals(username) || Constants.RESERVED_USER.equals(username)) {
+ if (Constants.APIM_RESERVED_USER.equals(username) || Constants.RESERVED_USER.equals(username) ||
+ Constants.SCOPE_PUBLISH_RESERVED_USER.equals(username)) {
continue;
}
user = getBasicUserInfo(username);
@@ -485,6 +486,7 @@ public class UserManagementServiceImpl implements UserManagementService {
if (commonUsers != null) {
commonUsers.remove(Constants.APIM_RESERVED_USER);
commonUsers.remove(Constants.RESERVED_USER);
+ commonUsers.remove(Constants.SCOPE_PUBLISH_RESERVED_USER);
}
if (!skipSearch(commonUsers) && StringUtils.isNotEmpty(firstName)) {
@@ -660,7 +662,8 @@ public class UserManagementServiceImpl implements UserManagementService {
userList = new ArrayList<>();
UserInfo user;
for (String username : users) {
- if (Constants.APIM_RESERVED_USER.equals(username) || Constants.RESERVED_USER.equals(username)) {
+ if (Constants.APIM_RESERVED_USER.equals(username) || Constants.RESERVED_USER.equals(username) ||
+ Constants.SCOPE_PUBLISH_RESERVED_USER.equals(username)) {
continue;
}
user = new UserInfo();
diff --git a/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/util/Constants.java b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/util/Constants.java
index 75ba2cc322..eb718ea320 100644
--- a/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/util/Constants.java
+++ b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/util/Constants.java
@@ -31,6 +31,7 @@ public class Constants {
public static final String USER_CLAIM_DEVICES = "http://wso2.org/claims/devices";
public static final String PRIMARY_USER_STORE = "PRIMARY";
public static final String APIM_RESERVED_USER = "apim_reserved_user";
+ public static final String SCOPE_PUBLISH_RESERVED_USER = "scope_publish_reserved_user";
public static final String RESERVED_USER = "reserved_user";
public static final String DEFAULT_STREAM_VERSION = "1.0.0";
public static final String SCOPE = "scope";
diff --git a/components/device-mgt/io.entgra.device.mgt.core.device.mgt.core/pom.xml b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.core/pom.xml
index a7ab146a4a..22a3e9c021 100644
--- a/components/device-mgt/io.entgra.device.mgt.core.device.mgt.core/pom.xml
+++ b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.core/pom.xml
@@ -374,6 +374,10 @@
2.3.1.wso2v1
compile
+
+ io.entgra.device.mgt.core
+ io.entgra.device.mgt.core.apimgt.extension.rest.api
+
diff --git a/components/device-mgt/io.entgra.device.mgt.core.device.mgt.core/src/main/java/io/entgra/device/mgt/core/device/mgt/core/internal/DeviceManagementDataHolder.java b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.core/src/main/java/io/entgra/device/mgt/core/device/mgt/core/internal/DeviceManagementDataHolder.java
index 5cdc29da25..6736874487 100644
--- a/components/device-mgt/io.entgra.device.mgt.core.device.mgt.core/src/main/java/io/entgra/device/mgt/core/device/mgt/core/internal/DeviceManagementDataHolder.java
+++ b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.core/src/main/java/io/entgra/device/mgt/core/device/mgt/core/internal/DeviceManagementDataHolder.java
@@ -18,6 +18,8 @@
package io.entgra.device.mgt.core.device.mgt.core.internal;
+import io.entgra.device.mgt.core.apimgt.extension.rest.api.APIApplicationServices;
+import io.entgra.device.mgt.core.apimgt.extension.rest.api.PublisherRESTAPIServices;
import io.entgra.device.mgt.core.device.mgt.common.metadata.mgt.DeviceStatusManagementService;
import io.entgra.device.mgt.core.server.bootup.heartbeat.beacon.service.HeartBeatManagementService;
import org.wso2.carbon.context.PrivilegedCarbonContext;
@@ -93,8 +95,9 @@ public class DeviceManagementDataHolder {
private MetadataManagementService metadataManagementService;
private WhiteLabelManagementService whiteLabelManagementService;
private TraccarManagementService traccarManagementService;
-
private DeviceStatusManagementService deviceStatusManagementService;
+ private APIApplicationServices apiApplicationServices;
+ private PublisherRESTAPIServices publisherRESTAPIServices;
private final Map deviceStatusTaskPluginConfigs = Collections.synchronizedMap(
new HashMap<>());
@@ -410,4 +413,38 @@ public class DeviceManagementDataHolder {
public void setTraccarManagementService(TraccarManagementService traccarManagementService) {
this.traccarManagementService = traccarManagementService;
}
+
+ /**
+ * Retrieves the Dynamic Client Registration REST API Service instance from OSGI service context.
+ * @return {@link APIApplicationServices} Dynamic Client Registration REST API Service
+ */
+ public APIApplicationServices getApiApplicationServices() {
+ PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext();
+ apiApplicationServices = (APIApplicationServices) ctx.getOSGiService(APIApplicationServices.class, null);
+ if (apiApplicationServices == null) {
+ throw new IllegalStateException("Dynamic Client Registration REST API Service was not initialized.");
+ }
+ return apiApplicationServices;
+ }
+
+ public void setApiApplicationServices(APIApplicationServices apiApplicationServices) {
+ this.apiApplicationServices = apiApplicationServices;
+ }
+
+ /**
+ * Retrieves the API Manager Publisher REST API Service instance from OSGI service context.
+ * @return {@link PublisherRESTAPIServices} API Manager Publisher REST API Service
+ */
+ public PublisherRESTAPIServices getPublisherRESTAPIServices() {
+ PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext();
+ publisherRESTAPIServices = (PublisherRESTAPIServices) ctx.getOSGiService(PublisherRESTAPIServices.class, null);
+ if (publisherRESTAPIServices == null) {
+ throw new IllegalStateException("API Manager Publisher REST API Service was not initialized.");
+ }
+ return publisherRESTAPIServices;
+ }
+
+ public void setPublisherRESTAPIServices(PublisherRESTAPIServices publisherRESTAPIServices) {
+ this.publisherRESTAPIServices = publisherRESTAPIServices;
+ }
}
diff --git a/components/device-mgt/io.entgra.device.mgt.core.device.mgt.core/src/main/java/io/entgra/device/mgt/core/device/mgt/core/internal/TenantCreateObserver.java b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.core/src/main/java/io/entgra/device/mgt/core/device/mgt/core/internal/TenantCreateObserver.java
index 9360428b56..eb5fe919d2 100644
--- a/components/device-mgt/io.entgra.device.mgt.core.device.mgt.core/src/main/java/io/entgra/device/mgt/core/device/mgt/core/internal/TenantCreateObserver.java
+++ b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.core/src/main/java/io/entgra/device/mgt/core/device/mgt/core/internal/TenantCreateObserver.java
@@ -17,12 +17,28 @@
*/
package io.entgra.device.mgt.core.device.mgt.core.internal;
+import com.google.gson.Gson;
+import io.entgra.device.mgt.core.apimgt.extension.rest.api.APIApplicationServices;
+import io.entgra.device.mgt.core.apimgt.extension.rest.api.PublisherRESTAPIServices;
+import io.entgra.device.mgt.core.apimgt.extension.rest.api.constants.Constants;
+import io.entgra.device.mgt.core.apimgt.extension.rest.api.dto.APIApplicationKey;
+import io.entgra.device.mgt.core.apimgt.extension.rest.api.dto.APIInfo.Scope;
+import io.entgra.device.mgt.core.apimgt.extension.rest.api.dto.AccessTokenInfo;
+import io.entgra.device.mgt.core.apimgt.extension.rest.api.exceptions.APIServicesException;
+import io.entgra.device.mgt.core.apimgt.extension.rest.api.exceptions.BadRequestException;
+import io.entgra.device.mgt.core.apimgt.extension.rest.api.exceptions.UnexpectedResponseException;
+import io.entgra.device.mgt.core.apimgt.extension.rest.api.util.APIPublisherUtils;
+import io.entgra.device.mgt.core.device.mgt.common.exceptions.MetadataKeyAlreadyExistsException;
+import io.entgra.device.mgt.core.device.mgt.common.exceptions.MetadataManagementException;
+import io.entgra.device.mgt.core.device.mgt.common.metadata.mgt.Metadata;
+import io.entgra.device.mgt.core.device.mgt.common.metadata.mgt.MetadataManagementService;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import io.entgra.device.mgt.core.device.mgt.core.DeviceManagementConstants;
import io.entgra.device.mgt.core.device.mgt.core.DeviceManagementConstants.User;
+import org.wso2.carbon.tenant.mgt.exception.TenantManagementException;
import org.wso2.carbon.user.api.AuthorizationManager;
import org.wso2.carbon.user.api.Permission;
import org.wso2.carbon.user.api.UserRealm;
@@ -30,12 +46,20 @@ import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.user.api.UserStoreManager;
import org.wso2.carbon.utils.AbstractAxis2ConfigurationContextObserver;
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;
+import org.wso2.carbon.utils.multitenancy.MultitenantUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
/**
* Load configuration files to tenant's registry.
*/
public class TenantCreateObserver extends AbstractAxis2ConfigurationContextObserver {
private static final Log log = LogFactory.getLog(TenantCreateObserver.class);
+ private String msg = null;
/**
* Create configuration context.
@@ -82,6 +106,19 @@ public class TenantCreateObserver extends AbstractAxis2ConfigurationContextObser
userStoreManager.updateRoleListOfUser(tenantAdminName, null,
new String[] {DeviceManagementConstants.User.DEFAULT_DEVICE_ADMIN,
DeviceManagementConstants.User.DEFAULT_DEVICE_USER});
+
+ Thread thread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ publishScopesToTenant(tenantDomain);
+ } catch (TenantManagementException e) {
+ log.error("Error occurred while generating API application for the tenant: " + tenantDomain + ".");
+ }
+ }
+ });
+ thread.start();
+
if (log.isDebugEnabled()) {
log.debug("Device management roles: " + User.DEFAULT_DEVICE_USER + ", " + User.DEFAULT_DEVICE_ADMIN +
" created for the tenant:" + tenantDomain + "."
@@ -94,4 +131,308 @@ public class TenantCreateObserver extends AbstractAxis2ConfigurationContextObser
log.error("Error occurred while creating roles for the tenant: " + tenantDomain + ".");
}
}
+
+ /**
+ * This method will create OAuth application under the given tenant domain and generate an access token against the
+ * client credentials. Once this access token is generated it will then be used to retrieve all the scopes that are already
+ * published to that tenant space. The scopes of the super tenant will also be retrieved in order to compare which scopes were added
+ * or removed. (A temporary admin user will be created in the sub tenant space to publish the scopes and will be deleted once
+ * the scope publishing task is done)
+ * @param tenantDomain tenant domain that the scopes will be published to.
+ * @throws TenantManagementException if there are any errors when publishing scopes to a tenant
+ */
+ private void publishScopesToTenant(String tenantDomain) throws TenantManagementException {
+ if (!MultitenantConstants.SUPER_TENANT_DOMAIN_NAME.equals(tenantDomain)) {
+
+ MetadataManagementService metadataManagementService = DeviceManagementDataHolder.getInstance().getMetadataManagementService();
+
+ Map superTenantPermScopeMapping = getPermScopeMapping(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME);
+ Map subTenantPermScopeMapping = getPermScopeMapping(tenantDomain);
+
+ if (superTenantPermScopeMapping == null) {
+ msg = "Error occurred while retrieving meta key '" + Constants.PERM_SCOPE_MAPPING_META_KEY + "' for tenant '" +
+ MultitenantConstants.SUPER_TENANT_DOMAIN_NAME + "'. Hence aborting publishing scopes to tenant: '" +
+ tenantDomain + "'.";
+ log.error(msg);
+ throw new TenantManagementException(msg);
+ }
+ if (superTenantPermScopeMapping.equals(subTenantPermScopeMapping)) {
+ if (log.isDebugEnabled()) {
+ log.debug( "Scopes in '" + tenantDomain + "' are up to date with super tenant scopes.");
+ }
+ return;
+ }
+
+ APIApplicationServices apiApplicationServices = DeviceManagementDataHolder.getInstance().getApiApplicationServices();
+ APIApplicationKey apiApplicationKey;
+ AccessTokenInfo accessTokenInfo;
+
+ try {
+ PrivilegedCarbonContext.startTenantFlow();
+ PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain, true);
+
+ APIPublisherUtils.createScopePublishUserIfNotExists(tenantDomain);
+ apiApplicationKey = apiApplicationServices.createAndRetrieveApplicationCredentials();
+ accessTokenInfo = apiApplicationServices.generateAccessTokenFromRegisteredApplication(
+ apiApplicationKey.getClientId(), apiApplicationKey.getClientSecret());
+ } catch (APIServicesException e) {
+ msg = "Error occurred while generating the API application for tenant: '" + tenantDomain + "'.";
+ log.error(msg, e);
+ throw new TenantManagementException(msg, e);
+ }
+
+ try {
+ PublisherRESTAPIServices publisherRESTAPIServices = DeviceManagementDataHolder.getInstance().getPublisherRESTAPIServices();
+ Scope[] superTenantScopes = getAllScopesFromSuperTenant(apiApplicationServices, publisherRESTAPIServices);
+
+ if (superTenantScopes != null) {
+ if (log.isDebugEnabled()) {
+ log.debug("Number of super tenant scopes already published - " + superTenantScopes.length);
+ }
+
+ Scope[] subTenantScopes = publisherRESTAPIServices.getScopes(apiApplicationKey, accessTokenInfo);
+
+ if (subTenantScopes.length > 0) {
+ // If there is already existing scopes on the sub tenant space then do a comparison with the
+ // super tenant scopes to add those new scopes to sub tenant space or to delete them from
+ // sub tenant space if it is not existing on the super tenant scope list.
+
+ if (log.isDebugEnabled()) {
+ log.debug("Number of sub tenant scopes already published - " + subTenantScopes.length);
+ }
+
+ List missingScopes = new ArrayList<>();
+ List deletedScopes = new ArrayList<>();
+
+ for (Scope superTenantScope : superTenantScopes) {
+ boolean isMatchingScope = false;
+ for (Scope subTenantScope : subTenantScopes) {
+ if (superTenantScope.getName().equals(subTenantScope.getName())) {
+ isMatchingScope = true;
+ break;
+ }
+ }
+ if (!isMatchingScope) {
+ if (log.isDebugEnabled()) {
+ log.debug("Missing scope found in sub tenant space - " +
+ superTenantScope.getName());
+ }
+ missingScopes.add(superTenantScope);
+ }
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("Total number of missing scopes found in sub tenant space - " +
+ missingScopes.size());
+ }
+
+ if (missingScopes.size() > 0) {
+ if (log.isDebugEnabled()) {
+ log.debug("Starting to add new/updated shared scopes to the tenant: '" + tenantDomain + "'.");
+ }
+ publishSharedScopes(missingScopes, publisherRESTAPIServices, apiApplicationKey,
+ accessTokenInfo);
+ }
+
+ for (Scope subTenantScope : subTenantScopes) {
+ boolean isMatchingScope = false;
+ for (Scope superTenantScope : superTenantScopes) {
+ if (superTenantScope.getName().equals(subTenantScope.getName())) {
+ isMatchingScope = true;
+ break;
+ }
+ }
+ if (!isMatchingScope) {
+ if (log.isDebugEnabled()) {
+ log.debug("Deleted scope found in sub tenant space - " +
+ subTenantScope.getName());
+ }
+ deletedScopes.add(subTenantScope);
+ }
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("Total number of deleted scopes found in sub tenant space - " +
+ deletedScopes.size());
+ }
+
+ if (deletedScopes.size() > 0) {
+ if (log.isDebugEnabled()) {
+ log.debug("Starting to delete shared scopes from the tenant: '" + tenantDomain + "'.");
+ }
+ for (Scope deletedScope : deletedScopes) {
+ if (publisherRESTAPIServices.isSharedScopeNameExists(apiApplicationKey, accessTokenInfo,
+ deletedScope.getName())) {
+ Scope scope = createScopeObject(deletedScope);
+ publisherRESTAPIServices.deleteSharedScope(apiApplicationKey, accessTokenInfo, scope);
+ }
+ }
+ }
+
+ if (missingScopes.size() > 0 || deletedScopes.size() > 0) {
+ updatePermScopeMetaData(superTenantPermScopeMapping, metadataManagementService);
+ }
+ } else {
+ if (log.isDebugEnabled()) {
+ log.debug("Starting to publish shared scopes to newly created tenant: '" + tenantDomain + "'.");
+ }
+
+ publishSharedScopes(Arrays.asList(superTenantScopes), publisherRESTAPIServices,
+ apiApplicationKey, accessTokenInfo);
+ updatePermScopeMetaData(superTenantPermScopeMapping, metadataManagementService);
+ }
+ } else {
+ msg = "Unable to publish scopes to sub tenants due to super tenant scopes list being empty.";
+ log.error(msg);
+ throw new TenantManagementException(msg);
+ }
+ } catch (BadRequestException e) {
+ msg = "Invalid request sent when publishing scopes to '" + tenantDomain + "' tenant space.";
+ log.error(msg, e);
+ throw new TenantManagementException(msg, e);
+ } catch (UnexpectedResponseException e) {
+ msg = "Unexpected response received when publishing scopes to '" + tenantDomain + "' tenant space.";
+ log.error(msg, e);
+ throw new TenantManagementException(msg, e);
+ } catch (APIServicesException e) {
+ msg = "Error occurred while publishing scopes to '" + tenantDomain + "' tenant space.";
+ log.error(msg, e);
+ throw new TenantManagementException(msg, e);
+ } catch (MetadataManagementException e) {
+ msg = "Error occurred trying to create metadata entry '" + Constants.PERM_SCOPE_MAPPING_META_KEY + "'.";
+ log.error(msg);
+ throw new TenantManagementException(msg);
+ } catch (MetadataKeyAlreadyExistsException e) {
+ msg = "Error occurred trying to create metadata entry '" + Constants.PERM_SCOPE_MAPPING_META_KEY + "'. The meta key " +
+ "already exists.";
+ log.error(msg);
+ throw new TenantManagementException(msg);
+ } finally {
+ APIPublisherUtils.removeScopePublishUserIfExists(tenantDomain);
+ PrivilegedCarbonContext.endTenantFlow();
+ }
+ }
+ }
+
+ /**
+ * This method will retrieve the value of the permission scope mapping meta key stored in each tenant's metadata
+ * @param tenantDomain the tenant domain that the permission scope mapping meta value retrieved from
+ * @return {@link Map} containing the permission key and the scope value
+ * @throws TenantManagementException if there is an error while retrieving permission scope metadata
+ */
+ private Map getPermScopeMapping(String tenantDomain) throws TenantManagementException {
+ if (log.isDebugEnabled()) {
+ log.debug("Retrieving permission scope mapping from metadata from the tenant: '" + tenantDomain + "'.");
+ }
+ Map permScopeMapping = null;
+ try {
+ PrivilegedCarbonContext.startTenantFlow();
+ PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain, true);
+ MetadataManagementService metadataManagementService = DeviceManagementDataHolder.getInstance().getMetadataManagementService();
+ Metadata metadata = metadataManagementService.retrieveMetadata(Constants.PERM_SCOPE_MAPPING_META_KEY);
+ if (metadata != null) {
+ permScopeMapping = new Gson().fromJson(metadata.getMetaValue().toString(), HashMap.class);
+ }
+ } catch (MetadataManagementException e) {
+ msg = "Error occurred while retrieving permission scope mapping from metadata for tenant: '" + tenantDomain + "'.";
+ log.error(msg, e);
+ throw new TenantManagementException(msg, e);
+ } finally {
+ PrivilegedCarbonContext.endTenantFlow();
+ }
+ return permScopeMapping;
+ }
+
+ /**
+ * This method will create a new metadata entry or update the existing metadata entry in the sub tenant metadata repository which is
+ * taken from the super tenant metadata
+ * @param superTenantPermScopeMapping {@link Map} containing the permission key and the scope value of the super tenant
+ * @param metadataManagementService {@link MetadataManagementService} instance
+ * @throws MetadataManagementException if there is an error while creating or updating the metadata entry
+ * @throws MetadataKeyAlreadyExistsException if the metadata key already exists while trying to create a new metadata entry
+ */
+ private void updatePermScopeMetaData(Map superTenantPermScopeMapping,
+ MetadataManagementService metadataManagementService) throws MetadataManagementException,
+ MetadataKeyAlreadyExistsException {
+
+ Metadata newMetaData = new Metadata();
+ newMetaData.setMetaKey(Constants.PERM_SCOPE_MAPPING_META_KEY);
+ newMetaData.setMetaValue(new Gson().toJson(superTenantPermScopeMapping));
+ if (metadataManagementService.retrieveMetadata(Constants.PERM_SCOPE_MAPPING_META_KEY) == null) {
+ metadataManagementService.createMetadata(newMetaData);
+ } else {
+ metadataManagementService.updateMetadata(newMetaData);
+ }
+ }
+
+ /**
+ * Get all the scopes from the super tenant space
+ * @param apiApplicationServices {@link APIApplicationServices} is used to create an OAuth application and retrieve client ID and secret
+ * @param publisherRESTAPIServices {@link PublisherRESTAPIServices} is used to get all scopes under a given tenant using client credentials
+ * @return array of {@link Scope}
+ * @throws BadRequestException if an invalid request is sent to the API Manager Publisher REST API Service
+ * @throws UnexpectedResponseException if an unexpected response is received from the API Manager Publisher REST API Service
+ * @throws TenantManagementException if an error occurred while processing the request sent to API Manager Publisher REST API Service
+ */
+ private Scope[] getAllScopesFromSuperTenant(APIApplicationServices apiApplicationServices,
+ PublisherRESTAPIServices publisherRESTAPIServices) throws BadRequestException,
+ UnexpectedResponseException, TenantManagementException {
+
+ try {
+ // Get all scopes of super tenant to compare later with the sub tenant scopes. This is done
+ // in order to see if any new scopes were added or deleted
+ PrivilegedCarbonContext.startTenantFlow();
+ PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME, true);
+ APIApplicationKey superTenantApiApplicationKey = apiApplicationServices.createAndRetrieveApplicationCredentials();
+ AccessTokenInfo superTenantAccessToken = apiApplicationServices.generateAccessTokenFromRegisteredApplication(
+ superTenantApiApplicationKey.getClientId(), superTenantApiApplicationKey.getClientSecret());
+ return publisherRESTAPIServices.getScopes(superTenantApiApplicationKey, superTenantAccessToken);
+ } catch (APIServicesException e) {
+ msg = "Error occurred while retrieving access token from super tenant";
+ log.error(msg, e);
+ throw new TenantManagementException(msg, e);
+ } finally {
+ PrivilegedCarbonContext.endTenantFlow();
+ }
+ }
+
+ /**
+ * Add shared scopes to the tenant space.
+ * @param scopeList {@link List} of {@link Scope}
+ * @param publisherRESTAPIServices {@link PublisherRESTAPIServices} is used to add shared scopes to a given tenant using client credentials
+ * @param apiApplicationKey {@link APIApplicationKey} contains client credentials of the OAuth application
+ * @param accessTokenInfo {@link AccessTokenInfo} contains token information generated from the client credentials
+ * @throws BadRequestException if an invalid request is sent to the API Manager Publisher REST API Service
+ * @throws UnexpectedResponseException if an unexpected response is received from the API Manager Publisher REST API Service
+ * @throws APIServicesException if an error occurred while processing the request sent to API Manager Publisher REST API Service
+ */
+ private void publishSharedScopes (List scopeList, PublisherRESTAPIServices publisherRESTAPIServices,
+ APIApplicationKey apiApplicationKey, AccessTokenInfo accessTokenInfo)
+ throws BadRequestException, UnexpectedResponseException, APIServicesException {
+
+ for (Scope tenantScope : scopeList) {
+ if (!publisherRESTAPIServices.isSharedScopeNameExists(apiApplicationKey, accessTokenInfo,
+ tenantScope.getName())) {
+ Scope scope = createScopeObject(tenantScope);
+ publisherRESTAPIServices.addNewSharedScope(apiApplicationKey, accessTokenInfo, scope);
+ }
+ }
+ }
+
+ /**
+ * Creates a new scope object from the passed scope which includes the id, display name, description, name and bindings.
+ * @param tenantScope existing {@link Scope} from a tenant
+ * @return {@link Scope}
+ */
+ private Scope createScopeObject (Scope tenantScope) {
+ Scope scope = new Scope();
+ scope.setId(tenantScope.getId());
+ scope.setDisplayName(tenantScope.getDisplayName());
+ scope.setDescription(tenantScope.getDescription());
+ scope.setName(tenantScope.getName());
+ List bindings = new ArrayList<>();
+ bindings.add(Constants.ADMIN_ROLE_KEY);
+ scope.setBindings(bindings);
+ return scope;
+ }
}
\ No newline at end of file