Skip to content

Commit

Permalink
Implements Patch Submodel Value Only (eclipse-basyx#230)
Browse files Browse the repository at this point in the history
* Adds Patch SubmodelValueOnly Endpoint to service and repository

Signed-off-by: Jannik Fried <[email protected]>

* Adds missing JSON files

Signed-off-by: Jannik Fried <[email protected]>

* Fixes tests

Signed-off-by: Jannik Fried <[email protected]>

* Replaces wrong JSON file

Signed-off-by: Jannik Fried <[email protected]>

* Applies changes according to code review

Signed-off-by: Jannik Fried <[email protected]>

* Update Readme.md

* Moves endpoint to correct row

* Adapts test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/AuthorizedSubmodelRepositoryTestSuite.java

Signed-off-by: Jannik Fried <[email protected]>

---------

Signed-off-by: Jannik Fried <[email protected]>
  • Loading branch information
FriedJannik authored Mar 8, 2024
1 parent e6e375a commit 67a2fcc
Show file tree
Hide file tree
Showing 21 changed files with 968 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -438,4 +438,11 @@ private void throwIfSubmodelDoesNotExist(String submodelId) {
throw new ElementDoesNotExistException(submodelId);
}

@Override
public void patchSubmodelElements(String submodelId, List<SubmodelElement> submodelElementList) {
Submodel submodel = getSubmodel(submodelId);
submodel.setSubmodelElements(submodelElementList);
submodelBackend.save(submodel);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -240,4 +240,9 @@ private RuntimeException mapExceptionSubmodelAccess(String submodelId, ApiExcept
return e;
}

@Override
public void patchSubmodelElements(String submodelId, List<SubmodelElement> submodelElementList) {
throw new FeatureNotImplementedException();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -263,4 +263,13 @@ public default String getName() {
* @throws FileDoesNotExistException
*/
public void deleteFileValue(String submodelId, String idShortPath) throws ElementDoesNotExistException, ElementNotAFileException, FileDoesNotExistException;

/**
* Replaces the submodel elements in a submodel
*
* @param submodelId
* the Submodel id
* @param submodelElementList
*/
public void patchSubmodelElements(String submodelId, List<SubmodelElement> submodelElementList);
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,5 +110,9 @@ public OperationVariable[] invokeOperation(String idShortPath, OperationVariable
return repoApi.invokeOperation(submodelId, idShortPath, input);
}

@Override
public void patchSubmodelElements(List<SubmodelElement> submodelElementList) {
repoApi.patchSubmodelElements(submodelId, submodelElementList);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ 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 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.

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.

## Action table for RBAC

Expand All @@ -84,12 +84,11 @@ Below is a reference table that shows which actions are used in what endpoints o
| Action | Endpoint |
|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| READ | GET /submodels <br /> GET /submodels/{submodelIdentifier} <br /> GET /submodels/{submodelIdentifier}/$value <br /> GET /submodels/{submodelIdentifier}/$metadata <br /> GET /submodels/{submodelIdentifier}/submodel-elements <br /> GET /submodels/{submodelIdentifier}/submodel-elements/{idShortPath} <br /> GET /submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/$value <br /> GET /submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/attachment |
| CREATE | POST /submodels <br /> |
| UPDATE | PUT /submodels/{submodelIdentifier} <br /> PUT /submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/attachment <br /> POST /submodels/{submodelIdentifier}/submodel-elements/{idShortPath} <br /> POST /submodels/{submodelIdentifier}/submodel-elements <br /> PATCH /submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/$value <br /> DELETE /submodels/{submodelIdentifier}/submodel-elements/{idShortPath} <br /> DELETE /submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/attachment |
| CREATE | POST /submodels <br /> |
| UPDATE | PUT /submodels/{submodelIdentifier} <br /> PUT /submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/attachment <br /> POST /submodels/{submodelIdentifier}/submodel-elements/{idShortPath} <br /> POST /submodels/{submodelIdentifier}/submodel-elements <br /> PATCH /submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/$value <br /> PATCH /submodels/{submodelIdentifier}/$value <br /> DELETE /submodels/{submodelIdentifier}/submodel-elements/{idShortPath} <br /> DELETE /submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/attachment |
| DELETE | DELETE /submodels/{submodelIdentifier} |
| EXECUTE | POST /submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/invoke <br /> |


Note: The invoke operation is not supported currently for off-the-shelf component


Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ public class AuthorizedSubmodelRepository implements SubmodelRepository {
private static final String ALL_ALLOWED_WILDCARD = "*";
private SubmodelRepository decorated;
private RbacPermissionResolver<SubmodelTargetInformation> permissionResolver;


public AuthorizedSubmodelRepository(SubmodelRepository decorated, RbacPermissionResolver<SubmodelTargetInformation> permissionResolver) {
this.decorated = decorated;
Expand All @@ -66,174 +65,183 @@ public AuthorizedSubmodelRepository(SubmodelRepository decorated, RbacPermission
@Override
public CursorResult<List<Submodel>> getAllSubmodels(PaginationInfo pInfo) {
boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(ALL_ALLOWED_WILDCARD, ALL_ALLOWED_WILDCARD));

throwExceptionIfInsufficientPermission(isAuthorized);

return decorated.getAllSubmodels(pInfo);
}

@Override
public Submodel getSubmodel(String submodelId) throws ElementDoesNotExistException {
boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(submodelId, ALL_ALLOWED_WILDCARD));

throwExceptionIfInsufficientPermission(isAuthorized);

return decorated.getSubmodel(submodelId);
}

@Override
public void updateSubmodel(String submodelId, Submodel submodel) throws ElementDoesNotExistException {
boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(submodelId, ALL_ALLOWED_WILDCARD));

throwExceptionIfInsufficientPermission(isAuthorized);

decorated.updateSubmodel(submodelId, submodel);
}

@Override
public void createSubmodel(Submodel submodel) throws CollidingIdentifierException {
boolean isAuthorized = permissionResolver.hasPermission(Action.CREATE, new SubmodelTargetInformation(submodel.getId(), ALL_ALLOWED_WILDCARD));

throwExceptionIfInsufficientPermission(isAuthorized);

decorated.createSubmodel(submodel);
}

@Override
public void deleteSubmodel(String submodelId) throws ElementDoesNotExistException {
boolean isAuthorized = permissionResolver.hasPermission(Action.DELETE, new SubmodelTargetInformation(submodelId, ALL_ALLOWED_WILDCARD));

throwExceptionIfInsufficientPermission(isAuthorized);

decorated.deleteSubmodel(submodelId);
}

@Override
public CursorResult<List<SubmodelElement>> getSubmodelElements(String submodelId, PaginationInfo pInfo) throws ElementDoesNotExistException {
boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(submodelId, ALL_ALLOWED_WILDCARD));

throwExceptionIfInsufficientPermission(isAuthorized);

return decorated.getSubmodelElements(submodelId, pInfo);
}

@Override
public SubmodelElement getSubmodelElement(String submodelId, String smeIdShortPath) throws ElementDoesNotExistException {
boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(submodelId, smeIdShortPath));

throwExceptionIfInsufficientPermission(isAuthorized);

return decorated.getSubmodelElement(submodelId, smeIdShortPath);
}

@Override
public SubmodelElementValue getSubmodelElementValue(String submodelId, String smeIdShortPath) throws ElementDoesNotExistException {
boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(submodelId, smeIdShortPath));

throwExceptionIfInsufficientPermission(isAuthorized);

return decorated.getSubmodelElementValue(submodelId, smeIdShortPath);
}

@Override
public void setSubmodelElementValue(String submodelId, String smeIdShortPath, SubmodelElementValue value) throws ElementDoesNotExistException {
boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(submodelId, smeIdShortPath));

throwExceptionIfInsufficientPermission(isAuthorized);

decorated.setSubmodelElementValue(submodelId, smeIdShortPath, value);
}

@Override
public void createSubmodelElement(String submodelId, SubmodelElement smElement) {
boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(submodelId, ALL_ALLOWED_WILDCARD));

throwExceptionIfInsufficientPermission(isAuthorized);

decorated.createSubmodelElement(submodelId, smElement);
}

