WIP: Add scope and permission updating process related improvements #538

Draft
rajitha wants to merge 1 commits from rajitha/device-mgt-core:permission-scope-improvements into master

@ -36,11 +36,16 @@ import io.entgra.device.mgt.core.apimgt.extension.rest.api.exceptions.APIService
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.util.SelfSyncingScopeTree;
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;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.exception.APIManagerPublisherException;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.internal.APIPublisherDataHolder;
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 io.entgra.device.mgt.core.device.mgt.common.permission.mgt.PermissionManagementException;
import io.entgra.device.mgt.core.device.mgt.core.config.DeviceConfigurationManager;
import io.entgra.device.mgt.core.device.mgt.core.config.DeviceManagementConfig;
import io.entgra.device.mgt.core.device.mgt.core.config.permission.DefaultPermission;
@ -68,7 +73,6 @@ 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 io.entgra.device.mgt.core.device.mgt.common.permission.mgt.PermissionManagementException;
import java.io.BufferedReader;
import java.io.File;
@ -82,9 +86,11 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
@ -570,8 +576,10 @@ public class APIPublisherServiceImpl implements APIPublisherService {
scope.setName(
scopeMapping[2] != null ? StringUtils.trim(scopeMapping[2]) : StringUtils.EMPTY);
// scope.setPermissions(
// scopeMapping[3] != null ? StringUtils.trim(scopeMapping[3]) : StringUtils.EMPTY);
String permission = scopeMapping[3] != null ? StringUtils.trim(scopeMapping[3]) : StringUtils.EMPTY;
// scopeMapping[3] != null ? StringUtils.trim
// (scopeMapping[3]) : StringUtils.EMPTY);
String permission = scopeMapping[3] != null ? StringUtils.trim(scopeMapping[3]) :
StringUtils.EMPTY;
List<String> rolesList = new ArrayList<>();
for (int i = 4; i < scopeMapping.length; i++) {
@ -586,7 +594,7 @@ public class APIPublisherServiceImpl implements APIPublisherService {
Scope[] scopes = publisherRESTAPIServices.getScopes(apiApplicationKey, accessTokenInfo);
for (int i = 0; i < scopes.length; i++) {
Scope relatedScope = scopes[i];
if (relatedScope.getName().equals(scopeMapping[2].toString())) {
if (relatedScope.getName().equals(scopeMapping[2])) {
scope.setId(relatedScope.getId());
scope.setUsageCount(relatedScope.getUsageCount());
//Including already existing roles
@ -595,7 +603,8 @@ public class APIPublisherServiceImpl implements APIPublisherService {
}
scope.setBindings(rolesList);
if (publisherRESTAPIServices.isSharedScopeNameExists(apiApplicationKey, accessTokenInfo, scope.getName())) {
if (publisherRESTAPIServices.isSharedScopeNameExists(apiApplicationKey, accessTokenInfo,
scope.getName())) {
publisherRESTAPIServices.updateSharedScope(apiApplicationKey, accessTokenInfo, scope);
// todo: permission changed in update path, is not handled yet.
} else {
@ -642,11 +651,94 @@ public class APIPublisherServiceImpl implements APIPublisherService {
}
}
synchronized private SelfSyncingScopeTree getSyncTree(APIApplicationKey apiApplicationKey,
AccessTokenInfo accessTokenInfo)
throws APIManagerPublisherException {
String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();
SelfSyncingScopeTree selfSyncingScopeTree =
APIPublisherDataHolder.getInstance().getSelfSyncTrees().get(tenantDomain);
try {
if (selfSyncingScopeTree == null) {
Metadata metadata;
try {
PrivilegedCarbonContext.startTenantFlow();
PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(MultitenantConstants
.SUPER_TENANT_DOMAIN_NAME, true);
MetadataManagementService metadataManagementService =
APIPublisherDataHolder.getInstance().getMetadataManagementService();
metadata = metadataManagementService.retrieveMetadata(Constants.PERM_SCOPE_MAPPING_META_KEY);
} finally {
PrivilegedCarbonContext.endTenantFlow();
}
if (metadata == null) {
String msg = "Metadata registry service doesn't contains the required metadata for " +
Constants.PERM_SCOPE_MAPPING_META_KEY;
log.error(msg);
throw new APIManagerPublisherException(msg);
}
// interchange the keys and values (this has to be done form the bringing
Map<String, String> permScopeMap = gson.fromJson(metadata.getMetaValue(), HashMap.class);
Map<String, String> scopePermMap = new HashMap<>();
for (String permission: permScopeMap.keySet()) {
scopePermMap.put(permScopeMap.get(permission), permission);
}
PublisherRESTAPIServices publisherRESTAPIServices =
APIPublisherDataHolder.getInstance().getPublisherRESTAPIServices();
Scope[] scopes = publisherRESTAPIServices.getScopes(apiApplicationKey, accessTokenInfo);
// o(n)
for (Scope scope : scopes) {
if (selfSyncingScopeTree == null) {
selfSyncingScopeTree = new SelfSyncingScopeTree(scope, scopePermMap.get(scope.getName()),
apiApplicationKey,
accessTokenInfo);
} else {
selfSyncingScopeTree.add(scope, scopePermMap.get(scope.getName()));
}
}
APIPublisherDataHolder.getInstance().getSelfSyncTrees().put(tenantDomain, selfSyncingScopeTree);
}
return selfSyncingScopeTree;
} catch (MetadataManagementException e) {
String msg = "Error encountered while retrieving metadata for " + Constants.PERM_SCOPE_MAPPING_META_KEY;
log.error(msg);
throw new APIManagerPublisherException(msg, e);
} catch (APIManagerPublisherException | BadRequestException
| UnexpectedResponseException | APIServicesException e) {
String msg = "Error encountered while invoking publisher rest services";
log.error(msg);
throw new APIManagerPublisherException(msg, e);
}
}
private Scope getUpdatedScope(Scope previousScope, String role, boolean remove) {
Scope scope = new Scope();
scope.setId(previousScope.getId());
scope.setName(previousScope.getName());
scope.setDisplayName(previousScope.getDisplayName());
scope.setUsageCount(previousScope.getUsageCount());
Set<String> bindings = new HashSet<>(previousScope.getBindings());
if (remove) {
bindings.remove(role);
} else {
bindings.add(role);
}
scope.setBindings(new ArrayList<>(bindings));
return scope;
}
@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();
APIApplicationServices apiApplicationServices =
APIPublisherDataHolder.getInstance().getApiApplicationServices();
// PublisherRESTAPIServices publisherRESTAPIServices =
// APIPublisherDataHolder.getInstance().getPublisherRESTAPIServices();
APIApplicationKey apiApplicationKey;
AccessTokenInfo accessTokenInfo;
try {
@ -663,40 +755,70 @@ public class APIPublisherServiceImpl implements APIPublisherService {
throw new APIManagerPublisherException(e);
}
try {
Scope[] scopeList = publisherRESTAPIServices.getScopes(apiApplicationKey, accessTokenInfo);
SelfSyncingScopeTree selfSyncingScopeTree = getSyncTree(apiApplicationKey, accessTokenInfo);
Map<String, String> permScopeMap = APIPublisherDataHolder.getInstance().getPermScopeMapping();
if (permissions.length != 0) {
updateScopes(roleName, publisherRESTAPIServices, apiApplicationKey, accessTokenInfo, scopeList, permissions, permScopeMap, false);
}
if (removedPermissions.length != 0) {
updateScopes(roleName, publisherRESTAPIServices, apiApplicationKey, accessTokenInfo, scopeList, removedPermissions, permScopeMap, true);
for (String permission : permissions) {
Scope scope = selfSyncingScopeTree.findScope(permission);
if (scope != null) {
selfSyncingScopeTree.add(getUpdatedScope(scope, roleName, false), permission);
} else {
log.warn("Can not find a scope binding for permission [ " + permission + " ]");
}
}
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);
for (String permission : removedPermissions) {
Scope scope = selfSyncingScopeTree.findScope(permission);
if (scope != null) {
selfSyncingScopeTree.add(getUpdatedScope(scope, roleName, true), permission);
} else {
log.warn("Can not find a scope binding for permission [ " + permission + " ]");
}
} 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);
} finally {
APIPublisherUtils.removeScopePublishUserIfExists(tenantDomain);
}
// 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);
// } finally {
// APIPublisherUtils.removeScopePublishUserIfExists(tenantDomain);
// }
// try {
//
// Scope[] scopeList = publisherRESTAPIServices.getScopes(apiApplicationKey, accessTokenInfo);
//
// Map<String, String> permScopeMap = APIPublisherDataHolder.getInstance().getPermScopeMapping();
// if (permissions.length != 0) {
// updateScopes(roleName, publisherRESTAPIServices, apiApplicationKey, accessTokenInfo, scopeList, permissions, permScopeMap, false);
// }
// if (removedPermissions.length != 0) {
// updateScopes(roleName, publisherRESTAPIServices, apiApplicationKey, accessTokenInfo, scopeList, removedPermissions, permScopeMap, true);
// }
//
// 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);
// } finally {
// APIPublisherUtils.removeScopePublishUserIfExists(tenantDomain);
// }
}
/**
@ -851,7 +973,7 @@ public class APIPublisherServiceImpl implements APIPublisherService {
scopeSet.add(scopeObject);
List<String> scopes = new ArrayList<>();
scopes.addAll(Arrays.asList(apiUriTemplate.getScope().getKey()));
scopes.add(apiUriTemplate.getScope().getKey());
operation.setScopes(scopes);
}
}

@ -21,6 +21,7 @@ import io.entgra.device.mgt.core.apimgt.extension.rest.api.APIApplicationService
import io.entgra.device.mgt.core.apimgt.extension.rest.api.PublisherRESTAPIServices;
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.apimgt.webapp.publisher.util.SelfSyncingScopeTree;
import io.entgra.device.mgt.core.device.mgt.common.metadata.mgt.MetadataManagementService;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.PostApiPublishingObsever;
import org.wso2.carbon.context.CarbonContext;
@ -38,6 +39,7 @@ import java.util.Map;
import java.util.Stack;
import java.util.List;
import java.util.ArrayList;
import java.util.WeakHashMap;
public class APIPublisherDataHolder {
@ -53,6 +55,11 @@ public class APIPublisherDataHolder {
private APIApplicationServices apiApplicationServices;
private PublisherRESTAPIServices publisherRESTAPIServices;
private MetadataManagementService metadataManagementService;
private static final WeakHashMap<String, SelfSyncingScopeTree> selfSyncTrees = new WeakHashMap<>();
public WeakHashMap<String, SelfSyncingScopeTree> getSelfSyncTrees() {
return selfSyncTrees;
}
private static APIPublisherDataHolder thisInstance = new APIPublisherDataHolder();

@ -0,0 +1,116 @@
/*
* 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.webapp.publisher.util;
import io.entgra.device.mgt.core.apimgt.extension.rest.api.PublisherRESTAPIServices;
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.webapp.publisher.exception.APIManagerPublisherException;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.internal.APIPublisherDataHolder;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
public class SelfSyncingScopeTree {
private static final Log log = LogFactory.getLog(SelfSyncingScopeTree.class);
private static final PublisherRESTAPIServices publisherRESTAPIServices =
APIPublisherDataHolder.getInstance().getPublisherRESTAPIServices();
private final APIApplicationKey apiApplicationKey;
private final AccessTokenInfo accessTokenInfo;
private final Map<String, SelfSyncingScopeTree> childTrees = new ConcurrentHashMap<>();
private String pathKey;
private Scope scope;
public SelfSyncingScopeTree(Scope scope, String permission, APIApplicationKey apiApplicationKey,
AccessTokenInfo accessTokenInfo) throws APIManagerPublisherException {
this.apiApplicationKey = apiApplicationKey;
this.accessTokenInfo = accessTokenInfo;
add(scope, permission);
}
public void add(Scope scope, String permission) throws APIManagerPublisherException {
List<String> pathKeys = Arrays.stream(StringUtils.split(permission, "/")).collect(Collectors.toList());
if (pathKeys.size() == 1) {
this.pathKey = pathKeys.get(0);
if (this.scope == null) {
this.scope = scope;
} else {
this.update(scope);
}
return;
}
if (this.pathKey == null) {
this.pathKey = pathKeys.get(0);
}
pathKeys.remove(0);
if (childTrees.containsKey(pathKeys.get(0))) {
childTrees.get(pathKeys.get(0)).add(scope, String.join("/", pathKeys));
} else {
childTrees.put(pathKeys.get(0), new SelfSyncingScopeTree(scope, String.join("/", pathKeys),
apiApplicationKey, accessTokenInfo));
}
}
public Scope findScope(String permission) {
if (this.scope != null) {
return this.scope;
}
List<String> pathKeys = Arrays.stream(StringUtils.split(permission, "/")).collect(Collectors.toList());
pathKeys.remove(0);
return childTrees.get(pathKeys.get(0)).findScope(String.join("/", pathKeys));
}
private void update(Scope scope) throws APIManagerPublisherException {
try {
if (publisherRESTAPIServices.isSharedScopeNameExists(apiApplicationKey, accessTokenInfo,
this.scope.getName())) {
if (publisherRESTAPIServices.updateSharedScope(apiApplicationKey, accessTokenInfo, scope)) {
this.scope = scope;
}
} else {
log.warn("Found a scope which is not reflect in the APIM end. Scope [ " + scope.getName() + " ]");
}
} catch (APIServicesException | BadRequestException | UnexpectedResponseException e) {
String msg = "Error encountered while updating the scope [ " + scope.getName() + "]";
log.error(msg, e);
throw new APIManagerPublisherException(msg, e);
}
}
}
Loading…
Cancel
Save