From 1fde569f4a6959114b1fbce79bc171ec50e3440c Mon Sep 17 00:00:00 2001 From: navodzoysa Date: Wed, 3 Jan 2024 21:06:31 +0530 Subject: [PATCH] Add scope publishing to sub tenants --- .../pom.xml | 2 +- .../rest/api/APIApplicationServices.java | 1 - .../rest/api/APIApplicationServicesImpl.java | 182 +-------- .../rest/api/PublisherRESTAPIServices.java | 3 + .../api/PublisherRESTAPIServicesImpl.java | 76 ++++ .../rest/api/constants/Constants.java | 6 +- .../rest/api/util/APIPublisherUtils.java | 91 +++++ .../webapp/publisher/APIPublisherService.java | 2 +- .../publisher/APIPublisherServiceImpl.java | 149 ++++--- .../impl/UserManagementServiceImpl.java | 7 +- .../device/mgt/api/jaxrs/util/Constants.java | 1 + .../internal/DeviceManagementDataHolder.java | 39 +- .../core/internal/TenantCreateObserver.java | 375 ++++++++++++++---- 13 files changed, 635 insertions(+), 299 deletions(-) create mode 100644 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 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 f850b4c50d..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 @@ -162,4 +162,4 @@ - + \ No newline at end of file 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/APIApplicationServices.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/APIApplicationServices.java index b47edc6c21..d628c4ce25 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/APIApplicationServices.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/APIApplicationServices.java @@ -25,7 +25,6 @@ import io.entgra.device.mgt.core.apimgt.extension.rest.api.exceptions.APIService public interface APIApplicationServices { APIApplicationKey createAndRetrieveApplicationCredentials() throws APIServicesException; - void createAndRetrieveApplicationCredentialsAndGenerateToken() throws APIServicesException; AccessTokenInfo generateAccessTokenFromRegisteredApplication(String clientId, String clientSecret) throws APIServicesException; 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 4d559b1a70..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 @@ -19,10 +19,6 @@ package io.entgra.device.mgt.core.apimgt.extension.rest.api; import com.google.gson.Gson; -import io.entgra.device.mgt.core.apimgt.extension.rest.api.dto.APIInfo.Scope; -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.internal.APIManagerServiceDataHolder; import org.json.JSONObject; import io.entgra.device.mgt.core.apimgt.extension.rest.api.util.HttpsTrustManagerUtils; import io.entgra.device.mgt.core.apimgt.extension.rest.api.dto.APIApplicationKey; @@ -37,19 +33,12 @@ 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 org.wso2.carbon.user.api.UserRealm; -import org.wso2.carbon.user.api.UserStoreException; -import org.wso2.carbon.user.api.UserStoreManager; -import org.wso2.carbon.utils.multitenancy.MultitenantUtils; import java.io.IOException; -import java.security.SecureRandom; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; public class APIApplicationServicesImpl implements APIApplicationServices { @@ -62,38 +51,13 @@ public class APIApplicationServicesImpl implements APIApplicationServices { getAPIManagerConfigurationService().getAPIManagerConfiguration(); @Override - public APIApplicationKey createAndRetrieveApplicationCredentials() - throws APIServicesException { - - log.error("=====createAndRetrieveApplicationCredentials=====1"); - - String serverUser = null; - String serverPassword = null; - try { - log.error("=====createAndRetrieveApplicationCredentials=====2"); - UserRealm userRealm = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUserRealm(); - String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); - UserStoreManager userStoreManager = userRealm.getUserStoreManager(); - - createUserIfNotExists(Constants.RESERVED_USER_NAME, Constants.RESERVED_USER_PASSWORD, userStoreManager); - - if(tenantDomain.equals("carbon.super")) { - log.error("=====createAndRetrieveApplicationCredentials=====3"); - serverUser = config.getFirstProperty(Constants.SERVER_USER); - serverPassword = config.getFirstProperty(Constants.SERVER_PASSWORD); - } else { - log.error("=====createAndRetrieveApplicationCredentials=====4"); - serverUser = Constants.RESERVED_USER_NAME + "@" + tenantDomain; - serverPassword = Constants.RESERVED_USER_PASSWORD; - } - } catch (UserStoreException e) { - throw new RuntimeException(e); - } + 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); - log.error("=====createAndRetrieveApplicationCredentials=====5"); - JSONObject jsonObject = new JSONObject(); jsonObject.put("callbackUrl", Constants.EMPTY_STRING); jsonObject.put("clientName", Constants.CLIENT_NAME); @@ -101,8 +65,6 @@ public class APIApplicationServicesImpl implements APIApplicationServices { jsonObject.put("owner", serverUser); jsonObject.put("saasApp", true); - log.error("=====createAndRetrieveApplicationCredentials=====6"); - RequestBody requestBody = RequestBody.Companion.create(jsonObject.toString(), JSON); Request request = new Request.Builder() .url(applicationEndpoint) @@ -110,12 +72,8 @@ public class APIApplicationServicesImpl implements APIApplicationServices { .post(requestBody) .build(); - log.error("=====createAndRetrieveApplicationCredentials=====7"); - try { - log.error("=====createAndRetrieveApplicationCredentials=====8"); try (Response response = client.newCall(request).execute()) { - log.error("=====createAndRetrieveApplicationCredentials=====9"); return gson.fromJson(response.body().string(), APIApplicationKey.class); } } catch (IOException e) { @@ -125,95 +83,13 @@ public class APIApplicationServicesImpl implements APIApplicationServices { } } - @Override - public void createAndRetrieveApplicationCredentialsAndGenerateToken() - throws APIServicesException { - - log.error("=====createAndRetrieveApplicationCredentials=====1"); - - String serverUser = null; - String serverPassword = null; - try { - log.error("=====createAndRetrieveApplicationCredentials=====2"); - UserRealm userRealm = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUserRealm(); - String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); - UserStoreManager userStoreManager = userRealm.getUserStoreManager(); - - createUserIfNotExists(Constants.RESERVED_USER_NAME, Constants.RESERVED_USER_PASSWORD, userStoreManager); - - if(tenantDomain.equals("carbon.super")) { - log.error("=====createAndRetrieveApplicationCredentials=====3"); - serverUser = config.getFirstProperty(Constants.SERVER_USER); - serverPassword = config.getFirstProperty(Constants.SERVER_PASSWORD); - } else { - log.error("=====createAndRetrieveApplicationCredentials=====4"); - serverUser = Constants.RESERVED_USER_NAME + "@" + tenantDomain; - serverPassword = Constants.RESERVED_USER_PASSWORD; - } - } catch (UserStoreException e) { - throw new RuntimeException(e); - } - - String applicationEndpoint = config.getFirstProperty(Constants.DCR_END_POINT); - - log.error("=====createAndRetrieveApplicationCredentials=====5"); - - JSONObject jsonObject = new JSONObject(); - jsonObject.put("callbackUrl", Constants.EMPTY_STRING); - jsonObject.put("clientName", Constants.CLIENT_NAME); - jsonObject.put("grantType", Constants.GRANT_TYPE); - jsonObject.put("owner", serverUser); - jsonObject.put("saasApp", true); - - log.error("=====createAndRetrieveApplicationCredentials=====6"); - - RequestBody requestBody = RequestBody.Companion.create(jsonObject.toString(), JSON); - Request request = new Request.Builder() - .url(applicationEndpoint) - .addHeader(Constants.AUTHORIZATION_HEADER_NAME, Credentials.basic(serverUser, serverPassword)) - .post(requestBody) - .build(); - - log.error("=====createAndRetrieveApplicationCredentials=====7"); - - try { - log.error("=====createAndRetrieveApplicationCredentials=====8"); - try (Response response = client.newCall(request).execute()) { - log.error("=====createAndRetrieveApplicationCredentials=====9"); - APIApplicationKey apiApplicationKey = gson.fromJson(response.body().string(), APIApplicationKey.class); - AccessTokenInfo accessTokenInfo = generateAccessTokenFromRegisteredApplication( - apiApplicationKey.getClientId(), apiApplicationKey.getClientSecret()); - - PublisherRESTAPIServices publisherRESTAPIServices = new PublisherRESTAPIServicesImpl(); - - Scope[] scopes = publisherRESTAPIServices.getScopes(apiApplicationKey, accessTokenInfo); - - } catch (BadRequestException e) { - throw new RuntimeException(e); - } catch (UnexpectedResponseException e) { - throw new RuntimeException(e); - } - } catch (IOException e) { - msg = "Error occurred while processing the response"; - log.error(msg, e); - throw new APIServicesException(e); - } - } - @Override public AccessTokenInfo generateAccessTokenFromRegisteredApplication(String consumerKey, String consumerSecret) throws APIServicesException { String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); - String userName = null; - String userPassword = null; - if(tenantDomain.equals("carbon.super")) { - userName = config.getFirstProperty(Constants.SERVER_USER); - userPassword = config.getFirstProperty(Constants.SERVER_PASSWORD); - } else { - userName = "shamalka@shamalka.com"; - userPassword = "admin"; - } + String userName = getScopePublishUserName(tenantDomain); + String userPassword = getScopePublishUserPassword(tenantDomain); JSONObject params = new JSONObject(); params.put(Constants.GRANT_TYPE_PARAM_NAME, Constants.PASSWORD_GRANT_TYPE); @@ -224,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); @@ -256,38 +131,19 @@ public class APIApplicationServicesImpl implements APIApplicationServices { } } - private void createUserIfNotExists(String username, String password, UserStoreManager userStoreManager) { - - try { - if (!userStoreManager.isExistingUser(MultitenantUtils.getTenantAwareUsername(username))) { - String[] roles = {"admin"}; - userStoreManager.addUser(MultitenantUtils.getTenantAwareUsername(username), password, roles, null, ""); - -// userStoreManager.updateCredential(MultitenantUtils.getTenantAwareUsername(username), "reservedpwd", password); - } - } catch (UserStoreException e) { - String msg = "Error when trying to fetch tenant details"; - log.error(msg); + 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 generateInitialUserPassword() { - int passwordLength = 6; - //defining the pool of characters to be used for initial password generation - String lowerCaseCharset = "abcdefghijklmnopqrstuvwxyz"; - String upperCaseCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - String numericCharset = "0123456789"; - SecureRandom randomGenerator = new SecureRandom(); - String totalCharset = lowerCaseCharset + upperCaseCharset + numericCharset; - int totalCharsetLength = totalCharset.length(); - StringBuilder initialUserPassword = new StringBuilder(); - for (int i = 0; i < passwordLength; i++) { - initialUserPassword.append( - totalCharset.charAt(randomGenerator.nextInt(totalCharsetLength))); - } - if (log.isDebugEnabled()) { - log.debug("Initial user password is created for new user: " + initialUserPassword); + 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; } - return initialUserPassword.toString(); } } 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 72c28f709f..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,8 +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 RESERVED_USER_NAME = "test_reserved_user"; - public static final String RESERVED_USER_PASSWORD = "reserved_user"; + 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/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 3a39d9d33d..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; @@ -109,9 +117,8 @@ public class APIPublisherServiceImpl implements APIPublisherService { APIApplicationServices apiApplicationServices = APIPublisherDataHolder.getInstance().getApiApplicationServices(); PublisherRESTAPIServices publisherRESTAPIServices = APIPublisherDataHolder.getInstance().getPublisherRESTAPIServices(); - APIApplicationKey apiApplicationKey = null; - AccessTokenInfo accessTokenInfo = null; - + APIApplicationKey apiApplicationKey; + AccessTokenInfo accessTokenInfo; try { boolean tenantFound = false; @@ -143,11 +150,11 @@ public class APIPublisherServiceImpl implements APIPublisherService { } if (tenantFound) { - PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername(apiConfig.getOwner()); int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); try { + APIPublisherUtils.createScopePublishUserIfNotExists(tenantDomain); apiApplicationKey = apiApplicationServices.createAndRetrieveApplicationCredentials(); accessTokenInfo = apiApplicationServices.generateAccessTokenFromRegisteredApplication( apiApplicationKey.getClientId(), apiApplicationKey.getClientSecret()); @@ -431,6 +438,7 @@ public class APIPublisherServiceImpl implements APIPublisherService { log.error(msg, e); throw new APIManagerPublisherException(e); } finally { + APIPublisherUtils.removeScopePublishUserIfExists(tenantDomain); PrivilegedCarbonContext.endTenantFlow(); } } @@ -442,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); + + 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); + 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"); } } @@ -490,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 { @@ -597,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()); @@ -665,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/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 395d9ceda6..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,23 +17,27 @@ */ 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.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.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.stratos.common.exception.TenantManagementClientException; import org.wso2.carbon.tenant.mgt.exception.TenantManagementException; import org.wso2.carbon.user.api.AuthorizationManager; import org.wso2.carbon.user.api.Permission; @@ -44,19 +48,18 @@ import org.wso2.carbon.utils.AbstractAxis2ConfigurationContextObserver; import org.wso2.carbon.utils.multitenancy.MultitenantConstants; import org.wso2.carbon.utils.multitenancy.MultitenantUtils; -import java.security.SecureRandom; -import java.util.Stack; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +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 final ExecutorService executor = Executors.newSingleThreadExecutor(); - - + private String msg = null; /** * Create configuration context. @@ -104,32 +107,18 @@ public class TenantCreateObserver extends AbstractAxis2ConfigurationContextObser new String[] {DeviceManagementConstants.User.DEFAULT_DEVICE_ADMIN, DeviceManagementConstants.User.DEFAULT_DEVICE_USER}); -// String password = this.generateInitialUserPassword(); - -// createUserIfNotExists("test_reserved_user", password, userStoreManager); - Thread thread = new Thread(new Runnable() { @Override public void run() { try { - createApplication(tenantDomain); + publishScopesToTenant(tenantDomain); } catch (TenantManagementException e) { - throw new RuntimeException(e); + log.error("Error occurred while generating API application for the tenant: " + tenantDomain + "."); } } }); thread.start(); - -// executor.submit(() -> { -// try { -// createApplication(); -// } catch (TenantManagementException e) { -// throw new RuntimeException(e); -// } -// }); - - if (log.isDebugEnabled()) { log.debug("Device management roles: " + User.DEFAULT_DEVICE_USER + ", " + User.DEFAULT_DEVICE_ADMIN + " created for the tenant:" + tenantDomain + "." @@ -143,61 +132,307 @@ public class TenantCreateObserver extends AbstractAxis2ConfigurationContextObser } } + /** + * 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(); - private void createApplication(String tenantDomain) throws TenantManagementException { - PrivilegedCarbonContext.startTenantFlow(); - PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain, true); + Map superTenantPermScopeMapping = getPermScopeMapping(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME); + Map subTenantPermScopeMapping = getPermScopeMapping(tenantDomain); - PublisherRESTAPIServices publisherRESTAPIServices = new PublisherRESTAPIServicesImpl(); - APIApplicationServices apiApplicationServices = new APIApplicationServicesImpl(); - APIApplicationKey apiApplicationKey = null; - AccessTokenInfo accessTokenInfo = null; - try { - apiApplicationServices.createAndRetrieveApplicationCredentialsAndGenerateToken(); -// log.error("apiApplicationKey: " + apiApplicationKey.getClientId()); -// log.error("apiApplicationKey: " + apiApplicationKey.getClientSecret()); -// 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 TenantManagementException(errorMsg, e); + 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(); + } } -// Scope[] scopes = publisherRESTAPIServices.getScopes(apiApplicationKey, accessTokenInfo); } - private void createUserIfNotExists(String username, String password, UserStoreManager userStoreManager) { + /** + * 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 { - if (!userStoreManager.isExistingUser(MultitenantUtils.getTenantAwareUsername(username))) { - String[] roles = {"admin"}; - userStoreManager.addUser(MultitenantUtils.getTenantAwareUsername(username), password, roles, null, ""); - - userStoreManager.updateCredential(MultitenantUtils.getTenantAwareUsername(username), "reservedpwd", password); + 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 (UserStoreException e) { - String msg = "Error when trying to fetch tenant details"; - log.error(msg); + } 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; } - private String generateInitialUserPassword() { - int passwordLength = 6; - //defining the pool of characters to be used for initial password generation - String lowerCaseCharset = "abcdefghijklmnopqrstuvwxyz"; - String upperCaseCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - String numericCharset = "0123456789"; - SecureRandom randomGenerator = new SecureRandom(); - String totalCharset = lowerCaseCharset + upperCaseCharset + numericCharset; - int totalCharsetLength = totalCharset.length(); - StringBuilder initialUserPassword = new StringBuilder(); - for (int i = 0; i < passwordLength; i++) { - initialUserPassword.append( - totalCharset.charAt(randomGenerator.nextInt(totalCharsetLength))); + /** + * 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); } - if (log.isDebugEnabled()) { - log.debug("Initial user password is created for new user: " + initialUserPassword); + } + + /** + * 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); + } } - return initialUserPassword.toString(); } -} + /** + * 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