Skip to content

Commit

Permalink
Improves access rule processing algorithm for performance optimization (
Browse files Browse the repository at this point in the history
eclipse-basyx#367)

* Initial commit

Signed-off-by: Mohammad Ghazanfar Ali Danish <[email protected]>

* Improves access rule processing algorithm for performance optimization

Signed-off-by: Mohammad Ghazanfar Ali Danish <[email protected]>

* Modifies remove Rbac rule parameter

Signed-off-by: Mohammad Ghazanfar Ali Danish <[email protected]>

* Fixes incorrect Javadoc

Signed-off-by: Mohammad Ghazanfar Ali Danish <[email protected]>

---------

Signed-off-by: Mohammad Ghazanfar Ali Danish <[email protected]>
  • Loading branch information
mdanish98 authored Jul 31, 2024
1 parent 938397f commit 8bbe124
Show file tree
Hide file tree
Showing 14 changed files with 185 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ Note:
* The Action are fixed as of now and limited to (CREATE, READ, UPDATE, DELETE, and EXECUTE) but later user configurable mapping of these actions would be provided.
* For the serialization related requests there should be defined rules for accessing the AASs/Submodels/Concept Descriptions, as the serialization requires all of these elements. For e.g, a role with serialization is configured for Aas Environment target information but if there is no role for reading the AAS/Submodel/Concept Description then the request will be denied.
* For the upload related requests there should be defined rules for reading, creating, and updating the AASs/Submodels/Concept Descriptions, as the upload requests performs creation, updation, and request operations on the AASs/Submodels/Concept Descriptions contained in the uploaded file, hence appropriate rules should be configured for the subject in consideration.
* Each rule should be unique in combination of role + action + target information

## Action table for RBAC

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ The role defines which role is allowed to perform the defined actions. The role

The targetInformation defines coarse-grained control over the resource, you may define the aasId with a wildcard (\*), it means the defined role x with action y can access any Asset Administration Shell Descriptors on the registry. You can also define a specific AAS Identifier in place of the wildcard (\*), then the role x with action y could be performed only on that particular AAS Descriptor.

Note: The Action are fixed as of now and limited to (CREATE, READ, UPDATE, DELETE, and EXECUTE) but later user configurable mapping of these actions would be provided.
Note:
* The Action are fixed as of now and limited to (CREATE, READ, UPDATE, DELETE, and EXECUTE) but later user configurable mapping of these actions would be provided.
* Each rule should be unique in combination of role + action + target information

## Action table for RBAC

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ The role defines which role is allowed to perform the defined actions. The role

The targetInformation defines coarse-grained control over the resource, you may define the aasIds with a wildcard (\*), it means the defined role x with action y can access any Asset Administration Shell on the repository. You can also define a specific AAS Identifier in place of the wildcard (\*), then the role x with action y could be performed only on that particular AAS. There could be a single aasId or multiple aasIds as a list (cf. basyx-deleter above).

Note: The Action are fixed as of now and limited to (CREATE, READ, UPDATE, DELETE, and EXECUTE) but later user configurable mapping of these actions would be provided.
Note:
* The Action are fixed as of now and limited to (CREATE, READ, UPDATE, DELETE, and EXECUTE) but later user configurable mapping of these actions would be provided.
* Each rule should be unique in combination of role + action + target information

## Action table for RBAC

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

package org.eclipse.digitaltwin.basyx.authorization.rbac;

import java.util.HashMap;
import java.util.Objects;
import java.util.Set;

Expand All @@ -37,6 +38,7 @@

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.NamedType;
import com.fasterxml.jackson.databind.module.SimpleModule;

/**
* A base configuration for Rbac based authorization
Expand All @@ -55,6 +57,10 @@ public ObjectMapper getAasMapper(Jackson2ObjectMapperBuilder builder) {
Set<Class<?>> subtypes = reflections.getTypesAnnotatedWith(TargetInformationSubtype.class);

subtypes.stream().map(this::createNamedType).filter(Objects::nonNull).forEach(mapper::registerSubtypes);

SimpleModule module = new SimpleModule();
module.addDeserializer(HashMap.class, new RbacRuleDeserializer());
mapper.registerModule(module);

return mapper;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,29 +25,47 @@

package org.eclipse.digitaltwin.basyx.authorization.rbac;

import java.util.List;
import java.util.Map;

/**
* InMemory implementation of the {@link RbacStorage}
*
* @author danish
*/
public class InMemoryAuthorizationRbacStorage implements RbacStorage {
private final List<RbacRule> rbacRules;
private final Map<String, RbacRule> rbacRules;

public InMemoryAuthorizationRbacStorage(List<RbacRule> rbacRuleList) {
this.rbacRules = rbacRuleList;
public InMemoryAuthorizationRbacStorage(Map<String, RbacRule> rbacRules) {
this.rbacRules = rbacRules;
}

public List<RbacRule> getRbacRules() {
public Map<String, RbacRule> getRbacRules() {
return rbacRules;
}

public void addRule(RbacRule rbacRule) {
rbacRules.add(rbacRule);

rbacRule.getAction().stream().map(action -> RbacRuleKeyGenerator.generateKey(rbacRule.getRole(), action.toString(), rbacRule.getTargetInformation().getClass().getName())).filter(key -> !rbacRules.containsKey(key)).map(key -> rbacRules.put(key, rbacRule));
}

public void removeRule(RbacRule rbacRule) {
rbacRules.remove(rbacRule);
public void removeRule(String key) {
if (!exist(key))
throw new RuntimeException("Rule doesn't exist in policy store");

rbacRules.remove(key);
}

@Override
public RbacRule getRbacRule(String key) {
if (!exist(key))
throw new RuntimeException("Rule doesn't exist in policy store");

return rbacRules.get(key);
}

@Override
public boolean exist(String key) {
return rbacRules.containsKey(key);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
import org.eclipse.digitaltwin.basyx.authorization.CommonAuthorizationProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;

/**
* Configurations for {@link RbacRule}
Expand Down Expand Up @@ -63,7 +63,7 @@ public RbacRuleConfiguration(ObjectMapper objectMapper, ResourceLoader resourceL
public RbacStorage createInMemoryRbacStorage() throws IOException {

if (filePath.isBlank())
return new InMemoryAuthorizationRbacStorage(new ArrayList<>());
return new InMemoryAuthorizationRbacStorage(new HashMap<>());

return new InMemoryAuthorizationRbacStorage(new RbacRuleInitializer(objectMapper, filePath, resourceLoader).deserialize());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*******************************************************************************
* Copyright (C) 2024 the Eclipse BaSyx Authors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* SPDX-License-Identifier: MIT
******************************************************************************/

package org.eclipse.digitaltwin.basyx.authorization.rbac;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;

/**
* A specific deserializer for deserializing list of {@link RbacRule} into HashMap
*
* @author danish
*/
public class RbacRuleDeserializer extends JsonDeserializer<HashMap<String, RbacRule>> {

@Override
public HashMap<String, RbacRule> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
ObjectMapper mapper = (ObjectMapper) p.getCodec();
List<RbacRule> rbacRules = mapper.readValue(p, new TypeReference<List<RbacRule>>() {
});
HashMap<String, RbacRule> result = new HashMap<>();

for (RbacRule rule : rbacRules) {
rule.getAction().stream().map(action -> RbacRuleKeyGenerator.generateKey(rule.getRole(), action.toString(), rule.getTargetInformation().getClass().getName())).forEach(key -> result.put(key, rule));
}

return result;
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.HashMap;

import org.eclipse.digitaltwin.basyx.core.exceptions.MissingAuthorizationConfigurationException;
import org.springframework.core.io.ResourceLoader;
Expand All @@ -54,23 +54,26 @@ public RbacRuleInitializer(ObjectMapper objectMapper, String filePath, ResourceL
}

/**
* Provides the list of {@link RbacRule} from the resource
* Provides the Map of {@link RbacRule} from the resource
*
* @return list of rbac rules
* It auto-generates the key based on hash of combination of role, {@link Action}, and the concrete {@link TargetInformation}
* class.
*
* @return map of rbac rules
* @throws IOException
*/
public List<RbacRule> deserialize() throws IOException {
return objectMapper.readValue(getFile(rbacJsonFilePath), new TypeReference<List<RbacRule>>() {
public HashMap<String, RbacRule> deserialize() throws IOException {
return objectMapper.readValue(getFile(rbacJsonFilePath), new TypeReference<HashMap<String, RbacRule>>() {
});
}

private File getFile(String filePath) {

try {
return resourceLoader.getResource(filePath).getFile();
} catch(IOException e) {
} catch (IOException e) {
throw new MissingAuthorizationConfigurationException(filePath);
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*******************************************************************************
* Copyright (C) 2024 the Eclipse BaSyx Authors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* SPDX-License-Identifier: MIT
******************************************************************************/

package org.eclipse.digitaltwin.basyx.authorization.rbac;

/**
* A helper class to generate the key based on hash of combination of role, {@link Action}, and the concrete {@link TargetInformation}
* class.
*
* @author danish
*/
public class RbacRuleKeyGenerator {

/**
* Generates the key based on hash of combination of role, {@link Action}, and the concrete {@link TargetInformation}
* class.
*
* <p> For e.g., role = Engineer, Action = READ, TargetInformation Class = org.eclipse.digitaltwin.basyx.aasrepository.feature.authorization.AasTargetInformation.java
* <br>
* So the hash code of concatenation of these is: -1428731317.
*
* </p>
*
* @param role
* @param action
* @param clazz
* @return
*/
public static String generateKey(String role, String action, String clazz) {
return String.valueOf((role + action + clazz).hashCode());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,18 @@

package org.eclipse.digitaltwin.basyx.authorization.rbac;

import java.util.List;
import java.util.Map;

/**
* An interface for definig storage of {@link RbacRule}
* An interface for defining storage of {@link RbacRule}
*
* @author danish
*/
public interface RbacStorage {

public List<RbacRule> getRbacRules();
public Map<String, RbacRule> getRbacRules();
public RbacRule getRbacRule(String key);
public void addRule(RbacRule rbacRule);
public void removeRule(RbacRule rbacRule);
public void removeRule(String key);
public boolean exist(String key);
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@
*/
public class SimpleRbacPermissionResolver<T extends TargetInformation> implements RbacPermissionResolver<T> {

private static final String ALL_ALLOWED_WILDCARD = "*";

private Logger logger = LoggerFactory.getLogger(SimpleRbacPermissionResolver.class);

private RbacStorage rbacStorage;
Expand Down Expand Up @@ -84,27 +82,14 @@ public boolean hasPermission(final Action action, final T targetInformation) {

private Stream<RbacRule> getMatchingRules(final List<String> roles, final Action action, final T targetInformation) {

List<RbacRule> filteredRbacRulesForTargetInfo = this.rbacStorage.getRbacRules().stream().filter(rbacRule -> hasMatchingTargetInformation(targetInformation, rbacRule)).collect(Collectors.toList());
List<RbacRule> filteredRbacRulesForTargetInfos = roles.stream().map(role -> RbacRuleKeyGenerator.generateKey(role, action.toString(), targetInformation.getClass().getName())).filter(rbacStorage::exist).map(rbacStorage::getRbacRule).collect(Collectors.toList());

return filteredRbacRulesForTargetInfo.parallelStream().filter(rbacRule -> checkRolesMatchRbacRule(rbacRule, roles)).filter(rbacRule -> checkActionMatchesRbacRule(rbacRule, action))
.filter(rbacRule -> checkRbacRuleMatchesTargetInfo(rbacRule, targetInformation));
}

private boolean checkRolesMatchRbacRule(final RbacRule rbacRule, final List<String> roles) {
return rbacRule.getRole().equals(ALL_ALLOWED_WILDCARD) || (roles != null && roles.stream().anyMatch(role -> rbacRule.getRole().equals(role)));
}

private boolean checkActionMatchesRbacRule(final RbacRule rbacRule, final Action action) {
return rbacRule.getAction().stream().anyMatch(rbacRuleAction -> rbacRuleAction.equals(action));
return filteredRbacRulesForTargetInfos.stream().filter(rbacRule -> checkRbacRuleMatchesTargetInfo(rbacRule, targetInformation));
}

private boolean checkRbacRuleMatchesTargetInfo(final RbacRule rbacRule, final T targetInformation) {

return targetPermissionVerifier.isVerified(rbacRule, targetInformation);
}

private boolean hasMatchingTargetInformation(final T targetInformation, RbacRule rbacRule) {

return targetInformation.getClass().isInstance(rbacRule.getTargetInformation());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ The role defines which role is allowed to perform the defined actions. The role
The targetInformation defines coarse-grained control over the resource, you may define the conceptDescriptionIds with a wildcard (\*), it means the defined role x with action y can access any Concept Description on the repository. You can also define a specific Concept Description Identifier in place of the wildcard (\*), then the role x with action y could be performed only on that particular Concept Description.
There could be a single conceptDescriptionId or multiple conceptDescriptionIds as a list (cf. basyx-reader-two above).

Note: The Action are fixed as of now and limited to (CREATE, READ, UPDATE, DELETE, and EXECUTE) but later user configurable mapping of these actions would be provided.
Note:
* The Action are fixed as of now and limited to (CREATE, READ, UPDATE, DELETE, and EXECUTE) but later user configurable mapping of these actions would be provided.
* Each rule should be unique in combination of role + action + target information

## Action table for RBAC

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ The role defines which role is allowed to perform the defined actions. The role

The targetInformation defines coarse-grained control over the resource, you may define the submodelId with a wildcard (\*), it means the defined role x with action y can access any Submodel Descriptors on the registry. You can also define a specific Submodel Identifier in place of the wildcard (\*), then the role x with action y could be performed only on that particular Submodel Descriptor.

Note: The Action are fixed as of now and limited to (CREATE, READ, UPDATE, DELETE, and EXECUTE) but later user configurable mapping of these actions would be provided.
Note:
* The Action are fixed as of now and limited to (CREATE, READ, UPDATE, DELETE, and EXECUTE) but later user configurable mapping of these actions would be provided.
* Each rule should be unique in combination of role + action + target information

## Action table for RBAC

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ The role defines which role is allowed to perform the defined actions. The role
The targetInformation defines coarse-grained control over the resource, you may define the submodelIds and submodelElementIdShortPaths with a wildcard (\*), it means the defined role x with action y can access any Submodel and any SubmodelElement on the repository. You can also define a specific Submodel Identifier in place of the wildcard (\*), then the role x with action y could be performed only on that particular Submodel. Similarly, you can define a specific SubmodelElement IdShort path, then you can only access the SubmodelElement corresponding to that IdShort path. It means that the whole Submodel GET request would not be possible if the IdShort path for a specific SubmodelElement is provided, because the requestor only has access for a specific SubmodelElement.
There could be a single submodelId/submodelElementIdShortPath or multiple submodelIds/submodelElementIdShortPaths as a list (cf. basyx-sme-reader).

Note: The Action are fixed as of now and limited to (CREATE, READ, UPDATE, DELETE and EXECUTE) but later user configurable mapping of these actions would be provided.
Note:
* The Action are fixed as of now and limited to (CREATE, READ, UPDATE, DELETE and EXECUTE) but later user configurable mapping of these actions would be provided.
* Each rule should be unique in combination of role + action + target information

## Action table for RBAC

Expand Down

0 comments on commit 8bbe124

Please sign in to comment.