From 8881e43e8264d07a4e35ce73f6466c97405d0794 Mon Sep 17 00:00:00 2001 From: Mohammad Ghazanfar Ali Danish <62088117+mdanish98@users.noreply.github.com> Date: Fri, 3 May 2024 13:23:15 +0200 Subject: [PATCH] Refactors RBAC rules to support list of target informations (#257) * Refactors RBAC rules to support list of target informations Signed-off-by: Mohammad Ghazanfar Ali Danish * Refactors Readme Signed-off-by: Mohammad Ghazanfar Ali Danish * Updates Readme Signed-off-by: Mohammad Ghazanfar Ali Danish * Refactors Readme Signed-off-by: Mohammad Ghazanfar Ali Danish * Fixes minor typo in rbac_rules Signed-off-by: Mohammad Ghazanfar Ali Danish --------- Signed-off-by: Mohammad Ghazanfar Ali Danish --- .../Readme.md | 14 +- .../src/test/resources/rbac_rules.json | 190 +++++++++--------- .../src/main/resources/rbac_rules.json | 120 +++++------ .../Readme.md | 10 +- .../authorization/AasTargetInformation.java | 19 +- .../AuthorizedAasRepository.java | 44 ++-- .../rbac/AasTargetPermissionVerifier.java | 18 +- .../src/main/resources/rbac_rules.json | 20 +- .../Readme.md | 18 +- ...uthorizedConceptDescriptionRepository.java | 22 +- .../ConceptDescriptionTargetInformation.java | 19 +- ...ptDescriptionTargetPermissionVerifier.java | 20 +- .../src/main/resources/rbac_rules.json | 16 +- .../Readme.md | 19 +- .../AuthorizedSubmodelRepository.java | 46 +++-- .../SubmodelTargetInformation.java | 29 +-- .../SubmodelTargetPermissionVerifier.java | 23 ++- .../src/main/resources/rbac_rules.json | 68 +++---- 18 files changed, 395 insertions(+), 320 deletions(-) diff --git a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/Readme.md b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/Readme.md index 0e12ecf82..760078f1d 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/Readme.md +++ b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/Readme.md @@ -28,7 +28,7 @@ spring.security.oauth2.resourceserver.jwt.issuer-uri= http://localhost:9096/real ## RBAC rule configuration -For configuring RBAC rules, all the rbac rules should be configured inside a json file, the rules are defined as below: +For configuring RBAC rules, all the rbac rules should be configured inside a json file, the rules are defined as below: ``` [ @@ -75,11 +75,11 @@ Note: Below is a reference table that shows which actions are used in what endpoints of the AasEnvironment: -| Action | Endpoint | -|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Action | Endpoint | +|---------|--------------------| | READ | GET /serialization | -| CREATE | - | -| UPDATE | - -| DELETE | - -| EXECUTE | - | +| CREATE | POST /update | +| UPDATE | - | +| DELETE | - | +| EXECUTE | - | diff --git a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/resources/rbac_rules.json b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/resources/rbac_rules.json index bb2e396fb..8a91a29f8 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/resources/rbac_rules.json +++ b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/resources/rbac_rules.json @@ -4,7 +4,7 @@ "action": "READ", "targetInformation": { "@type": "aas", - "aasId": "*" + "aasIds": "*" } }, { @@ -12,7 +12,7 @@ "action": ["CREATE", "READ", "UPDATE", "DELETE"], "targetInformation": { "@type": "aas", - "aasId": "*" + "aasIds": "*" } }, { @@ -20,7 +20,7 @@ "action": "READ", "targetInformation": { "@type": "aas", - "aasId": "specificAasId" + "aasIds": "specificAasId" } }, { @@ -28,7 +28,7 @@ "action": "CREATE", "targetInformation": { "@type": "aas", - "aasId": "*" + "aasIds": "*" } }, { @@ -36,7 +36,7 @@ "action": "UPDATE", "targetInformation": { "@type": "aas", - "aasId": "*" + "aasIds": "*" } }, { @@ -44,7 +44,7 @@ "action": "UPDATE", "targetInformation": { "@type": "aas", - "aasId": "specificAasId" + "aasIds": "specificAasId" } }, { @@ -52,7 +52,7 @@ "action": "UPDATE", "targetInformation": { "@type": "aas", - "aasId": "*" + "aasIds": "*" } }, { @@ -60,7 +60,7 @@ "action": "UPDATE", "targetInformation": { "@type": "aas", - "aasId": "specificAasId-2" + "aasIds": "specificAasId-2" } }, { @@ -68,7 +68,7 @@ "action": "DELETE", "targetInformation": { "@type": "aas", - "aasId": "*" + "aasIds": "*" } }, { @@ -76,7 +76,7 @@ "action": "DELETE", "targetInformation": { "@type": "aas", - "aasId": "specificAasId-2" + "aasIds": "specificAasId-2" } }, { @@ -84,8 +84,8 @@ "action": "READ", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -93,8 +93,8 @@ "action": ["CREATE", "READ", "UPDATE", "DELETE", "EXECUTE"], "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -102,8 +102,8 @@ "action": "READ", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId", - "submodelElementIdShortPath": "*" + "submodelIds": "specificSubmodelId", + "submodelElementIdShortPaths": "*" } }, { @@ -111,8 +111,8 @@ "action": "READ", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId", - "submodelElementIdShortPath": "smc2.specificSubmodelElementIdShort" + "submodelIds": "specificSubmodelId", + "submodelElementIdShortPaths": "smc2.specificSubmodelElementIdShort" } }, { @@ -120,8 +120,8 @@ "action": "READ", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId", - "submodelElementIdShortPath": "smc2.specificFileSubmodelElementIdShort" + "submodelIds": "specificSubmodelId", + "submodelElementIdShortPaths": "smc2.specificFileSubmodelElementIdShort" } }, { @@ -129,8 +129,8 @@ "action": "CREATE", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -138,8 +138,8 @@ "action": "UPDATE", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -147,8 +147,8 @@ "action": "UPDATE", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId", - "submodelElementIdShortPath": "*" + "submodelIds": "specificSubmodelId", + "submodelElementIdShortPaths": "*" } }, { @@ -156,8 +156,8 @@ "action": "UPDATE", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId", - "submodelElementIdShortPath": "smc2.specificFileSubmodelElementIdShort" + "submodelIds": "specificSubmodelId", + "submodelElementIdShortPaths": "smc2.specificFileSubmodelElementIdShort" } }, { @@ -165,8 +165,8 @@ "action": "UPDATE", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId", - "submodelElementIdShortPath": "smc2" + "submodelIds": "specificSubmodelId", + "submodelElementIdShortPaths": "smc2" } }, { @@ -174,8 +174,8 @@ "action": "UPDATE", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId-2", - "submodelElementIdShortPath": "smc1.specificSubmodelElementIdShort-2" + "submodelIds": "specificSubmodelId-2", + "submodelElementIdShortPaths": "smc1.specificSubmodelElementIdShort-2" } }, { @@ -183,8 +183,8 @@ "action": "UPDATE", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId-2", - "submodelElementIdShortPath": "smc2.specificFileSubmodelElementIdShort" + "submodelIds": "specificSubmodelId-2", + "submodelElementIdShortPaths": "smc2.specificFileSubmodelElementIdShort" } }, { @@ -192,8 +192,8 @@ "action": "DELETE", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -201,8 +201,8 @@ "action": "DELETE", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId-2", - "submodelElementIdShortPath": "*" + "submodelIds": "specificSubmodelId-2", + "submodelElementIdShortPaths": "*" } }, { @@ -210,8 +210,8 @@ "action": "EXECUTE", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -219,8 +219,8 @@ "action": "EXECUTE", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId", - "submodelElementIdShortPath": "square" + "submodelIds": "specificSubmodelId", + "submodelElementIdShortPaths": "square" } }, { @@ -228,8 +228,8 @@ "action": "READ", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId-2", - "submodelElementIdShortPath": "smc2.specificFileSubmodelElementIdShort" + "submodelIds": "specificSubmodelId-2", + "submodelElementIdShortPaths": "smc2.specificFileSubmodelElementIdShort" } }, { @@ -237,7 +237,7 @@ "action": "READ", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -245,7 +245,7 @@ "action": ["CREATE", "READ", "UPDATE", "DELETE"], "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -253,7 +253,7 @@ "action": "READ", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "specificConceptDescriptionId" + "conceptDescriptionIds": "specificConceptDescriptionId" } }, { @@ -261,7 +261,7 @@ "action": "CREATE", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -278,7 +278,7 @@ "action": "UPDATE", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -286,7 +286,7 @@ "action": "UPDATE", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "specificConceptDescriptionId" + "conceptDescriptionIds": "specificConceptDescriptionId" } }, { @@ -294,7 +294,7 @@ "action": "DELETE", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -302,7 +302,7 @@ "action": "DELETE", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "specificConceptDescriptionId-2" + "conceptDescriptionIds": "specificConceptDescriptionId-2" } }, { @@ -337,7 +337,7 @@ "action": "READ", "targetInformation": { "@type": "aas", - "aasId": "*" + "aasIds": "*" } }, { @@ -345,8 +345,8 @@ "action": "READ", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -354,7 +354,7 @@ "action": "READ", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -380,7 +380,7 @@ "action": "READ", "targetInformation": { "@type": "aas", - "aasId": "*" + "aasIds": "*" } }, { @@ -388,8 +388,8 @@ "action": "READ", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -397,7 +397,7 @@ "action": "READ", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -414,7 +414,7 @@ "action": "CREATE", "targetInformation": { "@type": "aas", - "aasId": "*" + "aasIds": "*" } }, { @@ -422,7 +422,7 @@ "action": "READ", "targetInformation": { "@type": "aas", - "aasId": "*" + "aasIds": "*" } }, { @@ -430,7 +430,7 @@ "action": "UPDATE", "targetInformation": { "@type": "aas", - "aasId": "*" + "aasIds": "*" } }, { @@ -438,8 +438,8 @@ "action": "READ", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -447,7 +447,7 @@ "action": "READ", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -455,7 +455,7 @@ "action": "UPDATE", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -463,8 +463,8 @@ "action": "CREATE", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -472,8 +472,8 @@ "action": "UPDATE", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -481,7 +481,7 @@ "action": "CREATE", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -498,7 +498,7 @@ "action": "CREATE", "targetInformation": { "@type": "aas", - "aasId": "http://customer.com/aas/9175_7013_7091_9168" + "aasIds": "http://customer.com/aas/9175_7013_7091_9168" } }, { @@ -506,7 +506,7 @@ "action": "UPDATE", "targetInformation": { "@type": "aas", - "aasId": "http://customer.com/aas/9175_7013_7091_9168" + "aasIds": "http://customer.com/aas/9175_7013_7091_9168" } }, { @@ -514,7 +514,7 @@ "action": "READ", "targetInformation": { "@type": "aas", - "aasId": "http://customer.com/aas/9175_7013_7091_9168" + "aasIds": "http://customer.com/aas/9175_7013_7091_9168" } }, { @@ -522,8 +522,8 @@ "action": "READ", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -531,7 +531,7 @@ "action": "READ", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -539,7 +539,7 @@ "action": "UPDATE", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -547,8 +547,8 @@ "action": "CREATE", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -556,8 +556,8 @@ "action": "UPDATE", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -565,7 +565,7 @@ "action": "CREATE", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -582,7 +582,7 @@ "action": "CREATE", "targetInformation": { "@type": "aas", - "aasId": "http://customer.com/aas/9175_7013_7091_9168" + "aasIds": "http://customer.com/aas/9175_7013_7091_9168" } }, { @@ -590,7 +590,7 @@ "action": "READ", "targetInformation": { "@type": "aas", - "aasId": "http://customer.com/aas/9175_7013_7091_9168" + "aasIds": "http://customer.com/aas/9175_7013_7091_9168" } }, { @@ -598,8 +598,8 @@ "action": "READ", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -607,7 +607,7 @@ "action": "READ", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -615,7 +615,7 @@ "action": "UPDATE", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -623,8 +623,8 @@ "action": "CREATE", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -632,8 +632,8 @@ "action": "UPDATE", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -641,7 +641,7 @@ "action": "CREATE", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { diff --git a/basyx.aasenvironment/basyx.aasenvironment.component/src/main/resources/rbac_rules.json b/basyx.aasenvironment/basyx.aasenvironment.component/src/main/resources/rbac_rules.json index 8ea3e6ab8..0dff84bb7 100644 --- a/basyx.aasenvironment/basyx.aasenvironment.component/src/main/resources/rbac_rules.json +++ b/basyx.aasenvironment/basyx.aasenvironment.component/src/main/resources/rbac_rules.json @@ -4,7 +4,7 @@ "action": "READ", "targetInformation": { "@type": "aas", - "aasId": "*" + "aasIds": "*" } }, { @@ -12,7 +12,7 @@ "action": ["CREATE", "READ", "UPDATE", "DELETE"], "targetInformation": { "@type": "aas", - "aasId": "*" + "aasIds": "*" } }, { @@ -20,7 +20,7 @@ "action": "READ", "targetInformation": { "@type": "aas", - "aasId": "specificAasId" + "aasIds": "specificAasId" } }, { @@ -28,7 +28,7 @@ "action": "CREATE", "targetInformation": { "@type": "aas", - "aasId": "*" + "aasIds": "*" } }, { @@ -36,7 +36,7 @@ "action": "UPDATE", "targetInformation": { "@type": "aas", - "aasId": "*" + "aasIds": "*" } }, { @@ -44,7 +44,7 @@ "action": "UPDATE", "targetInformation": { "@type": "aas", - "aasId": "specificAasId" + "aasIds": "specificAasId" } }, { @@ -52,7 +52,7 @@ "action": "UPDATE", "targetInformation": { "@type": "aas", - "aasId": "*" + "aasIds": "*" } }, { @@ -60,7 +60,7 @@ "action": "UPDATE", "targetInformation": { "@type": "aas", - "aasId": "specificAasId-2" + "aasIds": "specificAasId-2" } }, { @@ -68,7 +68,7 @@ "action": "DELETE", "targetInformation": { "@type": "aas", - "aasId": "*" + "aasIds": "*" } }, { @@ -76,7 +76,7 @@ "action": "DELETE", "targetInformation": { "@type": "aas", - "aasId": "specificAasId-2" + "aasIds": "specificAasId-2" } }, { @@ -84,8 +84,8 @@ "action": "READ", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -93,8 +93,8 @@ "action": ["CREATE", "READ", "UPDATE", "DELETE", "EXECUTE"], "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -102,8 +102,8 @@ "action": "READ", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId", - "submodelElementIdShortPath": "*" + "submodelIds": "specificSubmodelId", + "submodelElementIdShortPaths": "*" } }, { @@ -111,8 +111,8 @@ "action": "READ", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId", - "submodelElementIdShortPath": "smc2.specificSubmodelElementIdShort" + "submodelIds": "specificSubmodelId", + "submodelElementIdShortPaths": "smc2.specificSubmodelElementIdShort" } }, { @@ -120,8 +120,8 @@ "action": "READ", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId", - "submodelElementIdShortPath": "smc2.specificFileSubmodelElementIdShort" + "submodelIds": "specificSubmodelId", + "submodelElementIdShortPaths": "smc2.specificFileSubmodelElementIdShort" } }, { @@ -129,8 +129,8 @@ "action": "CREATE", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -138,8 +138,8 @@ "action": "UPDATE", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -147,8 +147,8 @@ "action": "UPDATE", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId", - "submodelElementIdShortPath": "*" + "submodelIds": "specificSubmodelId", + "submodelElementIdShortPaths": "*" } }, { @@ -156,8 +156,8 @@ "action": "UPDATE", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId", - "submodelElementIdShortPath": "smc2.specificFileSubmodelElementIdShort" + "submodelIds": "specificSubmodelId", + "submodelElementIdShortPaths": "smc2.specificFileSubmodelElementIdShort" } }, { @@ -165,8 +165,8 @@ "action": "UPDATE", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId", - "submodelElementIdShortPath": "smc2" + "submodelIds": "specificSubmodelId", + "submodelElementIdShortPaths": "smc2" } }, { @@ -174,8 +174,8 @@ "action": "UPDATE", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId-2", - "submodelElementIdShortPath": "smc1.specificSubmodelElementIdShort-2" + "submodelIds": "specificSubmodelId-2", + "submodelElementIdShortPaths": "smc1.specificSubmodelElementIdShort-2" } }, { @@ -183,8 +183,8 @@ "action": "UPDATE", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId-2", - "submodelElementIdShortPath": "smc2.specificFileSubmodelElementIdShort" + "submodelIds": "specificSubmodelId-2", + "submodelElementIdShortPaths": "smc2.specificFileSubmodelElementIdShort" } }, { @@ -192,8 +192,8 @@ "action": "DELETE", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -201,8 +201,8 @@ "action": "DELETE", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId-2", - "submodelElementIdShortPath": "*" + "submodelIds": "specificSubmodelId-2", + "submodelElementIdShortPaths": "*" } }, { @@ -210,8 +210,8 @@ "action": "EXECUTE", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -219,8 +219,8 @@ "action": "EXECUTE", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId", - "submodelElementIdShortPath": "square" + "submodelIds": "specificSubmodelId", + "submodelElementIdShortPaths": "square" } }, { @@ -228,8 +228,8 @@ "action": "READ", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId-2", - "submodelElementIdShortPath": "smc2.specificFileSubmodelElementIdShort" + "submodelIds": "specificSubmodelId-2", + "submodelElementIdShortPaths": "smc2.specificFileSubmodelElementIdShort" } }, { @@ -237,7 +237,7 @@ "action": "READ", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -245,7 +245,7 @@ "action": ["CREATE", "READ", "UPDATE", "DELETE"], "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -253,7 +253,7 @@ "action": "READ", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "specificConceptDescriptionId" + "conceptDescriptionIds": "specificConceptDescriptionId" } }, { @@ -261,7 +261,7 @@ "action": "CREATE", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -269,7 +269,7 @@ "action": "UPDATE", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -277,7 +277,7 @@ "action": "UPDATE", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "specificConceptDescriptionId" + "conceptDescriptionIds": "specificConceptDescriptionId" } }, { @@ -285,7 +285,7 @@ "action": "DELETE", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -293,7 +293,7 @@ "action": "DELETE", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "specificConceptDescriptionId-2" + "conceptDescriptionIds": "specificConceptDescriptionId-2" } }, { @@ -328,7 +328,7 @@ "action": "READ", "targetInformation": { "@type": "aas", - "aasId": "*" + "aasIds": "*" } }, { @@ -336,8 +336,8 @@ "action": "READ", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -345,7 +345,7 @@ "action": "READ", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -371,7 +371,7 @@ "action": "READ", "targetInformation": { "@type": "aas", - "aasId": "*" + "aasIds": "*" } }, { @@ -379,8 +379,8 @@ "action": "READ", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -388,7 +388,7 @@ "action": "READ", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { diff --git a/basyx.aasrepository/basyx.aasrepository-feature-authorization/Readme.md b/basyx.aasrepository/basyx.aasrepository-feature-authorization/Readme.md index 368de5893..da8ef4146 100644 --- a/basyx.aasrepository/basyx.aasrepository-feature-authorization/Readme.md +++ b/basyx.aasrepository/basyx.aasrepository-feature-authorization/Readme.md @@ -37,7 +37,7 @@ For configuring RBAC rules, all the rbac rules should be configured inside a jso "action": "READ", "targetInformation": { "@type": "aas", - "aasId": "*" + "aasIds": "*" } }, { @@ -45,7 +45,7 @@ For configuring RBAC rules, all the rbac rules should be configured inside a jso "action": ["CREATE", "READ", "UPDATE", "DELETE"], "targetInformation": { "@type": "aas", - "aasId": "*" + "aasIds": "*" } }, { @@ -53,7 +53,7 @@ For configuring RBAC rules, all the rbac rules should be configured inside a jso "action": "DELETE", "targetInformation": { "@type": "aas", - "aasId": "specificAasId" + "aasIds": ["testAasId1", "specificAasId", "testAasId2"] } } ] @@ -61,13 +61,13 @@ For configuring RBAC rules, all the rbac rules should be configured inside a jso The role defines which role is allowed to perform the defined actions. The role is as per the configuration of identity providers or based on the organization. Action could be CREATE, READ, UPDATE, DELETE, and EXECUTE, there could be a single action or multiple actions as a list (cf. admin configuration above). -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 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. +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. ## Action table for RBAC -Below is a reference table that shows which actions are used in what endpoints of the AAS Repository: +Below is a reference table that shows which actions are used in what endpoints of the AAS Repository: | Action | Endpoint | |---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| diff --git a/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/AasTargetInformation.java b/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/AasTargetInformation.java index 45497ee7f..f33b00c44 100644 --- a/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/AasTargetInformation.java +++ b/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/AasTargetInformation.java @@ -27,6 +27,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; @@ -41,23 +42,23 @@ @TargetInformationSubtype(getValue = "aas") public class AasTargetInformation implements TargetInformation { - private String aasId; + private List aasIds; @JsonCreator - public AasTargetInformation(final @JsonProperty("aasId") String aasId) { - this.aasId = aasId; + public AasTargetInformation(final @JsonProperty("aasIds") List aasIds) { + this.aasIds = aasIds; } @Override public Map toMap() { final Map map = new HashMap<>(); - map.put("aasId", aasId); + map.put("aasIds", aasIds); return map; } @Override public int hashCode() { - return Objects.hash(aasId); + return Objects.hash(aasIds); } @Override @@ -69,16 +70,16 @@ public boolean equals(Object obj) { if (getClass() != obj.getClass()) return false; AasTargetInformation other = (AasTargetInformation) obj; - return Objects.equals(aasId, other.aasId); + return Objects.equals(aasIds, other.aasIds); } @Override public String toString() { - return "AasTargetInformation [aasId=" + aasId + "]"; + return "AasTargetInformation [aasIds=" + aasIds + "]"; } - public String getAasId() { - return aasId; + public List getAasIds() { + return aasIds; } } diff --git a/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/AuthorizedAasRepository.java b/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/AuthorizedAasRepository.java index ff51722db..a4f128864 100644 --- a/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/AuthorizedAasRepository.java +++ b/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/AuthorizedAasRepository.java @@ -27,6 +27,8 @@ import java.io.File; import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell; @@ -60,7 +62,7 @@ public AuthorizedAasRepository(AasRepository decorated, RbacPermissionResolver> getAllAas(PaginationInfo pInfo) { - boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new AasTargetInformation("*")); + boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new AasTargetInformation(getIdAsList("*"))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -69,7 +71,7 @@ public CursorResult> getAllAas(PaginationInfo pIn @Override public AssetAdministrationShell getAas(String shellId) throws ElementDoesNotExistException { - boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new AasTargetInformation(shellId)); + boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new AasTargetInformation(getIdAsList(shellId))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -78,7 +80,7 @@ public AssetAdministrationShell getAas(String shellId) throws ElementDoesNotExis @Override public void createAas(AssetAdministrationShell shell) throws CollidingIdentifierException { - boolean isAuthorized = permissionResolver.hasPermission(Action.CREATE, new AasTargetInformation(shell.getId())); + boolean isAuthorized = permissionResolver.hasPermission(Action.CREATE, new AasTargetInformation(getIdAsList(shell.getId()))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -87,7 +89,7 @@ public void createAas(AssetAdministrationShell shell) throws CollidingIdentifier @Override public void updateAas(String shellId, AssetAdministrationShell shell) { - boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new AasTargetInformation(shellId)); + boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new AasTargetInformation(getIdAsList(shellId))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -96,7 +98,7 @@ public void updateAas(String shellId, AssetAdministrationShell shell) { @Override public void deleteAas(String shellId) { - boolean isAuthorized = permissionResolver.hasPermission(Action.DELETE, new AasTargetInformation(shellId)); + boolean isAuthorized = permissionResolver.hasPermission(Action.DELETE, new AasTargetInformation(getIdAsList(shellId))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -105,7 +107,7 @@ public void deleteAas(String shellId) { @Override public CursorResult> getSubmodelReferences(String shellId, PaginationInfo paginationInfo) { - boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new AasTargetInformation(shellId)); + boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new AasTargetInformation(getIdAsList(shellId))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -114,7 +116,7 @@ public CursorResult> getSubmodelReferences(String shellId, Pagin @Override public void addSubmodelReference(String shellId, Reference submodelReference) { - boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new AasTargetInformation(shellId)); + boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new AasTargetInformation(getIdAsList(shellId))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -123,7 +125,7 @@ public void addSubmodelReference(String shellId, Reference submodelReference) { @Override public void removeSubmodelReference(String shellId, String submodelId) { - boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new AasTargetInformation(shellId)); + boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new AasTargetInformation(getIdAsList(shellId))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -132,7 +134,7 @@ public void removeSubmodelReference(String shellId, String submodelId) { @Override public void setAssetInformation(String shellId, AssetInformation shellInfo) throws ElementDoesNotExistException { - boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new AasTargetInformation(shellId)); + boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new AasTargetInformation(getIdAsList(shellId))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -141,7 +143,7 @@ public void setAssetInformation(String shellId, AssetInformation shellInfo) thro @Override public AssetInformation getAssetInformation(String shellId) throws ElementDoesNotExistException { - boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new AasTargetInformation(shellId)); + boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new AasTargetInformation(getIdAsList(shellId))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -149,30 +151,30 @@ public AssetInformation getAssetInformation(String shellId) throws ElementDoesNo } @Override - public File getThumbnail(String aasId) { - boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new AasTargetInformation(aasId)); + public File getThumbnail(String shellId) { + boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new AasTargetInformation(getIdAsList(shellId))); throwExceptionIfInsufficientPermission(isAuthorized); - return decorated.getThumbnail(aasId); + return decorated.getThumbnail(shellId); } @Override - public void setThumbnail(String aasId, String fileName, String contentType, InputStream inputStream) { - boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new AasTargetInformation(aasId)); + public void setThumbnail(String shellId, String fileName, String contentType, InputStream inputStream) { + boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new AasTargetInformation(getIdAsList(shellId))); throwExceptionIfInsufficientPermission(isAuthorized); - decorated.setThumbnail(aasId, fileName, contentType, inputStream); + decorated.setThumbnail(shellId, fileName, contentType, inputStream); } @Override - public void deleteThumbnail(String aasId) { - boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new AasTargetInformation(aasId)); + public void deleteThumbnail(String shellId) { + boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new AasTargetInformation(getIdAsList(shellId))); throwExceptionIfInsufficientPermission(isAuthorized); - decorated.deleteThumbnail(aasId); + decorated.deleteThumbnail(shellId); } @Override @@ -180,6 +182,10 @@ public String getName() { return decorated.getName(); } + private ArrayList getIdAsList(String id) { + return new ArrayList<>(Arrays.asList(id)); + } + private void throwExceptionIfInsufficientPermission(boolean isAuthorized) { if (!isAuthorized) throw new InsufficientPermissionException("Insufficient Permission: The current subject does not have the required permissions for this operation."); diff --git a/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/rbac/AasTargetPermissionVerifier.java b/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/rbac/AasTargetPermissionVerifier.java index a69d3b881..f71b454b9 100644 --- a/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/rbac/AasTargetPermissionVerifier.java +++ b/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/rbac/AasTargetPermissionVerifier.java @@ -25,6 +25,8 @@ package org.eclipse.digitaltwin.basyx.aasrepository.feature.authorization.rbac; +import java.util.List; + import org.eclipse.digitaltwin.basyx.aasrepository.feature.authorization.AasTargetInformation; import org.eclipse.digitaltwin.basyx.authorization.rbac.RbacRule; import org.eclipse.digitaltwin.basyx.authorization.rbac.TargetPermissionVerifier; @@ -40,11 +42,23 @@ public class AasTargetPermissionVerifier implements TargetPermissionVerifier targetInformationShellIds = targetInformation.getAasIds(); AasTargetInformation rbacRuleAasTargetInformation = (AasTargetInformation) rbacRule.getTargetInformation(); - return rbacRuleAasTargetInformation.getAasId().equals(ALL_ALLOWED_WILDCARD) || rbacRuleAasTargetInformation.getAasId().equals(shellId); + List rbacRuleShellIds = rbacRuleAasTargetInformation.getAasIds(); + + return areShellsAllowed(rbacRuleShellIds, targetInformationShellIds); + } + + private boolean areShellsAllowed(List rbacRuleShellIds, List targetInformationShellIds) { + + return allShellsAllowed(rbacRuleShellIds) || rbacRuleShellIds.containsAll(targetInformationShellIds); + } + + private boolean allShellsAllowed(List rbacRuleShellIds) { + + return rbacRuleShellIds.size() == 1 && rbacRuleShellIds.get(0).equals(ALL_ALLOWED_WILDCARD); } } diff --git a/basyx.aasrepository/basyx.aasrepository.component/src/main/resources/rbac_rules.json b/basyx.aasrepository/basyx.aasrepository.component/src/main/resources/rbac_rules.json index 26082c0fc..6bf880c8d 100644 --- a/basyx.aasrepository/basyx.aasrepository.component/src/main/resources/rbac_rules.json +++ b/basyx.aasrepository/basyx.aasrepository.component/src/main/resources/rbac_rules.json @@ -4,7 +4,7 @@ "action": "READ", "targetInformation": { "@type": "aas", - "aasId": "*" + "aasIds": "*" } }, { @@ -12,7 +12,7 @@ "action": ["CREATE", "READ", "UPDATE", "DELETE"], "targetInformation": { "@type": "aas", - "aasId": "*" + "aasIds": "*" } }, { @@ -20,7 +20,7 @@ "action": "READ", "targetInformation": { "@type": "aas", - "aasId": "specificAasId" + "aasIds": ["testAasId1", "specificAasId", "testAasId2"] } }, { @@ -28,7 +28,7 @@ "action": "CREATE", "targetInformation": { "@type": "aas", - "aasId": "*" + "aasIds": "*" } }, { @@ -36,7 +36,7 @@ "action": "UPDATE", "targetInformation": { "@type": "aas", - "aasId": "*" + "aasIds": "*" } }, { @@ -44,7 +44,7 @@ "action": "UPDATE", "targetInformation": { "@type": "aas", - "aasId": "specificAasId" + "aasIds": "specificAasId" } }, { @@ -52,7 +52,7 @@ "action": "UPDATE", "targetInformation": { "@type": "aas", - "aasId": "*" + "aasIds": "*" } }, { @@ -60,7 +60,7 @@ "action": "UPDATE", "targetInformation": { "@type": "aas", - "aasId": "specificAasId-2" + "aasIds": "specificAasId-2" } }, { @@ -68,7 +68,7 @@ "action": "DELETE", "targetInformation": { "@type": "aas", - "aasId": "*" + "aasIds": "*" } }, { @@ -76,7 +76,7 @@ "action": "DELETE", "targetInformation": { "@type": "aas", - "aasId": "specificAasId-2" + "aasIds": "specificAasId-2" } } ] \ No newline at end of file diff --git a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/Readme.md b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/Readme.md index 83c3fe9c1..284d7c821 100644 --- a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/Readme.md +++ b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/Readme.md @@ -37,7 +37,7 @@ For configuring RBAC rules, all the rbac rules should be configured inside a jso "action": "READ", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -45,7 +45,7 @@ For configuring RBAC rules, all the rbac rules should be configured inside a jso "action": ["CREATE", "READ", "UPDATE", "DELETE"], "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -53,15 +53,25 @@ For configuring RBAC rules, all the rbac rules should be configured inside a jso "action": "DELETE", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } + }, + { + "role": "basyx-reader-two", + "action": "READ", + "targetInformation": { + "@type": "concept-description", + "conceptDescriptionIds": ["testCDId1","specificConceptDescriptionId","testCDId2"] + } + } } ] ``` The role defines which role is allowed to perform the defined actions. The role is as per the configuration of identity providers or based on the organization. Action could be CREATE, READ, UPDATE, DELETE, and EXECUTE, there could be a single action or multiple actions as a list (cf. admin configuration above). -The targetInformation defines coarse-grained control over the resource, you may define the conceptDescriptionId 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. +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. diff --git a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/AuthorizedConceptDescriptionRepository.java b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/AuthorizedConceptDescriptionRepository.java index 24a81244f..ec181040c 100644 --- a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/AuthorizedConceptDescriptionRepository.java +++ b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/AuthorizedConceptDescriptionRepository.java @@ -25,6 +25,8 @@ package org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.feature.authorization; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.eclipse.digitaltwin.aas4j.v3.model.ConceptDescription; @@ -57,7 +59,7 @@ public AuthorizedConceptDescriptionRepository(ConceptDescriptionRepository decor @Override public CursorResult> getAllConceptDescriptions(PaginationInfo pInfo) { - boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new ConceptDescriptionTargetInformation("*")); + boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new ConceptDescriptionTargetInformation(getIdAsList("*"))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -66,7 +68,7 @@ public CursorResult> getAllConceptDescriptions(Paginati @Override public CursorResult> getAllConceptDescriptionsByIdShort(String idShort, PaginationInfo pInfo) { - boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new ConceptDescriptionTargetInformation("*")); + boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new ConceptDescriptionTargetInformation(getIdAsList("*"))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -75,7 +77,7 @@ public CursorResult> getAllConceptDescriptionsByIdShort @Override public CursorResult> getAllConceptDescriptionsByIsCaseOf(Reference isCaseOf, PaginationInfo pInfo) { - boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new ConceptDescriptionTargetInformation("*")); + boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new ConceptDescriptionTargetInformation(getIdAsList("*"))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -84,7 +86,7 @@ public CursorResult> getAllConceptDescriptionsByIsCaseO @Override public CursorResult> getAllConceptDescriptionsByDataSpecificationReference(Reference dataSpecificationReference, PaginationInfo pInfo) { - boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new ConceptDescriptionTargetInformation("*")); + boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new ConceptDescriptionTargetInformation(getIdAsList("*"))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -93,7 +95,7 @@ public CursorResult> getAllConceptDescriptionsByDataSpe @Override public ConceptDescription getConceptDescription(String conceptDescriptionId) throws ElementDoesNotExistException { - boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new ConceptDescriptionTargetInformation(conceptDescriptionId)); + boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new ConceptDescriptionTargetInformation(getIdAsList(conceptDescriptionId))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -102,7 +104,7 @@ public ConceptDescription getConceptDescription(String conceptDescriptionId) thr @Override public void updateConceptDescription(String conceptDescriptionId, ConceptDescription conceptDescription) throws ElementDoesNotExistException { - boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new ConceptDescriptionTargetInformation(conceptDescriptionId)); + boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new ConceptDescriptionTargetInformation(getIdAsList(conceptDescriptionId))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -111,7 +113,7 @@ public void updateConceptDescription(String conceptDescriptionId, ConceptDescrip @Override public void createConceptDescription(ConceptDescription conceptDescription) throws CollidingIdentifierException, MissingIdentifierException { - boolean isAuthorized = permissionResolver.hasPermission(Action.CREATE, new ConceptDescriptionTargetInformation(conceptDescription.getId())); + boolean isAuthorized = permissionResolver.hasPermission(Action.CREATE, new ConceptDescriptionTargetInformation(getIdAsList(conceptDescription.getId()))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -121,12 +123,16 @@ public void createConceptDescription(ConceptDescription conceptDescription) thro @Override public void deleteConceptDescription(String conceptDescriptionId) throws ElementDoesNotExistException { - boolean isAuthorized = permissionResolver.hasPermission(Action.DELETE, new ConceptDescriptionTargetInformation(conceptDescriptionId)); + boolean isAuthorized = permissionResolver.hasPermission(Action.DELETE, new ConceptDescriptionTargetInformation(getIdAsList(conceptDescriptionId))); throwExceptionIfInsufficientPermission(isAuthorized); decorated.deleteConceptDescription(conceptDescriptionId); } + + private List getIdAsList(String id) { + return new ArrayList<>(Arrays.asList(id)); + } private void throwExceptionIfInsufficientPermission(boolean isAuthorized) { if (!isAuthorized) diff --git a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/ConceptDescriptionTargetInformation.java b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/ConceptDescriptionTargetInformation.java index 8a91d14fd..d6a8f154b 100644 --- a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/ConceptDescriptionTargetInformation.java +++ b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/ConceptDescriptionTargetInformation.java @@ -27,6 +27,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; @@ -41,23 +42,23 @@ @TargetInformationSubtype(getValue = "concept-description") public class ConceptDescriptionTargetInformation implements TargetInformation { - private String conceptDescriptionId; + private List conceptDescriptionIds; @JsonCreator - public ConceptDescriptionTargetInformation(final @JsonProperty("conceptDescriptionId") String conceptDescriptionId) { - this.conceptDescriptionId = conceptDescriptionId; + public ConceptDescriptionTargetInformation(final @JsonProperty("conceptDescriptionIds") List conceptDescriptionIds) { + this.conceptDescriptionIds = conceptDescriptionIds; } @Override public Map toMap() { final Map map = new HashMap<>(); - map.put("conceptDescriptionId", conceptDescriptionId); + map.put("conceptDescriptionIds", conceptDescriptionIds); return map; } @Override public int hashCode() { - return Objects.hash(conceptDescriptionId); + return Objects.hash(conceptDescriptionIds); } @Override @@ -69,16 +70,16 @@ public boolean equals(Object obj) { if (getClass() != obj.getClass()) return false; ConceptDescriptionTargetInformation other = (ConceptDescriptionTargetInformation) obj; - return Objects.equals(conceptDescriptionId, other.conceptDescriptionId); + return Objects.equals(conceptDescriptionIds, other.conceptDescriptionIds); } @Override public String toString() { - return "ConceptDescriptionTargetInformation [conceptDescriptionId=" + conceptDescriptionId + "]"; + return "ConceptDescriptionTargetInformation [conceptDescriptionIds=" + conceptDescriptionIds + "]"; } - public String getConceptDescriptionId() { - return conceptDescriptionId; + public List getConceptDescriptionIds() { + return conceptDescriptionIds; } } diff --git a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/rbac/ConceptDescriptionTargetPermissionVerifier.java b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/rbac/ConceptDescriptionTargetPermissionVerifier.java index d516c8095..dd2ed3d3a 100644 --- a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/rbac/ConceptDescriptionTargetPermissionVerifier.java +++ b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/rbac/ConceptDescriptionTargetPermissionVerifier.java @@ -29,6 +29,8 @@ import org.eclipse.digitaltwin.basyx.authorization.rbac.TargetPermissionVerifier; import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.feature.authorization.ConceptDescriptionTargetInformation; +import java.util.List; + /** * Verifies the {@link ConceptDescriptionTargetInformation} against the {@link RbacRule} * @@ -40,11 +42,23 @@ public class ConceptDescriptionTargetPermissionVerifier implements TargetPermiss @Override public boolean isVerified(RbacRule rbacRule, ConceptDescriptionTargetInformation targetInformation) { - String conceptDescriptionId = targetInformation.getConceptDescriptionId(); + List targetInformationConceptDescriptionIds = targetInformation.getConceptDescriptionIds(); ConceptDescriptionTargetInformation rbacRuleConceptDescriptionTargetInformation = (ConceptDescriptionTargetInformation) rbacRule.getTargetInformation(); - - return rbacRuleConceptDescriptionTargetInformation.getConceptDescriptionId().equals(ALL_ALLOWED_WILDCARD) || rbacRuleConceptDescriptionTargetInformation.getConceptDescriptionId().equals(conceptDescriptionId); + + List rbacRuleConceptDescriptionIds = rbacRuleConceptDescriptionTargetInformation.getConceptDescriptionIds(); + + return areConceptDescriptionsAllowed(rbacRuleConceptDescriptionIds, targetInformationConceptDescriptionIds); + } + + private boolean areConceptDescriptionsAllowed(List rbacRuleConceptDescriptionIds, List targetInformationConceptDescriptionIds) { + + return allConceptDescriptionsAllowed(rbacRuleConceptDescriptionIds) || rbacRuleConceptDescriptionIds.containsAll(targetInformationConceptDescriptionIds); + } + + private boolean allConceptDescriptionsAllowed(List rbacRuleShellIds) { + + return rbacRuleShellIds.size() == 1 && rbacRuleShellIds.get(0).equals(ALL_ALLOWED_WILDCARD); } } diff --git a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository.component/src/main/resources/rbac_rules.json b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository.component/src/main/resources/rbac_rules.json index 4aea91ffc..353684baa 100644 --- a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository.component/src/main/resources/rbac_rules.json +++ b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository.component/src/main/resources/rbac_rules.json @@ -4,7 +4,7 @@ "action": "READ", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -12,7 +12,7 @@ "action": ["CREATE", "READ", "UPDATE", "DELETE"], "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -20,7 +20,7 @@ "action": "READ", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "specificConceptDescriptionId" + "conceptDescriptionIds": ["testCDId1","specificConceptDescriptionId","testCDId2"] } }, { @@ -28,7 +28,7 @@ "action": "CREATE", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -36,7 +36,7 @@ "action": "UPDATE", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -44,7 +44,7 @@ "action": "UPDATE", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "specificConceptDescriptionId" + "conceptDescriptionIds": "specificConceptDescriptionId" } }, { @@ -52,7 +52,7 @@ "action": "DELETE", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "*" + "conceptDescriptionIds": "*" } }, { @@ -60,7 +60,7 @@ "action": "DELETE", "targetInformation": { "@type": "concept-description", - "conceptDescriptionId": "specificConceptDescriptionId-2" + "conceptDescriptionIds": "specificConceptDescriptionId-2" } } ] \ No newline at end of file diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/Readme.md b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/Readme.md index 306f0fbab..f4b1d1a6c 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/Readme.md +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/Readme.md @@ -37,8 +37,8 @@ For configuring RBAC rules, all the rbac rules should be configured inside a jso "action": "READ", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -46,8 +46,8 @@ For configuring RBAC rules, all the rbac rules should be configured inside a jso "action": ["CREATE", "READ", "UPDATE", "DELETE", "EXECUTE"], "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -55,8 +55,8 @@ For configuring RBAC rules, all the rbac rules should be configured inside a jso "action": "READ", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId", - "submodelElementIdShortPath": "*" + "submodelIds": "specificSubmodelId", + "submodelElementIdShortPaths": "*" } }, { @@ -64,8 +64,8 @@ For configuring RBAC rules, all the rbac rules should be configured inside a jso "action": "READ", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId", - "submodelElementIdShortPath": "smc2.specificSubmodelElementIdShort" + "submodelIds": ["specificSubmodelId", "testSMId1", "testSMId2"], + "submodelElementIdShortPaths": ["testSMEIdShortPath1","smc2.specificSubmodelElementIdShort","testSMEIdShortPath2"] } } ] @@ -73,7 +73,8 @@ For configuring RBAC rules, all the rbac rules should be configured inside a jso The role defines which role is allowed to perform the defined actions. The role is as per the configuration of identity providers or based on the organization. Action could be CREATE, READ, UPDATE, DELETE, and EXECUTE, there could be a single action or multiple actions as a list (cf. admin configuration above). -The targetInformation defines coarse-grained control over the resource, you may define the submodelId and submodelElementIdShortPath 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. +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. diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/AuthorizedSubmodelRepository.java b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/AuthorizedSubmodelRepository.java index 270a15b7c..bf221b333 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/AuthorizedSubmodelRepository.java +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/AuthorizedSubmodelRepository.java @@ -27,6 +27,8 @@ import java.io.File; import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.eclipse.digitaltwin.aas4j.v3.model.OperationVariable; @@ -64,7 +66,7 @@ public AuthorizedSubmodelRepository(SubmodelRepository decorated, RbacPermission @Override public CursorResult> getAllSubmodels(PaginationInfo pInfo) { - boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(ALL_ALLOWED_WILDCARD, ALL_ALLOWED_WILDCARD)); + boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(getIdAsList(ALL_ALLOWED_WILDCARD), getIdAsList(ALL_ALLOWED_WILDCARD))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -73,7 +75,7 @@ public CursorResult> getAllSubmodels(PaginationInfo pInfo) { @Override public Submodel getSubmodel(String submodelId) throws ElementDoesNotExistException { - boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(submodelId, ALL_ALLOWED_WILDCARD)); + boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(getIdAsList(submodelId), getIdAsList(ALL_ALLOWED_WILDCARD))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -82,7 +84,7 @@ public Submodel getSubmodel(String submodelId) throws ElementDoesNotExistExcepti @Override public void updateSubmodel(String submodelId, Submodel submodel) throws ElementDoesNotExistException { - boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(submodelId, ALL_ALLOWED_WILDCARD)); + boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(getIdAsList(submodelId), getIdAsList(ALL_ALLOWED_WILDCARD))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -91,7 +93,7 @@ public void updateSubmodel(String submodelId, Submodel submodel) throws ElementD @Override public void createSubmodel(Submodel submodel) throws CollidingIdentifierException { - boolean isAuthorized = permissionResolver.hasPermission(Action.CREATE, new SubmodelTargetInformation(submodel.getId(), ALL_ALLOWED_WILDCARD)); + boolean isAuthorized = permissionResolver.hasPermission(Action.CREATE, new SubmodelTargetInformation(getIdAsList(submodel.getId()), getIdAsList(ALL_ALLOWED_WILDCARD))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -100,7 +102,7 @@ public void createSubmodel(Submodel submodel) throws CollidingIdentifierExceptio @Override public void deleteSubmodel(String submodelId) throws ElementDoesNotExistException { - boolean isAuthorized = permissionResolver.hasPermission(Action.DELETE, new SubmodelTargetInformation(submodelId, ALL_ALLOWED_WILDCARD)); + boolean isAuthorized = permissionResolver.hasPermission(Action.DELETE, new SubmodelTargetInformation(getIdAsList(submodelId), getIdAsList(ALL_ALLOWED_WILDCARD))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -109,7 +111,7 @@ public void deleteSubmodel(String submodelId) throws ElementDoesNotExistExceptio @Override public CursorResult> getSubmodelElements(String submodelId, PaginationInfo pInfo) throws ElementDoesNotExistException { - boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(submodelId, ALL_ALLOWED_WILDCARD)); + boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(getIdAsList(submodelId), getIdAsList(ALL_ALLOWED_WILDCARD))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -118,7 +120,7 @@ public CursorResult> getSubmodelElements(String submodelId @Override public SubmodelElement getSubmodelElement(String submodelId, String smeIdShortPath) throws ElementDoesNotExistException { - boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(submodelId, smeIdShortPath)); + boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(getIdAsList(submodelId), getIdAsList(smeIdShortPath))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -127,7 +129,7 @@ public SubmodelElement getSubmodelElement(String submodelId, String smeIdShortPa @Override public SubmodelElementValue getSubmodelElementValue(String submodelId, String smeIdShortPath) throws ElementDoesNotExistException { - boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(submodelId, smeIdShortPath)); + boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(getIdAsList(submodelId), getIdAsList(smeIdShortPath))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -136,7 +138,7 @@ public SubmodelElementValue getSubmodelElementValue(String submodelId, String sm @Override public void setSubmodelElementValue(String submodelId, String smeIdShortPath, SubmodelElementValue value) throws ElementDoesNotExistException { - boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(submodelId, smeIdShortPath)); + boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(getIdAsList(submodelId), getIdAsList(smeIdShortPath))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -145,7 +147,7 @@ public void setSubmodelElementValue(String submodelId, String smeIdShortPath, Su @Override public void createSubmodelElement(String submodelId, SubmodelElement smElement) { - boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(submodelId, ALL_ALLOWED_WILDCARD)); + boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(getIdAsList(submodelId), getIdAsList(ALL_ALLOWED_WILDCARD))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -154,7 +156,7 @@ public void createSubmodelElement(String submodelId, SubmodelElement smElement) @Override public void createSubmodelElement(String submodelId, String idShortPath, SubmodelElement smElement) throws ElementDoesNotExistException { - boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(submodelId, idShortPath)); + boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(getIdAsList(submodelId), getIdAsList(idShortPath))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -163,7 +165,7 @@ public void createSubmodelElement(String submodelId, String idShortPath, Submode @Override public void updateSubmodelElement(String submodelId, String idShortPath, SubmodelElement submodelElement) throws ElementDoesNotExistException { - boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(submodelId, idShortPath)); + boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(getIdAsList(submodelId), getIdAsList(idShortPath))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -172,7 +174,7 @@ public void updateSubmodelElement(String submodelId, String idShortPath, Submode @Override public void deleteSubmodelElement(String submodelId, String idShortPath) throws ElementDoesNotExistException { - boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(submodelId, idShortPath)); + boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(getIdAsList(submodelId), getIdAsList(idShortPath))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -181,7 +183,7 @@ public void deleteSubmodelElement(String submodelId, String idShortPath) throws @Override public OperationVariable[] invokeOperation(String submodelId, String idShortPath, OperationVariable[] input) throws ElementDoesNotExistException { - boolean isAuthorized = permissionResolver.hasPermission(Action.EXECUTE, new SubmodelTargetInformation(submodelId, idShortPath)); + boolean isAuthorized = permissionResolver.hasPermission(Action.EXECUTE, new SubmodelTargetInformation(getIdAsList(submodelId), getIdAsList(idShortPath))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -190,7 +192,7 @@ public OperationVariable[] invokeOperation(String submodelId, String idShortPath @Override public SubmodelValueOnly getSubmodelByIdValueOnly(String submodelId) throws ElementDoesNotExistException { - boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(submodelId, ALL_ALLOWED_WILDCARD)); + boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(getIdAsList(submodelId), getIdAsList(ALL_ALLOWED_WILDCARD))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -199,7 +201,7 @@ public SubmodelValueOnly getSubmodelByIdValueOnly(String submodelId) throws Elem @Override public Submodel getSubmodelByIdMetadata(String submodelId) throws ElementDoesNotExistException { - boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(submodelId, ALL_ALLOWED_WILDCARD)); + boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(getIdAsList(submodelId), getIdAsList(ALL_ALLOWED_WILDCARD))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -208,7 +210,7 @@ public Submodel getSubmodelByIdMetadata(String submodelId) throws ElementDoesNot @Override public File getFileByPathSubmodel(String submodelId, String idShortPath) throws ElementDoesNotExistException, ElementNotAFileException, FileDoesNotExistException { - boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(submodelId, idShortPath)); + boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(getIdAsList(submodelId), getIdAsList(idShortPath))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -217,7 +219,7 @@ public File getFileByPathSubmodel(String submodelId, String idShortPath) throws @Override public void setFileValue(String submodelId, String idShortPath, String fileName, InputStream inputStream) throws ElementDoesNotExistException, ElementNotAFileException { - boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(submodelId, idShortPath)); + boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(getIdAsList(submodelId), getIdAsList(idShortPath))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -226,7 +228,7 @@ public void setFileValue(String submodelId, String idShortPath, String fileName, @Override public void deleteFileValue(String submodelId, String idShortPath) throws ElementDoesNotExistException, ElementNotAFileException, FileDoesNotExistException { - boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(submodelId, idShortPath)); + boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(getIdAsList(submodelId), getIdAsList(idShortPath))); throwExceptionIfInsufficientPermission(isAuthorized); @@ -235,12 +237,16 @@ public void deleteFileValue(String submodelId, String idShortPath) throws Elemen @Override public void patchSubmodelElements(String submodelId, List submodelElementList) { - boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(submodelId, ALL_ALLOWED_WILDCARD)); + boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(getIdAsList(submodelId), getIdAsList(ALL_ALLOWED_WILDCARD))); throwExceptionIfInsufficientPermission(isAuthorized); decorated.patchSubmodelElements(submodelId, submodelElementList); } + + private List getIdAsList(String id) { + return new ArrayList<>(Arrays.asList(id)); + } private void throwExceptionIfInsufficientPermission(boolean isAuthorized) { if (!isAuthorized) diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/SubmodelTargetInformation.java b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/SubmodelTargetInformation.java index ebf5182e8..4c6fb53f2 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/SubmodelTargetInformation.java +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/SubmodelTargetInformation.java @@ -27,6 +27,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; @@ -41,26 +42,26 @@ @TargetInformationSubtype(getValue = "submodel") public class SubmodelTargetInformation implements TargetInformation { - private String submodelId; - private String submodelElementIdShortPath; + private List submodelIds; + private List submodelElementIdShortPaths; @JsonCreator - public SubmodelTargetInformation(final @JsonProperty("submodelId") String submodelId, final @JsonProperty("submodelElementIdShortPath") String submodelElementIdShortPath) { - this.submodelId = submodelId; - this.submodelElementIdShortPath = submodelElementIdShortPath; + public SubmodelTargetInformation(final @JsonProperty("submodelIds") List submodelIds, final @JsonProperty("submodelElementIdShortPaths") List submodelElementIdShortPaths) { + this.submodelIds = submodelIds; + this.submodelElementIdShortPaths = submodelElementIdShortPaths; } @Override public Map toMap() { final Map map = new HashMap<>(); - map.put("submodelId", submodelId); - map.put("submodelElementIdShortPath", submodelElementIdShortPath); + map.put("submodelIds", submodelIds); + map.put("submodelElementIdShortPaths", submodelElementIdShortPaths); return map; } @Override public int hashCode() { - return Objects.hash(submodelElementIdShortPath, submodelId); + return Objects.hash(submodelElementIdShortPaths, submodelIds); } @Override @@ -72,20 +73,20 @@ public boolean equals(Object obj) { if (getClass() != obj.getClass()) return false; SubmodelTargetInformation other = (SubmodelTargetInformation) obj; - return Objects.equals(submodelElementIdShortPath, other.submodelElementIdShortPath) && Objects.equals(submodelId, other.submodelId); + return Objects.equals(submodelElementIdShortPaths, other.submodelElementIdShortPaths) && Objects.equals(submodelIds, other.submodelIds); } @Override public String toString() { - return "SubmodelTargetInformation [submodelId=" + submodelId + ", submodelElementIdShortPath=" + submodelElementIdShortPath + "]"; + return "SubmodelTargetInformation [submodelIds=" + submodelIds + ", submodelElementIdShortPaths=" + submodelElementIdShortPaths + "]"; } - public String getSubmodelId() { - return submodelId; + public List getSubmodelIds() { + return submodelIds; } - public String getSubmodelElementIdShortPath() { - return submodelElementIdShortPath; + public List getSubmodelElementIdShortPaths() { + return submodelElementIdShortPaths; } } diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/rbac/SubmodelTargetPermissionVerifier.java b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/rbac/SubmodelTargetPermissionVerifier.java index e18e8f9f4..f543c4b8b 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/rbac/SubmodelTargetPermissionVerifier.java +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/rbac/SubmodelTargetPermissionVerifier.java @@ -25,6 +25,8 @@ package org.eclipse.digitaltwin.basyx.submodelrepository.feature.authorization.rbac; +import java.util.List; + import org.eclipse.digitaltwin.basyx.authorization.rbac.RbacRule; import org.eclipse.digitaltwin.basyx.authorization.rbac.TargetPermissionVerifier; import org.eclipse.digitaltwin.basyx.submodelrepository.feature.authorization.SubmodelTargetInformation; @@ -41,15 +43,28 @@ public class SubmodelTargetPermissionVerifier implements TargetPermissionVerifie @Override public boolean isVerified(RbacRule rbacRule, SubmodelTargetInformation targetInformation) { - String submodelId = targetInformation.getSubmodelId(); - String submodelElementIdShortPath = targetInformation.getSubmodelElementIdShortPath(); + List targetInformationSubmodelIds = targetInformation.getSubmodelIds(); + List targetInformationSubmodelElementIdShortPath = targetInformation.getSubmodelElementIdShortPaths(); SubmodelTargetInformation rbacRuleSubmodelTargetInformation = (SubmodelTargetInformation) rbacRule.getTargetInformation(); + + List rbacRuleSubmodelIds = rbacRuleSubmodelTargetInformation.getSubmodelIds(); + List rbacRuleSubmodelElementIdShortPath = rbacRuleSubmodelTargetInformation.getSubmodelElementIdShortPaths(); - if (rbacRuleSubmodelTargetInformation.getSubmodelId().equals(ALL_ALLOWED_WILDCARD) || rbacRuleSubmodelTargetInformation.getSubmodelId().equals(submodelId)) - return rbacRuleSubmodelTargetInformation.getSubmodelElementIdShortPath().equals(ALL_ALLOWED_WILDCARD) || rbacRuleSubmodelTargetInformation.getSubmodelElementIdShortPath().equals(submodelElementIdShortPath); + if (areElementsAllowed(rbacRuleSubmodelIds, targetInformationSubmodelIds)) + return areElementsAllowed(rbacRuleSubmodelElementIdShortPath, targetInformationSubmodelElementIdShortPath); return false; } + + private boolean areElementsAllowed(List rbacRuleIds, List targetInformationIds) { + + return allElementsAllowed(rbacRuleIds) || rbacRuleIds.containsAll(targetInformationIds); + } + + private boolean allElementsAllowed(List rbacRuleIds) { + + return rbacRuleIds.size() == 1 && rbacRuleIds.get(0).equals(ALL_ALLOWED_WILDCARD); + } } diff --git a/basyx.submodelrepository/basyx.submodelrepository.component/src/main/resources/rbac_rules.json b/basyx.submodelrepository/basyx.submodelrepository.component/src/main/resources/rbac_rules.json index 11f37b9c1..ef11e18f6 100644 --- a/basyx.submodelrepository/basyx.submodelrepository.component/src/main/resources/rbac_rules.json +++ b/basyx.submodelrepository/basyx.submodelrepository.component/src/main/resources/rbac_rules.json @@ -4,8 +4,8 @@ "action": "READ", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -13,8 +13,8 @@ "action": ["CREATE", "READ", "UPDATE", "DELETE", "EXECUTE"], "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -22,8 +22,8 @@ "action": "READ", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId", - "submodelElementIdShortPath": "*" + "submodelIds": "specificSubmodelId", + "submodelElementIdShortPaths": "*" } }, { @@ -31,8 +31,8 @@ "action": "READ", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId", - "submodelElementIdShortPath": "smc2.specificSubmodelElementIdShort" + "submodelIds": ["specificSubmodelId", "testSMId1", "testSMId2"], + "submodelElementIdShortPaths": ["testSMEIdShortPath1","smc2.specificSubmodelElementIdShort","testSMEIdShortPath2"] } }, { @@ -40,8 +40,8 @@ "action": "READ", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId", - "submodelElementIdShortPath": "smc2.specificFileSubmodelElementIdShort" + "submodelIds": "specificSubmodelId", + "submodelElementIdShortPaths": "smc2.specificFileSubmodelElementIdShort" } }, { @@ -49,8 +49,8 @@ "action": "CREATE", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -58,8 +58,8 @@ "action": "UPDATE", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -67,8 +67,8 @@ "action": "UPDATE", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId", - "submodelElementIdShortPath": "*" + "submodelIds": "specificSubmodelId", + "submodelElementIdShortPaths": "*" } }, { @@ -76,8 +76,8 @@ "action": "UPDATE", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId", - "submodelElementIdShortPath": "smc2.specificFileSubmodelElementIdShort" + "submodelIds": "specificSubmodelId", + "submodelElementIdShortPaths": "smc2.specificFileSubmodelElementIdShort" } }, { @@ -85,8 +85,8 @@ "action": "UPDATE", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId", - "submodelElementIdShortPath": "smc2" + "submodelIds": "specificSubmodelId", + "submodelElementIdShortPaths": "smc2" } }, { @@ -94,8 +94,8 @@ "action": "UPDATE", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId-2", - "submodelElementIdShortPath": "smc1.specificSubmodelElementIdShort-2" + "submodelIds": "specificSubmodelId-2", + "submodelElementIdShortPaths": "smc1.specificSubmodelElementIdShort-2" } }, { @@ -103,8 +103,8 @@ "action": "UPDATE", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId-2", - "submodelElementIdShortPath": "smc2.specificFileSubmodelElementIdShort" + "submodelIds": "specificSubmodelId-2", + "submodelElementIdShortPaths": "smc2.specificFileSubmodelElementIdShort" } }, { @@ -112,8 +112,8 @@ "action": "DELETE", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -121,8 +121,8 @@ "action": "DELETE", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId-2", - "submodelElementIdShortPath": "*" + "submodelIds": "specificSubmodelId-2", + "submodelElementIdShortPaths": "*" } }, { @@ -130,8 +130,8 @@ "action": "EXECUTE", "targetInformation": { "@type": "submodel", - "submodelId": "*", - "submodelElementIdShortPath": "*" + "submodelIds": "*", + "submodelElementIdShortPaths": "*" } }, { @@ -139,8 +139,8 @@ "action": "EXECUTE", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId", - "submodelElementIdShortPath": "square" + "submodelIds": "specificSubmodelId", + "submodelElementIdShortPaths": "square" } }, { @@ -148,8 +148,8 @@ "action": "READ", "targetInformation": { "@type": "submodel", - "submodelId": "specificSubmodelId-2", - "submodelElementIdShortPath": "smc2.specificFileSubmodelElementIdShort" + "submodelIds": "specificSubmodelId-2", + "submodelElementIdShortPaths": "smc2.specificFileSubmodelElementIdShort" } } ] \ No newline at end of file