Skip to content

Commit 67a2fcc

Browse files
authored
Implements Patch Submodel Value Only (#230)
* 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]>
1 parent e6e375a commit 67a2fcc

File tree

21 files changed

+968
-74
lines changed

21 files changed

+968
-74
lines changed

basyx.submodelrepository/basyx.submodelrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/backend/CrudSubmodelRepository.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,4 +438,11 @@ private void throwIfSubmodelDoesNotExist(String submodelId) {
438438
throw new ElementDoesNotExistException(submodelId);
439439
}
440440

441+
@Override
442+
public void patchSubmodelElements(String submodelId, List<SubmodelElement> submodelElementList) {
443+
Submodel submodel = getSubmodel(submodelId);
444+
submodel.setSubmodelElements(submodelElementList);
445+
submodelBackend.save(submodel);
446+
}
447+
441448
}

basyx.submodelrepository/basyx.submodelrepository-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/ConnectedSubmodelRepository.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,4 +240,9 @@ private RuntimeException mapExceptionSubmodelAccess(String submodelId, ApiExcept
240240
return e;
241241
}
242242

243+
@Override
244+
public void patchSubmodelElements(String submodelId, List<SubmodelElement> submodelElementList) {
245+
throw new FeatureNotImplementedException();
246+
}
247+
243248
}

basyx.submodelrepository/basyx.submodelrepository-core/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/SubmodelRepository.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,4 +263,13 @@ public default String getName() {
263263
* @throws FileDoesNotExistException
264264
*/
265265
public void deleteFileValue(String submodelId, String idShortPath) throws ElementDoesNotExistException, ElementNotAFileException, FileDoesNotExistException;
266+
267+
/**
268+
* Replaces the submodel elements in a submodel
269+
*
270+
* @param submodelId
271+
* the Submodel id
272+
* @param submodelElementList
273+
*/
274+
public void patchSubmodelElements(String submodelId, List<SubmodelElement> submodelElementList);
266275
}

basyx.submodelrepository/basyx.submodelrepository-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/core/SubmodelRepositorySubmodelServiceWrapper.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,5 +110,9 @@ public OperationVariable[] invokeOperation(String idShortPath, OperationVariable
110110
return repoApi.invokeOperation(submodelId, idShortPath, input);
111111
}
112112

113+
@Override
114+
public void patchSubmodelElements(List<SubmodelElement> submodelElementList) {
115+
repoApi.patchSubmodelElements(submodelId, submodelElementList);
116+
}
113117

114118
}

basyx.submodelrepository/basyx.submodelrepository-feature-authorization/Readme.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ The role defines which role is allowed to perform the defined actions. The role
7575

7676
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.
7777

78-
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.
78+
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.
7979

8080
## Action table for RBAC
8181

@@ -84,12 +84,11 @@ Below is a reference table that shows which actions are used in what endpoints o
8484
| Action | Endpoint |
8585
|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
8686
| 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 |
87-
| CREATE | POST /submodels <br /> |
88-
| 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 |
87+
| CREATE | POST /submodels <br /> |
88+
| 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 |
8989
| DELETE | DELETE /submodels/{submodelIdentifier} |
9090
| EXECUTE | POST /submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/invoke <br /> |
9191

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

9594

basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/AuthorizedSubmodelRepository.java

Lines changed: 50 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ public class AuthorizedSubmodelRepository implements SubmodelRepository {
5656
private static final String ALL_ALLOWED_WILDCARD = "*";
5757
private SubmodelRepository decorated;
5858
private RbacPermissionResolver<SubmodelTargetInformation> permissionResolver;
59-
6059

6160
public AuthorizedSubmodelRepository(SubmodelRepository decorated, RbacPermissionResolver<SubmodelTargetInformation> permissionResolver) {
6261
this.decorated = decorated;
@@ -66,174 +65,183 @@ public AuthorizedSubmodelRepository(SubmodelRepository decorated, RbacPermission
6665
@Override
6766
public CursorResult<List<Submodel>> getAllSubmodels(PaginationInfo pInfo) {
6867
boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(ALL_ALLOWED_WILDCARD, ALL_ALLOWED_WILDCARD));
69-
68+
7069
throwExceptionIfInsufficientPermission(isAuthorized);
71-
70+
7271
return decorated.getAllSubmodels(pInfo);
7372
}
7473

7574
@Override
7675
public Submodel getSubmodel(String submodelId) throws ElementDoesNotExistException {
7776
boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(submodelId, ALL_ALLOWED_WILDCARD));
78-
77+
7978
throwExceptionIfInsufficientPermission(isAuthorized);
80-
79+
8180
return decorated.getSubmodel(submodelId);
8281
}
8382

8483
@Override
8584
public void updateSubmodel(String submodelId, Submodel submodel) throws ElementDoesNotExistException {
8685
boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(submodelId, ALL_ALLOWED_WILDCARD));
87-
86+
8887
throwExceptionIfInsufficientPermission(isAuthorized);
89-
88+
9089
decorated.updateSubmodel(submodelId, submodel);
9190
}
9291