@Override
public void createSubmodelElement(String submodelId, String idShortPath, SubmodelElement smElement) throws ElementDoesNotExistException {
boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(submodelId, idShortPath));

throwExceptionIfInsufficientPermission(isAuthorized);

decorated.createSubmodelElement(submodelId, idShortPath, smElement);
}

@Override
public void updateSubmodelElement(String submodelId, String idShortPath, SubmodelElement submodelElement) throws ElementDoesNotExistException {
boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(submodelId, idShortPath));

throwExceptionIfInsufficientPermission(isAuthorized);

decorated.updateSubmodelElement(submodelId, idShortPath, submodelElement);
}

@Override
public void deleteSubmodelElement(String submodelId, String idShortPath) throws ElementDoesNotExistException {
boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(submodelId, idShortPath));

throwExceptionIfInsufficientPermission(isAuthorized);
decorated.deleteSubmodelElement(submodelId, idShortPath);

decorated.deleteSubmodelElement(submodelId, idShortPath);
}

@Override
public OperationVariable[] invokeOperation(String submodelId, String idShortPath, OperationVariable[] input) throws ElementDoesNotExistException {
boolean isAuthorized = permissionResolver.hasPermission(Action.EXECUTE, new SubmodelTargetInformation(submodelId, idShortPath));

throwExceptionIfInsufficientPermission(isAuthorized);

return decorated.invokeOperation(submodelId, idShortPath, input);
}

