diff --git a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/pom.xml b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/pom.xml index 876a269e114..f40935568f9 100644 --- a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/pom.xml +++ b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/pom.xml @@ -185,6 +185,8 @@ io.entgra.device.mgt.core.apimgt.webapp.publisher.exception, io.entgra.device.mgt.core.apimgt.webapp.publisher.lifecycle.listener, io.entgra.device.mgt.core.apimgt.webapp.publisher.lifecycle.util, + io.entgra.device.mgt.core.device.mgt.common.exceptions, + io.entgra.device.mgt.core.device.mgt.common.metadata.mgt, org.wso2.carbon.base;version="1.0", org.wso2.carbon.context;version="4.6", org.wso2.carbon;version="4.6", 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 5edf16d076f..94eae16cb83 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 @@ -35,4 +35,9 @@ public interface APIPublisherService { void publishAPI(APIConfig api) throws APIManagerPublisherException; void updateScopeRoleMapping() throws APIManagerPublisherException; + + void addDefaultScopesIfNotExist(); + + void updateScopeRoleMapping(String roleName, String[] permissions) throws APIManagerPublisherException; + } 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 46e7481857e..d78de2c9013 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 @@ -17,6 +17,7 @@ */ package io.entgra.device.mgt.core.apimgt.webapp.publisher; +import io.entgra.device.mgt.core.apimgt.annotations.Scopes; 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; @@ -165,7 +166,7 @@ public class APIPublisherServiceImpl implements APIPublisherService { for (int i = 0; i < apiList.length(); i++) { JSONObject apiObj = apiList.getJSONObject(i); if (apiObj.getString("name").equals(apiIdentifier.getApiName().replace(Constants.SPACE, - Constants.EMPTY_STRING))){ + Constants.EMPTY_STRING))) { apiFound = true; apiIdentifier.setUuid(apiObj.getString("id")); break; @@ -364,7 +365,7 @@ public class APIPublisherServiceImpl implements APIPublisherService { apiUuid, apiRevisionId, apiRevisionDeploymentList); if (CREATED_STATUS.equals(existingAPI.getString("lifeCycleStatus"))) { - publisherRESTAPIServices.changeLifeCycleStatus(apiApplicationKey,accessTokenInfo, + publisherRESTAPIServices.changeLifeCycleStatus(apiApplicationKey, accessTokenInfo, apiUuid, PUBLISH_ACTION); } } @@ -435,6 +436,44 @@ public class APIPublisherServiceImpl implements APIPublisherService { } } + public void addDefaultScopesIfNotExist() { + ArrayList defaultScopes = new ArrayList<>(); + defaultScopes.add("dm:devices:any:permitted"); + defaultScopes.add("dm:device:api:subscribe"); + defaultScopes.add("am:admin:lc:app:approve"); + defaultScopes.add("am:admin:lc:app:create"); + defaultScopes.add("am:admin:lc:app:reject"); + defaultScopes.add("am:admin:lc:app:block"); + defaultScopes.add("am:admin:lc:app:review"); + defaultScopes.add("am:admin:lc:app:retire"); + defaultScopes.add("am:admin:lc:app:deprecate"); + defaultScopes.add("am:admin:lc:app:publish"); + + APIApplicationServices apiApplicationServices = new APIApplicationServicesImpl(); + try { + APIApplicationKey apiApplicationKey = + apiApplicationServices.createAndRetrieveApplicationCredentials(); + AccessTokenInfo accessTokenInfo = + apiApplicationServices.generateAccessTokenFromRegisteredApplication( + apiApplicationKey.getClientId(), apiApplicationKey.getClientSecret()); + + PublisherRESTAPIServices publisherRESTAPIServices = new PublisherRESTAPIServicesImpl(); + + Scope scope = new Scope(); + for (String defaultScope: defaultScopes) { + //todo check whether scope is available or not + scope.setName(defaultScope); + scope.setDescription(defaultScope); + scope.setKey(defaultScope); + scope.setRoles("Internal/devicemgt-user"); + publisherRESTAPIServices.addNewSharedScope(apiApplicationKey, accessTokenInfo, scope); + } + } catch (BadRequestException | UnexpectedResponseException | APIServicesException e) { + log.error("Error occurred while adding default scopes"); + } + } + + @Override public void updateScopeRoleMapping() throws APIManagerPublisherException { @@ -573,8 +612,7 @@ public class APIPublisherServiceImpl implements APIPublisherService { } } - } - catch (APIServicesException e) { + } catch (APIServicesException e) { String errorMsg = "Error while processing Publisher REST API response"; log.error(errorMsg, e); throw new APIManagerPublisherException(e); @@ -586,11 +624,94 @@ public class APIPublisherServiceImpl implements APIPublisherService { String errorMsg = "Unexpected response from the server"; log.error(errorMsg, e); throw new APIManagerPublisherException(e); - }finally { + } finally { PrivilegedCarbonContext.endTenantFlow(); } } + @Override + public void updateScopeRoleMapping(String roleName, String[] permissions) throws APIManagerPublisherException { + APIApplicationServices apiApplicationServices = new APIApplicationServicesImpl(); + 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 { + PublisherRESTAPIServices publisherRESTAPIServices = new PublisherRESTAPIServicesImpl(); + JSONObject scopeObject = publisherRESTAPIServices.getScopes(apiApplicationKey, accessTokenInfo); + + Map permScopeMap = APIPublisherDataHolder.getInstance().getPermScopeMapping(); + for (String permission : permissions) { + String scopeValue = permScopeMap.get(permission); + if (scopeValue == null) { + String msg = "Found invalid permission: " + permission + ". Hence aborting the scope role " + + "mapping process"; + log.error(msg); + throw new APIManagerPublisherException(msg); + } + + JSONArray scopeList = (JSONArray) scopeObject.get("list"); + for (int i = 0; i < scopeList.length(); i++) { + JSONObject scopeObj = scopeList.getJSONObject(i); + if (scopeObj.getString("name").equals(scopeValue)) { + Scope scope = new Scope(); + scope.setName(scopeObj.getString("name")); + scope.setKey(scopeObj.getString("name")); + scope.setDescription(scopeObj.getString("description")); + scope.setId(scopeObj.getString("id")); + + // Including already existing roles + JSONArray existingRolesArray = (JSONArray) scopeObj.get("bindings"); + List existingRoleList = new ArrayList(); + + for (int j = 0; j < existingRolesArray.length(); j++) { + existingRoleList.add((String) existingRolesArray.get(j)); + } + if (!existingRoleList.contains(roleName)) { + existingRoleList.add(roleName); + } + scope.setRoles(String.join(",", existingRoleList)); + + if (publisherRESTAPIServices.isSharedScopeNameExists(apiApplicationKey, accessTokenInfo, scope.getKey())) { + publisherRESTAPIServices.updateSharedScope(apiApplicationKey, accessTokenInfo, scope); + } else { + // todo: come to this level means, that scope is removed from API, but haven't removed from the scope-role-permission-mappings list + log.warn(scope.getKey() + " not available as shared scope"); + } + break; + } + } + } + try { + updatePermissions(roleName, Arrays.asList(permissions)); + } catch (UserStoreException e) { + String errorMsg = "Error occurred when adding permissions to role: " + roleName; + log.error(errorMsg, e); + throw new APIManagerPublisherException(errorMsg, e); + } + } catch (APIServicesException e) { + String errorMsg = "Error while processing Publisher REST API response"; + log.error(errorMsg, e); + throw new APIManagerPublisherException(errorMsg, e); + } catch (BadRequestException e) { + String errorMsg = "Error while calling Publisher REST APIs"; + log.error(errorMsg, e); + throw new APIManagerPublisherException(errorMsg, e); + } catch (UnexpectedResponseException e) { + String errorMsg = "Unexpected response from the server"; + log.error(errorMsg, e); + throw new APIManagerPublisherException(errorMsg, e); + } + } + private void updatePermissions(String role, List permissions) throws UserStoreException { AuthorizationManager authorizationManager = APIPublisherDataHolder.getInstance().getUserRealm() .getAuthorizationManager(); @@ -730,7 +851,7 @@ public class APIPublisherServiceImpl implements APIPublisherService { // if ws endpoint if (config.getEndpointType() != null && "WS".equals(config.getEndpointType())) { - endpointConfig = "{\n" + + endpointConfig = "{\n" + " \"endpoint_type\": \"ws\",\n" + " \"sandbox_endpoints\": {\n" + " \"url\": \"" + config.getEndpoint() + "\"\n" + @@ -778,4 +899,17 @@ public class APIPublisherServiceImpl implements APIPublisherService { return apiInfo; } + + /** + * This method will construct the permission scope mapping hash map. This will call in each API publish call. + * @param scopes API Scopes + */ + private void constructPemScopeMap(Set scopes) { + APIPublisherDataHolder apiPublisherDataHolder = APIPublisherDataHolder.getInstance(); + Map permScopeMap = apiPublisherDataHolder.getPermScopeMapping(); + for (ApiScope scope : scopes) { + permScopeMap.put(scope.getPermissions(), scope.getKey()); + } + apiPublisherDataHolder.setPermScopeMapping(permScopeMap); + } } 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/APIPublisherStartupHandler.java b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/APIPublisherStartupHandler.java index c986c798ce6..244cc99b064 100644 --- a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/APIPublisherStartupHandler.java +++ b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/APIPublisherStartupHandler.java @@ -86,6 +86,7 @@ public class APIPublisherStartupHandler implements ServerStartupObserver { try { publisher.updateScopeRoleMapping(); + publisher.addDefaultScopesIfNotExist(); } catch (APIManagerPublisherException e) { log.error("failed to update scope role mapping.", e); } 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/exception/APIManagerPublisherException.java b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/exception/APIManagerPublisherException.java index 47e28aefb9e..281fdbbf167 100644 --- a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/exception/APIManagerPublisherException.java +++ b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/exception/APIManagerPublisherException.java @@ -28,4 +28,12 @@ public class APIManagerPublisherException extends Exception { public APIManagerPublisherException(Throwable cause) { super(cause); } + + public APIManagerPublisherException(String errorMessage) { + super(errorMessage); + } + + public APIManagerPublisherException(String errorMessage, Throwable cause) { + super(errorMessage, cause); + } } 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/internal/APIPublisherDataHolder.java b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/internal/APIPublisherDataHolder.java index 4b47f8ba0fa..0d67edc7a9b 100644 --- a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/internal/APIPublisherDataHolder.java +++ b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/internal/APIPublisherDataHolder.java @@ -19,6 +19,7 @@ package io.entgra.device.mgt.core.apimgt.webapp.publisher.internal; import io.entgra.device.mgt.core.apimgt.webapp.publisher.APIConfig; import io.entgra.device.mgt.core.apimgt.webapp.publisher.APIPublisherService; +import io.entgra.device.mgt.core.device.mgt.common.metadata.mgt.MetadataManagementService; import org.wso2.carbon.context.CarbonContext; import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.registry.core.service.RegistryService; @@ -29,6 +30,8 @@ import org.wso2.carbon.user.core.service.RealmService; import org.wso2.carbon.user.core.tenant.TenantManager; import org.wso2.carbon.utils.ConfigurationContextService; +import java.util.HashMap; +import java.util.Map; import java.util.Stack; public class APIPublisherDataHolder { @@ -40,6 +43,10 @@ public class APIPublisherDataHolder { private RegistryService registryService; private boolean isServerStarted; private Stack unpublishedApis = new Stack<>(); + private Map permScopeMapping; + + private MetadataManagementService metadataManagementService; + private static APIPublisherDataHolder thisInstance = new APIPublisherDataHolder(); private APIPublisherDataHolder() { @@ -138,4 +145,15 @@ public class APIPublisherDataHolder { this.unpublishedApis = unpublishedApis; } + public Map getPermScopeMapping() {return permScopeMapping;} + + public void setPermScopeMapping(Map permScopeMapping) {this.permScopeMapping = permScopeMapping;} + + public MetadataManagementService getMetadataManagementService() { + return metadataManagementService; + } + + public void setMetadataManagementService(MetadataManagementService metadataManagementService) { + this.metadataManagementService = metadataManagementService; + } } 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/internal/APIPublisherServiceComponent.java b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/internal/APIPublisherServiceComponent.java index 8ab4d924b86..616a1c3987f 100644 --- a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/internal/APIPublisherServiceComponent.java +++ b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/internal/APIPublisherServiceComponent.java @@ -17,6 +17,7 @@ */ package io.entgra.device.mgt.core.apimgt.webapp.publisher.internal; +import io.entgra.device.mgt.core.device.mgt.common.metadata.mgt.MetadataManagementService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.osgi.framework.BundleContext; @@ -29,6 +30,8 @@ import org.wso2.carbon.core.ServerStartupObserver; import org.wso2.carbon.registry.core.service.RegistryService; import org.wso2.carbon.user.core.service.RealmService; +import java.util.HashMap; + /** * @scr.component name="io.entgra.device.mgt.core.apimgt.webapp.publisher" immediate="true" * @scr.reference name="user.realmservice.default" @@ -43,6 +46,12 @@ import org.wso2.carbon.user.core.service.RealmService; * policy="dynamic" * bind="setRegistryService" * unbind="unsetRegistryService" + * @scr.reference name="io.entgra.meta.mgt" + * interface="io.entgra.device.mgt.core.device.mgt.common.metadata.mgt.MetadataManagementService" + * cardinality="0..1" + * policy="dynamic" + * bind="setMetaDataMgtService" + * unbind="unsetMetaDataMgtService" */ public class APIPublisherServiceComponent { @@ -62,6 +71,7 @@ public class APIPublisherServiceComponent { /* Registering declarative service instances exposed by DeviceManagementServiceComponent */ this.registerServices(componentContext); + APIPublisherDataHolder.getInstance().setPermScopeMapping(new HashMap<>()); if (log.isDebugEnabled()) { log.debug("Webapp publisher bundle has been successfully initialized"); @@ -113,4 +123,15 @@ public class APIPublisherServiceComponent { APIPublisherDataHolder.getInstance().setRegistryService(null); } + protected void setMetaDataMgtService(MetadataManagementService metadataManagementService) { + if (metadataManagementService != null && log.isDebugEnabled()) { + log.debug("Meta data mgt mgt service initialized"); + } + APIPublisherDataHolder.getInstance().setMetadataManagementService(metadataManagementService); + } + + protected void unsetMetaDataMgtService(MetadataManagementService metadataManagementService) { + APIPublisherDataHolder.getInstance().setMetadataManagementService(null); + } + } 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/lifecycle/listener/APIPublisherLifecycleListener.java b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/lifecycle/listener/APIPublisherLifecycleListener.java index fcded266889..544b04637ff 100644 --- a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/lifecycle/listener/APIPublisherLifecycleListener.java +++ b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/lifecycle/listener/APIPublisherLifecycleListener.java @@ -17,6 +17,18 @@ */ package io.entgra.device.mgt.core.apimgt.webapp.publisher.lifecycle.listener; +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.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.webapp.publisher.dto.ApiScope; +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.catalina.Lifecycle; import org.apache.catalina.LifecycleEvent; import org.apache.catalina.LifecycleListener; @@ -35,8 +47,7 @@ import org.wso2.carbon.user.api.UserStoreException; import javax.servlet.ServletContext; import java.io.IOException; -import java.util.List; -import java.util.Set; +import java.util.*; @SuppressWarnings("unused") public class APIPublisherLifecycleListener implements LifecycleListener { @@ -49,68 +60,142 @@ public class APIPublisherLifecycleListener implements LifecycleListener { @Override public void lifecycleEvent(LifecycleEvent lifecycleEvent) { - if (Lifecycle.AFTER_START_EVENT.equals(lifecycleEvent.getType()) && WebappPublisherConfig.getInstance() - .isPublished()) { - StandardContext context = (StandardContext) lifecycleEvent.getLifecycle(); - ServletContext servletContext = context.getServletContext(); - String param = servletContext.getInitParameter(PARAM_MANAGED_API_ENABLED); - boolean isManagedApi = (param != null && !param.isEmpty()) && Boolean.parseBoolean(param); + if (Lifecycle.AFTER_START_EVENT.equals(lifecycleEvent.getType()) ) { + if (WebappPublisherConfig.getInstance() + .isPublished()) { + StandardContext context = (StandardContext) lifecycleEvent.getLifecycle(); + ServletContext servletContext = context.getServletContext(); + String param = servletContext.getInitParameter(PARAM_MANAGED_API_ENABLED); + boolean isManagedApi = (param != null && !param.isEmpty()) && Boolean.parseBoolean(param); - String profile = System.getProperty(PROPERTY_PROFILE); - if (WebappPublisherConfig.getInstance().getProfiles().getProfile().contains(profile.toLowerCase()) - && isManagedApi) { - try { - AnnotationProcessor annotationProcessor = new AnnotationProcessor(context); - Set annotatedSwaggerAPIClasses = annotationProcessor. - scanStandardContext(io.swagger.annotations.SwaggerDefinition.class.getName()); - List apiDefinitions = annotationProcessor.extractAPIInfo(servletContext, - annotatedSwaggerAPIClasses); - for (APIResourceConfiguration apiDefinition : apiDefinitions) { - APIConfig apiConfig = APIPublisherUtil.buildApiConfig(servletContext, apiDefinition); - APIPublisherUtil.setResourceAuthTypes(servletContext,apiConfig); - try { - int tenantId = APIPublisherDataHolder.getInstance().getTenantManager(). - getTenantId(apiConfig.getTenantDomain()); + String profile = System.getProperty(PROPERTY_PROFILE); + if (WebappPublisherConfig.getInstance().getProfiles().getProfile().contains(profile.toLowerCase()) + && isManagedApi) { + try { + AnnotationProcessor annotationProcessor = new AnnotationProcessor(context); + Set annotatedSwaggerAPIClasses = annotationProcessor. + scanStandardContext(io.swagger.annotations.SwaggerDefinition.class.getName()); + List apiDefinitions = annotationProcessor.extractAPIInfo(servletContext, + annotatedSwaggerAPIClasses); + + APIPublisherDataHolder apiPublisherDataHolder = APIPublisherDataHolder.getInstance(); + MetadataManagementService metadataManagementService = + apiPublisherDataHolder.getMetadataManagementService(); + Metadata metadata = metadataManagementService.retrieveMetadata("perm-scope-mapping"); + if (metadata != null) { + HashMap permScopeMapping = + new Gson().fromJson(metadata.getMetaValue().toString(), HashMap.class); + apiPublisherDataHolder.setPermScopeMapping(permScopeMapping); + } + + Map permScopeMap = apiPublisherDataHolder.getPermScopeMapping(); + for (APIResourceConfiguration apiDefinition : apiDefinitions) { + APIConfig apiConfig = APIPublisherUtil.buildApiConfig(servletContext, apiDefinition); + for (ApiScope scope : apiConfig.getScopes()) { + permScopeMap.put(scope.getPermissions(), scope.getKey()); + } + APIPublisherUtil.setResourceAuthTypes(servletContext,apiConfig); + try { + int tenantId = APIPublisherDataHolder.getInstance().getTenantManager(). + getTenantId(apiConfig.getTenantDomain()); - boolean isTenantActive = APIPublisherDataHolder.getInstance(). - getTenantManager().isTenantActive(tenantId); - if (isTenantActive) { - boolean isServerStarted = APIPublisherDataHolder.getInstance().isServerStarted(); - if (isServerStarted) { - APIPublisherService apiPublisherService = - APIPublisherDataHolder.getInstance().getApiPublisherService(); - if (apiPublisherService == null) { - throw new IllegalStateException( - "API Publisher service is not initialized properly"); + boolean isTenantActive = APIPublisherDataHolder.getInstance(). + getTenantManager().isTenantActive(tenantId); + if (isTenantActive) { + boolean isServerStarted = APIPublisherDataHolder.getInstance().isServerStarted(); + if (isServerStarted) { + APIPublisherService apiPublisherService = + APIPublisherDataHolder.getInstance().getApiPublisherService(); + if (apiPublisherService == null) { + throw new IllegalStateException( + "API Publisher service is not initialized properly"); + } + apiPublisherService.publishAPI(apiConfig); + } else { + if (log.isDebugEnabled()) { + log.debug("Server has not started yet. Hence adding API '" + + apiConfig.getName() + "' to the queue"); + } + APIPublisherDataHolder.getInstance().getUnpublishedApis().push(apiConfig); } - apiPublisherService.publishAPI(apiConfig); } else { - if (log.isDebugEnabled()) { - log.debug("Server has not started yet. Hence adding API '" + - apiConfig.getName() + "' to the queue"); - } - APIPublisherDataHolder.getInstance().getUnpublishedApis().push(apiConfig); + log.error("No tenant [" + apiConfig.getTenantDomain() + "] " + + "found when publishing the Web app"); } + } catch (Throwable e) { + log.error("Error occurred while publishing API '" + apiConfig.getName() + + "' with the context '" + apiConfig.getContext() + + "' and version '" + apiConfig.getVersion() + "'", e); + } + } + apiPublisherDataHolder.setPermScopeMapping(permScopeMap); + + Map permScopeMapping = apiPublisherDataHolder.getPermScopeMapping(); + if (!permScopeMapping.isEmpty()) { + Metadata existingMetaData = metadataManagementService.retrieveMetadata("perm-scope" + + "-mapping"); + if (existingMetaData != null) { + existingMetaData.setMetaValue(new Gson().toJson(apiPublisherDataHolder.getPermScopeMapping() + )); + metadataManagementService.updateMetadata(existingMetaData); } else { - log.error("No tenant [" + apiConfig.getTenantDomain() + "] " + - "found when publishing the Web app"); + Metadata newMetaData = new Metadata(); + newMetaData.setMetaKey("perm-scope-mapping"); + permScopeMapping = + apiPublisherDataHolder.getPermScopeMapping(); + + //Todo fix this properly with a config + Map defaultScopePermMap = new HashMap<>(); + defaultScopePermMap.put("/permission/admin/device-mgt/devices/any-device/permitted-actions-under-owning-device", "dm:devices:any:permitted"); + defaultScopePermMap.put("/permission/admin/device-mgt/device/api/subscribe", "dm:device:api:subscribe"); + defaultScopePermMap.put("/permission/admin/app-mgt/life-cycle/application/approve", "am:admin:lc:app:approve"); + defaultScopePermMap.put("/permission/admin/app-mgt/life-cycle/application/create", "am:admin:lc:app:create"); + defaultScopePermMap.put("/permission/admin/app-mgt/life-cycle/application/reject", "am:admin:lc:app:reject"); + defaultScopePermMap.put("/permission/admin/app-mgt/life-cycle/application/block", "am:admin:lc:app:block"); + defaultScopePermMap.put("/permission/admin/app-mgt/life-cycle/application/review", "am:admin:lc:app:review"); + defaultScopePermMap.put("/permission/admin/app-mgt/life-cycle/application/retire", "am:admin:lc:app:retire"); + defaultScopePermMap.put("/permission/admin/app-mgt/life-cycle/application/deprecate", "am:admin:lc:app:deprecate"); + defaultScopePermMap.put("/permission/admin/app-mgt/life-cycle/application/publish", "am:admin:lc:app:publish"); + + for (Map.Entry mapElement : defaultScopePermMap.entrySet()) { + String key = mapElement.getKey(); + String value = mapElement.getValue(); + permScopeMapping.put(key,value); + } + apiPublisherDataHolder.setPermScopeMapping(permScopeMapping); + newMetaData.setMetaValue(new Gson().toJson(permScopeMapping)); + metadataManagementService.createMetadata(newMetaData); } - } catch (Throwable e) { - log.error("Error occurred while publishing API '" + apiConfig.getName() + - "' with the context '" + apiConfig.getContext() + - "' and version '" + apiConfig.getVersion() + "'", e); } + } catch (IOException e) { + log.error("Error encountered while discovering annotated classes", e); + } catch (ClassNotFoundException e) { + log.error("Error while scanning class for annotations", e); + } catch (UserStoreException e) { + log.error("Error while retrieving tenant admin user for the tenant domain" + + PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(), e); + } catch (Throwable e) { + // This is done to stop tomcat failure if a webapp failed to publish apis. + log.error("Failed to Publish api from " + servletContext.getContextPath(), e); + } + } + } else { + APIPublisherDataHolder apiPublisherDataHolder = APIPublisherDataHolder.getInstance(); + MetadataManagementService metadataManagementService = + apiPublisherDataHolder.getMetadataManagementService(); + try { + Metadata existingMetaData = metadataManagementService.retrieveMetadata("perm-scope" + + "-mapping"); + if (existingMetaData != null) { + existingMetaData.setMetaValue(new Gson().toJson(apiPublisherDataHolder.getPermScopeMapping() + )); + metadataManagementService.updateMetadata(existingMetaData); + } else { + log.error("Couldn't find 'perm-scope-mapping' Meta entry while API publishing has been turned" + + " off."); } - } catch (IOException e) { - log.error("Error encountered while discovering annotated classes", e); - } catch (ClassNotFoundException e) { - log.error("Error while scanning class for annotations", e); - } catch (UserStoreException e) { - log.error("Error while retrieving tenant admin user for the tenant domain" - + PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(), e); - } catch (Throwable e) { - // This is done to stop tomcat failure if a webapp failed to publish apis. - log.error("Failed to Publish api from " + servletContext.getContextPath(), e); + } catch (MetadataManagementException e) { + log.error("Failed to Load Meta-Mgt data.", e); } } } diff --git a/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/pom.xml b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/pom.xml index b486fc4b4e8..48128f3dd79 100644 --- a/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/pom.xml +++ b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/pom.xml @@ -455,5 +455,10 @@ io.entgra.device.mgt.core.apimgt.analytics.extension provided + + io.entgra.device.mgt.core + io.entgra.device.mgt.core.apimgt.webapp.publisher + provided + \ No newline at end of file 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/RoleManagementServiceImpl.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/RoleManagementServiceImpl.java index 748bcc485c0..036c4f67660 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/RoleManagementServiceImpl.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/RoleManagementServiceImpl.java @@ -17,6 +17,7 @@ */ package io.entgra.device.mgt.core.device.mgt.api.jaxrs.service.impl; +import io.entgra.device.mgt.core.apimgt.webapp.publisher.exception.APIManagerPublisherException; import io.entgra.device.mgt.core.device.mgt.common.exceptions.MetadataManagementException; import io.entgra.device.mgt.core.device.mgt.common.group.mgt.GroupManagementException; import io.entgra.device.mgt.core.device.mgt.common.metadata.mgt.Metadata; @@ -28,6 +29,7 @@ import org.apache.commons.logging.LogFactory; import org.wso2.carbon.CarbonConstants; import org.wso2.carbon.base.MultitenantConstants; import org.wso2.carbon.context.CarbonContext; +import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.context.RegistryType; import io.entgra.device.mgt.core.device.mgt.api.jaxrs.beans.ErrorResponse; import io.entgra.device.mgt.core.device.mgt.api.jaxrs.beans.RoleInfo; @@ -56,6 +58,8 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URLEncoder; import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import static io.entgra.device.mgt.core.device.mgt.api.jaxrs.util.Constants.PRIMARY_USER_STORE; @@ -277,15 +281,32 @@ public class RoleManagementServiceImpl implements RoleManagementService { } } - private UIPermissionNode getAllRolePermissions(String roleName, UserRealm userRealm) throws UserAdminException { - org.wso2.carbon.user.core.UserRealm userRealmCore = null; - if (userRealm instanceof org.wso2.carbon.user.core.UserRealm) { - userRealmCore = (org.wso2.carbon.user.core.UserRealm) userRealm; + private List processAndFilterPermissions(UIPermissionNode[] rolePermissions, List permissionPaths, List permissions) { + + for (UIPermissionNode rolePermission : rolePermissions) { + if (permissionPaths.isEmpty()) { + return permissions; + } + + if (rolePermission.getNodeList().length == 0) { + if (permissionPaths.contains(rolePermission.getResourcePath())) { + permissions.add(rolePermission.getResourcePath()); + } + } + permissionPaths.remove(rolePermission.getResourcePath()); + if (rolePermission.getNodeList().length != 0) { + processAndFilterPermissions(rolePermission.getNodeList(), permissionPaths, permissions); + } } - final UserRealmProxy userRealmProxy = new UserRealmProxy(userRealmCore); - final UIPermissionNode rolePermissions = - userRealmProxy.getRolePermissions(roleName, MultitenantConstants.SUPER_TENANT_ID); - return rolePermissions; + return permissions; + } + + private String[] getPlatformUIPermissions(String roleName, UserRealm userRealm, String[] permissions) + throws UserAdminException { + UIPermissionNode uiPermissionNode = getUIPermissionNode(roleName, userRealm); + List permissionsArray = processAndFilterPermissions(uiPermissionNode.getNodeList(), new ArrayList<>(Arrays.asList(permissions)), + new ArrayList<>()); + return permissionsArray.toArray(new String[0]); } private UIPermissionNode getUIPermissionNode(String roleName, UserRealm userRealm) @@ -361,18 +382,6 @@ public class RoleManagementServiceImpl implements RoleManagementService { } } - private List getAuthorizedPermissions(UIPermissionNode uiPermissionNode, List list) { - for (UIPermissionNode permissionNode : uiPermissionNode.getNodeList()) { - if (permissionNode.isSelected()) { - list.add(permissionNode.getResourcePath()); - } - if (permissionNode.getNodeList() != null && permissionNode.getNodeList().length > 0) { - getAuthorizedPermissions(permissionNode, list); - } - } - return list; - } - @POST @Override public Response addRole(RoleInfo roleInfo) { @@ -383,6 +392,7 @@ public class RoleManagementServiceImpl implements RoleManagementService { if (log.isDebugEnabled()) { log.debug("Persisting the role in the underlying user store"); } + Permission[] permissions = null; if (roleInfo.getPermissions() != null && roleInfo.getPermissions().length > 0) { permissions = new Permission[roleInfo.getPermissions().length]; @@ -392,7 +402,33 @@ public class RoleManagementServiceImpl implements RoleManagementService { } } userStoreManager.addRole(roleInfo.getRoleName(), roleInfo.getUsers(), permissions); - authorizeRoleForAppmgt(roleInfo.getRoleName(), roleInfo.getPermissions()); + try { + if (roleInfo.getPermissions() != null && roleInfo.getPermissions().length > 0) { + String finalRoleName = roleInfo.getRoleName(); + String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(true); + final UserRealm userRealm = DeviceMgtAPIUtils.getUserRealm(); + Thread thread = new Thread(new Runnable() { + @Override + public void run() { + try { + PrivilegedCarbonContext.startTenantFlow(); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain, true); + DeviceMgtAPIUtils.getApiPublisher().updateScopeRoleMapping(roleInfo.getRoleName(), + RoleManagementServiceImpl.this.getPlatformUIPermissions(finalRoleName, userRealm, roleInfo.getPermissions())); + } catch (APIManagerPublisherException | UserAdminException e) { + log.error("Error Occurred while updating role scope mapping. ", e); + } finally { + PrivilegedCarbonContext.endTenantFlow(); + } + } + }); + thread.start(); + } + } catch (UserStoreException e) { + String msg = "Error occurred while loading the user store."; + log.error(msg, e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); + } //TODO fix what's returned in the entity return Response.created(new URI(API_BASE_PATH + "/" + URLEncoder.encode(roleInfo.getRoleName(), "UTF-8"))). @@ -502,11 +538,10 @@ public class RoleManagementServiceImpl implements RoleManagementService { final UserRealm userRealm = DeviceMgtAPIUtils.getUserRealm(); final UserStoreManager userStoreManager = userRealm.getUserStoreManager(); if (!userStoreManager.isExistingRole(roleName)) { - String msg = "No role exists with the name : " + roleName ; + String msg = "No role exists with the name : " + roleName; return Response.status(404).entity(msg).build(); } - final AuthorizationManager authorizationManager = userRealm.getAuthorizationManager(); if (log.isDebugEnabled()) { log.debug("Updating the role to user store"); } @@ -528,31 +563,24 @@ public class RoleManagementServiceImpl implements RoleManagementService { } if (roleInfo.getPermissions() != null) { - // Get all role permissions - final UIPermissionNode rolePermissions = this.getAllRolePermissions(roleName, userRealm); - List permissions = new ArrayList(); - final UIPermissionNode emmRolePermissions = (UIPermissionNode) this.getRolePermissions(roleName); - List emmConsolePermissions = new ArrayList(); - this.getAuthorizedPermissions(emmRolePermissions, emmConsolePermissions); - emmConsolePermissions.removeAll(new ArrayList(Arrays.asList(roleInfo.getPermissions()))); - this.getAuthorizedPermissions(rolePermissions, permissions); - for (String permission : roleInfo.getPermissions()) { - permissions.add(permission); - } - permissions.removeAll(emmConsolePermissions); - String[] allApplicablePerms = new String[permissions.size()]; - allApplicablePerms = permissions.toArray(allApplicablePerms); - roleInfo.setPermissions(allApplicablePerms); - - // Delete all authorizations for the current role before authorizing the permission tree - authorizationManager.clearRoleAuthorization(roleName); - if (roleInfo.getPermissions().length > 0) { - for (int i = 0; i < roleInfo.getPermissions().length; i++) { - String permission = roleInfo.getPermissions()[i]; - authorizationManager.authorizeRole(roleName, permission, CarbonConstants.UI_PERMISSION_ACTION); + String finalRoleName = roleName; + String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(true); + Thread thread = new Thread(new Runnable() { + @Override + public void run() { + try { + PrivilegedCarbonContext.startTenantFlow(); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain, true); + DeviceMgtAPIUtils.getApiPublisher().updateScopeRoleMapping(roleInfo.getRoleName(), + RoleManagementServiceImpl.this.getPlatformUIPermissions(finalRoleName, userRealm, roleInfo.getPermissions())); + } catch (APIManagerPublisherException | UserAdminException e) { + log.error("Error Occurred while updating role scope mapping. ", e); + } finally { + PrivilegedCarbonContext.endTenantFlow(); + } } - } - authorizeRoleForAppmgt(roleName, roleInfo.getPermissions()); + }); + thread.start(); } //TODO: Need to send the updated role information in the entity back to the client return Response.status(Response.Status.OK).entity("Role '" + roleInfo.getRoleName() + "' has " + @@ -569,70 +597,12 @@ public class RoleManagementServiceImpl implements RoleManagementService { String msg = "Role already exists with name : " + role + ". Try with another role name."; log.warn(msg); return Response.status(Response.Status.CONFLICT).entity(msg).build(); - }else{ + } else { String msg = "Error occurred while updating role '" + roleName + "'"; log.error(msg, e); return Response.serverError().entity( new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build(); } - } catch (UserAdminException e) { - String msg = "Error occurred while updating permissions of the role '" + roleName + "'"; - log.error(msg, e); - return Response.serverError().entity( - new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build(); - } - } - - /** - * When presented with role and a set of permissions, if given role has permission to - * perform mobile app management, said role will be given rights mobile app collection in the - * governance registry. - * - * @param role - * @param permissions - * @return state of role update Operation - */ - private boolean authorizeRoleForAppmgt(String role, String[] permissions) { - String permissionString = - "ra^true:rd^false:wa^true:wd^false:da^true:dd^false:aa^true:ad^false"; - String resourcePath = "/_system/governance/mobileapps/"; - boolean appmPermAvailable = false; - - if (permissions != null) { - for (int i = 0; i < permissions.length; i++) - switch (permissions[i]) { - case "/permission/admin/manage/mobileapp": - appmPermAvailable = true; - break; - case "/permission/admin/manage/mobileapp/create": - appmPermAvailable = true; - break; - case "/permission/admin/manage/mobileapp/publish": - appmPermAvailable = true; - break; - } - } - - if (appmPermAvailable) { - try { - Registry registry = CarbonContext.getThreadLocalCarbonContext(). - getRegistry(RegistryType.SYSTEM_GOVERNANCE); - ChangeRolePermissionsUtil.changeRolePermissions((UserRegistry) registry, - resourcePath, role + ":" + permissionString); - - return true; - } catch (Exception e) { - String msg = "Error while retrieving user registry in order to update permissions " - + "for resource : " + resourcePath; - log.error(msg, e); - return false; - } - } else { - if (log.isDebugEnabled()) { - log.debug("Mobile App Management permissions not selected, therefore role : " + - role + " not given permission for registry collection : " + resourcePath); - } - return false; } } 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/DeviceMgtAPIUtils.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/DeviceMgtAPIUtils.java index 2760482a651..1c3be6a7979 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/DeviceMgtAPIUtils.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/DeviceMgtAPIUtils.java @@ -18,6 +18,7 @@ package io.entgra.device.mgt.core.device.mgt.api.jaxrs.util; +import io.entgra.device.mgt.core.apimgt.webapp.publisher.APIPublisherService; import io.entgra.device.mgt.core.application.mgt.common.services.ApplicationManager; import io.entgra.device.mgt.core.application.mgt.common.services.SubscriptionManager; import org.apache.axis2.AxisFault; @@ -157,6 +158,8 @@ public class DeviceMgtAPIUtils { private static volatile SubscriptionManager subscriptionManager; private static volatile ApplicationManager applicationManager; + private static volatile APIPublisherService apiPublisher; + static { String keyStorePassword = ServerConfiguration.getInstance().getFirstProperty("Security.KeyStore.Password"); String trustStorePassword = ServerConfiguration.getInstance().getFirstProperty( @@ -235,6 +238,24 @@ public class DeviceMgtAPIUtils { return applicationManager; } + public static APIPublisherService getApiPublisher() { + if (apiPublisher == null) { + synchronized (DeviceMgtAPIUtils.class) { + if (apiPublisher == null) { + PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + apiPublisher = + (APIPublisherService) ctx.getOSGiService(APIPublisherService.class, null); + if (apiPublisher == null) { + String msg = "Application Manager service has not initialized."; + log.error(msg); + throw new IllegalStateException(msg); + } + } + } + } + return apiPublisher; + } + public static void scheduleTaskService(int notifierFrequency) { TaskScheduleService taskScheduleService; try { diff --git a/features/device-mgt/io.entgra.device.mgt.core.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/h2.sql b/features/device-mgt/io.entgra.device.mgt.core.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/h2.sql index f261a3b49e5..4d87dbdcca1 100644 --- a/features/device-mgt/io.entgra.device.mgt.core.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/h2.sql +++ b/features/device-mgt/io.entgra.device.mgt.core.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/h2.sql @@ -594,7 +594,7 @@ CREATE TABLE IF NOT EXISTS DM_METADATA ( METADATA_ID INT AUTO_INCREMENT NOT NULL, DATA_TYPE VARCHAR(16) NOT NULL, METADATA_KEY VARCHAR(128) NOT NULL, - METADATA_VALUE VARCHAR(8000) NOT NULL, + METADATA_VALUE VARCHAR(20000) NOT NULL, TENANT_ID INTEGER NOT NULL, PRIMARY KEY (METADATA_ID), CONSTRAINT METADATA_KEY_TENANT_ID UNIQUE (METADATA_KEY, TENANT_ID)