9392
@Override
9493
public void createSubmodel(Submodel submodel) throws CollidingIdentifierException {
9594
boolean isAuthorized = permissionResolver.hasPermission(Action.CREATE, new SubmodelTargetInformation(submodel.getId(), ALL_ALLOWED_WILDCARD));
96-
95+
9796
throwExceptionIfInsufficientPermission(isAuthorized);
98-
97+
9998
decorated.createSubmodel(submodel);
10099
}
101100

102101
@Override
103102
public void deleteSubmodel(String submodelId) throws ElementDoesNotExistException {
104103
boolean isAuthorized = permissionResolver.hasPermission(Action.DELETE, new SubmodelTargetInformation(submodelId, ALL_ALLOWED_WILDCARD));
105-
104+
106105
throwExceptionIfInsufficientPermission(isAuthorized);
107-
106+
108107
decorated.deleteSubmodel(submodelId);
109108
}
110109

111110
@Override
112111
public CursorResult<List<SubmodelElement>> getSubmodelElements(String submodelId, PaginationInfo pInfo) throws ElementDoesNotExistException {
113112
boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(submodelId, ALL_ALLOWED_WILDCARD));
114-
113+
115114
throwExceptionIfInsufficientPermission(isAuthorized);
116-
115+
117116
return decorated.getSubmodelElements(submodelId, pInfo);
118117
}
119118

120119
@Override
121120
public SubmodelElement getSubmodelElement(String submodelId, String smeIdShortPath) throws ElementDoesNotExistException {
122121
boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(submodelId, smeIdShortPath));
123-
122+
124123
throwExceptionIfInsufficientPermission(isAuthorized);
125-
124+
126125
return decorated.getSubmodelElement(submodelId, smeIdShortPath);
127126
}
128127

129128
@Override
130129
public SubmodelElementValue getSubmodelElementValue(String submodelId, String smeIdShortPath) throws ElementDoesNotExistException {
131130
boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(submodelId, smeIdShortPath));
132-
131+
133132
throwExceptionIfInsufficientPermission(isAuthorized);
134-
133+
135134
return decorated.getSubmodelElementValue(submodelId, smeIdShortPath);
136135
}
137136

138137
@Override
139138
public void setSubmodelElementValue(String submodelId, String smeIdShortPath, SubmodelElementValue value) throws ElementDoesNotExistException {
140139
boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(submodelId, smeIdShortPath));
141-
140+
142141
throwExceptionIfInsufficientPermission(isAuthorized);
143-
142+
144143
decorated.setSubmodelElementValue(submodelId, smeIdShortPath, value);
145144
}
146145

147146
@Override
148147
public void createSubmodelElement(String submodelId, SubmodelElement smElement) {
149148
boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(submodelId, ALL_ALLOWED_WILDCARD));
150-
149+
151150
throwExceptionIfInsufficientPermission(isAuthorized);
152-
151+
153152
decorated.createSubmodelElement(submodelId, smElement);
154153
}
155154

