use of com.nedap.archie.rm.support.identification.ObjectVersionId in project ehrbase by ehrbase.
the class CustomMethodSecurityExpressionRoot method templateHandling.
/**
* Handles template ID extraction of specific payload.
* <p>
* Payload will be a response body string, in case of @PostAuthorize.
* <p>
* Payload will be request body string, or already deserialized object (e.g. EhrStatus), in case of @PreAuthorize.
* @param type Object type of scope
* @param payload Payload object, either request's input or response's output
* @param contentType Content type from the scope
* @param requestMap ABAC request attribute map to add the result
* @param authType Pre- or PostAuthorize, determines payload style (string or object)
*/
private void templateHandling(String type, Object payload, String contentType, Map<String, Object> requestMap, String authType) {
switch(type) {
case BaseController.EHR:
throw new IllegalArgumentException("ABAC: Unsupported configuration: Can't set template ID for EHR type.");
case BaseController.EHR_STATUS:
throw new IllegalArgumentException("ABAC: Unsupported configuration: Can't set template ID for EHR_STATUS type.");
case BaseController.COMPOSITION:
String content = "";
if (authType.equals(POST)) {
// @PostAuthorize gives a ResponseEntity type for "returnObject", so payload is of that type
if (((ResponseEntity) payload).hasBody()) {
Object body = ((ResponseEntity) payload).getBody();
// can have "No content" here (even with some data in the body) if the compo was (logically) deleted
if (((ResponseEntity<?>) payload).getStatusCode().equals(HttpStatus.NO_CONTENT)) {
if (body instanceof Map) {
Object error = ((Map<?, ?>) body).get("error");
if (error != null) {
if (((String) error).contains("delet")) {
// composition was deleted, so nothing to check here, skip
break;
}
}
}
throw new InternalServerException("ABAC: Unexpected empty response from composition reuquest");
}
if (body instanceof OriginalVersionResponseData) {
// case of versioned_composition --> fast path, because template is easy to get
if (((OriginalVersionResponseData<?>) body).getData() instanceof Composition) {
String template = Objects.requireNonNull(((Composition) ((OriginalVersionResponseData<?>) body).getData()).getArchetypeDetails().getTemplateId()).getValue();
requestMap.put(TEMPLATE, template);
// special case, so done here, exit
break;
}
} else if (body instanceof String) {
content = (String) body;
} else {
throw new InternalServerException("ABAC: unexpected composition payload object");
}
} else {
throw new InternalServerException("ABAC: unexpected empty response body");
}
} else if (authType.equals(PRE)) {
try {
// try if this is the Delete composition case. Payload would contain the UUID of the compo.
ObjectVersionId versionId = new ObjectVersionId((String) payload);
UUID compositionUid = UUID.fromString(versionId.getRoot().getValue());
Optional<CompositionDto> compoDto = compositionService.retrieve(compositionUid, null);
if (compoDto.isPresent()) {
Composition c = compoDto.get().getComposition();
requestMap.put(TEMPLATE, c.getArchetypeDetails().getTemplateId().getValue());
// special case, so done here, exit
break;
} else {
throw new InternalServerException("ABAC: unexpected empty response from composition delete");
}
} catch (IllegalArgumentException e) {
// if not an UUID, the payload is a composition itself so continue
content = (String) payload;
}
} else {
throw new InternalServerException("ABAC: invalid auth type given.");
}
String templateId;
if (MediaType.parseMediaType(contentType).isCompatibleWith(MediaType.APPLICATION_JSON)) {
templateId = compositionService.getTemplateIdFromInputComposition(content, CompositionFormat.JSON);
} else if (MediaType.parseMediaType(contentType).isCompatibleWith(MediaType.APPLICATION_XML)) {
templateId = compositionService.getTemplateIdFromInputComposition(content, CompositionFormat.XML);
} else {
throw new IllegalArgumentException("ABAC: Only JSON and XML composition are supported.");
}
requestMap.put(TEMPLATE, templateId);
break;
case BaseController.CONTRIBUTION:
CompositionFormat format;
if (MediaType.parseMediaType(contentType).isCompatibleWith(MediaType.APPLICATION_JSON)) {
format = CompositionFormat.JSON;
} else if (MediaType.parseMediaType(contentType).isCompatibleWith(MediaType.APPLICATION_XML)) {
format = CompositionFormat.XML;
} else {
throw new IllegalArgumentException("ABAC: Only JSON and XML composition are supported.");
}
if (payload instanceof String) {
Set<String> templates = contributionService.getListOfTemplates((String) payload, format);
requestMap.put(TEMPLATE, templates);
break;
} else {
throw new InternalServerException("ABAC: invalid POST contribution payload.");
}
case BaseController.QUERY:
// special case of type QUERY, where multiple subjects are possible
if (payload instanceof Map) {
if (((Map<?, ?>) payload).containsKey(AuditVariables.TEMPLATE_PATH)) {
Set<String> templates = (Set) ((Map<?, ?>) payload).get(AuditVariables.TEMPLATE_PATH);
Set<String> templateSet = new HashSet<>(templates);
// put result set into the requestMap and exit
requestMap.put(TEMPLATE, templateSet);
break;
} else {
throw new InternalServerException("ABAC: AQL audit template data unavailable.");
}
} else {
throw new InternalServerException("ABAC: AQL audit template data malformed.");
}
default:
throw new InternalServerException("ABAC: Invalid type given from Pre- or PostAuthorize");
}
}
use of com.nedap.archie.rm.support.identification.ObjectVersionId in project ehrbase by ehrbase.
the class FolderAccessTest method shouldInsertFolderWithSubfolders.
@Test
@Ignore
public void shouldInsertFolderWithSubfolders() throws Exception {
// the creation and commit returning valid ids implies that the FolderMockDataProvider.java has provided the corresponding result for each SQL generated when inserting
// create folder to insert
Folder folder = new Folder();
UIDBasedId uid = new ObjectVersionId("f8a2af65-fe89-45a4-9456-07c5e17b1634");
ObjectVersionId uidim = new ObjectVersionId("f8a2af65-fe89-45a4-9456-07c5e17b1634");
folder.setUid(uid);
DvText name = new DvText();
name.setValue("nameOfFolder1");
folder.setName(name);
folder.setArchetypeNodeId("archetype_1");
ItemStructure is = new ItemStructure() {
@Override
public List getItems() {
Item item = new Item() {
@Override
public DvText getName() {
return new DvText("fol1");
}
};
List<Item> items = new ArrayList<>();
items.add(item);
return items;
}
};
folder.setDetails(is);
// create subfolder
Folder folder2 = new Folder();
UIDBasedId uid2 = new ObjectVersionId("f0a2af65-fe89-45a4-9456-07c5e17b1634");
ObjectVersionId uidim2 = new ObjectVersionId("f0a2af65-fe89-45a4-9456-07c5e17b1634");
folder2.setUid(uid2);
DvText name2 = new DvText();
name2.setValue("nameOfFolder2");
folder2.setName(name2);
folder2.setArchetypeNodeId("archetype_2");
ItemStructure is2 = new ItemStructure() {
@Override
public List getItems() {
Item item = new Item() {
@Override
public DvText getName() {
return new DvText("fol2");
}
};
List<Item> items = new ArrayList<>();
items.add(item);
return items;
}
};
folder2.setDetails(is2);
// create subfolder (grandson)
Folder folder3 = new Folder();
UIDBasedId uid3 = new ObjectVersionId("f4a2af65-fe89-45a4-9456-07c5e17b1634");
ObjectVersionId uidim3 = new ObjectVersionId("f4a2af65-fe89-45a4-9456-07c5e17b1634");
folder3.setUid(uid3);
DvText name3 = new DvText();
name3.setValue("nameOfFolder3");
folder3.setName(name3);
folder3.setArchetypeNodeId("archetype_3");
ItemStructure is3 = new ItemStructure() {
@Override
public List getItems() {
Item item = new Item() {
@Override
public DvText getName() {
return super.getName();
}
};
List<Item> items = new ArrayList<>();
items.add(item);
return items;
}
};
folder3.setDetails(is3);
// add subfolders
folder.getFolders().add(folder2);
folder2.addFolder(folder3);
// insert folder
FolderAccess fa1 = new FolderAccess(testDomainAccess);
FolderAccess fa2 = (FolderAccess) FolderAccess.getNewFolderAccessInstance(fa1, folder, DateTime.now(), UUID.fromString("f6a2af65-fe89-45a4-9456-07c5e17b1634"));
assertEquals("f8a2af65-fe89-45a4-9456-07c5e17b1634", fa2.getFolderRecord().getId().toString());
assertEquals("archetype_1", fa2.getFolderRecord().getArchetypeNodeId());
assertEquals("nameOfFolder1", fa2.getFolderRecord().getName());
String expected = ("'{\n" + " \"_type\" : \"\",\n" + " \"items\" : [ {\n" + " \"name\" : {\n" + " \"_type\" : \"DV_TEXT\",\n" + " \"value\" : \"fol1\"\n" + " }\n" + " } ]\n" + "}'::jsonb").replaceAll("\\n|\\r\\n", // avoids problems amond different platforms due to different representations of line change.
System.getProperty("line.separator"));
// avoids problems amond different platforms due to different representations of line change.
;
StringWriter expectedStringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(expectedStringWriter);
// avoids problems amond different platforms due to different representations of line change.
printWriter.print(expected);
printWriter.close();
assertEquals(expectedStringWriter.toString(), fa2.getFolderRecord().getDetails().toString());
assertEquals("f0a2af65-fe89-45a4-9456-07c5e17b1634", ((FolderAccess) fa2.getSubfoldersList().get(UUID.fromString("f0a2af65-fe89-45a4-9456-07c5e17b1634"))).getFolderRecord().getId().toString());
assertEquals("archetype_2", ((FolderAccess) fa2.getSubfoldersList().get(UUID.fromString("f0a2af65-fe89-45a4-9456-07c5e17b1634"))).getFolderRecord().getArchetypeNodeId());
assertEquals("nameOfFolder2", ((FolderAccess) fa2.getSubfoldersList().get(UUID.fromString("f0a2af65-fe89-45a4-9456-07c5e17b1634"))).getFolderRecord().getName());
String expected2 = ("'{\n" + " \"_type\" : \"\",\n" + " \"items\" : [ {\n" + " \"name\" : {\n" + " \"_type\" : \"DV_TEXT\",\n" + " \"value\" : \"fol2\"\n" + " }\n" + " } ]\n" + "}'::jsonb").replaceAll("\\n|\\r\\n", // avoids problems amond different platforms due to different representations of line change.
System.getProperty("line.separator"));
expectedStringWriter = new StringWriter();
// avoids problems amond different platforms due to different representations of line change.
printWriter = new PrintWriter(expectedStringWriter);
printWriter.print(expected2);
printWriter.close();
assertEquals(expectedStringWriter.toString(), ((FolderAccess) fa2.getSubfoldersList().get(UUID.fromString("f0a2af65-fe89-45a4-9456-07c5e17b1634"))).getFolderRecord().getDetails().toString());
assertEquals("f4a2af65-fe89-45a4-9456-07c5e17b1634", ((FolderAccess) ((FolderAccess) fa2.getSubfoldersList().get(UUID.fromString("f0a2af65-fe89-45a4-9456-07c5e17b1634"))).getSubfoldersList().get(UUID.fromString("f4a2af65-fe89-45a4-9456-07c5e17b1634"))).getFolderRecord().getId().toString());
assertEquals("archetype_3", ((FolderAccess) ((FolderAccess) fa2.getSubfoldersList().get(UUID.fromString("f0a2af65-fe89-45a4-9456-07c5e17b1634"))).getSubfoldersList().get(UUID.fromString("f4a2af65-fe89-45a4-9456-07c5e17b1634"))).getFolderRecord().getArchetypeNodeId());
assertEquals("nameOfFolder3", ((FolderAccess) ((FolderAccess) fa2.getSubfoldersList().get(UUID.fromString("f0a2af65-fe89-45a4-9456-07c5e17b1634"))).getSubfoldersList().get(UUID.fromString("f4a2af65-fe89-45a4-9456-07c5e17b1634"))).getFolderRecord().getName());
String expected3 = ("'{\n" + " \"_type\" : \"\",\n" + " \"items\" : [ { } ]\n" + "}'::jsonb").replaceAll("\\n|\\r\\n", // avoids problems amond different platforms due to different representations of line change.
System.getProperty("line.separator"));
expectedStringWriter = new StringWriter();
printWriter = new PrintWriter(expectedStringWriter);
// avoids problems amond different platforms due to different representations of line change.
printWriter.print(expected3);
assertEquals(expectedStringWriter.toString(), ((FolderAccess) ((FolderAccess) fa2.getSubfoldersList().get(UUID.fromString("f0a2af65-fe89-45a4-9456-07c5e17b1634"))).getSubfoldersList().get(UUID.fromString("f4a2af65-fe89-45a4-9456-07c5e17b1634"))).getFolderRecord().getDetails().toString());
expectedStringWriter.flush();
printWriter.flush();
printWriter.close();
UUID storedFolderUid = fa2.commit(LocalDateTime.now(), UUID.randomUUID());
// commit should return the top level folderId
assertEquals("f8a2af65-fe89-45a4-9456-07c5e17b1634", storedFolderUid.toString());
}
use of com.nedap.archie.rm.support.identification.ObjectVersionId in project ehrbase by ehrbase.
the class CompositionServiceImp method getOriginalVersionComposition.
@Override
public Optional<OriginalVersion<Composition>> getOriginalVersionComposition(UUID versionedObjectUid, int version) {
// check for valid version parameter
if ((version == 0) || I_CompositionAccess.getLastVersionNumber(getDataAccess(), versionedObjectUid) < version) {
throw new ObjectNotFoundException("versioned_composition", "No VERSIONED_COMPOSITION with given version: " + version);
}
// retrieve requested object
I_CompositionAccess compositionAccess = I_CompositionAccess.retrieveCompositionVersion(getDataAccess(), versionedObjectUid, version);
if (compositionAccess == null) {
return Optional.empty();
}
// create data for output, i.e. fields of the OriginalVersion<Composition>
ObjectVersionId versionId = new ObjectVersionId(versionedObjectUid + "::" + getServerConfig().getNodename() + "::" + version);
DvCodedText lifecycleState = new DvCodedText("complete", new CodePhrase(// TODO: once lifecycle state is supported, get it here dynamically
"532"));
AuditDetails commitAudit = compositionAccess.getAuditDetailsAccess().getAsAuditDetails();
ObjectRef<HierObjectId> contribution = new ObjectRef<>(new HierObjectId(compositionAccess.getContributionId().toString()), "openehr", "contribution");
List<UUID> attestationIdList = I_AttestationAccess.retrieveListOfAttestationsByRef(getDataAccess(), compositionAccess.getAttestationRef());
List<Attestation> attestations = // as default, gets content if available in the following lines
null;
if (!attestationIdList.isEmpty()) {
attestations = new ArrayList<>();
for (UUID id : attestationIdList) {
I_AttestationAccess a = new AttestationAccess(getDataAccess()).retrieveInstance(id);
attestations.add(a.getAsAttestation());
}
}
ObjectVersionId precedingVersionId = null;
// check if there is a preceding version and set it, if available
if (version > 1) {
// in the current scope version is an int and therefore: preceding = current - 1
precedingVersionId = new ObjectVersionId(versionedObjectUid + "::" + getServerConfig().getNodename() + "::" + (version - 1));
}
Optional<CompositionDto> compositionDto = retrieve(versionedObjectUid, version);
Composition composition = null;
if (compositionDto.isPresent()) {
composition = compositionDto.get().getComposition();
}
OriginalVersion<Composition> versionComposition = new OriginalVersion<>(versionId, precedingVersionId, composition, lifecycleState, commitAudit, contribution, null, null, attestations);
return Optional.of(versionComposition);
}
use of com.nedap.archie.rm.support.identification.ObjectVersionId in project ehrbase by ehrbase.
the class CompositionServiceImp method revisionHistoryItemFromComposition.
private RevisionHistoryItem revisionHistoryItemFromComposition(OriginalVersion<Composition> composition) {
ObjectVersionId objectVersionId = composition.getUid();
// Note: is List but only has more than one item when there are contributions regarding this
// object of change type attestation
List<AuditDetails> auditDetailsList = new ArrayList<>();
// retrieving the audits
auditDetailsList.add(composition.getCommitAudit());
// add retrieval of attestations, if there are any
if (composition.getAttestations() != null) {
for (Attestation a : composition.getAttestations()) {
AuditDetails newAudit = new AuditDetails(a.getSystemId(), a.getCommitter(), a.getTimeCommitted(), a.getChangeType(), a.getDescription());
auditDetailsList.add(newAudit);
}
}
return new RevisionHistoryItem(objectVersionId, auditDetailsList);
}
use of com.nedap.archie.rm.support.identification.ObjectVersionId in project ehrbase by ehrbase.
the class CompositionServiceImp method internalUpdate.
/**
* Update of an existing composition. With optional custom contribution, or existing one will be
* updated.
*
* @param compositionId ID of existing composition
* @param composition RMObject instance of the given Composition which represents the new
* version
* @param systemId Audit system; or NULL if contribution is given
* @param committerId Audit committer; or NULL if contribution is given
* @param description (Optional) Audit description; or NULL if contribution is given
* @param contributionId NULL if new one should be created; or ID of given custom contribution
* @return Version UID pointing to updated composition
*/
private ObjectVersionId internalUpdate(UUID compositionId, Composition composition, UUID systemId, UUID committerId, String description, UUID contributionId) {
boolean result;
try {
var compositionAccess = I_CompositionAccess.retrieveInstance(getDataAccess(), compositionId);
if (compositionAccess == null) {
throw new ObjectNotFoundException(I_CompositionAccess.class.getName(), "Could not find composition: " + compositionId);
}
// validate RM composition
validationService.check(composition);
// Check if template ID is not the same in existing and given data -> error
String existingTemplateId = compositionAccess.getContent().get(0).getTemplateId();
String inputTemplateId = composition.getArchetypeDetails().getTemplateId().getValue();
if (!existingTemplateId.equals(inputTemplateId)) {
// check if base template ID doesn't match (template ID schema: "$NAME.$LANG.v$VER")
if (!existingTemplateId.split("\\.")[0].equals(inputTemplateId.split("\\.")[0])) {
throw new InvalidApiParameterException("Can't update composition to have different template.");
}
// if base matches, check if given template ID is just a new version of the correct template
int existingTemplateIdVersion = Integer.parseInt(existingTemplateId.split("\\.v")[1]);
int inputTemplateIdVersion = Integer.parseInt(inputTemplateId.substring(inputTemplateId.lastIndexOf("\\.v") + 1));
if (inputTemplateIdVersion < existingTemplateIdVersion) {
throw new InvalidApiParameterException("Can't update composition with wrong template version bump.");
}
}
// to keep reference to entry to update: pull entry out of composition access and replace
// composition content with input, then write back to the original access
List<I_EntryAccess> contentList = compositionAccess.getContent();
contentList.get(0).setCompositionData(composition);
compositionAccess.setContent(contentList);
compositionAccess.setComposition(composition);
if (contributionId != null) {
// if custom contribution should be set
compositionAccess.setContributionId(contributionId);
result = compositionAccess.update(LocalDateTime.now(), contributionId);
} else {
// else existing one will be updated
if (committerId == null || systemId == null) {
throw new InternalServerException("Failed to update composition, missing mandatory audit meta data.");
}
result = compositionAccess.update(LocalDateTime.now(), committerId, systemId, description, ContributionChangeType.MODIFICATION);
}
} catch (ObjectNotFoundException | InvalidApiParameterException e) {
// otherwise exceptions would always get sucked up by the catch below
throw e;
} catch (Exception e) {
throw new InternalServerException(e);
}
if (!result) {
throw new InternalServerException("Update failed on composition:" + compositionId);
}
return new ObjectVersionId(compositionId.toString(), this.getServerConfig().getNodename(), getLastVersionNumber(compositionId).toString());
}
Aggregations