@Override
public SubmodelValueOnly getSubmodelByIdValueOnly(String submodelId) throws ElementDoesNotExistException {
boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(submodelId, ALL_ALLOWED_WILDCARD));

throwExceptionIfInsufficientPermission(isAuthorized);

return decorated.getSubmodelByIdValueOnly(submodelId);
}

@Override
public Submodel getSubmodelByIdMetadata(String submodelId) throws ElementDoesNotExistException {
boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(submodelId, ALL_ALLOWED_WILDCARD));

throwExceptionIfInsufficientPermission(isAuthorized);

return decorated.getSubmodelByIdMetadata(submodelId);
}

@Override
public File getFileByPathSubmodel(String submodelId, String idShortPath) throws ElementDoesNotExistException, ElementNotAFileException, FileDoesNotExistException {
boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(submodelId, idShortPath));

throwExceptionIfInsufficientPermission(isAuthorized);

return decorated.getFileByPathSubmodel(submodelId, idShortPath);
}

@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));

throwExceptionIfInsufficientPermission(isAuthorized);

decorated.setFileValue(submodelId, idShortPath, fileName, inputStream);
}

@Override
public void deleteFileValue(String submodelId, String idShortPath) throws ElementDoesNotExistException, ElementNotAFileException, FileDoesNotExistException {
boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(submodelId, idShortPath));

throwExceptionIfInsufficientPermission(isAuthorized);

decorated.deleteFileValue(submodelId, idShortPath);
}


@Override
public void patchSubmodelElements(String submodelId, List<SubmodelElement> submodelElementList) {
boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(submodelId, ALL_ALLOWED_WILDCARD));

throwExceptionIfInsufficientPermission(isAuthorized);

decorated.patchSubmodelElements(submodelId, submodelElementList);
}

private void throwExceptionIfInsufficientPermission(boolean isAuthorized) {
if (!isAuthorized)
throw new InsufficientPermissionException("Insufficient Permission: The current subject does not have the required permissions for this operation.");
Expand Down
Loading

0 comments on commit 67a2fcc

Please sign in to comment.