156155
@Override
157156
public void createSubmodelElement(String submodelId, String idShortPath, SubmodelElement smElement) throws ElementDoesNotExistException {
158157
boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(submodelId, idShortPath));
159-
158+
160159
throwExceptionIfInsufficientPermission(isAuthorized);
161-
160+
162161
decorated.createSubmodelElement(submodelId, idShortPath, smElement);
163162
}
164-
163+
165164
@Override
166165
public void updateSubmodelElement(String submodelId, String idShortPath, SubmodelElement submodelElement) throws ElementDoesNotExistException {
167166
boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(submodelId, idShortPath));
168-
167+
169168
throwExceptionIfInsufficientPermission(isAuthorized);
170-
169+
171170
decorated.updateSubmodelElement(submodelId, idShortPath, submodelElement);
172171
}
173172

174173
@Override
175174
public void deleteSubmodelElement(String submodelId, String idShortPath) throws ElementDoesNotExistException {
176175
boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(submodelId, idShortPath));
177-
176+
178177
throwExceptionIfInsufficientPermission(isAuthorized);
179-
180-
decorated.deleteSubmodelElement(submodelId, idShortPath);
178+
179+
decorated.deleteSubmodelElement(submodelId, idShortPath);
181180
}
182181

183182
@Override
184183
public OperationVariable[] invokeOperation(String submodelId, String idShortPath, OperationVariable[] input) throws ElementDoesNotExistException {
185184
boolean isAuthorized = permissionResolver.hasPermission(Action.EXECUTE, new SubmodelTargetInformation(submodelId, idShortPath));
186-
185+
187186
throwExceptionIfInsufficientPermission(isAuthorized);
188-
187+
189188
return decorated.invokeOperation(submodelId, idShortPath, input);
190189
}
191190

192191
@Override
193192
public SubmodelValueOnly getSubmodelByIdValueOnly(String submodelId) throws ElementDoesNotExistException {
194193
boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(submodelId, ALL_ALLOWED_WILDCARD));
195-
194+
196195
throwExceptionIfInsufficientPermission(isAuthorized);
197-
196+
198197
return decorated.getSubmodelByIdValueOnly(submodelId);
199198
}
200199

201200
@Override
202201
public Submodel getSubmodelByIdMetadata(String submodelId) throws ElementDoesNotExistException {
203202
boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(submodelId, ALL_ALLOWED_WILDCARD));
204-
203+
205204
throwExceptionIfInsufficientPermission(isAuthorized);
206-
205+
207206
return decorated.getSubmodelByIdMetadata(submodelId);
208207
}
209208

210209
@Override
211210
public File getFileByPathSubmodel(String submodelId, String idShortPath) throws ElementDoesNotExistException, ElementNotAFileException, FileDoesNotExistException {
212211
boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(submodelId, idShortPath));
213-
212+
214213
throwExceptionIfInsufficientPermission(isAuthorized);
215-
214+
216215
return decorated.getFileByPathSubmodel(submodelId, idShortPath);
217216
}
218217

219218
@Override
220219
public void setFileValue(String submodelId, String idShortPath, String fileName, InputStream inputStream) throws ElementDoesNotExistException, ElementNotAFileException {
221220
boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(submodelId, idShortPath));
222-
221+
223222
throwExceptionIfInsufficientPermission(isAuthorized);
224-
223+
225224
decorated.setFileValue(submodelId, idShortPath, fileName, inputStream);
226225
}
227226

228227
@Override
229228
public void deleteFileValue(String submodelId, String idShortPath) throws ElementDoesNotExistException, ElementNotAFileException, FileDoesNotExistException {
230229
boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(submodelId, idShortPath));
231-
230+
232231
throwExceptionIfInsufficientPermission(isAuthorized);
233-
232+
234233
decorated.deleteFileValue(submodelId, idShortPath);
235234
}
236-
235+
236+
@Override
237+
public void patchSubmodelElements(String submodelId, List<SubmodelElement> submodelElementList) {
238+
boolean isAuthorized = permissionResolver.hasPermission(Action.UPDATE, new SubmodelTargetInformation(submodelId, ALL_ALLOWED_WILDCARD));
239+
240+
throwExceptionIfInsufficientPermission(isAuthorized);
241+
242+
decorated.patchSubmodelElements(submodelId, submodelElementList);
243+
}
244+
237245
private void throwExceptionIfInsufficientPermission(boolean isAuthorized) {
238246
if (!isAuthorized)
239247
throw new InsufficientPermissionException("Insufficient Permission: The current subject does not have the required permissions for this operation.");

0 commit comments

Comments
 (0)