diff --git a/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/plugin/ProcessPluginImpl.java b/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/plugin/ProcessPluginImpl.java index 65b78c26f..1dcc2c95f 100644 --- a/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/plugin/ProcessPluginImpl.java +++ b/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/plugin/ProcessPluginImpl.java @@ -26,7 +26,6 @@ import org.camunda.bpm.engine.delegate.TaskListener; import org.camunda.bpm.engine.delegate.VariableScope; import org.camunda.bpm.engine.impl.bpmn.parser.FieldDeclaration; -import org.camunda.bpm.engine.impl.util.ClassDelegateUtil; import org.camunda.bpm.engine.variable.value.PrimitiveValue; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.ActivityDefinition; @@ -454,7 +453,7 @@ public TaskListener getTaskListener(String className, List fie VariableScope variableScope) { UserTaskListener target = get(UserTaskListener.class, className); - ClassDelegateUtil.applyFieldDeclaration(fieldDeclarations, target); + injectFields(target, fieldDeclarations, variableScope); return new UserTaskListenerDelegate(getProcessPluginApi(), variablesFactory, target); } diff --git a/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/service/QuestionnaireResponseHelperImpl.java b/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/service/QuestionnaireResponseHelperImpl.java index 6b1e6e94c..7247732ab 100644 --- a/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/service/QuestionnaireResponseHelperImpl.java +++ b/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/service/QuestionnaireResponseHelperImpl.java @@ -2,12 +2,17 @@ import java.util.Collections; import java.util.List; +import java.util.Objects; +import java.util.Set; import java.util.stream.Stream; import org.hl7.fhir.r4.model.BooleanType; +import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.DateTimeType; import org.hl7.fhir.r4.model.DateType; import org.hl7.fhir.r4.model.DecimalType; +import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.IntegerType; import org.hl7.fhir.r4.model.Questionnaire; import org.hl7.fhir.r4.model.QuestionnaireResponse; @@ -102,4 +107,44 @@ public String getLocalVersionlessAbsoluteUrl(QuestionnaireResponse questionnaire return questionnaireResponse.getIdElement().toVersionless() .withServerBase(serverBaseUrl, ResourceType.QuestionnaireResponse.name()).getValue(); } + + @Override + public Extension createQuestionnaireAuthorizationExtension(Set practitioners, + Set practitionerRoles) + { + Extension e = new Extension(EXTENSION_QUESTIONNAIRE_AUTHORIZATION); + + if (practitioners != null) + practitioners.stream().map(this::createQuestionnaireAuthorizationPractitionerSubExtension) + .forEach(e::addExtension); + if (practitionerRoles != null) + practitionerRoles.stream().map(this::createQuestionnaireAuthorizationPractitionerRoleSubExtension) + .forEach(e::addExtension); + + return e; + } + + @Override + public Extension createQuestionnaireAuthorizationPractitionerSubExtension(Identifier practitioner) + { + Objects.requireNonNull(practitioner, "practitioner"); + if (!practitioner.hasSystem()) + throw new IllegalArgumentException("practitioner.system missing"); + if (!practitioner.hasValue()) + throw new IllegalArgumentException("practitioner.value missing"); + + return new Extension(EXTENSION_QUESTIONNAIRE_AUTHORIZATION_PRACTITIONER).setValue(practitioner); + } + + @Override + public Extension createQuestionnaireAuthorizationPractitionerRoleSubExtension(Coding practitionerRole) + { + Objects.requireNonNull(practitionerRole, "practitionerRole"); + if (!practitionerRole.hasSystem()) + throw new IllegalArgumentException("practitionerRole.system missing"); + if (!practitionerRole.hasCode()) + throw new IllegalArgumentException("practitionerRole.code missing"); + + return new Extension(EXTENSION_QUESTIONNAIRE_AUTHORIZATION_PRACTITIONER_ROLE).setValue(practitionerRole); + } } diff --git a/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/variables/VariablesImpl.java b/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/variables/VariablesImpl.java index 9aebebce6..7eb2703b2 100644 --- a/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/variables/VariablesImpl.java +++ b/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/variables/VariablesImpl.java @@ -11,6 +11,7 @@ import java.util.stream.Stream; import org.camunda.bpm.engine.delegate.DelegateExecution; +import org.camunda.bpm.engine.variable.Variables.SerializationDataFormats; import org.camunda.bpm.engine.variable.value.TypedValue; import org.hl7.fhir.r4.model.QuestionnaireResponse; import org.hl7.fhir.r4.model.Resource; @@ -96,7 +97,7 @@ private JsonHolder toJsonHolder(Object json) } @SuppressWarnings("unchecked") - private T fromJsonJolder(JsonHolder holder) + private T fromJsonHolder(JsonHolder holder) { try { @@ -372,6 +373,13 @@ public void setString(String variableName, String value) setVariable(variableName, org.camunda.bpm.engine.variable.Variables.stringValue(value)); } + @Override + public void setStringList(String variableName, List value) + { + setVariable(variableName, org.camunda.bpm.engine.variable.Variables.objectValue(value) + .serializationDataFormat(SerializationDataFormats.JAVA).create()); + } + @Override public void setByteArray(String variableName, byte[] value) { @@ -444,7 +452,7 @@ public T getVariable(String variableName) Object variable = execution.getVariable(variableName); if (variable instanceof JsonHolder jsonVariable) - return (T) fromJsonJolder(jsonVariable); + return (T) fromJsonHolder(jsonVariable); else return (T) variable; } @@ -461,6 +469,13 @@ public void setStringLocal(String variableName, String value) setVariableLocal(variableName, org.camunda.bpm.engine.variable.Variables.stringValue(value)); } + @Override + public void setStringListLocal(String variableName, List value) + { + setVariable(variableName, org.camunda.bpm.engine.variable.Variables.objectValue(value) + .serializationDataFormat(SerializationDataFormats.JAVA).create()); + } + @Override public void setByteArrayLocal(String variableName, byte[] value) { @@ -533,7 +548,7 @@ public T getVariableLocal(String variableName) Object variable = execution.getVariable(variableName); if (variable instanceof JsonHolder jsonHolder) - return (T) fromJsonJolder(jsonHolder); + return (T) fromJsonHolder(jsonHolder); else return (T) variable; } diff --git a/dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/activity/DefaultUserTaskListener.java b/dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/activity/DefaultUserTaskListener.java index 57d08d313..a66f3dbda 100644 --- a/dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/activity/DefaultUserTaskListener.java +++ b/dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/activity/DefaultUserTaskListener.java @@ -1,10 +1,16 @@ package dev.dsf.bpe.v2.activity; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; import java.util.stream.Collectors; import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.Questionnaire; import org.hl7.fhir.r4.model.QuestionnaireResponse; import org.hl7.fhir.r4.model.Reference; @@ -17,7 +23,9 @@ import dev.dsf.bpe.v2.ProcessPluginApi; import dev.dsf.bpe.v2.activity.values.CreateQuestionnaireResponseValues; +import dev.dsf.bpe.v2.constants.CodeSystems; import dev.dsf.bpe.v2.constants.CodeSystems.BpmnUserTask; +import dev.dsf.bpe.v2.constants.NamingSystems; import dev.dsf.bpe.v2.variables.Variables; /** @@ -30,11 +38,112 @@ * To modify the behavior of the listener, for example to set default values in the created 'in-progress' * {@link QuestionnaireResponse}, extend this class, register it as a prototype {@link Bean} and specify the class name * as a task listener with event type 'create' in the BPMN. + *

+ * This listener will add a questionnaire authorization extension to the {@link QuestionnaireResponse} if practitioner + * roles or practitioner identifiers are set. A single role or identifier can be configured via a BPMN field injections + * using fields practitionerRole and practitioner with a String constant or expression. To + * configure multiple roles or identifiers use fields practitionerRoles and practitioners with + * an expression to access a process variable of type List<String>. Note: To use field injects the + * fully qualified name of this class needs to be set as the task listener JavaClass. + *

+ * This class (as is) does not need to be registered as a prototype {@link Bean}. */ public class DefaultUserTaskListener implements UserTaskListener { private static final Logger logger = LoggerFactory.getLogger(DefaultUserTaskListener.class); + protected static final String PROFILE_QUESTIONNAIRE_RESPONSE = "http://dsf.dev/fhir/StructureDefinition/questionnaire-response"; + + private static final record KeyAndValue(String key, String value) + { + static Function fromString(String defaultSystem) + { + return keyAndValue -> + { + Objects.requireNonNull(keyAndValue, "keyAndValue"); + + String[] split = keyAndValue.split("\\|"); + + if (split.length == 1) + return new KeyAndValue(defaultSystem, split[0]); + if (split.length == 2) + return new KeyAndValue(split[0], split[1]); + else + throw new IllegalArgumentException("Invalid format: must be a simple 'value' for default key " + + defaultSystem + " or 'key|value'"); + }; + } + + Identifier toIdentifier() + { + return new Identifier().setSystem(key).setValue(value); + } + + Coding toCoding() + { + return new Coding().setSystem(key).setCode(value); + } + } + + private final Set practitionerRoles = new HashSet<>(); + private final Set practitioners = new HashSet<>(); + + /** + * @param practitionerRole + * does nothing if null or blank + * @deprecated only for field injection + */ + @Deprecated + public void setPractitionerRole(String practitionerRole) + { + if (practitionerRole != null && !practitionerRole.isBlank()) + setPractitionerRoles(List.of(practitionerRole)); + } + + /** + * @param practitioner + * does nothing if null or blank + * @deprecated only for field injection + */ + @Deprecated + public void setPractitioner(String practitioner) + { + if (practitioner != null && !practitioner.isBlank()) + setPractitioners(List.of(practitioner)); + } + + /** + * @param practitionerRoles + * does nothing if null, ignores null and blank values + * @deprecated only for field injection + */ + @Deprecated + public void setPractitionerRoles(List practitionerRoles) + { + if (practitionerRoles != null) + { + practitionerRoles.stream().filter(Objects::nonNull).filter(p -> !p.isBlank()) + .map(KeyAndValue.fromString(CodeSystems.PractitionerRole.SYSTEM)) + .forEach(this.practitionerRoles::add); + } + } + + /** + * @param practitioners + * does nothing if null, ignores null and blank values + * @deprecated only for field injection + */ + @Deprecated + public void setPractitioners(List practitioners) + { + if (practitioners != null) + { + practitioners.stream().filter(Objects::nonNull).filter(p -> !p.isBlank()) + .map(KeyAndValue.fromString(NamingSystems.PractitionerIdentifier.SID)) + .forEach(this.practitioners::add); + } + } + @Override public void notify(ProcessPluginApi api, Variables variables, CreateQuestionnaireResponseValues createQuestionnaireResponseValues) throws Exception @@ -89,6 +198,7 @@ private QuestionnaireResponse createDefaultQuestionnaireResponse(ProcessPluginAp String questionnaireUrlWithVersion, String businessKey, String userTaskId) { QuestionnaireResponse questionnaireResponse = new QuestionnaireResponse(); + questionnaireResponse.getMeta().addProfile(PROFILE_QUESTIONNAIRE_RESPONSE); questionnaireResponse.setQuestionnaire(questionnaireUrlWithVersion); questionnaireResponse.setStatus(QuestionnaireResponse.QuestionnaireResponseStatus.INPROGRESS); @@ -104,6 +214,13 @@ private QuestionnaireResponse createDefaultQuestionnaireResponse(ProcessPluginAp BpmnUserTask.Codes.USER_TASK_ID, "The user-task-id of the process execution", new StringType(userTaskId)); + Set practitioners = getPractitioners(); + Set practitionerRoles = getPractitionerRoles(); + + if (!practitioners.isEmpty() || !practitionerRoles.isEmpty()) + questionnaireResponse.addExtension(api.getQuestionnaireResponseHelper() + .createQuestionnaireAuthorizationExtension(practitioners, practitionerRoles)); + return questionnaireResponse; } @@ -186,4 +303,24 @@ protected void afterQuestionnaireResponseCreate(ProcessPluginApi api, Variables { // Nothing to do in default behavior } + + /** + * Override this method if you do not want to configure practitioner roles via field-injection in BPMN + * + * @return practitioner-role entries used in creating the questionnaire authorization extension + */ + protected Set getPractitionerRoles() + { + return practitionerRoles.stream().map(KeyAndValue::toCoding).collect(Collectors.toUnmodifiableSet()); + } + + /** + * Override this method if you do not want to configure practitioner identifiers via field-injection in BPMN + * + * @return practitioner entries used in creating the questionnaire authorization extension + */ + protected Set getPractitioners() + { + return practitioners.stream().map(KeyAndValue::toIdentifier).collect(Collectors.toUnmodifiableSet()); + } } diff --git a/dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/error/ErrorBoundaryEvent.java b/dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/error/ErrorBoundaryEvent.java index b68b319f2..b3dd61093 100644 --- a/dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/error/ErrorBoundaryEvent.java +++ b/dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/error/ErrorBoundaryEvent.java @@ -1,5 +1,7 @@ package dev.dsf.bpe.v2.error; +import java.util.Objects; + public class ErrorBoundaryEvent extends RuntimeException { private static final long serialVersionUID = 3161271266680097207L; @@ -7,10 +9,21 @@ public class ErrorBoundaryEvent extends RuntimeException private final String errorCode; private final String errorMessage; + /** + * @param errorCode + * not null, not empty + * @param errorMessage + * not null, not empty + */ public ErrorBoundaryEvent(String errorCode, String errorMessage) { - this.errorCode = errorCode; - this.errorMessage = errorMessage; + this.errorCode = Objects.requireNonNull(errorCode, "errorCode"); + this.errorMessage = Objects.requireNonNull(errorMessage, "errorMessage"); + + if (errorCode.isEmpty()) + throw new IllegalArgumentException("errorCode empty"); + if (errorMessage.isEmpty()) + throw new IllegalArgumentException("errorMessage empty"); } public String getErrorCode() diff --git a/dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/service/QuestionnaireResponseHelper.java b/dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/service/QuestionnaireResponseHelper.java index 19b2b6377..6b0c450c6 100644 --- a/dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/service/QuestionnaireResponseHelper.java +++ b/dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/service/QuestionnaireResponseHelper.java @@ -2,9 +2,13 @@ import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.Questionnaire; import org.hl7.fhir.r4.model.QuestionnaireResponse; import org.hl7.fhir.r4.model.Type; @@ -14,6 +18,10 @@ */ public interface QuestionnaireResponseHelper { + String EXTENSION_QUESTIONNAIRE_AUTHORIZATION = "http://dsf.dev/fhir/StructureDefinition/extension-questionnaire-authorization"; + String EXTENSION_QUESTIONNAIRE_AUTHORIZATION_PRACTITIONER = "practitioner"; + String EXTENSION_QUESTIONNAIRE_AUTHORIZATION_PRACTITIONER_ROLE = "practitioner-role"; + default Optional getFirstItemLeaveMatchingLinkId( QuestionnaireResponse questionnaireResponse, String linkId) { @@ -45,4 +53,27 @@ Stream getItemLeavesAs void addItemLeafWithAnswer(QuestionnaireResponse questionnaireResponse, String linkId, String text, Type answer); String getLocalVersionlessAbsoluteUrl(QuestionnaireResponse questionnaireResponse); + + /** + * @param practitioners + * may be null + * @param practitionerRoles + * may be null + * @return questionnaire authorization extension with url {@value #EXTENSION_QUESTIONNAIRE_AUTHORIZATION} + */ + Extension createQuestionnaireAuthorizationExtension(Set practitioners, Set practitionerRoles); + + /** + * @param practitioner + * not null, system and value set + * @return practitioner extension url {@value #EXTENSION_QUESTIONNAIRE_AUTHORIZATION_PRACTITIONER} + */ + Extension createQuestionnaireAuthorizationPractitionerSubExtension(Identifier practitioner); + + /** + * @param practitionerRole + * not null, system and code set + * @return practitioner-role extension url {@value #EXTENSION_QUESTIONNAIRE_AUTHORIZATION_PRACTITIONER_ROLE} + */ + Extension createQuestionnaireAuthorizationPractitionerRoleSubExtension(Coding practitionerRole); } diff --git a/dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/variables/Variables.java b/dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/variables/Variables.java index 77263f3ca..9367cdcdd 100644 --- a/dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/variables/Variables.java +++ b/dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/variables/Variables.java @@ -378,6 +378,33 @@ default String getString(String variableName) return getVariable(variableName); } + /** + * Sets execution variable with the given variableName to the given {@link List} of {@link String} + * + * @param variableName + * not null + * @param value + * may be null + * @see #getStringList(String) + */ + void setStringList(String variableName, List value); + + /** + * Retrieves {@link List} of {@link String} execution variable with the given variableName + * + * @param variableName + * not null + * @return value from execution variables for the given variableName, may be null + * @throws ClassCastException + * if the stored value is not a {@link List} + * @see #setStringList(String, List) + * @see #getVariable(String) + */ + default List getStringList(String variableName) + { + return getVariable(variableName); + } + /** * Sets execution variable with the given variableName to the given {@link Boolean} * @@ -648,6 +675,33 @@ default String getStringLocal(String variableName) return getVariableLocal(variableName); } + /** + * Sets local variable with the given variableName to the given {@link List} of {@link String} + * + * @param variableName + * not null + * @param value + * may be null + * @see #getStringListLocal(String) + */ + void setStringListLocal(String variableName, List value); + + /** + * Retrieves {@link List} of {@link String} local variable with the given variableName + * + * @param variableName + * not null + * @return value from execution variables for the given variableName, may be null + * @throws ClassCastException + * if the stored value is not a {@link List} + * @see #setStringListLocal(String, List) + * @see #getVariableLocal(String) + */ + default String getStringListLocal(String variableName) + { + return getVariableLocal(variableName); + } + /** * Sets local variable with the given variableName to the given {@link Boolean} * diff --git a/dsf-bpe/dsf-bpe-server/src/main/java/dev/dsf/bpe/spring/config/WebsocketConfig.java b/dsf-bpe/dsf-bpe-server/src/main/java/dev/dsf/bpe/spring/config/WebsocketConfig.java index 3627013a3..843ae8a72 100644 --- a/dsf-bpe/dsf-bpe-server/src/main/java/dev/dsf/bpe/spring/config/WebsocketConfig.java +++ b/dsf-bpe/dsf-bpe-server/src/main/java/dev/dsf/bpe/spring/config/WebsocketConfig.java @@ -67,7 +67,7 @@ public ResourceHandler questionnaireResponseHandler() { return new QuestionnaireResponseHandler(camundaConfig.processEngine().getRepositoryService(), pluginConfig.processPluginManager(), fhirConfig.fhirContext(), - camundaConfig.processEngine().getTaskService()); + camundaConfig.processEngine().getTaskService(), dsfClientConfig.clientProvider().getWebserviceClient()); } @Bean diff --git a/dsf-bpe/dsf-bpe-server/src/main/java/dev/dsf/bpe/subscription/QuestionnaireResponseHandler.java b/dsf-bpe/dsf-bpe-server/src/main/java/dev/dsf/bpe/subscription/QuestionnaireResponseHandler.java index e7eb69f54..fad579f23 100644 --- a/dsf-bpe/dsf-bpe-server/src/main/java/dev/dsf/bpe/subscription/QuestionnaireResponseHandler.java +++ b/dsf-bpe/dsf-bpe-server/src/main/java/dev/dsf/bpe/subscription/QuestionnaireResponseHandler.java @@ -20,6 +20,7 @@ import ca.uhn.fhir.context.FhirContext; import dev.dsf.bpe.api.Constants; import dev.dsf.bpe.api.plugin.ProcessPlugin; +import dev.dsf.bpe.client.dsf.WebserviceClient; import dev.dsf.bpe.plugin.ProcessPluginManager; public class QuestionnaireResponseHandler extends AbstractResourceHandler @@ -28,13 +29,15 @@ public class QuestionnaireResponseHandler extends AbstractResourceHandler private static final Logger logger = LoggerFactory.getLogger(QuestionnaireResponseHandler.class); private final TaskService userTaskService; + private final WebserviceClient webserviceClient; public QuestionnaireResponseHandler(RepositoryService repositoryService, ProcessPluginManager processPluginManager, - FhirContext fhirContext, TaskService userTaskService) + FhirContext fhirContext, TaskService userTaskService, WebserviceClient webserviceClient) { super(repositoryService, processPluginManager, fhirContext); this.userTaskService = userTaskService; + this.webserviceClient = webserviceClient; } @Override @@ -43,6 +46,7 @@ public void afterPropertiesSet() throws Exception super.afterPropertiesSet(); Objects.requireNonNull(userTaskService, "userTaskService"); + Objects.requireNonNull(webserviceClient, "webserviceClient"); } @Override @@ -82,6 +86,19 @@ public void onResource(QuestionnaireResponse questionnaireResponse) Map variables = Map.of(Constants.QUESTIONNAIRE_RESPONSE_VARIABLE, fhirQuestionnaireResponseVariable); + try + { + questionnaireResponse.setStatus(QuestionnaireResponseStatus.AMENDED); + webserviceClient.update(questionnaireResponse); + } + catch (Exception e) + { + logger.debug("Unable to update QuestionnaireResponse (status amended) with id {}", + questionnaireResponse.getId(), e); + logger.warn("Unable to update QuestionnaireResponse (status amended) with id {}: {} - {}", + questionnaireResponse.getId(), e.getClass().getName(), e.getMessage()); + } + logger.info( "QuestionnaireResponse '{}' for Questionnaire '{}' completed [userTaskId: {}, businessKey: {}, user: {}]", questionnaireResponseId, questionnaire, userTaskId, businessKey, user + "|" + userType); diff --git a/dsf-bpe/dsf-bpe-server/src/main/java/dev/dsf/bpe/subscription/TaskHandler.java b/dsf-bpe/dsf-bpe-server/src/main/java/dev/dsf/bpe/subscription/TaskHandler.java index 03055145d..7a238bdb3 100644 --- a/dsf-bpe/dsf-bpe-server/src/main/java/dev/dsf/bpe/subscription/TaskHandler.java +++ b/dsf-bpe/dsf-bpe-server/src/main/java/dev/dsf/bpe/subscription/TaskHandler.java @@ -130,7 +130,7 @@ public void onResource(Task task) if (businessKey == null) { businessKey = UUID.randomUUID().toString(); - logger.debug("Adding business-key {} to Task with id {}", businessKey, task.getId()); + logger.debug("Adding business-key {} to Task with id {}", businessKey, task.getIdElement().getIdPart()); task.addInput().setType(new CodeableConcept().addCoding(new Coding() .setSystem(Constants.BPMN_MESSAGE_URL).setCode(Constants.BPMN_MESSAGE_BUSINESS_KEY))) .setValue(new StringType(businessKey)); @@ -170,9 +170,10 @@ public void onResource(Task task) } catch (Exception e) { - logger.debug("Unable to handle Task with id {}", task.getId(), e); - logger.warn("Unable to handle Task with id {}: {} - {}", task.getId(), e.getClass().getName(), - e.getMessage()); + logger.debug("Unable to update Task with id {} (status in-progress)", task.getIdElement().getIdPart(), + e); + logger.warn("Unable to update Task with id {} (status in-progress): {} - {}", + task.getIdElement().getIdPart(), e.getClass().getName(), e.getMessage()); updateTaskFailed(task, "Unable to update Task to status 'in-progress'"); } @@ -186,25 +187,25 @@ public void onResource(Task task) } catch (MismatchingMessageCorrelationException e) { - logger.debug("Unable to handle Task with id {}", task.getId(), e); - logger.warn("Unable to handle Task with id {}: {} - {}", task.getId(), e.getClass().getName(), - e.getMessage()); + logger.debug("Unable to handle Task with id {}", task.getIdElement().getIdPart(), e); + logger.warn("Unable to handle Task with id {}: {} - {}", task.getIdElement().getIdPart(), + e.getClass().getName(), e.getMessage()); updateTaskFailed(task, "Unable to correlate Task"); } catch (ProcessNotFoundException e) { - logger.debug("Unable to handle Task with id {}", task.getId(), e); - logger.warn("Unable to handle Task with id {}: {} - {}", task.getId(), e.getClass().getName(), - e.getMessage()); + logger.debug("Unable to handle Task with id {}", task.getIdElement().getIdPart(), e); + logger.warn("Unable to handle Task with id {}: {} - {}", task.getIdElement().getIdPart(), + e.getClass().getName(), e.getMessage()); updateTaskFailed(task, e.getShortMessage()); } catch (Exception e) { - logger.debug("Unable to handle Task with id {}", task.getId(), e); - logger.error("Unable to handle Task with id {}: {} - {}", task.getId(), e.getClass().getName(), - e.getMessage()); + logger.debug("Unable to handle Task with id {}", task.getIdElement().getIdPart(), e); + logger.error("Unable to handle Task with id {}: {} - {}", task.getIdElement().getIdPart(), + e.getClass().getName(), e.getMessage()); updateTaskFailed(task, e); } @@ -229,8 +230,8 @@ private void updateTaskFailed(Task task, String message) } catch (Exception e) { - logger.debug("Unable to update Task with id {} (status failed)", task.getId(), e); - logger.error("Unable to update Task with id {} (status failed): {} - {}", task.getId(), + logger.debug("Unable to update Task with id {} (status failed)", task.getIdElement().getIdPart(), e); + logger.error("Unable to update Task with id {} (status failed): {} - {}", task.getIdElement().getIdPart(), e.getClass().getName(), e.getMessage()); } } diff --git a/dsf-bpe/dsf-bpe-server/src/test/java/dev/dsf/bpe/integration/AbstractIntegrationTest.java b/dsf-bpe/dsf-bpe-server/src/test/java/dev/dsf/bpe/integration/AbstractIntegrationTest.java index 05a07f321..0d1e1766c 100644 --- a/dsf-bpe/dsf-bpe-server/src/test/java/dev/dsf/bpe/integration/AbstractIntegrationTest.java +++ b/dsf-bpe/dsf-bpe-server/src/test/java/dev/dsf/bpe/integration/AbstractIntegrationTest.java @@ -341,18 +341,28 @@ private static JettyServer startFhirServer(ServerSocketChannel statusConnectorCh String.valueOf(X509Certificates.PASSWORD)); initParameters.put("dev.dsf.fhir.server.roleConfig", String.format(""" - - practitioner-test-user: + - dic-user: thumbprint: %s dsf-role: - - CREATE - - READ - - UPDATE - - DELETE - - SEARCH - - HISTORY + - CREATE: [Task] + - READ: &tqqr [Task, Questionnaire, QuestionnaireResponse] + - UPDATE: [QuestionnaireResponse] + - SEARCH: *tqqr + - HISTORY: *tqqr practitioner-role: - http://dsf.dev/fhir/CodeSystem/practitioner-role|DIC_USER - """, certificates.getPractitionerClientCertificate().certificateSha512ThumbprintHex())); + - uac-user: + thumbprint: %s + dsf-role: + - CREATE: [Task] + - READ: &tqqr [Task, Questionnaire, QuestionnaireResponse] + - UPDATE: [QuestionnaireResponse] + - SEARCH: *tqqr + - HISTORY: *tqqr + practitioner-role: + - http://dsf.dev/fhir/CodeSystem/practitioner-role|UAC_USER + """, certificates.getDicUserClientCertificate().certificateSha512ThumbprintHex(), + certificates.getUacUserClientCertificate().certificateSha512ThumbprintHex())); KeyStore clientCertificateTrustStore = KeyStoreCreator .jksForTrustedCertificates(certificates.getCaCertificate()); @@ -445,11 +455,35 @@ private static JettyServer startBpeServer(ServerSocketChannel statusConnectorCha password: '#[password]' via-proxy: base-url: 'http://via.proxy/fhir' + dic-user: + base-url: '#[fhirBaseUrl]' + test-connection-on-startup: yes + enable-debug-logging: no + cert-auth: + private-key-file: '#[dic.client.key]' + certificate-file: '#[dic.client.crt]' + password: '#[password]' + uac-user: + base-url: '#[fhirBaseUrl]' + test-connection-on-startup: yes + enable-debug-logging: no + cert-auth: + private-key-file: '#[uac.client.key]' + certificate-file: '#[uac.client.crt]' + password: '#[password]' """.replaceAll(Pattern.quote("#[fhirBaseUrl]"), Matcher.quoteReplacement(fhirBaseUrl)) .replaceAll(Pattern.quote("#[client.key]"), Matcher.quoteReplacement(certificates.getClientCertificatePrivateKeyFile().toString())) .replaceAll(Pattern.quote("#[client.crt]"), Matcher.quoteReplacement(certificates.getClientCertificateFile().toString())) + .replaceAll(Pattern.quote("#[dic.client.key]"), + Matcher.quoteReplacement(certificates.getDicUserClientCertificatePrivateKeyFile().toString())) + .replaceAll(Pattern.quote("#[dic.client.crt]"), + Matcher.quoteReplacement(certificates.getDicUserClientCertificateFile().toString())) + .replaceAll(Pattern.quote("#[uac.client.key]"), + Matcher.quoteReplacement(certificates.getUacUserClientCertificatePrivateKeyFile().toString())) + .replaceAll(Pattern.quote("#[uac.client.crt]"), + Matcher.quoteReplacement(certificates.getUacUserClientCertificateFile().toString())) .replaceAll(Pattern.quote("#[password]"), Matcher.quoteReplacement(String.valueOf(X509Certificates.PASSWORD))); initParameters.put("dev.dsf.bpe.fhir.client.connections.config", fhirConnectionsYaml); diff --git a/dsf-bpe/dsf-bpe-server/src/test/java/dev/dsf/bpe/integration/PluginV2IntegrationTest.java b/dsf-bpe/dsf-bpe-server/src/test/java/dev/dsf/bpe/integration/PluginV2IntegrationTest.java index 32b8b9d4b..e99047010 100644 --- a/dsf-bpe/dsf-bpe-server/src/test/java/dev/dsf/bpe/integration/PluginV2IntegrationTest.java +++ b/dsf-bpe/dsf-bpe-server/src/test/java/dev/dsf/bpe/integration/PluginV2IntegrationTest.java @@ -269,4 +269,28 @@ public void startAutowireTest() throws Exception { executePluginTest(createTestTask("AutowireTest")); } + + @Test + public void startQuestionnaireTest() throws Exception + { + executePluginTest(createTestTask("QuestionnaireTest")); + } + + @Test + public void startQuestionnaireTestRole() throws Exception + { + executePluginTest(createTestTask("QuestionnaireTestRole")); + } + + @Test + public void startQuestionnaireTestIdentifier() throws Exception + { + executePluginTest(createTestTask("QuestionnaireTestIdentifier")); + } + + @Test + public void startQuestionnaireTestIdentifierMulti() throws Exception + { + executePluginTest(createTestTask("QuestionnaireTestIdentifierMulti")); + } } \ No newline at end of file diff --git a/dsf-bpe/dsf-bpe-server/src/test/java/dev/dsf/bpe/integration/X509Certificates.java b/dsf-bpe/dsf-bpe-server/src/test/java/dev/dsf/bpe/integration/X509Certificates.java index 9970a5f74..172d7b463 100644 --- a/dsf-bpe/dsf-bpe-server/src/test/java/dev/dsf/bpe/integration/X509Certificates.java +++ b/dsf-bpe/dsf-bpe-server/src/test/java/dev/dsf/bpe/integration/X509Certificates.java @@ -71,7 +71,8 @@ public String certificateSha512ThumbprintHex() private CertificateAndPrivateKey bpeServerCertificate; private CertificateAndPrivateKey fhirServerCertificate; private CertificateAndPrivateKey clientCertificate; - private CertificateAndPrivateKey practitionerClientCertificate; + private CertificateAndPrivateKey dicUserClientCertificate; + private CertificateAndPrivateKey uacUserClientCertificate; private CertificateAndPrivateKey externalClientCertificate; private X509Certificate caCertificate; @@ -80,8 +81,10 @@ public String certificateSha512ThumbprintHex() private Path clientCertificatePrivateKeyFile; private Path externalClientCertificateFile; private Path externalClientCertificatePrivateKeyFile; - private Path practitionerClientCertificateFile; - private Path practitionerClientCertificatePrivateKeyFile; + private Path dicUserClientCertificateFile; + private Path dicUserClientCertificatePrivateKeyFile; + private Path uacUserClientCertificateFile; + private Path uacUserClientCertificatePrivateKeyFile; private List filesToDelete; @@ -117,9 +120,14 @@ public CertificateAndPrivateKey getExternalClientCertificate() return externalClientCertificate; } - public CertificateAndPrivateKey getPractitionerClientCertificate() + public CertificateAndPrivateKey getDicUserClientCertificate() { - return practitionerClientCertificate; + return dicUserClientCertificate; + } + + public CertificateAndPrivateKey getUacUserClientCertificate() + { + return uacUserClientCertificate; } public X509Certificate getCaCertificate() @@ -152,14 +160,24 @@ public Path getExternalClientCertificatePrivateKeyFile() return externalClientCertificatePrivateKeyFile; } - public Path getPractitionerClientCertificateFile() + public Path getDicUserClientCertificateFile() + { + return dicUserClientCertificateFile; + } + + public Path getDicUserClientCertificatePrivateKeyFile() + { + return dicUserClientCertificatePrivateKeyFile; + } + + public Path getUacUserClientCertificateFile() { - return practitionerClientCertificateFile; + return uacUserClientCertificateFile; } - public Path getPractitionerClientCertificatePrivateKeyFile() + public Path getUacUserClientCertificatePrivateKeyFile() { - return practitionerClientCertificatePrivateKeyFile; + return uacUserClientCertificatePrivateKeyFile; } private void createX509Certificates() throws InvalidKeyException, NoSuchAlgorithmException, KeyStoreException, @@ -172,8 +190,10 @@ private void createX509Certificates() throws InvalidKeyException, NoSuchAlgorith Path clientCertificatePrivateKeyFile = Paths.get("target", UUID.randomUUID().toString() + ".pem"); Path externalClientCertificateFile = Paths.get("target", UUID.randomUUID().toString() + ".pem"); Path externalClientCertificatePrivateKeyFile = Paths.get("target", UUID.randomUUID().toString() + ".pem"); - Path practitionerClientCertificateFile = Paths.get("target", UUID.randomUUID().toString() + ".pem"); - Path practitionerClientCertificatePrivateKeyFile = Paths.get("target", UUID.randomUUID().toString() + ".pem"); + Path dicUserClientCertificateFile = Paths.get("target", UUID.randomUUID().toString() + ".pem"); + Path dicUserClientCertificatePrivateKeyFile = Paths.get("target", UUID.randomUUID().toString() + ".pem"); + Path uacUserClientCertificateFile = Paths.get("target", UUID.randomUUID().toString() + ".pem"); + Path uacUserClientCertificatePrivateKeyFile = Paths.get("target", UUID.randomUUID().toString() + ".pem"); CertificateAuthority ca = CertificateAuthority .builderSha384EcdsaSecp384r1("DE", null, null, null, null, "Junit Test CA") @@ -213,15 +233,25 @@ private void createX509Certificates() throws InvalidKeyException, NoSuchAlgorith .toFile(externalClientCertificatePrivateKeyFile); // external client -- - // -- practitioner client - CertificationRequestAndPrivateKey practitionerClientRequest = CertificationRequest - .builder(ca, "DE", null, null, null, null, "practitioner-client").generateKeyPair() - .setEmail("practitioner@test.org").build(); - X509Certificate practitionerClientCertificate = ca.signClientCertificate(practitionerClientRequest); - PemWriter.writeCertificate(practitionerClientCertificate, practitionerClientCertificateFile); - PemWriter.writePrivateKey(practitionerClientRequest.getPrivateKey()).asPkcs8().encryptedAes128(PASSWORD) - .toFile(practitionerClientCertificatePrivateKeyFile); - // practitioner client -- + // -- dic user client + CertificationRequestAndPrivateKey dicUserClientRequest = CertificationRequest + .builder(ca, "DE", null, null, null, null, "dic-user-client").generateKeyPair() + .setEmail("dic-user@test.org").build(); + X509Certificate dicUserClientCertificate = ca.signClientCertificate(dicUserClientRequest); + PemWriter.writeCertificate(dicUserClientCertificate, dicUserClientCertificateFile); + PemWriter.writePrivateKey(dicUserClientRequest.getPrivateKey()).asPkcs8().encryptedAes128(PASSWORD) + .toFile(dicUserClientCertificatePrivateKeyFile); + // dic user client -- + + // -- uac user client + CertificationRequestAndPrivateKey uacUserClientRequest = CertificationRequest + .builder(ca, "DE", null, null, null, null, "uac-user-client").generateKeyPair() + .setEmail("uac-user@test.org").build(); + X509Certificate uacUserClientCertificate = ca.signClientCertificate(uacUserClientRequest); + PemWriter.writeCertificate(uacUserClientCertificate, uacUserClientCertificateFile); + PemWriter.writePrivateKey(uacUserClientRequest.getPrivateKey()).asPkcs8().encryptedAes128(PASSWORD) + .toFile(uacUserClientCertificatePrivateKeyFile); + // uac user client -- this.caCertificate = caCertificate; this.bpeServerCertificate = new CertificateAndPrivateKey(caCertificate, bpeServerCertificate, @@ -232,20 +262,25 @@ private void createX509Certificates() throws InvalidKeyException, NoSuchAlgorith clientRequest.getPrivateKey()); this.externalClientCertificate = new CertificateAndPrivateKey(caCertificate, externalClientCertificate, externalClientRequest.getPrivateKey()); - this.practitionerClientCertificate = new CertificateAndPrivateKey(caCertificate, practitionerClientCertificate, - practitionerClientRequest.getPrivateKey()); + this.dicUserClientCertificate = new CertificateAndPrivateKey(caCertificate, dicUserClientCertificate, + dicUserClientRequest.getPrivateKey()); + this.uacUserClientCertificate = new CertificateAndPrivateKey(caCertificate, uacUserClientCertificate, + uacUserClientRequest.getPrivateKey()); this.caCertificateFile = caCertificateFile; this.clientCertificateFile = clientCertificateFile; this.clientCertificatePrivateKeyFile = clientCertificatePrivateKeyFile; this.externalClientCertificateFile = externalClientCertificateFile; this.externalClientCertificatePrivateKeyFile = externalClientCertificatePrivateKeyFile; - this.practitionerClientCertificateFile = practitionerClientCertificateFile; - this.practitionerClientCertificatePrivateKeyFile = practitionerClientCertificatePrivateKeyFile; + this.dicUserClientCertificateFile = dicUserClientCertificateFile; + this.dicUserClientCertificatePrivateKeyFile = dicUserClientCertificatePrivateKeyFile; + this.uacUserClientCertificateFile = uacUserClientCertificateFile; + this.uacUserClientCertificatePrivateKeyFile = uacUserClientCertificatePrivateKeyFile; filesToDelete = List.of(caCertificateFile, clientCertificateFile, clientCertificatePrivateKeyFile, - externalClientCertificateFile, externalClientCertificatePrivateKeyFile, - practitionerClientCertificateFile, practitionerClientCertificatePrivateKeyFile); + externalClientCertificateFile, externalClientCertificatePrivateKeyFile, dicUserClientCertificateFile, + dicUserClientCertificatePrivateKeyFile, uacUserClientCertificateFile, + uacUserClientCertificatePrivateKeyFile); } private void deleteX509Certificates() diff --git a/dsf-bpe/dsf-bpe-test-plugin-v1/src/main/java/dev/dsf/bpe/test/service/AbstractTest.java b/dsf-bpe/dsf-bpe-test-plugin-v1/src/main/java/dev/dsf/bpe/test/service/AbstractTest.java index 9d9903d11..7ed3124ee 100644 --- a/dsf-bpe/dsf-bpe-test-plugin-v1/src/main/java/dev/dsf/bpe/test/service/AbstractTest.java +++ b/dsf-bpe/dsf-bpe-test-plugin-v1/src/main/java/dev/dsf/bpe/test/service/AbstractTest.java @@ -23,7 +23,7 @@ protected void doExecute(DelegateExecution execution, Variables variables) throw { PluginTestExecutor.execute(this, output(variables, "test-method-succeeded"), output(variables, "test-method-failed"), () -> variables.updateTask(variables.getStartTask()), - execution, variables); + _ -> null, execution, variables); } private Consumer output(Variables variables, String code) diff --git a/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/AbstractTest.java b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/AbstractTest.java index eba2c4d1d..d3c9b22d9 100644 --- a/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/AbstractTest.java +++ b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/AbstractTest.java @@ -1,20 +1,33 @@ package dev.dsf.bpe.test; import java.util.function.Consumer; +import java.util.function.Function; import org.hl7.fhir.r4.model.StringType; import dev.dsf.bpe.v2.ProcessPluginApi; +import dev.dsf.bpe.v2.error.ErrorBoundaryEvent; import dev.dsf.bpe.v2.variables.Variables; public abstract class AbstractTest { + public static final Function TO_ERROR_BOUNDARY_EVENT = _ -> new ErrorBoundaryEvent( + "test_failed", "test_failed"); + protected void executeTests(ProcessPluginApi api, Variables variables, Object... otherTestMethodArgs) throws Exception { PluginTestExecutor.execute(this, output(api, variables, "test-method-succeeded"), - output(api, variables, "test-method-failed"), () -> variables.updateTask(variables.getStartTask()), api, - variables, otherTestMethodArgs); + output(api, variables, "test-method-failed"), () -> variables.updateTask(variables.getStartTask()), + _ -> null, api, variables, otherTestMethodArgs); + } + + protected void executeTests(ProcessPluginApi api, Variables variables, Function onError, + Object... otherTestMethodArgs) throws Exception + { + PluginTestExecutor.execute(this, output(api, variables, "test-method-succeeded"), + output(api, variables, "test-method-failed"), () -> variables.updateTask(variables.getStartTask()), + onError, api, variables, otherTestMethodArgs); } private Consumer output(ProcessPluginApi api, Variables variables, String code) diff --git a/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/TestProcessPluginDefinition.java b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/TestProcessPluginDefinition.java index 44e50e351..2532eaa58 100644 --- a/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/TestProcessPluginDefinition.java +++ b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/TestProcessPluginDefinition.java @@ -48,10 +48,11 @@ public Map> getFhirResourcesByProcessId() { var aTest = "fhir/ActivityDefinition/dsf-test.xml"; var cTest = "fhir/CodeSystem/dsf-test.xml"; + var qTest = "fhir/Questionnaire/test.xml"; var sTest = "fhir/StructureDefinition/dsf-task-test.xml"; var sContinueSentTest = "fhir/StructureDefinition/dsf-task-continue-send-test.xml"; var vTest = "fhir/ValueSet/dsf-test.xml"; - return Map.of("dsfdev_test", List.of(aTest, cTest, sContinueSentTest, sTest, vTest)); + return Map.of("dsfdev_test", List.of(aTest, cTest, qTest, sContinueSentTest, sTest, vTest)); } } diff --git a/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/service/QuestionnaireTestAnswer.java b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/service/QuestionnaireTestAnswer.java new file mode 100644 index 000000000..b1e7239a9 --- /dev/null +++ b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/service/QuestionnaireTestAnswer.java @@ -0,0 +1,238 @@ +package dev.dsf.bpe.test.service; + +import static dev.dsf.bpe.test.PluginTestExecutor.expectFalse; +import static dev.dsf.bpe.test.PluginTestExecutor.expectNotNull; +import static dev.dsf.bpe.test.PluginTestExecutor.expectSame; +import static dev.dsf.bpe.test.PluginTestExecutor.expectTrue; + +import java.time.Duration; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.hl7.fhir.r4.model.BooleanType; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.DateTimeType; +import org.hl7.fhir.r4.model.DateType; +import org.hl7.fhir.r4.model.DecimalType; +import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.Identifier; +import org.hl7.fhir.r4.model.IntegerType; +import org.hl7.fhir.r4.model.QuestionnaireResponse; +import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemComponent; +import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseStatus; +import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.ResourceType; +import org.hl7.fhir.r4.model.StringType; +import org.hl7.fhir.r4.model.TimeType; +import org.hl7.fhir.r4.model.Type; +import org.hl7.fhir.r4.model.UrlType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ca.uhn.fhir.model.api.TemporalPrecisionEnum; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; +import dev.dsf.bpe.test.AbstractTest; +import dev.dsf.bpe.test.PluginTest; +import dev.dsf.bpe.v2.ProcessPluginApi; +import dev.dsf.bpe.v2.activity.ServiceTask; +import dev.dsf.bpe.v2.constants.CodeSystems; +import dev.dsf.bpe.v2.constants.NamingSystems; +import dev.dsf.bpe.v2.error.ErrorBoundaryEvent; +import dev.dsf.bpe.v2.variables.Variables; + +public class QuestionnaireTestAnswer extends AbstractTest implements ServiceTask +{ + private static final Logger logger = LoggerFactory.getLogger(QuestionnaireTestAnswer.class); + + private String type; + + /** + * @param type + * @deprecated only for BPMN field injection + */ + @Deprecated + public void setType(String type) + { + this.type = type; + } + + @Override + public void execute(ProcessPluginApi api, Variables variables) throws ErrorBoundaryEvent, Exception + { + executeTests(api, variables, TO_ERROR_BOUNDARY_EVENT); + } + + @PluginTest + public void updateQuestionnaireResponse(ProcessPluginApi api) throws Exception + { + Bundle resultBundle = null; + for (int i = 0; i < 5; i++) + { + // Sleep to wait for QuestionnaireResponse to have been created with status in-progress + logger.info("sleeping ..."); + Thread.sleep(Duration.ofMillis(250)); + + logger.info("searching ..."); + resultBundle = api.getDsfClientProvider().getLocalDsfClient().search(QuestionnaireResponse.class, + Map.of("status", List.of(QuestionnaireResponseStatus.INPROGRESS.toCode()))); + + if (resultBundle != null && resultBundle.getTotal() == 1) + break; + } + + expectNotNull(resultBundle); + expectSame(1, resultBundle.getTotal()); + expectSame(1, resultBundle.getEntry().size()); + + BundleEntryComponent entry = resultBundle.getEntryFirstRep(); + expectNotNull(entry); + expectTrue(entry.hasResource()); + expectTrue(entry.getResource() instanceof QuestionnaireResponse); + + QuestionnaireResponse qr = (QuestionnaireResponse) entry.getResource(); + + expectTrue(qr.hasAuthor()); + expectTrue(qr.getAuthor().hasIdentifier()); + expectSame("http://dsf.dev/sid/organization-identifier", qr.getAuthor().getIdentifier().getSystem()); + expectSame("Test_Organization", qr.getAuthor().getIdentifier().getValue()); + + if (type != null) + { + expectTrue( + qr.hasExtension("http://dsf.dev/fhir/StructureDefinition/extension-questionnaire-authorization")); + Extension authExt = qr + .getExtensionByUrl("http://dsf.dev/fhir/StructureDefinition/extension-questionnaire-authorization"); + + if ("role".equals(type)) + { + expectTrue(authExt.hasExtension("practitioner-role")); + List roleExts = authExt.getExtensionsByUrl("practitioner-role"); + expectNotNull(roleExts); + expectSame(1, roleExts.size()); + + Extension roleExt = roleExts.get(0); + expectTrue(roleExt.hasValue()); + expectSame(Coding.class, roleExt.getValue().getClass()); + + Coding role = (Coding) roleExt.getValue(); + expectSame(CodeSystems.PractitionerRole.SYSTEM, role.getSystem()); + expectSame(CodeSystems.PractitionerRole.Codes.DIC_USER, role.getCode()); + } + else if ("identifier".equals(type)) + { + expectTrue(authExt.hasExtension("practitioner")); + List idExts = authExt.getExtensionsByUrl("practitioner"); + expectNotNull(idExts); + expectSame(1, idExts.size()); + + Extension idExt = idExts.get(0); + expectTrue(idExt.hasValue()); + expectSame(Identifier.class, idExt.getValue().getClass()); + + Identifier id = (Identifier) idExt.getValue(); + expectSame(NamingSystems.PractitionerIdentifier.SID, id.getSystem()); + expectSame("dic-user@test.org", id.getValue()); + } + else if ("identifiers".equals(type)) + { + expectTrue(authExt.hasExtension("practitioner")); + List idExts = authExt.getExtensionsByUrl("practitioner"); + expectNotNull(idExts); + expectSame(3, idExts.size()); + + idExts.stream().filter(Extension::hasValue).filter(e -> e.getValue() instanceof Identifier) + .map(e -> (Identifier) e.getValue()).map(Identifier::getSystem) + .allMatch(NamingSystems.PractitionerIdentifier.SID::equals); + + List values = idExts.stream().filter(Extension::hasValue) + .filter(e -> e.getValue() instanceof Identifier).map(e -> (Identifier) e.getValue()) + .map(Identifier::getValue).toList(); + expectSame(3, values.size()); + + expectTrue(values.contains("dic-user@test.org")); + expectTrue(values.contains("foo@invalid")); + expectTrue(values.contains("bar@invalid")); + } + } + else + expectFalse( + qr.hasExtension("http://dsf.dev/fhir/StructureDefinition/extension-questionnaire-authorization")); + + qr.setAuthored(new Date()); + qr.setStatus(QuestionnaireResponseStatus.COMPLETED); + qr.getItem().forEach(item -> + { + switch (item.getLinkId()) + { + case "string-example" -> set(item, new StringType("string-example answer")); + + case "text-example" -> set(item, new StringType("text-example answer")); + + case "integer-example" -> set(item, new IntegerType(666)); + + case "decimal-example" -> set(item, new DecimalType(Math.PI)); + + case "date-example" -> set(item, new DateType(new Date())); + + case "time-example" -> set(item, new TimeType("11:55:00")); + + case "date-time-example" -> set(item, new DateTimeType(new Date(), TemporalPrecisionEnum.MONTH)); + + case "url-example" -> set(item, new UrlType("http://test.com/foo")); + + case "reference-example" -> set(item, + new Reference() + .setIdentifier(new Identifier().setSystem("http://dsf.dev/sid/organization-identifier") + .setValue("External_Test_Organization"))); + + case "boolean-example" -> set(item, new BooleanType(true)); + } + }); + + if (type != null) + { + expectFalse(update(api, qr, "uac-user", "uac-user@test.org")); + expectTrue(update(api, qr, "dic-user", "dic-user@test.org")); + } + else + api.getDsfClientProvider().getLocalDsfClient().update(qr); + } + + private void set(QuestionnaireResponseItemComponent item, Type value) + { + item.getAnswerFirstRep().setValue(value); + } + + private boolean update(ProcessPluginApi api, QuestionnaireResponse qr, String clientId, String identifierValue) + { + qr.setAuthor(null).getAuthor().setType(ResourceType.Practitioner.name()) + .setIdentifier(NamingSystems.PractitionerIdentifier.withValue(identifierValue)); + + Optional oClient = api.getFhirClientProvider().getClient(clientId); + + expectTrue(oClient.isPresent()); + + IGenericClient client = oClient.get(); + try + { + + MethodOutcome outcome = client.update().resource(qr).execute(); + expectNotNull(outcome); + + return outcome.getResponseStatusCode() == 200; + } + catch (BaseServerResponseException e) + { + logger.info("QuestionnaireResponse update, status {}, {} : {}", e.getStatusCode(), e.getClass().getName(), + e.getMessage()); + + return false; + } + } +} diff --git a/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/service/QuestionnaireTestAnswerCheck.java b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/service/QuestionnaireTestAnswerCheck.java new file mode 100644 index 000000000..1adeb1bd5 --- /dev/null +++ b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/service/QuestionnaireTestAnswerCheck.java @@ -0,0 +1,190 @@ +package dev.dsf.bpe.test.service; + +import static dev.dsf.bpe.test.PluginTestExecutor.expectFalse; +import static dev.dsf.bpe.test.PluginTestExecutor.expectNotNull; +import static dev.dsf.bpe.test.PluginTestExecutor.expectSame; +import static dev.dsf.bpe.test.PluginTestExecutor.expectTrue; + +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.apache.commons.lang3.time.DateUtils; +import org.hl7.fhir.r4.model.BooleanType; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; +import org.hl7.fhir.r4.model.DateTimeType; +import org.hl7.fhir.r4.model.DateType; +import org.hl7.fhir.r4.model.DecimalType; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.Identifier; +import org.hl7.fhir.r4.model.IntegerType; +import org.hl7.fhir.r4.model.PrimitiveType; +import org.hl7.fhir.r4.model.QuestionnaireResponse; +import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemComponent; +import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseStatus; +import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.StringType; +import org.hl7.fhir.r4.model.TimeType; +import org.hl7.fhir.r4.model.Type; +import org.hl7.fhir.r4.model.UriType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ca.uhn.fhir.model.api.TemporalPrecisionEnum; +import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; +import dev.dsf.bpe.test.AbstractTest; +import dev.dsf.bpe.test.PluginTest; +import dev.dsf.bpe.v2.ProcessPluginApi; +import dev.dsf.bpe.v2.activity.ServiceTask; +import dev.dsf.bpe.v2.constants.NamingSystems; +import dev.dsf.bpe.v2.error.ErrorBoundaryEvent; +import dev.dsf.bpe.v2.variables.Variables; + +public class QuestionnaireTestAnswerCheck extends AbstractTest implements ServiceTask +{ + private static final Logger logger = LoggerFactory.getLogger(QuestionnaireTestAnswerCheck.class); + + private String type; + + /** + * @param type + * @deprecated only for BPMN field injection + */ + @Deprecated + public void setType(String type) + { + this.type = type; + } + + @Override + public void execute(ProcessPluginApi api, Variables variables) throws ErrorBoundaryEvent, Exception + { + executeTests(api, variables); + } + + @PluginTest + public void checkQuestionnaireResponse(ProcessPluginApi api) throws Exception + { + Bundle resultBundle = api.getDsfClientProvider().getLocalDsfClient().search(QuestionnaireResponse.class, + Map.of("status", List.of(QuestionnaireResponseStatus.AMENDED.toCode()))); + + expectNotNull(resultBundle); + expectSame(1, resultBundle.getTotal()); + expectSame(1, resultBundle.getEntry().size()); + + BundleEntryComponent e = resultBundle.getEntryFirstRep(); + expectNotNull(e); + expectTrue(e.hasResource()); + expectTrue(e.getResource() instanceof QuestionnaireResponse); + + QuestionnaireResponse qr = (QuestionnaireResponse) e.getResource(); + + expectTrue(qr.hasAuthored()); + expectTrue(qr.hasAuthor()); + expectTrue(qr.getAuthor().hasIdentifier()); + + if (type != null) + { + expectSame(NamingSystems.PractitionerIdentifier.SID, qr.getAuthor().getIdentifier().getSystem()); + expectSame("dic-user@test.org", qr.getAuthor().getIdentifier().getValue()); + } + else + { + expectSame(NamingSystems.OrganizationIdentifier.SID, qr.getAuthor().getIdentifier().getSystem()); + expectSame("Test_Organization", qr.getAuthor().getIdentifier().getValue()); + } + + qr.getItem().forEach(item -> + { + switch (item.getLinkId()) + { + case "string-example" -> test(item, new StringType("string-example answer")); + + case "text-example" -> test(item, new StringType("text-example answer")); + + case "integer-example" -> test(item, new IntegerType(666)); + + case "decimal-example" -> test(item, new DecimalType(Math.PI)); + + case "date-example" -> test(item, new DateType(new Date())); + + case "time-example" -> test(item, new TimeType("11:55:00")); + + case "date-time-example" -> test(item, new DateTimeType(new Date(), TemporalPrecisionEnum.MONTH)); + + // TODO potential bug, QuestionnaireResponse has "url-example" item with UriType not UrlType answer + case "url-example" -> test(item, new UriType("http://test.com/foo")); + + case "reference-example" -> test(item, + new Reference() + .setIdentifier(new Identifier().setSystem("http://dsf.dev/sid/organization-identifier") + .setValue("External_Test_Organization"))); + + case "boolean-example" -> test(item, new BooleanType(true)); + } + }); + + if (type != null) + { + expectFalse(read(api, qr.getIdElement(), "uac-user")); + expectTrue(read(api, qr.getIdElement(), "dic-user")); + } + } + + private void test(QuestionnaireResponseItemComponent item, Type expected) + { + Type value = item.getAnswerFirstRep().getValue(); + + expectNotNull(value); + expectSame(expected.getClass(), value.getClass()); + + switch (value) + { + case DateType d -> expectSame(0, DateUtils.truncatedCompareTo(((DateType) expected).getValue(), + d.getValue(), Calendar.DAY_OF_MONTH)); + + case DateTimeType d -> expectSame(0, + DateUtils.truncatedCompareTo(((DateTimeType) expected).getValue(), d.getValue(), Calendar.MONTH)); + + case PrimitiveType p -> expectSame(((PrimitiveType) expected).getValue(), p.getValue()); + + case Reference r -> { + expectTrue(r.hasIdentifier()); + expectNotNull(r.getIdentifier()); + expectTrue(r.getIdentifier().hasSystem()); + expectTrue(r.getIdentifier().hasValue()); + expectSame(((Reference) expected).getIdentifier().getSystem(), r.getIdentifier().getSystem()); + expectSame(((Reference) expected).getIdentifier().getValue(), r.getIdentifier().getValue()); + } + + default -> + throw new IllegalArgumentException("Value of type " + value.getClass().getName() + " not supported"); + } + } + + private boolean read(ProcessPluginApi api, IdType id, String clientId) + { + Optional oClient = api.getFhirClientProvider().getClient(clientId); + + expectTrue(oClient.isPresent()); + + IGenericClient client = oClient.get(); + + try + { + client.read().resource(QuestionnaireResponse.class).withId(id).execute(); + return true; + } + catch (BaseServerResponseException e) + { + logger.info("QuestionnaireResponse read, status {}, {} : {}", e.getStatusCode(), e.getClass().getName(), + e.getMessage()); + + return false; + } + } +} diff --git a/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/service/QuestionnaireTestSetIdentifies.java b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/service/QuestionnaireTestSetIdentifies.java new file mode 100644 index 000000000..8753bd1b6 --- /dev/null +++ b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/service/QuestionnaireTestSetIdentifies.java @@ -0,0 +1,17 @@ +package dev.dsf.bpe.test.service; + +import java.util.List; + +import dev.dsf.bpe.v2.ProcessPluginApi; +import dev.dsf.bpe.v2.activity.ServiceTask; +import dev.dsf.bpe.v2.error.ErrorBoundaryEvent; +import dev.dsf.bpe.v2.variables.Variables; + +public class QuestionnaireTestSetIdentifies implements ServiceTask +{ + @Override + public void execute(ProcessPluginApi api, Variables variables) throws ErrorBoundaryEvent, Exception + { + variables.setStringList("identifierValues", List.of("dic-user@test.org", "foo@invalid", "bar@invalid")); + } +} diff --git a/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/spring/config/Config.java b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/spring/config/Config.java index 4e2bb08e5..59a9a2854 100644 --- a/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/spring/config/Config.java +++ b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/spring/config/Config.java @@ -39,6 +39,9 @@ import dev.dsf.bpe.test.service.MimeTypeServiceTest; import dev.dsf.bpe.test.service.OrganizationProviderTest; import dev.dsf.bpe.test.service.ProxyTest; +import dev.dsf.bpe.test.service.QuestionnaireTestAnswer; +import dev.dsf.bpe.test.service.QuestionnaireTestAnswerCheck; +import dev.dsf.bpe.test.service.QuestionnaireTestSetIdentifies; import dev.dsf.bpe.test.service.TargetProviderTest; import dev.dsf.bpe.test.service.TestActivitySelector; import dev.dsf.bpe.v2.ProcessPluginDeploymentListener; @@ -77,7 +80,9 @@ public static ActivityPrototypeBeanCreator activityPrototypeBeanCreator() ContinueSendTest.class, ContinueSendTestSend.class, ContinueSendTestEvaluate.class, JsonVariableTestSet.class, JsonVariableTestGet.class, CryptoServiceTest.class, MimeTypeServiceTest.class, FhirBinaryVariableTestSet.class, FhirBinaryVariableTestGet.class, - DsfClientTest.class, TargetProviderTest.class, DataLoggerTest.class, AutowireTest.class); + DsfClientTest.class, TargetProviderTest.class, DataLoggerTest.class, AutowireTest.class, + QuestionnaireTestAnswer.class, QuestionnaireTestAnswerCheck.class, + QuestionnaireTestSetIdentifies.class); } @Bean diff --git a/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/resources/bpe/test.bpmn b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/resources/bpe/test.bpmn index cb9edc603..346966c76 100644 --- a/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/resources/bpe/test.bpmn +++ b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/resources/bpe/test.bpmn @@ -43,6 +43,10 @@ Flow_1x18y7y Flow_0312al1 Flow_06ychsk + Flow_0upusgm + Flow_0paqotx + Flow_1wpvvjm + Flow_1ih8var @@ -74,6 +78,10 @@ Flow_0scl0xx Flow_0vj0i7z Flow_04532de + Flow_1kiws3i + Flow_1xuklwp + Flow_1u24rv8 + Flow_0civp0p Flow_0a1kwg9 @@ -340,10 +348,224 @@ ${testActivity == 'CompressionServiceTest'} + + + Flow_060oe1s + Flow_1ih55cr + + + Flow_0upusgm + Flow_0ae3sja + Flow_060oe1s + + + + + + Flow_1ih55cr + Flow_1dlruuj + Flow_0i42902 + + + + Flow_0ae3sja + Flow_1dlruuj + + + + Flow_0i42902 + Flow_1kiws3i + + + ${testActivity == 'QuestionnaireTest'} + + + + + + + DIC_USER + + + + Flow_1kpy9c3 + Flow_0aaka7r + + + Flow_0paqotx + Flow_1kpy9c3 + Flow_0xkardm + + + Flow_0aaka7r + Flow_1350s7q + Flow_0ex0gwu + + + + + role + + + Flow_0xkardm + Flow_1350s7q + + + + + role + + + Flow_0ex0gwu + Flow_1xuklwp + + + + + + + + + + + dic-user@test.org + + + + Flow_1y5hp6n + Flow_0v3xkot + + + Flow_1wpvvjm + Flow_1y5hp6n + Flow_0flr1f5 + + + Flow_0v3xkot + Flow_17u74ny + Flow_11uh18y + + + + + identifier + + + Flow_0flr1f5 + Flow_17u74ny + + + + + identifier + + + Flow_11uh18y + Flow_1u24rv8 + + + + + + + + ${testActivity == 'QuestionnaireTestRole'} + + + ${testActivity == 'QuestionnaireTestIdentifier'} + + + + + Flow_1quvbu0 + + + + + Flow_088qnot + + + + + Flow_1iyj1iu + + + + + Flow_1quvbu0 + + + Flow_088qnot + + + Flow_1iyj1iu + + + Flow_1mo1h7u + Flow_1yasbym + Flow_071d2qk + + + Flow_0l35yl8 + Flow_1g61506 + Flow_0hksomo + + + + + + ${identifierValues} + + + + Flow_1yasbym + Flow_0l35yl8 + + + + + identifiers + + + Flow_071d2qk + Flow_1g61506 + + + + + identifier + + + Flow_0hksomo + Flow_0civp0p + + + Flow_1tpaked + + + Flow_1tpaked + + + + + + + + + + ${testActivity == 'QuestionnaireTestIdentifierMulti'} + + + + + Flow_1ih8var + Flow_1mo1h7u + + @@ -443,10 +665,6 @@ - - - - @@ -479,13 +697,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -754,6 +1075,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/resources/fhir/Questionnaire/test.xml b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/resources/fhir/Questionnaire/test.xml new file mode 100644 index 000000000..9468b101d --- /dev/null +++ b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/resources/fhir/Questionnaire/test.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dsf-bpe/dsf-bpe-test-plugin/src/main/java/dev/dsf/bpe/test/PluginTestExecutor.java b/dsf-bpe/dsf-bpe-test-plugin/src/main/java/dev/dsf/bpe/test/PluginTestExecutor.java index 8937de47a..090a1296c 100644 --- a/dsf-bpe/dsf-bpe-test-plugin/src/main/java/dev/dsf/bpe/test/PluginTestExecutor.java +++ b/dsf-bpe/dsf-bpe-test-plugin/src/main/java/dev/dsf/bpe/test/PluginTestExecutor.java @@ -2,8 +2,11 @@ import java.lang.reflect.InvocationTargetException; import java.util.Arrays; +import java.util.List; import java.util.Objects; import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; import java.util.stream.Stream; import org.slf4j.Logger; @@ -30,12 +33,12 @@ public interface RunnableWithException } public static final void execute(Object testClass, Consumer addTestSucceededToStartTask, - Consumer addTestFailedToStartTask, Runnable updateStartTask, Object testMethodArg0, - Object testMethodArg1, Object... testMethodArgs) + Consumer addTestFailedToStartTask, Runnable updateStartTask, Function onError, + Object testMethodArg0, Object testMethodArg1, Object... testMethodArgs) throws Exception { - Arrays.stream(testClass.getClass().getDeclaredMethods()) + List errorsToThrow = Arrays.stream(testClass.getClass().getDeclaredMethods()) .filter(m -> m.getAnnotationsByType(PluginTest.class).length == 1) - .filter(m -> m.getParameterCount() <= testMethodArgs.length + 2).forEach(m -> + .filter(m -> m.getParameterCount() <= testMethodArgs.length + 2).map(m -> { try { @@ -57,6 +60,8 @@ public static final void execute(Object testClass, Consumer addTestSucce m.getName()); addTestSucceededToStartTask.accept(testClass.getClass().getName() + "." + m.getName()); + + return null; } catch (InvocationTargetException e) { @@ -73,6 +78,8 @@ public static final void execute(Object testClass, Consumer addTestSucce m.getName(), e.getClass().getName(), e.getMessage(), e); addTestFailedToStartTask.accept(testClass.getClass().getName() + "." + m.getName()); + + return onError.apply(e); } catch (Exception e) { @@ -80,10 +87,27 @@ public static final void execute(Object testClass, Consumer addTestSucce m.getName(), e.getClass().getName(), e.getMessage(), e); addTestFailedToStartTask.accept(testClass.getClass().getName() + "." + m.getName()); + + return onError.apply(e); } - }); + }).filter(Objects::nonNull).collect(Collectors.toList()); + + try + { + updateStartTask.run(); + } + finally + { + if (!errorsToThrow.isEmpty()) + { + Exception error = errorsToThrow.get(0); - updateStartTask.run(); + for (int i = 1; i < errorsToThrow.size(); i++) + error.addSuppressed(errorsToThrow.get(i)); + + throw error; + } + } } public static void expectNotNull(Object actual) diff --git a/dsf-common/dsf-common-auth/src/main/java/dev/dsf/common/auth/conf/AbstractIdentityProvider.java b/dsf-common/dsf-common-auth/src/main/java/dev/dsf/common/auth/conf/AbstractIdentityProvider.java index 293e93909..393ea55d8 100644 --- a/dsf-common/dsf-common-auth/src/main/java/dev/dsf/common/auth/conf/AbstractIdentityProvider.java +++ b/dsf-common/dsf-common-auth/src/main/java/dev/dsf/common/auth/conf/AbstractIdentityProvider.java @@ -50,6 +50,10 @@ public abstract class AbstractIdentityProvider implements Ide private final RoleConfig roleConfig; private final Set thumbprints; + /** + * @param roleConfig + * not null + */ public AbstractIdentityProvider(RoleConfig roleConfig) { this.roleConfig = roleConfig; @@ -58,6 +62,18 @@ public AbstractIdentityProvider(RoleConfig roleConfig) .collect(Collectors.toUnmodifiableSet()); } + private String getHost(String serverBaseUrl) + { + try + { + return new URI(serverBaseUrl).getHost(); + } + catch (URISyntaxException e) + { + throw new RuntimeException(e); + } + } + @Override public void afterPropertiesSet() throws Exception { @@ -231,23 +247,22 @@ protected final Optional toPractitioner(DsfOpenIdCredentials crede String iss = credentials.getStringClaimOrDefault("iss", ""); String sub = credentials.getStringClaimOrDefault("sub", ""); - Set emails = Stream.of(credentials.getStringClaimOrDefault("email", ""), toEmail(iss, sub)) - .filter(m -> m != null).distinct().collect(Collectors.toSet()); + String email = credentials.getStringClaimOrDefault("email", toEmail(iss, sub)); Stream surname = Stream.of(credentials.getStringClaimOrDefault("family_name", "")); Stream givenNames = Stream.of(credentials.getStringClaimOrDefault("given_name", "")); - return toPractitioner(surname, givenNames, emails.stream()); + return toPractitioner(surname, givenNames, email); } - private Optional toPractitioner(Stream surname, Stream givenNames, - Stream emails) + private Optional toPractitioner(Stream surname, Stream givenNames, String email) { Practitioner practitioner = new Practitioner(); - emails.filter(e -> e != null).filter(e -> e.contains("@")) - .map(e -> new Identifier().setSystem(PRACTITIONER_IDENTIFIER_SYSTEM).setValue(e)) - .forEach(practitioner::addIdentifier); + if (email != null) + practitioner.addIdentifier(new Identifier().setSystem(PRACTITIONER_IDENTIFIER_SYSTEM).setValue(email)); + else + return Optional.empty(); HumanName name = new HumanName(); name.setFamily(surname.collect(Collectors.joining(" "))); @@ -262,14 +277,7 @@ private String toEmail(String iss, String sub) if (iss == null || sub == null || iss.isBlank() || sub.isBlank()) return null; - try - { - return sub + "@" + new URI(iss).getHost(); - } - catch (URISyntaxException e) - { - return null; - } + return sub + "." + getHost(iss) + "@oidc.invalid"; } protected final Optional toPractitioner(X509Certificate certificate) @@ -281,7 +289,7 @@ protected final Optional toPractitioner(X509Certificate certificat if (!thumbprints.contains(thumbprint)) return Optional.empty(); - return toJcaX509CertificateHolder(certificate).flatMap(this::toPractitioner); + return toJcaX509CertificateHolder(certificate).flatMap(ch -> toPractitioner(ch, thumbprint)); } private Optional toJcaX509CertificateHolder(X509Certificate certificate) @@ -299,7 +307,7 @@ private Optional toJcaX509CertificateHolder(X509Certif } } - private Optional toPractitioner(JcaX509CertificateHolder certificate) + private Optional toPractitioner(JcaX509CertificateHolder certificate, String thumbprint) { X500Name subject = certificate.getSubject(); List givennames = getValues(subject, BCStyle.GIVENNAME); @@ -314,10 +322,11 @@ private Optional toPractitioner(JcaX509CertificateHolder certifica .filter(n -> n.getTagNo() == GeneralName.rfc822Name).map(GeneralName::getName) .map(IETFUtils::valueToString).toList(); - Stream emails = Stream.concat(Stream.concat(email1.stream(), email2.stream()), rfc822Names.stream()); + String email = Stream.of(email1.stream(), email2.stream(), rfc822Names.stream()).flatMap(Function.identity()) + .findFirst().orElse(thumbprint + "@certificate.invalid"); return toPractitioner(!surnames.isEmpty() ? surnames.stream() : commonName.stream(), givennames.stream(), - emails); + email); } private List getValues(X500Name name, ASN1ObjectIdentifier attribute) diff --git a/dsf-common/dsf-common-auth/src/main/java/dev/dsf/common/auth/conf/PractitionerIdentity.java b/dsf-common/dsf-common-auth/src/main/java/dev/dsf/common/auth/conf/PractitionerIdentity.java index 6ac8944a7..a54ab2f91 100644 --- a/dsf-common/dsf-common-auth/src/main/java/dev/dsf/common/auth/conf/PractitionerIdentity.java +++ b/dsf-common/dsf-common-auth/src/main/java/dev/dsf/common/auth/conf/PractitionerIdentity.java @@ -1,5 +1,6 @@ package dev.dsf.common.auth.conf; +import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -10,6 +11,7 @@ public interface PractitionerIdentity extends Identity { + String CODE_SYSTEM_PRACTITIONER_ROLE = "http://dsf.dev/fhir/CodeSystem/practitioner-role"; String PRACTITIONER_IDENTIFIER_SYSTEM = "http://dsf.dev/sid/practitioner-identifier"; /** @@ -24,6 +26,18 @@ public interface PractitionerIdentity extends Identity */ Set getPractionerRoles(); + default boolean hasPractionerRole(String dsfRole) + { + return dsfRole != null && hasPractionerRole(new Coding(CODE_SYSTEM_PRACTITIONER_ROLE, dsfRole, null)); + } + + default boolean hasPractionerRole(Coding coding) + { + return coding != null && coding.hasSystem() && coding.hasCode() + && getPractionerRoles().stream().filter(Objects::nonNull).anyMatch( + c -> coding.getSystem().equals(c.getSystem()) && coding.getCode().equals(c.getCode())); + } + /** * @return {@link Optional#empty()} if login via client certificate */ diff --git a/dsf-fhir/dsf-fhir-auth/src/main/java/dev/dsf/fhir/authorization/process/All.java b/dsf-fhir/dsf-fhir-auth/src/main/java/dev/dsf/fhir/authorization/process/All.java index 8abc6e996..0438f4936 100644 --- a/dsf-fhir/dsf-fhir-auth/src/main/java/dev/dsf/fhir/authorization/process/All.java +++ b/dsf-fhir/dsf-fhir-auth/src/main/java/dev/dsf/fhir/authorization/process/All.java @@ -2,7 +2,6 @@ import java.util.List; import java.util.Optional; -import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -30,11 +29,6 @@ public All(boolean localIdentity, String practitionerRoleSystem, String practiti this.practitionerRoleCode = practitionerRoleCode; } - private boolean needsPractitionerRole() - { - return practitionerRoleSystem != null && practitionerRoleCode != null; - } - @Override public boolean isRequesterAuthorized(Identity requester, Stream requesterAffiliations) { @@ -47,26 +41,25 @@ public boolean isRecipientAuthorized(Identity recipient, Stream getPractitionerRoles(Identity identity) + @Override + public String getPractitionerRoleCode() { - if (identity instanceof PractitionerIdentity p) - return p.getPractionerRoles(); - else - return Set.of(); + return practitionerRoleCode; } - private boolean hasPractitionerRole(Set practitionerRoles) + private boolean isAuthorized(Identity identity) { - return practitionerRoles.stream().anyMatch( - c -> practitionerRoleSystem.equals(c.getSystem()) && practitionerRoleCode.equals(c.getCode())); + return identity != null && identity.getOrganization() != null && identity.getOrganization().getActive() + && identity.isLocalIdentity() == localIdentity + && ((needsPractitionerRole() && hasPractitionerRole(identity)) + || (!needsPractitionerRole() && identity instanceof OrganizationIdentity) + || (identity instanceof PractitionerIdentity p && p.hasPractionerRole("DSF_ADMIN"))); } @Override @@ -145,12 +138,6 @@ private boolean practitionerExtensionMatches(Extension extension) && practitionerRoleMatches(value); } - private boolean practitionerRoleMatches(Coding coding) - { - return coding != null && coding.hasSystem() && coding.hasCode() - && practitionerRoleSystem.equals(coding.getSystem()) && practitionerRoleCode.equals(coding.getCode()); - } - @Override public boolean matches(Coding processAuthorizationCode) { diff --git a/dsf-fhir/dsf-fhir-auth/src/main/java/dev/dsf/fhir/authorization/process/Organization.java b/dsf-fhir/dsf-fhir-auth/src/main/java/dev/dsf/fhir/authorization/process/Organization.java index 1788df80a..cfa182d60 100644 --- a/dsf-fhir/dsf-fhir-auth/src/main/java/dev/dsf/fhir/authorization/process/Organization.java +++ b/dsf-fhir/dsf-fhir-auth/src/main/java/dev/dsf/fhir/authorization/process/Organization.java @@ -3,7 +3,6 @@ import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -41,11 +40,6 @@ public Organization(boolean localIdentity, String organizationIdentifier, String this.practitionerRoleCode = practitionerRoleCode; } - private boolean needsPractitionerRole() - { - return practitionerRoleSystem != null && practitionerRoleCode != null; - } - @Override public boolean isRequesterAuthorized(Identity requester, Stream requesterAffiliations) { @@ -58,12 +52,25 @@ public boolean isRecipientAuthorized(Identity recipient, Stream organizationIdentifier.equals(i.getValue())); } - private Set getPractitionerRoles(Identity identity) - { - if (identity instanceof PractitionerIdentity p) - return p.getPractionerRoles(); - else - return Set.of(); - } - - private boolean hasPractitionerRole(Set practitionerRoles) - { - return practitionerRoles.stream().anyMatch( - c -> practitionerRoleSystem.equals(c.getSystem()) && practitionerRoleCode.equals(c.getCode())); - } - @Override public Extension toRecipientExtension() { @@ -218,12 +211,6 @@ private boolean practitionerExtensionMatches(Extension extension) && practitionerRoleMatches(value); } - private boolean practitionerRoleMatches(Coding coding) - { - return coding != null && coding.hasSystem() && coding.hasCode() - && practitionerRoleSystem.equals(coding.getSystem()) && practitionerRoleCode.equals(coding.getCode()); - } - @Override public boolean matches(Coding processAuthorizationCode) { diff --git a/dsf-fhir/dsf-fhir-auth/src/main/java/dev/dsf/fhir/authorization/process/Recipient.java b/dsf-fhir/dsf-fhir-auth/src/main/java/dev/dsf/fhir/authorization/process/Recipient.java index 5da781971..4283970d1 100644 --- a/dsf-fhir/dsf-fhir-auth/src/main/java/dev/dsf/fhir/authorization/process/Recipient.java +++ b/dsf-fhir/dsf-fhir-auth/src/main/java/dev/dsf/fhir/authorization/process/Recipient.java @@ -27,13 +27,11 @@ static Recipient localRole(String parentOrganizationIdentifier, String roleSyste boolean recipientMatches(Extension recipientExtension); - boolean isRecipientAuthorized(Identity recipientUser, Stream recipientAffiliations); + boolean isRecipientAuthorized(Identity recipient, Stream recipientAffiliations); - default boolean isRecipientAuthorized(Identity recipientUser, - Collection recipientAffiliations) + default boolean isRecipientAuthorized(Identity recipient, Collection recipientAffiliations) { - return isRecipientAuthorized(recipientUser, - recipientAffiliations == null ? null : recipientAffiliations.stream()); + return isRecipientAuthorized(recipient, recipientAffiliations == null ? null : recipientAffiliations.stream()); } Extension toRecipientExtension(); diff --git a/dsf-fhir/dsf-fhir-auth/src/main/java/dev/dsf/fhir/authorization/process/Requester.java b/dsf-fhir/dsf-fhir-auth/src/main/java/dev/dsf/fhir/authorization/process/Requester.java index 6d8d29bf3..e675de5bc 100644 --- a/dsf-fhir/dsf-fhir-auth/src/main/java/dev/dsf/fhir/authorization/process/Requester.java +++ b/dsf-fhir/dsf-fhir-auth/src/main/java/dev/dsf/fhir/authorization/process/Requester.java @@ -80,13 +80,11 @@ static Requester role(boolean localIdentity, String parentOrganizationIdentifier boolean requesterMatches(Extension requesterExtension); - boolean isRequesterAuthorized(Identity requesterUser, Stream requesterAffiliations); + boolean isRequesterAuthorized(Identity requester, Stream requesterAffiliations); - default boolean isRequesterAuthorized(Identity requesterUser, - Collection requesterAffiliations) + default boolean isRequesterAuthorized(Identity requester, Collection requesterAffiliations) { - return isRequesterAuthorized(requesterUser, - requesterAffiliations == null ? null : requesterAffiliations.stream()); + return isRequesterAuthorized(requester, requesterAffiliations == null ? null : requesterAffiliations.stream()); } Extension toRequesterExtension(); diff --git a/dsf-fhir/dsf-fhir-auth/src/main/java/dev/dsf/fhir/authorization/process/Role.java b/dsf-fhir/dsf-fhir-auth/src/main/java/dev/dsf/fhir/authorization/process/Role.java index e1b38184b..4e74644cd 100644 --- a/dsf-fhir/dsf-fhir-auth/src/main/java/dev/dsf/fhir/authorization/process/Role.java +++ b/dsf-fhir/dsf-fhir-auth/src/main/java/dev/dsf/fhir/authorization/process/Role.java @@ -3,7 +3,6 @@ import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -52,11 +51,6 @@ public Role(boolean localIdentity, String parentOrganizationIdentifier, String o this.practitionerRoleCode = practitionerRoleCode; } - private boolean needsPractitionerRole() - { - return practitionerRoleSystem != null && practitionerRoleCode != null; - } - @Override public boolean isRequesterAuthorized(Identity requester, Stream requesterAffiliations) { @@ -69,13 +63,26 @@ public boolean isRecipientAuthorized(Identity recipient, Stream affiliations) { return identity != null && identity.getOrganization() != null && identity.getOrganization().getActive() && identity.isLocalIdentity() == localIdentity && affiliations != null && hasParentOrganizationMemberRole(identity.getOrganization(), affiliations) - && ((needsPractitionerRole() && hasPractitionerRole(getPractitionerRoles(identity))) - || (!needsPractitionerRole() && identity instanceof OrganizationIdentity)); + && ((needsPractitionerRole() && hasPractitionerRole(identity)) + || (!needsPractitionerRole() && identity instanceof OrganizationIdentity) + || (identity instanceof PractitionerIdentity p && p.hasPractionerRole("DSF_ADMIN"))); } private boolean hasParentOrganizationMemberRole(org.hl7.fhir.r4.model.Organization recipientOrganization, @@ -114,20 +121,6 @@ private boolean hasParentOrganizationMemberRole(org.hl7.fhir.r4.model.Organizati c -> c.getSystem().equals(organizationRoleSystem) && c.getCode().equals(organizationRoleCode)); } - private Set getPractitionerRoles(Identity identity) - { - if (identity instanceof PractitionerIdentity p) - return p.getPractionerRoles(); - else - return Set.of(); - } - - private boolean hasPractitionerRole(Set practitionerRoles) - { - return practitionerRoles.stream().anyMatch( - c -> practitionerRoleSystem.equals(c.getSystem()) && practitionerRoleCode.equals(c.getCode())); - } - @Override public Extension toRecipientExtension() { @@ -289,12 +282,6 @@ private boolean practitionerRoleExtensionMatches(Extension extension) && practitionerRoleMatches(value); } - private boolean practitionerRoleMatches(Coding coding) - { - return coding != null && coding.hasSystem() && coding.hasCode() - && practitionerRoleSystem.equals(coding.getSystem()) && practitionerRoleCode.equals(coding.getCode()); - } - @Override public boolean matches(Coding processAuthorizationCode) { diff --git a/dsf-fhir/dsf-fhir-auth/src/main/java/dev/dsf/fhir/authorization/process/WithAuthorization.java b/dsf-fhir/dsf-fhir-auth/src/main/java/dev/dsf/fhir/authorization/process/WithAuthorization.java index 3cd243309..d09146ebc 100644 --- a/dsf-fhir/dsf-fhir-auth/src/main/java/dev/dsf/fhir/authorization/process/WithAuthorization.java +++ b/dsf-fhir/dsf-fhir-auth/src/main/java/dev/dsf/fhir/authorization/process/WithAuthorization.java @@ -2,9 +2,36 @@ import org.hl7.fhir.r4.model.Coding; +import dev.dsf.common.auth.conf.Identity; +import dev.dsf.common.auth.conf.PractitionerIdentity; + public interface WithAuthorization { Coding getProcessAuthorizationCode(); boolean matches(Coding processAuthorizationCode); + + String getPractitionerRoleSystem(); + + String getPractitionerRoleCode(); + + default boolean needsPractitionerRole() + { + return getPractitionerRoleSystem() != null && getPractitionerRoleCode() != null; + } + + default boolean hasPractitionerRole(Identity identity) + { + return identity instanceof PractitionerIdentity p + && p.getPractionerRoles().stream().anyMatch(c -> getPractitionerRoleSystem().equals(c.getSystem()) + && getPractitionerRoleCode().equals(c.getCode())); + } + + + default boolean practitionerRoleMatches(Coding coding) + { + return coding != null && coding.hasSystem() && coding.hasCode() + && getPractitionerRoleSystem().equals(coding.getSystem()) + && getPractitionerRoleCode().equals(coding.getCode()); + } } diff --git a/dsf-fhir/dsf-fhir-auth/src/test/java/dev/dsf/fhir/authorization/process/AllTest.java b/dsf-fhir/dsf-fhir-auth/src/test/java/dev/dsf/fhir/authorization/process/AllTest.java index 7e689fd4d..05aace31c 100644 --- a/dsf-fhir/dsf-fhir-auth/src/test/java/dev/dsf/fhir/authorization/process/AllTest.java +++ b/dsf-fhir/dsf-fhir-auth/src/test/java/dev/dsf/fhir/authorization/process/AllTest.java @@ -1,7 +1,6 @@ package dev.dsf.fhir.authorization.process; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import java.util.stream.Stream; @@ -26,6 +25,9 @@ public class AllTest private static final Identity LOCAL_PRACTITIONER_ORG_ACTIVE = TestPractitionerIdentity.practitioner( new Organization().setActive(true), new Coding("http://dsf.dev/fhir/CodeSystem/practitioner-role", "DIC_USER", null)); + private static final Identity LOCAL_PRACTITIONER_ORG_ACTIVE_DSF_ADMIN = TestPractitionerIdentity.practitioner( + new Organization().setActive(true), + new Coding("http://dsf.dev/fhir/CodeSystem/practitioner-role", "DSF_ADMIN", null)); private static final Identity LOCAL_PRACTITIONER_ORG_ACTIVE_BAD_ROLE1 = TestPractitionerIdentity.practitioner( new Organization().setActive(true), new Coding("http://dsf.dev/fhir/CodeSystem/practitioner-role", "UAC_USER", null)); @@ -104,12 +106,29 @@ public void testRemoteAllRecipientNotOkLocalOrganization() throws Exception assertFalse(remote.isRecipientAuthorized(LOCAL_ORG_ACTIVE, Stream.empty())); } + @Test + public void testRemoteAllRecipientNotOkPractitioner() throws Exception + { + assertFalse(remote.isRecipientAuthorized(LOCAL_PRACTITIONER_ORG_ACTIVE, Stream.empty())); + assertFalse(remote.isRecipientAuthorized(LOCAL_PRACTITIONER_ORG_ACTIVE_BAD_ROLE1, Stream.empty())); + assertFalse(remote.isRecipientAuthorized(LOCAL_PRACTITIONER_ORG_ACTIVE_BAD_ROLE2, Stream.empty())); + assertFalse(remote.isRecipientAuthorized(LOCAL_PRACTITIONER_ORG_NOT_ACTIVE, Stream.empty())); + assertFalse(remote.isRecipientAuthorized(LOCAL_PRACTITIONER_ORG_ACTIVE_NO_ROLES, Stream.empty())); + assertFalse(remote.isRecipientAuthorized(LOCAL_PRACTITIONER_ORG_ACTIVE_DSF_ADMIN, Stream.empty())); + } + @Test public void testLocalAllRequesterOk() throws Exception { assertTrue(local.isRequesterAuthorized(LOCAL_ORG_ACTIVE, Stream.empty())); } + @Test + public void testLocalAllRequesterOkPractitionerAdmin() throws Exception + { + assertTrue(local.isRequesterAuthorized(LOCAL_PRACTITIONER_ORG_ACTIVE_DSF_ADMIN, Stream.empty())); + } + @Test public void testLocalAllRequesterNotOkOrganizationNotActive() throws Exception { @@ -170,6 +189,12 @@ public void testLocalAllPractitionerRequesterOk() throws Exception assertTrue(localPractitioner.isRequesterAuthorized(LOCAL_PRACTITIONER_ORG_ACTIVE, Stream.empty())); } + @Test + public void testLocalAllPractitionerRequesterOkPractitionerAdmin() throws Exception + { + assertTrue(localPractitioner.isRequesterAuthorized(LOCAL_PRACTITIONER_ORG_ACTIVE_DSF_ADMIN, Stream.empty())); + } + @Test public void testLocalAllPractitionerRequesterNotOkOrganizationNotActive() throws Exception { diff --git a/dsf-fhir/dsf-fhir-auth/src/test/java/dev/dsf/fhir/authorization/process/OrganizationTest.java b/dsf-fhir/dsf-fhir-auth/src/test/java/dev/dsf/fhir/authorization/process/OrganizationTest.java index a115344b6..b84a3d172 100644 --- a/dsf-fhir/dsf-fhir-auth/src/test/java/dev/dsf/fhir/authorization/process/OrganizationTest.java +++ b/dsf-fhir/dsf-fhir-auth/src/test/java/dev/dsf/fhir/authorization/process/OrganizationTest.java @@ -41,6 +41,9 @@ private static org.hl7.fhir.r4.model.Organization createFhirOrganization(String private static final Identity LOCAL_PRACTITIONER_ORG_ACTIVE = TestPractitionerIdentity.practitioner( createFhirOrganization(IDENTIFIER).setActive(true), new Coding("http://dsf.dev/fhir/CodeSystem/practitioner-role", "DIC_USER", null)); + private static final Identity LOCAL_PRACTITIONER_ORG_ACTIVE_DSF_ADMIN = TestPractitionerIdentity.practitioner( + createFhirOrganization(IDENTIFIER).setActive(true), + new Coding("http://dsf.dev/fhir/CodeSystem/practitioner-role", "DSF_ADMIN", null)); private static final Identity LOCAL_PRACTITIONER_ORG_ACTIVE_BAD_ROLE1 = TestPractitionerIdentity.practitioner( createFhirOrganization(IDENTIFIER).setActive(true), new Coding("http://dsf.dev/fhir/CodeSystem/practitioner-role", "UAC_USER", null)); @@ -134,12 +137,30 @@ public void testRemoteOrganizationRecipientNotOkIdentifierWrong() throws Excepti assertFalse(remote.isRecipientAuthorized(REMOTE_ORG_BAD_IDENTIFIER_ACTIVE, Stream.empty())); } + @Test + public void testRemoteOrganizationRecipientNotOkPractitioner() throws Exception + { + assertFalse(remote.isRecipientAuthorized(LOCAL_PRACTITIONER_ORG_ACTIVE, Stream.empty())); + assertFalse(remote.isRecipientAuthorized(LOCAL_PRACTITIONER_ORG_ACTIVE_DSF_ADMIN, Stream.empty())); + assertFalse(remote.isRecipientAuthorized(LOCAL_PRACTITIONER_ORG_ACTIVE_BAD_ROLE1, Stream.empty())); + assertFalse(remote.isRecipientAuthorized(LOCAL_PRACTITIONER_ORG_ACTIVE_BAD_ROLE2, Stream.empty())); + assertFalse(remote.isRecipientAuthorized(LOCAL_PRACTITIONER_ORG_NOT_ACTIVE, Stream.empty())); + assertFalse(remote.isRecipientAuthorized(LOCAL_PRACTITIONER_ORG_ACTIVE_NO_ROLES, Stream.empty())); + assertFalse(remote.isRecipientAuthorized(LOCAL_PRACTITIONER_ORG_BAD_IDENTIFIER_ACTIVE, Stream.empty())); + } + @Test public void testLocalOrganizationRequesterOk() throws Exception { assertTrue(local.isRequesterAuthorized(LOCAL_ORG_ACTIVE, Stream.empty())); } + @Test + public void testLocalOrganizationRequesterOkPractitionerAdmin() throws Exception + { + assertTrue(local.isRequesterAuthorized(LOCAL_PRACTITIONER_ORG_ACTIVE_DSF_ADMIN, Stream.empty())); + } + @Test public void testLocalOrganizationRequesterNotOkOrganizationNotActive() throws Exception { @@ -212,6 +233,12 @@ public void testLocalOrganizationPractitionerRequesterOk() throws Exception assertTrue(localPractitioner.isRequesterAuthorized(LOCAL_PRACTITIONER_ORG_ACTIVE, Stream.empty())); } + @Test + public void testLocalOrganizationPractitionerAdminRequesterOk() throws Exception + { + assertTrue(localPractitioner.isRequesterAuthorized(LOCAL_PRACTITIONER_ORG_ACTIVE_DSF_ADMIN, Stream.empty())); + } + @Test public void testLocalOrganizationPractitionerRequesterNotOkOrganizationNotActive() throws Exception { diff --git a/dsf-fhir/dsf-fhir-auth/src/test/java/dev/dsf/fhir/authorization/process/RoleTest.java b/dsf-fhir/dsf-fhir-auth/src/test/java/dev/dsf/fhir/authorization/process/RoleTest.java index df7172f44..a48bad4ff 100644 --- a/dsf-fhir/dsf-fhir-auth/src/test/java/dev/dsf/fhir/authorization/process/RoleTest.java +++ b/dsf-fhir/dsf-fhir-auth/src/test/java/dev/dsf/fhir/authorization/process/RoleTest.java @@ -63,6 +63,9 @@ private static org.hl7.fhir.r4.model.Organization createFhirOrganization(String private static final Identity LOCAL_PRACTITIONER_ORG_ACTIVE = TestPractitionerIdentity.practitioner( createFhirOrganization(MEMBER_IDENTIFIER).setActive(true), new Coding("http://dsf.dev/fhir/CodeSystem/practitioner-role", "DIC_USER", null)); + private static final Identity LOCAL_PRACTITIONER_ORG_ACTIVE_DSF_ADMIN = TestPractitionerIdentity.practitioner( + createFhirOrganization(MEMBER_IDENTIFIER).setActive(true), + new Coding("http://dsf.dev/fhir/CodeSystem/practitioner-role", "DSF_ADMIN", null)); private static final Identity LOCAL_PRACTITIONER_ORG_ACTIVE_BAD_ROLE1 = TestPractitionerIdentity.practitioner( createFhirOrganization(MEMBER_IDENTIFIER).setActive(true), new Coding("http://dsf.dev/fhir/CodeSystem/practitioner-role", "UAC_USER", null)); @@ -244,6 +247,17 @@ public void testRemoteRoleRecipientNotOkMemberRoleSystem() throws Exception assertFalse(remote.isRecipientAuthorized(REMOTE_ORG_ACTIVE, affiliations)); } + @Test + public void testRemoteRoleRecipientNotOkPractitioner() throws Exception + { + assertFalse(remote.isRecipientAuthorized(LOCAL_PRACTITIONER_ORG_ACTIVE, okAffiliation())); + assertFalse(remote.isRecipientAuthorized(LOCAL_PRACTITIONER_ORG_ACTIVE_DSF_ADMIN, okAffiliation())); + assertFalse(remote.isRecipientAuthorized(LOCAL_PRACTITIONER_ORG_ACTIVE_BAD_ROLE1, okAffiliation())); + assertFalse(remote.isRecipientAuthorized(LOCAL_PRACTITIONER_ORG_ACTIVE_BAD_ROLE2, okAffiliation())); + assertFalse(remote.isRecipientAuthorized(LOCAL_PRACTITIONER_ORG_NOT_ACTIVE, okAffiliation())); + assertFalse(remote.isRecipientAuthorized(LOCAL_PRACTITIONER_ORG_ACTIVE_NO_ROLES, okAffiliation())); + } + // --- --- --- @Test @@ -252,6 +266,12 @@ public void testLocalRoleRequesterOk() throws Exception assertTrue(local.isRequesterAuthorized(LOCAL_ORG_ACTIVE, okAffiliation())); } + @Test + public void testLocalRoleRequesterOkPractitionerAdmin() throws Exception + { + assertTrue(local.isRequesterAuthorized(LOCAL_PRACTITIONER_ORG_ACTIVE_DSF_ADMIN, okAffiliation())); + } + @Test public void testLocalRoleRequesterNotOkOrganizationNotActive() throws Exception { @@ -398,6 +418,12 @@ public void testLocalRolePractitionerRequesterOk() throws Exception assertTrue(localPractitioner.isRequesterAuthorized(LOCAL_PRACTITIONER_ORG_ACTIVE, okAffiliation())); } + @Test + public void testLocalRolePractitionerAdminRequesterOk() throws Exception + { + assertTrue(localPractitioner.isRequesterAuthorized(LOCAL_PRACTITIONER_ORG_ACTIVE_DSF_ADMIN, okAffiliation())); + } + @Test public void testLocalRolePractitionerRequesterNotOkOrganizationNotActive() throws Exception { diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ResourceQuestionnaireResponse.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ResourceQuestionnaireResponse.java index 2458e4ea8..32f5ccbe2 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ResourceQuestionnaireResponse.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ResourceQuestionnaireResponse.java @@ -33,7 +33,8 @@ public class ResourceQuestionnaireResponse extends AbstractResource item) + private record Element(String questionnaire, String businessKey, String userTaskId, ElementSystemValue author, + String authored, List item) { } @@ -63,9 +64,16 @@ protected Element toElement(QuestionnaireResponse resource) String businessKey = getStringValue(resource, CODESYSTEM_DSF_BPMN_USER_TASK_VALUE_BUSINESS_KEY); String userTaskId = getStringValue(resource, CODESYSTEM_DSF_BPMN_USER_TASK_VALUE_USER_TASK_ID); + ElementSystemValue author = resource.hasAuthor() && resource.getAuthor().hasIdentifier() + ? ElementSystemValue.from(resource.getAuthor().getIdentifier()) + : null; + String authored = resource.hasAuthoredElement() && resource.getAuthoredElement().hasValue() + ? formatDateTime(resource.getAuthoredElement().getValue()) + : null; + List item = resource.hasItem() ? resource.getItem().stream().map(this::toItem).toList() : null; - return new Element(questionnaire, businessKey, userTaskId, item); + return new Element(questionnaire, businessKey, userTaskId, author, authored, item); } private String getStringValue(QuestionnaireResponse resource, String linkId) diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/SearchSetQuestionnaireResponse.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/SearchSetQuestionnaireResponse.java index ea0dcc121..e5ae9c1c2 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/SearchSetQuestionnaireResponse.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/SearchSetQuestionnaireResponse.java @@ -4,7 +4,8 @@ public class SearchSetQuestionnaireResponse extends AbstractSearchSet { - private record Row(ElementId id, String status, String questionnaire, String businessKey, String lastUpdated) + private record Row(ElementId id, String status, String questionnaire, String author, String businessKey, + String lastUpdated) { } @@ -22,6 +23,10 @@ protected Row toRow(ElementId id, QuestionnaireResponse resource) ? resource.getQuestionnaireElement().getValue().replaceAll("\\|", " \\| ") : ""; + String author = resource.hasAuthor() && resource.getAuthor().hasIdentifier() + && resource.getAuthor().getIdentifier().hasValue() ? resource.getAuthor().getIdentifier().getValue() + : ""; + String businessKey = resource.getItem().stream() .filter(i -> "business-key".equals(i.getLinkId()) && i.hasAnswer() && i.getAnswer().size() == 1 && i.getAnswerFirstRep().hasValueStringType()) @@ -29,6 +34,6 @@ protected Row toRow(ElementId id, QuestionnaireResponse resource) String lastUpdated = formatLastUpdated(resource); - return new Row(id, status, questionnaire, businessKey, lastUpdated); + return new Row(id, status, questionnaire, author, businessKey, lastUpdated); } } diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ThymeleafTemplateServiceImpl.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ThymeleafTemplateServiceImpl.java index a15a7e876..9e9afb31d 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ThymeleafTemplateServiceImpl.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ThymeleafTemplateServiceImpl.java @@ -41,6 +41,7 @@ import ca.uhn.fhir.model.api.annotation.ResourceDef; import ca.uhn.fhir.parser.IParser; import dev.dsf.common.auth.conf.Identity; +import dev.dsf.common.auth.conf.PractitionerIdentity; import dev.dsf.common.ui.theme.Theme; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.PathSegment; @@ -80,6 +81,8 @@ public class ThymeleafTemplateServiceImpl implements ThymeleafTemplateService, I private static final Pattern JSON_ID_UUID_AND_VERSION_PATTERN = Pattern .compile("\"id\": \"(" + UUID + ")\",\\n([ ]*)\"meta\": \\{\\n([ ]*)\"versionId\": \"([0-9]+)\","); + private static final String CODE_SYSTEM_PRACTITIONER_ROLE = "http://dsf.dev/fhir/CodeSystem/practitioner-role"; + private final String serverBaseUrl; private final Theme theme; private final FhirContext fhirContext; @@ -142,6 +145,27 @@ public void writeTo(Resource resource, Class type, MediaType mediaType, UriIn context.setVariable("heading", getHeading(resource, uriInfo)); context.setVariable("username", securityContext.getUserPrincipal() instanceof Identity i ? i.getDisplayName() : null); + + String usernameTitle = ""; + if (securityContext.getUserPrincipal() instanceof PractitionerIdentity p) + { + if (p.getPractitionerIdentifierValue().isPresent()) + usernameTitle += "Mail: " + p.getPractitionerIdentifierValue().get(); + if (p.getPractitionerIdentifierValue().isPresent() && !p.getPractionerRoles().isEmpty()) + usernameTitle += " - "; + if (!p.getPractionerRoles().isEmpty()) + usernameTitle += p.getPractionerRoles().stream() + .map(c -> CODE_SYSTEM_PRACTITIONER_ROLE.equals(c.getSystem()) ? c.getCode() + : c.getSystem() + "|" + c.getCode()) + .collect(Collectors.joining(", ", "Roles: ", "")); + } + context.setVariable("usernameTitle", usernameTitle); + + context.setVariable("practitionerIdentifierValue", + securityContext.getUserPrincipal() instanceof PractitionerIdentity p + ? p.getPractitionerIdentifierValue().orElse(null) + : null); + context.setVariable("openid", "OPENID".equals(securityContext.getAuthenticationScheme())); context.setVariable("xml", toXml(mediaType, resource)); context.setVariable("json", toJson(mediaType, resource)); diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/AbstractAuthorizationRule.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/AbstractAuthorizationRule.java index 23f649f86..0a9669227 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/AbstractAuthorizationRule.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/AbstractAuthorizationRule.java @@ -21,6 +21,8 @@ import ca.uhn.fhir.model.api.annotation.ResourceDef; import dev.dsf.common.auth.conf.Identity; +import dev.dsf.common.auth.conf.OrganizationIdentity; +import dev.dsf.common.auth.conf.PractitionerIdentity; import dev.dsf.fhir.authentication.FhirServerRole; import dev.dsf.fhir.authentication.FhirServerRoleImpl; import dev.dsf.fhir.authentication.OrganizationProvider; @@ -249,6 +251,51 @@ private boolean hasCode(CodeSystem codeSystem, String cCode) .map(ConceptDefinitionComponent::getCode).anyMatch(c -> c.equals(cCode)); } + protected boolean isCurrentIdentityPartOfReferencedOrganization(Connection connection, Identity identity, + String referenceLocation, Reference reference) + { + if (reference == null) + { + logger.warn("Null reference while checking if user part of referenced organization"); + + return false; + } + else + { + ResourceReference resReference = new ResourceReference(referenceLocation, reference, Organization.class); + + ReferenceType type = resReference.getType(serverBase); + if (!EnumSet.of(ReferenceType.LITERAL_INTERNAL, ReferenceType.LOGICAL).contains(type)) + { + logger.warn("Reference of type {} not supported while checking if user part of referenced organization", + type); + + return false; + } + + Optional resource = referenceResolver.resolveReference(resReference, connection); + if (resource.isPresent() && resource.get() instanceof Organization) + { + // ignoring updates (version changes) to the organization id + boolean sameOrganization = identity.getOrganization().getIdElement().getIdPart() + .equals(resource.get().getIdElement().getIdPart()); + if (!sameOrganization) + logger.warn( + "Current user not part of organization {} while checking if user part of referenced organization", + resource.get().getIdElement().getValue()); + + return sameOrganization; + } + else + { + logger.warn( + "Reference to organization could not be resolved while checking if user part of referenced organization"); + + return false; + } + } + } + @SafeVarargs protected final Optional createIfLiteralInternalOrLogicalReference(String referenceLocation, Reference reference, Class... referenceTypes) @@ -374,4 +421,10 @@ private Optional reasonWebsocketAllowed(Connection connection, Identity return Optional.empty(); } } + + protected final boolean isLocalOrganizationOrDsfAdmin(Identity identity) + { + return identity.isLocalIdentity() && (identity instanceof OrganizationIdentity + || (identity instanceof PractitionerIdentity p && p.hasPractionerRole("DSF_ADMIN"))); + } } diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/QuestionnaireResponseAuthorizationRule.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/QuestionnaireResponseAuthorizationRule.java index 26b67d1b9..2e3c3a271 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/QuestionnaireResponseAuthorizationRule.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/QuestionnaireResponseAuthorizationRule.java @@ -6,17 +6,25 @@ import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.QuestionnaireResponse; import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent; import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemComponent; import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseStatus; +import org.hl7.fhir.r4.model.Reference; import org.hl7.fhir.r4.model.StringType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import dev.dsf.common.auth.conf.Identity; +import dev.dsf.common.auth.conf.OrganizationIdentity; +import dev.dsf.common.auth.conf.PractitionerIdentity; import dev.dsf.fhir.authentication.OrganizationProvider; import dev.dsf.fhir.authorization.read.ReadAccessHelper; import dev.dsf.fhir.dao.QuestionnaireResponseDao; @@ -33,6 +41,13 @@ public class QuestionnaireResponseAuthorizationRule private static final String CODESYSTEM_DSF_BPMN_USER_TASK_VALUE_BUSINESS_KEY = "business-key"; private static final String CODESYSTEM_DSF_BPMN_USER_TASK_VALUE_USER_TASK_ID = "user-task-id"; + private static final String EXTENSION_QUESTIONNAIRE_AUTHORIZATION = "http://dsf.dev/fhir/StructureDefinition/extension-questionnaire-authorization"; + private static final String EXTENSION_QUESTIONNAIRE_AUTHORIZATION_PRACTITIONER = "practitioner"; + private static final String EXTENSION_QUESTIONNAIRE_AUTHORIZATION_PRACTITIONER_ROLE = "practitioner-role"; + + private static final String NAMING_SYSTEM_ORGANIZATION_IDENTIFIER = "http://dsf.dev/sid/organization-identifier"; + private static final String NAMING_SYSTEM_PRACTITIONER_IDENTIFIER = "http://dsf.dev/sid/practitioner-identifier"; + public QuestionnaireResponseAuthorizationRule(DaoProvider daoProvider, String serverBase, ReferenceResolver referenceResolver, OrganizationProvider organizationProvider, ReadAccessHelper readAccessHelper, ParameterConverter parameterConverter) @@ -45,50 +60,141 @@ public QuestionnaireResponseAuthorizationRule(DaoProvider daoProvider, String se public Optional reasonCreateAllowed(Connection connection, Identity identity, QuestionnaireResponse newResource) { - if (identity.isLocalIdentity() && identity.hasDsfRole(createRole)) + if (identity.hasDsfRole(createRole)) { - Optional errors = newResourceOk(newResource, EnumSet.of(QuestionnaireResponseStatus.INPROGRESS)); - if (errors.isEmpty()) + if (isLocalOrganizationOrDsfAdmin(identity)) { - // TODO implement unique criteria based on UserTask.id when implemented as identifier - logger.info("Create of QuestionnaireResponse authorized for local user '{}'", identity.getName()); - return Optional.of("local user"); + Optional errors = newResourceOk(connection, identity, newResource, + EnumSet.of(QuestionnaireResponseStatus.INPROGRESS)); + + if (errors.isEmpty()) + { + logger.info( + "Create of QuestionnaireResponse authorized for local identity '{}', QuestionnaireResponse.status in-progress", + identity.getName()); + return Optional.of( + "Local organization identity or practitioner with role DSF_ADMIN, QuestionnaireResponse.status in-progress"); + } + else + { + logger.warn("Create of QuestionnaireResponse unauthorized, {}", errors.get()); + return Optional.empty(); + } } else { - logger.warn("Create of QuestionnaireResponse unauthorized, {}", errors.get()); + logger.warn( + "Create of QuestionnaireResponse unauthorized, '{}' not a local organization identity or practitioner identity with role DSF_ADMIN", + identity.getName()); + return Optional.empty(); } } else { - logger.warn("Create of QuestionnaireResponse unauthorized, not a local user or no create role"); + logger.warn("Create of QuestionnaireResponse unauthorized for identity '{}', no role {}", + identity.getName(), deleteRole); + return Optional.empty(); } } - private Optional newResourceOk(QuestionnaireResponse newResource, - EnumSet allowedStatus) + private Optional newResourceOk(Connection connection, Identity identity, QuestionnaireResponse newResource, + Set allowedStatus) { List errors = new ArrayList<>(); if (newResource.hasStatus()) { if (!allowedStatus.contains(newResource.getStatus())) - { errors.add("QuestionnaireResponse.status not one of " + allowedStatus); - } } else - { errors.add("QuestionnaireResponse.status missing"); - } getItemAndValidate(newResource, CODESYSTEM_DSF_BPMN_USER_TASK_VALUE_USER_TASK_ID, errors); if (!newResource.hasQuestionnaire()) errors.add("QuestionnaireResponse.questionnaire missing"); + if (QuestionnaireResponseStatus.COMPLETED.equals(newResource.getStatus()) + || QuestionnaireResponseStatus.AMENDED.equals(newResource.getStatus())) + { + if (newResource.hasAuthor()) + { + Reference author = newResource.getAuthor(); + + if (author.hasIdentifier()) + { + Identifier identifier = author.getIdentifier(); + + if (identifier.hasSystem() && identifier.hasValue()) + { + if (identity instanceof PractitionerIdentity p) + { + if (!NAMING_SYSTEM_PRACTITIONER_IDENTIFIER.equals(identifier.getSystem())) + errors.add("QuestionnaireResponse.author.identifier.system not " + + NAMING_SYSTEM_PRACTITIONER_IDENTIFIER); + + Optional practitionerIdentifierValue = p.getPractitionerIdentifierValue(); + if (practitionerIdentifierValue.isPresent()) + { + if (!practitionerIdentifierValue.get().equals(identifier.getValue())) + errors.add("QuestionnaireResponse.author not current practitioner identity"); + } + else + throw new RuntimeException("Authenticated practitioner user has no identifier"); + } + else if (identity instanceof OrganizationIdentity) + { + if (!NAMING_SYSTEM_ORGANIZATION_IDENTIFIER.equals(identifier.getSystem())) + { + errors.add("QuestionnaireResponse.author.identifier.system not " + + NAMING_SYSTEM_ORGANIZATION_IDENTIFIER); + } + + if (!isCurrentIdentityPartOfReferencedOrganization(connection, identity, + "QuestionnaireResponse.author", newResource.getAuthor())) + { + errors.add( + "QuestionnaireResponse.author current identity not part of referenced organization"); + } + } + } + else + { + errors.add( + "QuestionnaireResponse.author.identifier.system or QuestionnaireResponse.author.identifier.value missing"); + } + } + else + errors.add("QuestionnaireResponse.author.identifier missing"); + } + else + errors.add("QuestionnaireResponse.author missing"); + + if (!newResource.hasAuthored()) + errors.add("QuestionnaireResponse.authored missing"); + } + + Extension authExtension = newResource.getExtensionByUrl(EXTENSION_QUESTIONNAIRE_AUTHORIZATION); + if (authExtension != null) + { + if (!authExtension.getExtension().stream().allMatch(e -> + { + if (EXTENSION_QUESTIONNAIRE_AUTHORIZATION_PRACTITIONER.equals(e.getUrl())) + return e.getValue() instanceof Identifier i && i.hasSystem() && i.hasValue(); + else if (EXTENSION_QUESTIONNAIRE_AUTHORIZATION_PRACTITIONER_ROLE.equals(e.getUrl())) + return e.getValue() instanceof Coding c && c.hasSystem() && c.hasCode(); + else + return true; + })) + { + errors.add( + "QuestionnaireResponse.extension[url=" + EXTENSION_QUESTIONNAIRE_AUTHORIZATION + "] invalid"); + } + } + if (errors.isEmpty()) return Optional.empty(); else @@ -104,7 +210,7 @@ private Optional getItemAndValidate(QuestionnaireResponse newResource, S if (userTaskIds.size() != 1) { if (errors != null) - errors.add("QuestionnaireResponse.item('user-task-id') missing or more than one"); + errors.add("QuestionnaireResponse.item[linkId=" + linkId + "] missing or more than one"); return Optional.empty(); } @@ -114,7 +220,7 @@ private Optional getItemAndValidate(QuestionnaireResponse newResource, S if (!item.hasAnswer() || item.getAnswer().size() != 1) { if (errors != null) - errors.add("QuestionnaireResponse.item('user-task-id').answer missing or more than one"); + errors.add("QuestionnaireResponse.item[linkId=" + linkId + "].answer missing or more than one"); return Optional.empty(); } @@ -124,7 +230,7 @@ private Optional getItemAndValidate(QuestionnaireResponse newResource, S if (!answer.hasValue() || !(answer.getValue() instanceof StringType)) { if (errors != null) - errors.add("QuestionnaireResponse.item('user-task-id').answer.value missing or not a string"); + errors.add("QuestionnaireResponse.item[linkId=" + linkId + "].answer.value missing or not a string"); return Optional.empty(); } @@ -134,7 +240,7 @@ private Optional getItemAndValidate(QuestionnaireResponse newResource, S if (!value.hasValue()) { if (errors != null) - errors.add("QuestionnaireResponse.item('user-task-id').answer.value is blank"); + errors.add("QuestionnaireResponse.item[linkId=" + linkId + "].answer.value is blank"); return Optional.empty(); } @@ -146,66 +252,329 @@ private Optional getItemAndValidate(QuestionnaireResponse newResource, S public Optional reasonReadAllowed(Connection connection, Identity identity, QuestionnaireResponse existingResource) { - if (identity.isLocalIdentity() && identity.hasDsfRole(readRole)) + final String resourceId = existingResource.getIdElement().getIdPart(); + final long resourceVersion = existingResource.getIdElement().getVersionIdPartAsLong(); + + if (identity.hasDsfRole(readRole)) { - logger.info("Read of QuestionnaireResponse authorized for local user '{}'", identity.getName()); - return Optional.of("task.restriction.recipient resolved and local user part of referenced organization"); + if (isLocalOrganizationOrDsfAdmin(identity)) + { + logger.info("Read of QuestionnaireResponse/{}/_history/{} authorized for local identity '{}'", + resourceId, resourceVersion, identity.getName()); + + return Optional.of("Local organization identity or practitioner with role DSF_ADMIN"); + } + else if (identity instanceof PractitionerIdentity p && isPractitionerAuthorized(existingResource, p)) + { + logger.info( + "Read of QuestionnaireResponse/{}/_history/{} authorized for local practitioner identity '{}'", + resourceId, resourceVersion, identity.getName()); + + return Optional.of("Practitioner identity authorized by questionnaire-authorization extension"); + } + else + { + logger.warn( + "Read of QuestionnaireResponse/{}/_history/{} unauthorized, '{}' not a local organization identity or practitioner identity with role DSF_ADMIN or practitioner identity authorized by questionnaire-authorization extension", + resourceId, resourceVersion, identity.getName()); + + return Optional.empty(); + } } else { - logger.warn("Read of QuestionnaireResponse unauthorized, not a local user or no read role"); + logger.warn("Read of QuestionnaireResponse/{}/_history/{} unauthorized for identity '{}', no role {}", + resourceId, resourceVersion, identity.getName(), readRole); + return Optional.empty(); } } + private boolean isPractitionerAuthorized(QuestionnaireResponse existingResource, PractitionerIdentity identity) + { + if (existingResource == null) + return false; + + Extension authExtension = existingResource.getExtensionByUrl(EXTENSION_QUESTIONNAIRE_AUTHORIZATION); + + // allow access if extension not specified (DSF 1.x behavior) + if (authExtension == null) + return true; + + return authExtension.getExtension().stream().anyMatch(e -> + { + if (EXTENSION_QUESTIONNAIRE_AUTHORIZATION_PRACTITIONER.equals(e.getUrl()) + && e.getValue() instanceof Identifier i && i.hasSystem() && i.hasValue()) + { + return NAMING_SYSTEM_PRACTITIONER_IDENTIFIER.equals(i.getSystem()) + && identity.getPractitionerIdentifierValue().map(v -> v.equals(i.getValue())).orElse(false); + } + else if (EXTENSION_QUESTIONNAIRE_AUTHORIZATION_PRACTITIONER_ROLE.equals(e.getUrl()) + && e.getValue() instanceof Coding c && c.hasSystem() && c.hasCode()) + { + return identity.getPractionerRoles().stream() + .anyMatch(r -> r.getSystem().equals(c.getSystem()) && r.getCode().equals(c.getCode())); + } + else + return false; + }); + } + @Override public Optional reasonUpdateAllowed(Connection connection, Identity identity, QuestionnaireResponse oldResource, QuestionnaireResponse newResource) { - if (identity.isLocalIdentity() && identity.hasDsfRole(updateRole)) + final String oldResourceId = oldResource.getIdElement().getIdPart(); + final long oldResourceVersion = oldResource.getIdElement().getVersionIdPartAsLong(); + + if (identity.hasDsfRole(updateRole)) { - Optional errors = newResourceOk(newResource, - EnumSet.of(QuestionnaireResponseStatus.COMPLETED, QuestionnaireResponseStatus.STOPPED)); - if (errors.isEmpty()) + if (identity.isLocalIdentity()) { - if (modificationsOk(oldResource, newResource)) + if (QuestionnaireResponseStatus.INPROGRESS.equals(oldResource.getStatus()) + && QuestionnaireResponseStatus.COMPLETED.equals(newResource.getStatus())) { - logger.info("Update of QuestionnaireResponse authorized for local user '{}', modification allowed", - identity.getName()); - return Optional.of("local user; modification allowed"); + if (isLocalOrganizationOrDsfAdmin(identity)) + { + Optional errors = newResourceOk(connection, identity, newResource, + Set.of(QuestionnaireResponseStatus.COMPLETED)); + if (errors.isPresent()) + { + logger.warn("Update of QuestionnaireResponse/{}/_history/{} ({} -> {}) unauthorized, {}", + oldResourceId, oldResourceVersion, QuestionnaireResponseStatus.INPROGRESS.toCode(), + QuestionnaireResponseStatus.COMPLETED.toCode(), errors.get()); + + return Optional.empty(); + } + else if (!modificationsOk(oldResource, newResource)) + { + logger.warn( + "Update of QuestionnaireResponse/{}/_history/{} ({} -> {}) unauthorized, modification not allowed", + oldResourceId, oldResourceVersion, QuestionnaireResponseStatus.INPROGRESS.toCode(), + QuestionnaireResponseStatus.COMPLETED.toCode()); + + return Optional.empty(); + } + else + { + logger.info( + "Update of QuestionnaireResponse/{}/_history/{} ({} -> {}) authorized for local identity '{}'", + oldResourceId, oldResourceVersion, QuestionnaireResponseStatus.INPROGRESS.toCode(), + QuestionnaireResponseStatus.COMPLETED.toCode(), identity.getName()); + + return Optional.of( + "Local organization identity or practitioner with role DSF_ADMIN, old QuestionnaireResponse.status in-progress, new QuestionnaireResponse.status completed"); + } + } + else if (identity instanceof PractitionerIdentity p && isPractitionerAuthorized(oldResource, p)) + { + Optional errors = newResourceOk(connection, identity, newResource, + Set.of(QuestionnaireResponseStatus.COMPLETED)); + if (errors.isPresent()) + { + logger.warn("Update of QuestionnaireResponse/{}/_history/{} ({} -> {}) unauthorized, {}", + oldResourceId, oldResourceVersion, QuestionnaireResponseStatus.INPROGRESS.toCode(), + QuestionnaireResponseStatus.COMPLETED.toCode(), errors.get()); + + return Optional.empty(); + } + else if (!modificationsOk(oldResource, newResource)) + { + logger.warn( + "Update of QuestionnaireResponse/{}/_history/{} ({} -> {}) unauthorized, modification not allowed", + oldResourceId, oldResourceVersion, QuestionnaireResponseStatus.INPROGRESS.toCode(), + QuestionnaireResponseStatus.COMPLETED.toCode()); + + return Optional.empty(); + } + else + { + logger.info( + "Update of QuestionnaireResponse/{}/_history/{} ({} -> {}) authorized for local identity '{}'", + oldResourceId, oldResourceVersion, QuestionnaireResponseStatus.INPROGRESS.toCode(), + QuestionnaireResponseStatus.COMPLETED.toCode(), identity.getName()); + + return Optional.of( + "Practitioner identity authorized by questionnaire-authorization extension, old QuestionnaireResponse.status in-progress, new QuestionnaireResponse.status completed"); + } + } + else + { + logger.warn( + "Update of QuestionnaireResponse/{}/_history/{} ({} -> {}) unauthorized, '{}' not a local organization identity or practitioner identity with role DSF_ADMIN or practitioner identity authorized by questionnaire-authorization extension", + oldResourceId, oldResourceVersion, QuestionnaireResponseStatus.INPROGRESS.toCode(), + QuestionnaireResponseStatus.COMPLETED.toCode(), identity.getName()); + + return Optional.empty(); + } + } + else if (QuestionnaireResponseStatus.INPROGRESS.equals(oldResource.getStatus()) + && QuestionnaireResponseStatus.STOPPED.equals(newResource.getStatus())) + { + if (isLocalOrganizationOrDsfAdmin(identity)) + { + if (!modificationsOkStatusOnly(oldResource, newResource)) + { + logger.warn( + "Update of QuestionnaireResponse/{}/_history/{} ({} -> {}) unauthorized, modification not allowed", + oldResourceId, oldResourceVersion, QuestionnaireResponseStatus.INPROGRESS.toCode(), + QuestionnaireResponseStatus.STOPPED.toCode()); + + return Optional.empty(); + } + else + { + logger.info( + "Update of QuestionnaireResponse/{}/_history/{} ({} -> {}) authorized for local identity '{}'", + oldResourceId, oldResourceVersion, QuestionnaireResponseStatus.INPROGRESS.toCode(), + QuestionnaireResponseStatus.STOPPED.toCode(), identity.getName()); + + return Optional.of( + "Local organization identity or practitioner with role DSF_ADMIN, old QuestionnaireResponse.status in-progress, new QuestionnaireResponse.status stopped"); + } + } + else + { + logger.warn( + "Update of QuestionnaireResponse/{}/_history/{} ({} -> {}) unauthorized, '{}' not a local organization identity or practitioner identity with role DSF_ADMIN", + oldResourceId, oldResourceVersion, QuestionnaireResponseStatus.INPROGRESS.toCode(), + QuestionnaireResponseStatus.STOPPED.toCode(), identity.getName()); + + return Optional.empty(); + } + + } + else if (QuestionnaireResponseStatus.COMPLETED.equals(oldResource.getStatus()) + && QuestionnaireResponseStatus.AMENDED.equals(newResource.getStatus())) + { + if (isLocalOrganizationOrDsfAdmin(identity)) + { + if (!modificationsOkStatusOnly(oldResource, newResource)) + { + logger.warn( + "Update of QuestionnaireResponse/{}/_history/{} ({} -> {}) unauthorized, modification not allowed", + oldResourceId, oldResourceVersion, QuestionnaireResponseStatus.COMPLETED.toCode(), + QuestionnaireResponseStatus.AMENDED.toCode()); + + return Optional.empty(); + } + else + { + logger.info( + "Update of QuestionnaireResponse/{}/_history/{} ({} -> {}) authorized for local identity '{}'", + oldResourceId, oldResourceVersion, QuestionnaireResponseStatus.COMPLETED.toCode(), + QuestionnaireResponseStatus.AMENDED.toCode(), identity.getName()); + + return Optional.of( + "Local organization identity or practitioner with role DSF_ADMIN, old QuestionnaireResponse.status completed, new QuestionnaireResponse.status amended"); + } + } + else + { + logger.warn( + "Update of QuestionnaireResponse/{}/_history/{} ({} -> {}) unauthorized, '{}' not a local organization identity or practitioner identity with role DSF_ADMIN", + oldResourceId, oldResourceVersion, QuestionnaireResponseStatus.COMPLETED.toCode(), + QuestionnaireResponseStatus.AMENDED.toCode(), identity.getName()); + + return Optional.empty(); + } } else { - logger.warn("Update of QuestionnaireResponse unauthorized, modification not allowed"); + logger.warn( + "Update of QuestionnaireResponse/{}/_history/{} ({} -> {}) unauthorized for local identity '{}', old vs. new QuestionnaireResponse.status not one of {}", + oldResourceId, oldResourceVersion, + oldResource.getStatus() != null ? oldResource.getStatus().toCode() : null, + newResource.getStatus() != null ? newResource.getStatus().toCode() : null, + identity.getName(), Stream + .of(Stream.of(QuestionnaireResponseStatus.INPROGRESS, + QuestionnaireResponseStatus.COMPLETED), + Stream.of(QuestionnaireResponseStatus.INPROGRESS, + QuestionnaireResponseStatus.STOPPED), + Stream.of(QuestionnaireResponseStatus.COMPLETED, + QuestionnaireResponseStatus.AMENDED)) + .map(s -> s.map(QuestionnaireResponseStatus::toCode) + .collect(Collectors.joining("->"))) + .collect(Collectors.joining(", ", "[", "]"))); + return Optional.empty(); } } else { - logger.warn("Update of QuestionnaireResponse unauthorized, {}", errors.get()); + logger.warn("Update of QuestionnaireResponse/{}/_history/{} unauthorized, '{}' not a local identity", + oldResourceId, oldResourceVersion, identity.getName()); + return Optional.empty(); } } else { - logger.warn("Update of QuestionnaireResponse unauthorized, not a local user or no update role"); + logger.warn("Update of QuestionnaireResponse/{}/_history/{} unauthorized for identity '{}', no role {}", + oldResourceId, oldResourceVersion, identity.getName(), updateRole); + return Optional.empty(); } } - private boolean modificationsOk(QuestionnaireResponse oldResource, QuestionnaireResponse newResource) + private boolean modificationsOkStatusOnly(QuestionnaireResponse oldResource, QuestionnaireResponse newResource) { - boolean statusModificationOk = QuestionnaireResponseStatus.INPROGRESS.equals(oldResource.getStatus()) - && (QuestionnaireResponseStatus.COMPLETED.equals(newResource.getStatus()) - || QuestionnaireResponseStatus.STOPPED.equals(newResource.getStatus())); + QuestionnaireResponseStatus newResourceStatus = newResource.getStatus(); - if (!statusModificationOk) - logger.warn( - "Modifications only allowed if status changes from '{}' to '{}', current status of old resource is '{}' and of new resource is '{}'", - QuestionnaireResponseStatus.INPROGRESS, - QuestionnaireResponseStatus.COMPLETED + "|" + QuestionnaireResponseStatus.STOPPED, - oldResource.getStatus(), newResource.getStatus()); + newResource.setStatus(oldResource.getStatus()); + boolean resourceNotModified = oldResource.equalsDeep(newResource); + boolean authExtensionNotModified = modificationsOkQuestionnaireAuthorizationExtensionNotModified(oldResource, + newResource); + + newResource.setStatus(newResourceStatus); + + if (!resourceNotModified) + logger.warn("Modification of QuestionnaireResponse not allowed"); + + if (!authExtensionNotModified) + logger.warn("Modification of QuestionnaireResponse.extension[url={}] not allowed", + EXTENSION_QUESTIONNAIRE_AUTHORIZATION); + + return resourceNotModified && authExtensionNotModified; + } + + private boolean modificationsOkQuestionnaireAuthorizationExtensionNotModified(QuestionnaireResponse oldResource, + QuestionnaireResponse newResource) + { + Extension oldAuthExtension = oldResource.getExtensionByUrl(EXTENSION_QUESTIONNAIRE_AUTHORIZATION); + Extension newAuthExtension = newResource.getExtensionByUrl(EXTENSION_QUESTIONNAIRE_AUTHORIZATION); + + if (oldAuthExtension == null && newAuthExtension == null) + return true; + else if (oldAuthExtension != null && newAuthExtension != null) + { + Extension[] oldEx = oldAuthExtension.getExtension().stream() + .filter(e -> EXTENSION_QUESTIONNAIRE_AUTHORIZATION_PRACTITIONER.equals(e.getUrl()) + || EXTENSION_QUESTIONNAIRE_AUTHORIZATION_PRACTITIONER_ROLE.equals(e.getUrl())) + .toArray(Extension[]::new); + + Extension[] newEx = newAuthExtension.getExtension().stream() + .filter(e -> EXTENSION_QUESTIONNAIRE_AUTHORIZATION_PRACTITIONER.equals(e.getUrl()) + || EXTENSION_QUESTIONNAIRE_AUTHORIZATION_PRACTITIONER_ROLE.equals(e.getUrl())) + .toArray(Extension[]::new); + if (oldEx.length != newEx.length) + return false; + + for (int i = 0; i < oldEx.length; i++) + { + if (!oldEx[i].equalsDeep(newEx[i])) + return false; + } + + return true; + } + else + return false; + } + + private boolean modificationsOk(QuestionnaireResponse oldResource, QuestionnaireResponse newResource) + { String oldUserTaskId = getItemAndValidate(oldResource, CODESYSTEM_DSF_BPMN_USER_TASK_VALUE_USER_TASK_ID, new ArrayList<>()).orElse(null); String newUserTaskId = getItemAndValidate(newResource, CODESYSTEM_DSF_BPMN_USER_TASK_VALUE_USER_TASK_ID, @@ -239,21 +608,46 @@ private boolean modificationsOk(QuestionnaireResponse oldResource, Questionnaire logger.warn("Modifications of QuestionnaireResponse.questionnaire not allowed, changed from '{}' to '{}'", oldQuestionnaireUrlAndVersion, newQuestionnaireUrlAndVersion); - return statusModificationOk && userTaskIdOk && businesssKeyOk && questionnaireCanonicalOk; + boolean authExtensionNotModified = modificationsOkQuestionnaireAuthorizationExtensionNotModified(oldResource, + newResource); + + if (!authExtensionNotModified) + logger.warn("Modifications of QuestionnaireResponse.extension[url={}] not allowed", + EXTENSION_QUESTIONNAIRE_AUTHORIZATION); + + return userTaskIdOk && businesssKeyOk && questionnaireCanonicalOk && authExtensionNotModified; } @Override public Optional reasonDeleteAllowed(Connection connection, Identity identity, QuestionnaireResponse oldResource) { - if (identity.isLocalIdentity() && identity.hasDsfRole(deleteRole)) + final String oldResourceId = oldResource.getIdElement().getIdPart(); + final long oldResourceVersion = oldResource.getIdElement().getVersionIdPartAsLong(); + + if (identity.hasDsfRole(deleteRole)) { - logger.info("Delete of QuestionnaireResponse authorized for local user '{}'", identity.getName()); - return Optional.of("local user"); + if (isLocalOrganizationOrDsfAdmin(identity)) + { + logger.info("Delete of QuestionnaireResponse/{}/_history/{} authorized for local identity '{}'", + oldResourceId, oldResourceVersion, identity.getName()); + + return Optional.of("Local organization identity or practitioner with role DSF_ADMIN"); + } + else + { + logger.warn( + "Delete of QuestionnaireResponse/{}/_history/{} unauthorized, '{}' not a local organization identity or practitioner identity with role DSF_ADMIN", + oldResourceId, oldResourceVersion, identity.getName()); + + return Optional.empty(); + } } else { - logger.warn("Delete of QuestionnaireResponse unauthorized, not a local user or no delete role"); + logger.warn("Delete of QuestionnaireResponse/{}/_history/{} unauthorized for identity '{}', no role {}", + oldResourceId, oldResourceVersion, identity.getName(), deleteRole); + return Optional.empty(); } } diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/TaskAuthorizationRule.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/TaskAuthorizationRule.java index ce1ebb649..316abb3b4 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/TaskAuthorizationRule.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/TaskAuthorizationRule.java @@ -3,7 +3,6 @@ import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; -import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -32,6 +31,7 @@ import ca.uhn.fhir.context.FhirContext; import dev.dsf.common.auth.conf.Identity; import dev.dsf.common.auth.conf.OrganizationIdentity; +import dev.dsf.common.auth.conf.PractitionerIdentity; import dev.dsf.fhir.authentication.EndpointProvider; import dev.dsf.fhir.authentication.OrganizationProvider; import dev.dsf.fhir.authorization.process.ProcessAuthorizationHelper; @@ -45,7 +45,6 @@ import dev.dsf.fhir.search.SearchQueryParameterError; import dev.dsf.fhir.service.ReferenceResolver; import dev.dsf.fhir.service.ResourceReference; -import dev.dsf.fhir.service.ResourceReference.ReferenceType; public class TaskAuthorizationRule extends AbstractAuthorizationRule { @@ -61,6 +60,8 @@ public class TaskAuthorizationRule extends AbstractAuthorizationRule resource = referenceResolver.resolveReference(resReference, connection); - if (resource.isPresent() && resource.get() instanceof Organization) - { - // ignoring updates (version changes) to the organization id - boolean sameOrganization = identity.getOrganization().getIdElement().getIdPart() - .equals(resource.get().getIdElement().getIdPart()); - if (!sameOrganization) - logger.warn( - "Current user not part of organization {} while checking if user part of referenced organization", - resource.get().getIdElement().getValue()); - - return sameOrganization; - } - else - { - logger.warn( - "Reference to organization could not be resolved while checking if user part of referenced organization"); - - return false; - } - } - } - private boolean isLocalOrganization(Organization organization) { if (organization == null || !organization.hasIdElement()) @@ -146,90 +102,99 @@ private boolean isLocalOrganization(Organization organization) @Override public Optional reasonCreateAllowed(Connection connection, Identity identity, Task newResource) { - if (identity.hasDsfRole(createRole)) + if (!identity.hasDsfRole(createRole)) { - if (TaskStatus.DRAFT.equals(newResource.getStatus())) - { - if (identity.isLocalIdentity() && identity instanceof OrganizationIdentity) - { - Optional errors = draftTaskOk(connection, identity, newResource); - if (errors.isEmpty()) - { - if (!draftTaskExists(connection, newResource)) - { - logger.info( - "Create of Task authorized for local organization identity '{}', Task.status draft", - identity.getName()); - - return Optional.of("Local identity, Task.status draft"); - } - else - { - logger.warn("Create of Task unauthorized, unique resource already exists"); + logger.warn("Create of Task unauthorized for identity '{}', no role {}", identity.getName(), createRole); + return Optional.empty(); + } - return Optional.empty(); - } - } - else - { - logger.warn("Create of Task unauthorized for identity '{}', Task.status draft, {}", - identity.getName(), errors.get()); + if (TaskStatus.DRAFT.equals(newResource.getStatus())) + return reasonCreateDraftAllowed(connection, identity, newResource); + else if (TaskStatus.REQUESTED.equals(newResource.getStatus())) + return reasonCreateRequestedAllowed(connection, identity, newResource); + else + { + logger.warn("Create of Task unauthorized for identity '{}', Task.status not {} and not {}", + identity.getName(), TaskStatus.DRAFT.toCode(), TaskStatus.REQUESTED.toCode()); - return Optional.empty(); - } - } - else - { - logger.warn( - "Create of Task unauthorized for identity '{}', Task.status draft, not allowed for non local organization identity", - identity.getName()); + return Optional.empty(); + } + } - return Optional.empty(); - } - } - else if (TaskStatus.REQUESTED.equals(newResource.getStatus())) + private Optional reasonCreateDraftAllowed(Connection connection, Identity identity, Task newResource) + { + if (isLocalOrganizationOrDsfAdmin(identity)) + { + Optional errors = draftTaskOk(connection, identity, newResource); + if (errors.isEmpty()) { - Optional errors = requestedTaskOk(connection, identity, newResource); - if (errors.isEmpty()) + if (!draftTaskExists(connection, newResource)) { - if (taskAllowedForRequesterAndRecipient(connection, identity, newResource)) - { - logger.info( - "Create of Task authorized for identity '{}', Task.status requested, process allowed for current identity", - identity.getName()); - - return Optional.of( - "Local or remote identity, Task.status requested, Task.requester current identity's organization, Task.restriction.recipient local organization, " - + "process with instantiatesCanonical and message-name allowed for current identity, Task defines needed profile"); - } - else - { - logger.warn( - "Create of Task unauthorized for identity '{}', Task.status requested, process with instantiatesCanonical, message-name, requester or recipient not allowed", - identity.getName()); + logger.info("Create of Task authorized for local identity '{}', Task.status draft", + identity.getName()); - return Optional.empty(); - } + return Optional + .of("Local organization identity or practitioner with role DSF_ADMIN, Task.status draft"); } else { - logger.warn("Create of Task unauthorized for identity '{}', Task.status requested, {}", - identity.getName(), errors.get()); + logger.warn("Create of Task unauthorized, unique resource already exists"); return Optional.empty(); } } else { - logger.warn("Create of Task unauthorized for identity '{}', Task.status not {} and not {}", - identity.getName(), TaskStatus.DRAFT.toCode(), TaskStatus.REQUESTED.toCode()); + logger.warn("Create of Task unauthorized for identity '{}', Task.status draft, {}", identity.getName(), + errors.get()); return Optional.empty(); } } else { - logger.warn("Create of Task unauthorized for identity '{}', no role {}", identity.getName(), createRole); + logger.warn( + "Create of Task unauthorized, '{}' not a local organization identity or practitioner identity with role DSF_ADMIN", + identity.getName()); + + return Optional.empty(); + } + } + + private Optional reasonCreateRequestedAllowed(Connection connection, Identity identity, Task newResource) + { + Optional errors = requestedTaskOk(connection, identity, newResource); + if (!errors.isEmpty()) + { + logger.warn("Create of Task unauthorized for identity '{}', Task.status requested, {}", identity.getName(), + errors.get()); + + return Optional.empty(); + } + + if (taskAllowedForRequesterAndRecipient(connection, identity, newResource)) + { + logger.info( + "Create of Task authorized for identity '{}', Task.status requested, process allowed for current identity", + identity.getName()); + + String identityType = ""; + if (identity instanceof OrganizationIdentity) + identityType = "organization "; + else if (identity instanceof PractitionerIdentity) + identityType = "practitioner "; + else + throw new RuntimeException(identity.getClass().getName() + " identity not supported"); + + return Optional.of("Local or remote identity, Task.status requested, Task.requester current " + identityType + + "identity, Task.restriction.recipient local organization, " + + "process with instantiatesCanonical and message-name allowed for current identity, Task defines needed profile"); + } + else + { + logger.warn( + "Create of Task unauthorized for identity '{}', Task.status requested, process with instantiatesCanonical, message-name, requester or recipient not allowed", + identity.getName()); return Optional.empty(); } @@ -254,22 +219,50 @@ private Optional requestedTaskOk(Connection connection, Identity identit List errors = new ArrayList<>(); if (newResource.getIdentifier().stream().anyMatch(i -> NAMING_SYSTEM_TASK_IDENTIFIER.equals(i.getSystem()))) - { errors.add("Task.identifier[" + NAMING_SYSTEM_TASK_IDENTIFIER + "] defined"); - } if (newResource.hasRequester()) { - if (!isCurrentIdentityPartOfReferencedOrganization(connection, identity, "Task.requester", - newResource.getRequester())) + Reference requester = newResource.getRequester(); + + if (requester.hasIdentifier()) { - errors.add("Task.requester current identity not part of referenced organization"); + Identifier identifier = requester.getIdentifier(); + + if (identifier.hasSystem() && identifier.hasValue()) + { + if (identity instanceof PractitionerIdentity p) + { + if (!NAMING_SYSTEM_PRACTITIONER_IDENTIFIER.equals(identifier.getSystem())) + errors.add("Task.requester.identifier.system not " + NAMING_SYSTEM_PRACTITIONER_IDENTIFIER); + + Optional practitionerIdentifierValue = p.getPractitionerIdentifierValue(); + if (practitionerIdentifierValue.isPresent()) + { + if (!practitionerIdentifierValue.get().equals(identifier.getValue())) + errors.add("Task.requester not current practitioner identity"); + } + else + throw new RuntimeException("Authenticated practitioner user has no identifier"); + } + else if (identity instanceof OrganizationIdentity) + { + if (!NAMING_SYSTEM_ORGANIZATION_IDENTIFIER.equals(identifier.getSystem())) + errors.add("Task.requester.identifier.system not " + NAMING_SYSTEM_ORGANIZATION_IDENTIFIER); + + if (!isCurrentIdentityPartOfReferencedOrganization(connection, identity, "Task.requester", + newResource.getRequester())) + errors.add("Task.requester current identity not part of referenced organization"); + } + } + else + errors.add("Task.requester.identifier.system or Task.requester.identifier.value missing"); } + else + errors.add("Task.requester.identifier missing"); } else - { errors.add("Task.requester missing"); - } if (newResource.hasRestriction()) { @@ -286,24 +279,16 @@ private Optional requestedTaskOk(Connection connection, Identity identit errors.add("Task.restriction.recipient not local organization"); } else - { - errors.add("Task.restriction.recipient not a organization"); - } + errors.add("Task.restriction.recipient not an organization"); } else - { errors.add("Task.restriction.recipient could not be resolved"); - } } else - { errors.add("Task.restriction.recipient missing or more than one"); - } } else - { errors.add("Task.restriction not defined"); - } if (newResource.hasInstantiatesCanonical()) { @@ -314,9 +299,7 @@ private Optional requestedTaskOk(Connection connection, Identity identit } } else - { errors.add("Task.instantiatesCanonical not defined"); - } if (newResource.hasInput()) { @@ -328,14 +311,10 @@ private Optional requestedTaskOk(Connection connection, Identity identit } } else - { errors.add("Task.input empty"); - } if (newResource.hasOutput()) - { errors.add("Task.output not empty"); - } if (errors.isEmpty()) return Optional.empty(); @@ -531,7 +510,6 @@ private boolean taskAllowedForRequesterAndRecipient(Connection connection, Ident if (recipientOpt.isEmpty()) { logger.warn("Local organization does not exist"); - return false; } @@ -551,7 +529,6 @@ private boolean taskAllowedForRequesterAndRecipient(Connection connection, Ident { logger.warn("No ActivityDefinition with process-url '{}' and process-version '{}'", processUrl, processVersion); - return false; } else @@ -591,14 +568,12 @@ else if (!okForRequester) { logger.debug("Error while reading ActivityDefinitions", e); logger.warn("Error while reading ActivityDefinitions: {} - {}", e.getClass().getName(), e.getMessage()); - return false; } } else { logger.warn("Task.instantiatesCanonical not matching {} pattern", INSTANTIATES_CANONICAL_PATTERN_STRING); - return false; } } @@ -701,29 +676,84 @@ public Optional reasonReadAllowed(Connection connection, Identity identi if (identity.hasDsfRole(readRole)) { - if (identity.isLocalIdentity() && isCurrentIdentityPartOfReferencedOrganization(connection, identity, - "Task.restriction.recipient", existingResource.getRestriction().getRecipientFirstRep())) + if (identity instanceof OrganizationIdentity) { - logger.info( - "Read of Task/{}/_history/{} authorized for identity '{}', Task.restriction.recipient reference could be resolved and current identity part of referenced organization", - resourceId, resourceVersion, identity.getName()); + if (identity.isLocalIdentity() && isCurrentIdentityPartOfReferencedOrganization(connection, identity, + "Task.restriction.recipient", existingResource.getRestriction().getRecipientFirstRep())) + { + logger.info( + "Read of Task/{}/_history/{} authorized for identity '{}', identity is local organization and organization referenced as recipient", + resourceId, resourceVersion, identity.getName()); + + return Optional.of("Identity is local organization and organization referenced as recipient"); + } + else if (isCurrentIdentityPartOfReferencedOrganization(connection, identity, "Task.requester", + existingResource.getRequester())) + { + logger.info( + "Read of Task/{}/_history/{} authorized for identity '{}', identity is remote organization and organization referenced as requester", + resourceId, resourceVersion, identity.getName()); + + return Optional.of("Identity is remote organization and organization referenced as requester"); + } + else + { + logger.warn( + "Read of Task/{}/_history/{} unauthorized for identity '{}', organization not local and not referenced as requester", + resourceId, resourceVersion, identity.getName()); - return Optional - .of("Task.restriction.recipient resolved and local identity part of referenced organization"); + return Optional.empty(); + } } - else if (isCurrentIdentityPartOfReferencedOrganization(connection, identity, "Task.requester", - existingResource.getRequester())) + else if (identity instanceof PractitionerIdentity p) { - logger.info( - "Read of Task/{}/_history/{} authorized for identity '{}', Task.requester reference could be resolved and current identity part of referenced organization", - resourceId, resourceVersion, identity.getName()); + if (p.hasPractionerRole("DSF_ADMIN") + && isCurrentIdentityPartOfReferencedOrganization(connection, identity, + "Task.restriction.recipient", existingResource.getRestriction().getRecipientFirstRep())) + { + logger.info( + "Read of Task/{}/_history/{} authorized for identity '{}', identity is local practitioner, has role DSF_ADMIN and organization referenced as recipient", + resourceId, resourceVersion, identity.getName()); + + return Optional.of( + "Identity is local practitioner, has role DSF_ADMIN and organization referenced as recipient"); + } + else if (p.getPractitionerIdentifierValue() + .map(v -> existingResource.getRequester().hasIdentifier() + && NAMING_SYSTEM_PRACTITIONER_IDENTIFIER + .equals(existingResource.getRequester().getIdentifier().getSystem()) + && v.equals(existingResource.getRequester().getIdentifier().getValue())) + .orElse(false)) + { + logger.info( + "Read of Task/{}/_history/{} authorized for identity '{}', identity is local practitioner and practitioner referenced as requester", + resourceId, resourceVersion, identity.getName()); - return Optional.of("Task.requester resolved and identity part of referenced organization"); + return Optional.of("Identity is local practitioner and practitioner referenced as requester"); + } + else if (TaskStatus.DRAFT.equals(existingResource.getStatus()) + && isCurrentIdentityPartOfReferencedOrganization(connection, identity, + "Task.restriction.recipient", existingResource.getRestriction().getRecipientFirstRep())) + { + logger.info( + "Read of Task/{}/_history/{} authorized for identity '{}', identity is local practitioner and Task.status is DRAFT", + resourceId, resourceVersion, identity.getName()); + + return Optional.of("Identity is local practitioner and Task.status is DRAFT"); + } + else + { + logger.warn( + "Read of Task/{}/_history/{} unauthorized, '{}' not a local organization identity or practitioner identity with role DSF_ADMIN", + resourceId, resourceVersion, identity.getName()); + + return Optional.empty(); + } } else { logger.warn( - "Read of Task/{}/_history/{} unauthorized for identity '{}', Task.requester or Task.restriction.recipient references could not be resolved or current identity not part of referenced organizations", + "Read of Task/{}/_history/{} unauthorized for identity '{}', not a organization or practitioner identity", resourceId, resourceVersion, identity.getName()); return Optional.empty(); @@ -747,7 +777,7 @@ public Optional reasonUpdateAllowed(Connection connection, Identity iden if (identity.hasDsfRole(updateRole)) { - if (identity.isLocalIdentity() && identity instanceof OrganizationIdentity) + if (isLocalOrganizationOrDsfAdmin(identity)) { // DRAFT -> DRAFT if (TaskStatus.DRAFT.equals(oldResource.getStatus()) @@ -762,7 +792,8 @@ public Optional reasonUpdateAllowed(Connection connection, Identity iden oldResourceId, oldResourceVersion, TaskStatus.DRAFT.toCode(), TaskStatus.DRAFT.toCode(), identity.getName()); - return Optional.of("Local identity, old Task.status draft, new Task.status draft"); + return Optional.of( + "Local organization identity or practitioner with role DSF_ADMIN, old Task.status draft, new Task.status draft"); } else { @@ -964,7 +995,8 @@ else if (TaskStatus.INPROGRESS.equals(oldResource.getStatus()) } else { - logger.warn("Update of Task/{}/_history/{} unauthorized for non local organization identity '{}'", + logger.warn( + "Update of Task/{}/_history/{} unauthorized, '{}' not a local organization identity or practitioner identity with role DSF_ADMIN", oldResourceId, oldResourceVersion, identity.getName()); return Optional.empty(); @@ -1062,14 +1094,15 @@ public Optional reasonDeleteAllowed(Connection connection, Identity iden if (identity.hasDsfRole(deleteRole)) { - if (identity.isLocalIdentity() && identity instanceof OrganizationIdentity) + if (isLocalOrganizationOrDsfAdmin(identity)) { if (TaskStatus.DRAFT.equals(oldResource.getStatus())) { logger.info("Delete of Task/{}/_history/{} authorized for local identity '{}', Task.status draft", oldResourceId, oldResourceVersion, identity.getName()); - return Optional.of("Local identity, Task.status draft"); + return Optional + .of("Local organization identity or practitioner with role DSF_ADMIN, Task.status draft"); } else { @@ -1082,7 +1115,8 @@ public Optional reasonDeleteAllowed(Connection connection, Identity iden } else { - logger.warn("Delete of Task/{}/_history/{} unauthorized for non local organization identity '{}'", + logger.warn( + "Delete of Task/{}/_history/{} unauthorized, '{}' not a local organization identity or practitioner identity with role DSF_ADMIN", oldResourceId, oldResourceVersion, identity.getName()); return Optional.empty(); diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/jdbc/QuestionnaireResponseDaoJdbc.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/jdbc/QuestionnaireResponseDaoJdbc.java index 1917cfd72..be5d6dd5a 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/jdbc/QuestionnaireResponseDaoJdbc.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/jdbc/QuestionnaireResponseDaoJdbc.java @@ -9,6 +9,7 @@ import ca.uhn.fhir.context.FhirContext; import dev.dsf.fhir.dao.QuestionnaireResponseDao; import dev.dsf.fhir.search.filter.QuestionnaireResponseIdentityFilter; +import dev.dsf.fhir.search.parameters.QuestionnaireResponseAuthor; import dev.dsf.fhir.search.parameters.QuestionnaireResponseAuthored; import dev.dsf.fhir.search.parameters.QuestionnaireResponseIdentifier; import dev.dsf.fhir.search.parameters.QuestionnaireResponseQuestionnaire; @@ -24,7 +25,10 @@ public QuestionnaireResponseDaoJdbc(DataSource dataSource, DataSource permanentD super(dataSource, permanentDeleteDataSource, fhirContext, QuestionnaireResponse.class, "questionnaire_responses", "questionnaire_response", "questionnaire_response_id", QuestionnaireResponseIdentityFilter::new, - List.of(factory(QuestionnaireResponseAuthored.PARAMETER_NAME, QuestionnaireResponseAuthored::new), + List.of(factory(QuestionnaireResponseAuthor.PARAMETER_NAME, QuestionnaireResponseAuthor::new, + QuestionnaireResponseAuthor.getNameModifiers(), QuestionnaireResponseAuthor::new, + QuestionnaireResponseAuthor.getIncludeParameterValues()), + factory(QuestionnaireResponseAuthored.PARAMETER_NAME, QuestionnaireResponseAuthored::new), factory(QuestionnaireResponseIdentifier.PARAMETER_NAME, QuestionnaireResponseIdentifier::new, QuestionnaireResponseIdentifier.getNameModifiers()), factory(QuestionnaireResponseQuestionnaire.PARAMETER_NAME, diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/history/filter/QuestionnaireResponseHistoryIdentityFilter.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/history/filter/QuestionnaireResponseHistoryIdentityFilter.java index 8c7ac2b35..0e39c100d 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/history/filter/QuestionnaireResponseHistoryIdentityFilter.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/history/filter/QuestionnaireResponseHistoryIdentityFilter.java @@ -15,7 +15,7 @@ public class QuestionnaireResponseHistoryIdentityFilter extends QuestionnaireRes public QuestionnaireResponseHistoryIdentityFilter(Identity identity) { - super(identity, HISTORY_ROLE); + super(identity, HistoryIdentityFilter.RESOURCE_COLUMN, HISTORY_ROLE); } @Override diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/filter/QuestionnaireResponseIdentityFilter.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/filter/QuestionnaireResponseIdentityFilter.java index c20d01246..e0bc3a1dc 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/filter/QuestionnaireResponseIdentityFilter.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/filter/QuestionnaireResponseIdentityFilter.java @@ -2,10 +2,15 @@ import java.sql.PreparedStatement; import java.sql.SQLException; +import java.util.Set; +import java.util.stream.Collectors; +import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.ResourceType; import dev.dsf.common.auth.conf.Identity; +import dev.dsf.common.auth.conf.OrganizationIdentity; +import dev.dsf.common.auth.conf.PractitionerIdentity; import dev.dsf.fhir.authentication.FhirServerRole; import dev.dsf.fhir.authentication.FhirServerRoleImpl; @@ -14,43 +19,75 @@ public class QuestionnaireResponseIdentityFilter extends AbstractIdentityFilter private static final FhirServerRole SEARCH_ROLE = FhirServerRoleImpl.search(ResourceType.QuestionnaireResponse); private static final FhirServerRole READ_ROLE = FhirServerRoleImpl.read(ResourceType.QuestionnaireResponse); + private static final String RESOURCE_COLUMN = "questionnaire_response"; + + private final String resourceColumn; private final FhirServerRole operationRole; public QuestionnaireResponseIdentityFilter(Identity identity) { - this(identity, SEARCH_ROLE); + this(identity, RESOURCE_COLUMN, SEARCH_ROLE); } - public QuestionnaireResponseIdentityFilter(Identity identity, FhirServerRole operationRole) + public QuestionnaireResponseIdentityFilter(Identity identity, String resourceColumn, FhirServerRole operationRole) { super(identity, null, null); + this.resourceColumn = resourceColumn; this.operationRole = operationRole; } @Override public String getFilterQuery() { - // read allowed for local users if (identity.isLocalIdentity() && identity.hasDsfRole(operationRole) && identity.hasDsfRole(READ_ROLE)) - return ""; + { + if (identity instanceof OrganizationIdentity + || (identity instanceof PractitionerIdentity p && p.hasPractionerRole("DSF_ADMIN"))) + return ""; + else if (identity instanceof PractitionerIdentity p && p.getPractitionerIdentifierValue().isPresent()) + return "EXISTS (SELECT 1 FROM jsonb_array_elements(" + resourceColumn + "->'extension') AS authExt " + + "WHERE authExt->>'url' = 'http://dsf.dev/fhir/StructureDefinition/extension-questionnaire-authorization' " + + "AND EXISTS (SELECT 1 FROM jsonb_array_elements(authExt->'extension') AS ext " + + "WHERE ((ext->>'url' = 'practitioner' AND ext->'valueIdentifier'->>'value' = ?) " + + "OR (ext->>'url' = 'practitioner-role' AND (" + + "SELECT COUNT(*) FROM jsonb_array_elements(?::jsonb) AS allowed_roles " + + "WHERE allowed_roles->>'system' = ext->'valueCoding'->>'system' AND allowed_roles->>'code' = ext->'valueCoding'->>'code'" + + ") > 0))))"; + } - // read not allowed for non local users - else - return "FALSE"; + return "FALSE"; } @Override public int getSqlParameterCount() { - // no parameters - return 0; + if (identity.isLocalIdentity() && identity.hasDsfRole(operationRole) && identity.hasDsfRole(READ_ROLE) + && identity instanceof PractitionerIdentity p && !p.hasPractionerRole("DSF_ADMIN") + && p.getPractitionerIdentifierValue().isPresent()) + return 2; + else + return 0; } @Override public void modifyStatement(int parameterIndex, int subqueryParameterIndex, PreparedStatement statement) throws SQLException { - // no parameters to modify + if (identity.isLocalIdentity() && identity.hasDsfRole(operationRole) && identity.hasDsfRole(READ_ROLE) + && identity instanceof PractitionerIdentity p && !p.hasPractionerRole("DSF_ADMIN") + && p.getPractitionerIdentifierValue().isPresent()) + { + if (subqueryParameterIndex == 1) + statement.setString(parameterIndex, p.getPractitionerIdentifierValue().get()); + else if (subqueryParameterIndex == 2) + statement.setString(parameterIndex, toJson(p.getPractionerRoles())); + } + } + + private String toJson(Set roles) + { + return roles.stream().map(c -> "{\"system\":\"%s\",\"code\":\"%s\"}".formatted(c.getSystem(), c.getCode())) + .collect(Collectors.joining(",", "[", "]")); } } diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/filter/TaskIdentityFilter.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/filter/TaskIdentityFilter.java index 0eecef93f..98ece14d4 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/filter/TaskIdentityFilter.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/filter/TaskIdentityFilter.java @@ -6,6 +6,8 @@ import org.hl7.fhir.r4.model.ResourceType; import dev.dsf.common.auth.conf.Identity; +import dev.dsf.common.auth.conf.OrganizationIdentity; +import dev.dsf.common.auth.conf.PractitionerIdentity; import dev.dsf.fhir.authentication.FhirServerRole; import dev.dsf.fhir.authentication.FhirServerRoleImpl; @@ -37,20 +39,53 @@ public String getFilterQuery() { if (identity.hasDsfRole(operationRole) && identity.hasDsfRole(READ_ROLE)) { - // TODO modify for requester = Practitioner or PractitionerRole - return "(" + resourceColumn + "->'requester'->>'reference' = ? OR " + resourceColumn - + "->'requester'->>'reference' = ? OR " + resourceColumn - + "->'restriction'->'recipient' @> ?::jsonb OR " + resourceColumn - + "->'restriction'->'recipient' @> ?::jsonb)"; + if (identity instanceof OrganizationIdentity) + { + if (identity.isLocalIdentity()) + return resourceColumn + "->'restriction'->'recipient' @> ?::jsonb"; + else + return resourceColumn + "->'requester'->>'reference' = ?"; + } + else if (identity instanceof PractitionerIdentity p) + { + if (p.hasPractionerRole("DSF_ADMIN")) + return resourceColumn + "->'restriction'->'recipient' @> ?::jsonb"; + else if (p.getPractitionerIdentifierValue().isPresent()) + { + return "((" + resourceColumn + "->'requester'->'identifier'->>'system' = '" + + PractitionerIdentity.PRACTITIONER_IDENTIFIER_SYSTEM + "' AND " + resourceColumn + + "->'requester'->'identifier'->>'value' = ?) OR (" + resourceColumn + + "->>'status' = 'draft' AND " + resourceColumn + "->'restriction'->'recipient' @> ?::jsonb" + + "))"; + } + else + return "(" + resourceColumn + "->>'status' = 'draft' AND " + resourceColumn + + "->'restriction'->'recipient' @> ?::jsonb" + ")"; + } } - else - return "FALSE"; + + return "FALSE"; } @Override public int getSqlParameterCount() { - return identity.hasDsfRole(operationRole) && identity.hasDsfRole(READ_ROLE) ? 4 : 0; + if (identity.hasDsfRole(operationRole) && identity.hasDsfRole(READ_ROLE)) + { + if (identity instanceof OrganizationIdentity) + return 1; + else if (identity instanceof PractitionerIdentity p) + { + if (p.hasPractionerRole("DSF_ADMIN")) + return 1; + else if (p.getPractitionerIdentifierValue().isPresent()) + return 2; + else + return 1; + } + } + + return 0; } @Override @@ -59,17 +94,43 @@ public void modifyStatement(int parameterIndex, int subqueryParameterIndex, Prep { if (identity.hasDsfRole(operationRole) && identity.hasDsfRole(READ_ROLE)) { - if (subqueryParameterIndex == 1) - statement.setString(parameterIndex, identity.getOrganization().getIdElement().getValue()); - else if (subqueryParameterIndex == 2) - statement.setString(parameterIndex, - identity.getOrganization().getIdElement().toVersionless().getValue()); - else if (subqueryParameterIndex == 3) - statement.setString(parameterIndex, - "[{\"reference\": \"" + identity.getOrganization().getIdElement().getValue() + "\"}]"); - else if (subqueryParameterIndex == 4) - statement.setString(parameterIndex, "[{\"reference\": \"" - + identity.getOrganization().getIdElement().toVersionless().getValue() + "\"}]"); + if (identity instanceof OrganizationIdentity) + { + if (identity.isLocalIdentity()) + { + statement.setString(parameterIndex, "[{\"reference\": \"" + + identity.getOrganization().getIdElement().toUnqualifiedVersionless().getValue() + "\"}]"); + } + else + { + statement.setString(parameterIndex, + identity.getOrganization().getIdElement().toUnqualifiedVersionless().getValue()); + } + } + else if (identity instanceof PractitionerIdentity p) + { + if (p.hasPractionerRole("DSF_ADMIN")) + { + statement.setString(parameterIndex, "[{\"reference\": \"" + + identity.getOrganization().getIdElement().toUnqualifiedVersionless().getValue() + "\"}]"); + } + else if (p.getPractitionerIdentifierValue().isPresent()) + { + if (subqueryParameterIndex == 1) + statement.setString(parameterIndex, p.getPractitionerIdentifierValue().get()); + else if (subqueryParameterIndex == 2) + { + statement.setString(parameterIndex, "[{\"reference\": \"" + + identity.getOrganization().getIdElement().toUnqualifiedVersionless().getValue() + + "\"}]"); + } + } + else + { + statement.setString(parameterIndex, "[{\"reference\": \"" + + identity.getOrganization().getIdElement().toUnqualifiedVersionless().getValue() + "\"}]"); + } + } } } } diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/parameters/QuestionnaireResponseAuthor.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/parameters/QuestionnaireResponseAuthor.java new file mode 100644 index 000000000..2b9a1f3f6 --- /dev/null +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/parameters/QuestionnaireResponseAuthor.java @@ -0,0 +1,272 @@ +package dev.dsf.fhir.search.parameters; + +import java.sql.Array; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.model.Enumerations.SearchParamType; +import org.hl7.fhir.r4.model.Organization; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.Practitioner; +import org.hl7.fhir.r4.model.PractitionerRole; +import org.hl7.fhir.r4.model.QuestionnaireResponse; +import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.Resource; + +import dev.dsf.fhir.dao.ResourceDao; +import dev.dsf.fhir.dao.exception.ResourceDeletedException; +import dev.dsf.fhir.dao.provider.DaoProvider; +import dev.dsf.fhir.function.BiFunctionWithSqlException; +import dev.dsf.fhir.search.IncludeParameterDefinition; +import dev.dsf.fhir.search.IncludeParts; +import dev.dsf.fhir.search.SearchQueryParameter.SearchParameterDefinition; +import dev.dsf.fhir.search.parameters.basic.AbstractIdentifierParameter; +import dev.dsf.fhir.search.parameters.basic.AbstractReferenceParameter; + +@IncludeParameterDefinition(resourceType = QuestionnaireResponse.class, parameterName = QuestionnaireResponseAuthor.PARAMETER_NAME, targetResourceTypes = { + Practitioner.class, Organization.class, Patient.class, PractitionerRole.class }) +@SearchParameterDefinition(name = QuestionnaireResponseAuthor.PARAMETER_NAME, definition = "http://hl7.org/fhir/SearchParameter/QuestionnaireResponse-author", type = SearchParamType.REFERENCE, documentation = "The author of the questionnaire response") +public class QuestionnaireResponseAuthor extends AbstractReferenceParameter +{ + private static final String RESOURCE_TYPE_NAME = "QuestionnaireResponse"; + public static final String PARAMETER_NAME = "author"; + private static final String[] TARGET_RESOURCE_TYPE_NAMES = { "Practitioner", "Organization", "Patient", + "PractitionerRole" }; + // TODO add Device, RelatedPerson if supported, see also doResolveReferencesForMatching, matches, getIncludeSql + + public static List getIncludeParameterValues() + { + return Arrays.stream(TARGET_RESOURCE_TYPE_NAMES) + .map(target -> RESOURCE_TYPE_NAME + ":" + PARAMETER_NAME + ":" + target).toList(); + } + + private static final String IDENTIFIERS_SUBQUERY = "(SELECT practitioner->'identifier' FROM current_practitioners " + + "WHERE concat('Practitioner/', practitioner->>'id') = questionnaire_response->'author'->>'reference' " + + "UNION SELECT organization->'identifier' FROM current_organizations " + + "WHERE concat('Organization/', organization->>'id') = questionnaire_response->'author'->>'reference' " + + "UNION SELECT patient->'identifier' FROM current_patients " + + "WHERE concat('Patient/', patient->>'id') = questionnaire_response->'author'->>'reference' " + + "UNION SELECT practitioner_role->'identifier' FROM current_practitioner_roles " + + "WHERE concat('PractitionerRole/', practitioner_role->>'id') = questionnaire_response->'author'->>'reference')"; + + public QuestionnaireResponseAuthor() + { + super(QuestionnaireResponse.class, PARAMETER_NAME, TARGET_RESOURCE_TYPE_NAMES); + } + + @Override + public String getFilterQuery() + { + return switch (valueAndType.type) + { + // testing all TargetResourceTypeName/ID combinations + case ID -> "questionnaire_response->'author'->>'reference' = ANY (?)"; + case RESOURCE_NAME_AND_ID, URL, TYPE_AND_ID, TYPE_AND_RESOURCE_NAME_AND_ID -> + "questionnaire_response->'author'->>'reference' = ?"; + case IDENTIFIER -> switch (valueAndType.identifier.type) + { + case CODE -> "(" + IDENTIFIERS_SUBQUERY + + " @> ?::jsonb OR questionnaire_response->'author'->'identifier'->>'value' = ?)"; + case CODE_AND_SYSTEM -> "(" + IDENTIFIERS_SUBQUERY + + " @> ?::jsonb OR (questionnaire_response->'author'->'identifier'->>'system' = ? AND questionnaire_response->'author'->'identifier'->>'value' = ?))"; + case SYSTEM -> "(" + IDENTIFIERS_SUBQUERY + + " @> ?::jsonb OR questionnaire_response->'author'->'identifier'->>'system' = ?)"; + case CODE_AND_NO_SYSTEM_PROPERTY -> "((SELECT count(*) FROM jsonb_array_elements(" + + IDENTIFIERS_SUBQUERY + + ") identifier WHERE identifier->>'value' = ? AND NOT (identifier ?? 'system')) > 0" + + " OR (questionnaire_response->'author'->'identifier'->>'system' = NULL AND questionnaire_response->'author'->'identifier'->>'value' = ?))"; + }; + }; + } + + @Override + public int getSqlParameterCount() + { + return switch (valueAndType.type) + { + case ID, RESOURCE_NAME_AND_ID, URL, TYPE_AND_ID, TYPE_AND_RESOURCE_NAME_AND_ID -> 1; + case IDENTIFIER -> switch (valueAndType.identifier.type) + { + case CODE, SYSTEM, CODE_AND_NO_SYSTEM_PROPERTY -> 2; + case CODE_AND_SYSTEM -> 3; + }; + }; + } + + @Override + public void modifyStatement(int parameterIndex, int subqueryParameterIndex, PreparedStatement statement, + BiFunctionWithSqlException arrayCreator) throws SQLException + { + switch (valueAndType.type) + { + case ID -> { + Array array = arrayCreator.apply("TEXT", + Arrays.stream(TARGET_RESOURCE_TYPE_NAMES).map(n -> n + "/" + valueAndType.id).toArray()); + statement.setArray(parameterIndex, array); + } + + case RESOURCE_NAME_AND_ID, TYPE_AND_ID, TYPE_AND_RESOURCE_NAME_AND_ID -> + statement.setString(parameterIndex, valueAndType.resourceName + "/" + valueAndType.id); + + case URL -> statement.setString(parameterIndex, valueAndType.url); + + case IDENTIFIER -> { + switch (valueAndType.identifier.type) + { + case CODE -> { + if (subqueryParameterIndex == 1) + statement.setString(parameterIndex, + "[{\"value\": \"" + valueAndType.identifier.codeValue + "\"}]"); + else if (subqueryParameterIndex == 2) + statement.setString(parameterIndex, valueAndType.identifier.codeValue); + } + + case CODE_AND_SYSTEM -> { + if (subqueryParameterIndex == 1) + statement.setString(parameterIndex, "[{\"system\": \"" + valueAndType.identifier.systemValue + + "\", \"value\": \"" + valueAndType.identifier.codeValue + "\"}]"); + else if (subqueryParameterIndex == 2) + statement.setString(parameterIndex, valueAndType.identifier.systemValue); + else if (subqueryParameterIndex == 3) + statement.setString(parameterIndex, valueAndType.identifier.codeValue); + } + + case SYSTEM -> { + if (subqueryParameterIndex == 1) + statement.setString(parameterIndex, + "[{\"system\": \"" + valueAndType.identifier.systemValue + "\"}]"); + else if (subqueryParameterIndex == 2) + statement.setString(parameterIndex, valueAndType.identifier.systemValue); + } + + case CODE_AND_NO_SYSTEM_PROPERTY -> { + if (subqueryParameterIndex == 1) + statement.setString(parameterIndex, valueAndType.identifier.codeValue); + else if (subqueryParameterIndex == 2) + statement.setString(parameterIndex, valueAndType.identifier.codeValue); + } + } + } + } + } + + @Override + protected void doResolveReferencesForMatching(QuestionnaireResponse resource, DaoProvider daoProvider) + throws SQLException + { + Reference reference = resource.getAuthor(); + if (reference != null) + { + IIdType idType = reference.getReferenceElement(); + + if (idType.hasResourceType()) + { + if ("Practitioner".equals(idType.getResourceType())) + setResource(reference, idType, daoProvider.getPractitionerDao()); + else if ("Organization".equals(idType.getResourceType())) + setResource(reference, idType, daoProvider.getOrganizationDao()); + else if ("Patient".equals(idType.getResourceType())) + setResource(reference, idType, daoProvider.getPatientDao()); + else if ("PractitionerRole".equals(idType.getResourceType())) + setResource(reference, idType, daoProvider.getPractitionerRoleDao()); + } + } + } + + private void setResource(Reference reference, IIdType idType, ResourceDao dao) throws SQLException + { + try + { + if (idType.hasVersionIdPart()) + dao.readVersion(UUID.fromString(idType.getIdPart()), idType.getVersionIdPartAsLong()) + .ifPresent(reference::setResource); + else + dao.read(UUID.fromString(idType.getIdPart())).ifPresent(reference::setResource); + } + catch (ResourceDeletedException e) + { + // ignore while matching, will result in a non match if this would have been the matching resource + } + } + + @Override + protected boolean resourceMatches(QuestionnaireResponse resource) + { + if (ReferenceSearchType.IDENTIFIER.equals(valueAndType.type)) + { + return switch (resource.getAuthor().getResource()) + { + case Practitioner p -> p.getIdentifier().stream() + .anyMatch(AbstractIdentifierParameter.identifierMatches(valueAndType.identifier)); + case Organization o -> o.getIdentifier().stream() + .anyMatch(AbstractIdentifierParameter.identifierMatches(valueAndType.identifier)); + case Patient p -> p.getIdentifier().stream() + .anyMatch(AbstractIdentifierParameter.identifierMatches(valueAndType.identifier)); + case PractitionerRole r -> r.getIdentifier().stream() + .anyMatch(AbstractIdentifierParameter.identifierMatches(valueAndType.identifier)); + default -> false; + } || (resource.hasAuthor() && AbstractIdentifierParameter.identifierMatches(valueAndType.identifier, + resource.getAuthor().getIdentifier())); + } + else + { + String ref = resource.getAuthor().getReference(); + return ref != null && switch (valueAndType.type) + { + case ID -> ref.equals("Practitioner" + "/" + valueAndType.id) + || ref.equals("Organization" + "/" + valueAndType.id) + || ref.equals("Patient" + "/" + valueAndType.id) + || ref.equals("PractitionerRole" + "/" + valueAndType.id); + case RESOURCE_NAME_AND_ID -> ref.equals(valueAndType.resourceName + "/" + valueAndType.id); + case URL -> ref.equals(valueAndType.url); + default -> false; + }; + } + } + + @Override + protected String getSortSql(String sortDirectionWithSpacePrefix) + { + return "questionnaire_response->'author'->>'reference'"; + } + + @Override + protected String getIncludeSql(IncludeParts includeParts) + { + if (RESOURCE_TYPE_NAME.equals(includeParts.getSourceResourceTypeName()) + && PARAMETER_NAME.equals(includeParts.getSearchParameterName()) + && Arrays.stream(TARGET_RESOURCE_TYPE_NAMES) + .anyMatch(n -> n.equals(includeParts.getTargetResourceTypeName()))) + + return switch (includeParts.getTargetResourceTypeName()) + { + case "Practitioner" -> "(SELECT jsonb_build_array(practitioner) FROM current_practitioners" + + " WHERE concat('Practitioner/', practitioner->>'id') = questionnaire_response->'author'->>'reference') AS practitioners"; + + case "Organization" -> "(SELECT jsonb_build_array(organization) FROM current_organizations" + + " WHERE concat('Organization/', organization->>'id') = questionnaire_response->'author'->>'reference') AS organizations"; + + case "Patient" -> "(SELECT jsonb_build_array(patient) FROM current_patients" + + " WHERE concat('Patient/', patient->>'id') = questionnaire_response->'author'->>'reference') AS patients"; + + case "PractitionerRole" -> + "(SELECT jsonb_build_array(practitioner_role) FROM current_practitioner_roles" + + " WHERE concat('PractitionerRole/', practitioner_role->>'id') = questionnaire_response->'author'->>'reference') AS practitioner_roles"; + + default -> null; + }; + else + return null; + } + + @Override + protected void modifyIncludeResource(IncludeParts includeParts, Resource resource, Connection connection) + { + // Nothing to do for practitioners, organizations, patients or practitioner-roles + } +} diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/parameters/TaskRequester.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/parameters/TaskRequester.java index 7cef81433..ef3eb09ba 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/parameters/TaskRequester.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/parameters/TaskRequester.java @@ -70,9 +70,16 @@ public String getFilterQuery() "task->'requester'->>'reference' = ?"; case IDENTIFIER -> switch (valueAndType.identifier.type) { - case CODE, CODE_AND_SYSTEM, SYSTEM -> IDENTIFIERS_SUBQUERY + " @> ?::jsonb"; - case CODE_AND_NO_SYSTEM_PROPERTY -> "(SELECT count(*) FROM jsonb_array_elements(" + IDENTIFIERS_SUBQUERY - + ") identifier WHERE identifier->>'value' = ? AND NOT (identifier ?? 'system')) > 0"; + case CODE -> + "(" + IDENTIFIERS_SUBQUERY + " @> ?::jsonb OR task->'requester'->'identifier'->>'value' = ?)"; + case CODE_AND_SYSTEM -> "(" + IDENTIFIERS_SUBQUERY + + " @> ?::jsonb OR (task->'requester'->'identifier'->>'system' = ? AND task->'requester'->'identifier'->>'value' = ?))"; + case SYSTEM -> + "(" + IDENTIFIERS_SUBQUERY + " @> ?::jsonb OR task->'requester'->'identifier'->>'system' = ?)"; + case CODE_AND_NO_SYSTEM_PROPERTY -> "((SELECT count(*) FROM jsonb_array_elements(" + + IDENTIFIERS_SUBQUERY + + ") identifier WHERE identifier->>'value' = ? AND NOT (identifier ?? 'system')) > 0" + + " OR (task->'requester'->'identifier'->>'system' = NULL AND task->'requester'->'identifier'->>'value' = ?))"; }; }; } @@ -80,7 +87,15 @@ public String getFilterQuery() @Override public int getSqlParameterCount() { - return 1; + return switch (valueAndType.type) + { + case ID, RESOURCE_NAME_AND_ID, URL, TYPE_AND_ID, TYPE_AND_RESOURCE_NAME_AND_ID -> 1; + case IDENTIFIER -> switch (valueAndType.identifier.type) + { + case CODE, SYSTEM, CODE_AND_NO_SYSTEM_PROPERTY -> 2; + case CODE_AND_SYSTEM -> 3; + }; + }; } @Override @@ -89,38 +104,52 @@ public void modifyStatement(int parameterIndex, int subqueryParameterIndex, Prep { switch (valueAndType.type) { - case ID: + case ID -> { Array array = arrayCreator.apply("TEXT", Arrays.stream(TARGET_RESOURCE_TYPE_NAMES).map(n -> n + "/" + valueAndType.id).toArray()); statement.setArray(parameterIndex, array); - break; - case RESOURCE_NAME_AND_ID: - case TYPE_AND_ID: - case TYPE_AND_RESOURCE_NAME_AND_ID: + } + + case RESOURCE_NAME_AND_ID, TYPE_AND_ID, TYPE_AND_RESOURCE_NAME_AND_ID -> statement.setString(parameterIndex, valueAndType.resourceName + "/" + valueAndType.id); - break; - case URL: - statement.setString(parameterIndex, valueAndType.url); - break; - case IDENTIFIER: - { + + case URL -> statement.setString(parameterIndex, valueAndType.url); + + case IDENTIFIER -> { switch (valueAndType.identifier.type) { - case CODE: - statement.setString(parameterIndex, - "[{\"value\": \"" + valueAndType.identifier.codeValue + "\"}]"); - break; - case CODE_AND_SYSTEM: - statement.setString(parameterIndex, "[{\"value\": \"" + valueAndType.identifier.codeValue - + "\", \"system\": \"" + valueAndType.identifier.systemValue + "\"}]"); - break; - case CODE_AND_NO_SYSTEM_PROPERTY: - statement.setString(parameterIndex, valueAndType.identifier.codeValue); - break; - case SYSTEM: - statement.setString(parameterIndex, - "[{\"system\": \"" + valueAndType.identifier.systemValue + "\"}]"); - break; + case CODE -> { + if (subqueryParameterIndex == 1) + statement.setString(parameterIndex, + "[{\"value\": \"" + valueAndType.identifier.codeValue + "\"}]"); + else if (subqueryParameterIndex == 2) + statement.setString(parameterIndex, valueAndType.identifier.codeValue); + } + + case CODE_AND_SYSTEM -> { + if (subqueryParameterIndex == 1) + statement.setString(parameterIndex, "[{\"system\": \"" + valueAndType.identifier.systemValue + + "\", \"value\": \"" + valueAndType.identifier.codeValue + "\"}]"); + else if (subqueryParameterIndex == 2) + statement.setString(parameterIndex, valueAndType.identifier.systemValue); + else if (subqueryParameterIndex == 3) + statement.setString(parameterIndex, valueAndType.identifier.codeValue); + } + + case SYSTEM -> { + if (subqueryParameterIndex == 1) + statement.setString(parameterIndex, + "[{\"system\": \"" + valueAndType.identifier.systemValue + "\"}]"); + else if (subqueryParameterIndex == 2) + statement.setString(parameterIndex, valueAndType.identifier.systemValue); + } + + case CODE_AND_NO_SYSTEM_PROPERTY -> { + if (subqueryParameterIndex == 1) + statement.setString(parameterIndex, valueAndType.identifier.codeValue); + else if (subqueryParameterIndex == 2) + statement.setString(parameterIndex, valueAndType.identifier.codeValue); + } } } } @@ -177,7 +206,8 @@ protected boolean resourceMatches(Task resource) case PractitionerRole r -> r.getIdentifier().stream() .anyMatch(AbstractIdentifierParameter.identifierMatches(valueAndType.identifier)); default -> false; - }; + } || AbstractIdentifierParameter.identifierMatches(valueAndType.identifier, + resource.getRequester().getIdentifier()); } else { diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/webservice/impl/ConformanceServiceImpl.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/webservice/impl/ConformanceServiceImpl.java index 1637ac72d..28694c3c2 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/webservice/impl/ConformanceServiceImpl.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/webservice/impl/ConformanceServiceImpl.java @@ -75,104 +75,7 @@ import dev.dsf.fhir.help.SummaryMode; import dev.dsf.fhir.search.IncludeParameterDefinition; import dev.dsf.fhir.search.SearchQueryParameter.SearchParameterDefinition; -import dev.dsf.fhir.search.parameters.ActivityDefinitionDate; -import dev.dsf.fhir.search.parameters.ActivityDefinitionIdentifier; -import dev.dsf.fhir.search.parameters.ActivityDefinitionName; -import dev.dsf.fhir.search.parameters.ActivityDefinitionStatus; -import dev.dsf.fhir.search.parameters.ActivityDefinitionUrl; -import dev.dsf.fhir.search.parameters.ActivityDefinitionVersion; -import dev.dsf.fhir.search.parameters.BinaryContentType; -import dev.dsf.fhir.search.parameters.BundleIdentifier; -import dev.dsf.fhir.search.parameters.CodeSystemDate; -import dev.dsf.fhir.search.parameters.CodeSystemIdentifier; -import dev.dsf.fhir.search.parameters.CodeSystemName; -import dev.dsf.fhir.search.parameters.CodeSystemStatus; -import dev.dsf.fhir.search.parameters.CodeSystemUrl; -import dev.dsf.fhir.search.parameters.CodeSystemVersion; -import dev.dsf.fhir.search.parameters.DocumentReferenceIdentifier; -import dev.dsf.fhir.search.parameters.EndpointAddress; -import dev.dsf.fhir.search.parameters.EndpointIdentifier; -import dev.dsf.fhir.search.parameters.EndpointName; -import dev.dsf.fhir.search.parameters.EndpointOrganization; -import dev.dsf.fhir.search.parameters.EndpointStatus; -import dev.dsf.fhir.search.parameters.GroupIdentifier; -import dev.dsf.fhir.search.parameters.HealthcareServiceActive; -import dev.dsf.fhir.search.parameters.HealthcareServiceIdentifier; -import dev.dsf.fhir.search.parameters.HealthcareServiceName; -import dev.dsf.fhir.search.parameters.LibraryDate; -import dev.dsf.fhir.search.parameters.LibraryIdentifier; -import dev.dsf.fhir.search.parameters.LibraryName; -import dev.dsf.fhir.search.parameters.LibraryStatus; -import dev.dsf.fhir.search.parameters.LibraryUrl; -import dev.dsf.fhir.search.parameters.LibraryVersion; -import dev.dsf.fhir.search.parameters.LocationIdentifier; -import dev.dsf.fhir.search.parameters.LocationName; -import dev.dsf.fhir.search.parameters.MeasureDate; -import dev.dsf.fhir.search.parameters.MeasureDependsOn; -import dev.dsf.fhir.search.parameters.MeasureIdentifier; -import dev.dsf.fhir.search.parameters.MeasureName; -import dev.dsf.fhir.search.parameters.MeasureReportIdentifier; -import dev.dsf.fhir.search.parameters.MeasureStatus; -import dev.dsf.fhir.search.parameters.MeasureUrl; -import dev.dsf.fhir.search.parameters.MeasureVersion; -import dev.dsf.fhir.search.parameters.NamingSystemDate; -import dev.dsf.fhir.search.parameters.NamingSystemName; -import dev.dsf.fhir.search.parameters.NamingSystemStatus; -import dev.dsf.fhir.search.parameters.OrganizationActive; -import dev.dsf.fhir.search.parameters.OrganizationAffiliationActive; -import dev.dsf.fhir.search.parameters.OrganizationAffiliationEndpoint; -import dev.dsf.fhir.search.parameters.OrganizationAffiliationIdentifier; -import dev.dsf.fhir.search.parameters.OrganizationAffiliationParticipatingOrganization; -import dev.dsf.fhir.search.parameters.OrganizationAffiliationPrimaryOrganization; -import dev.dsf.fhir.search.parameters.OrganizationAffiliationRole; -import dev.dsf.fhir.search.parameters.OrganizationEndpoint; -import dev.dsf.fhir.search.parameters.OrganizationIdentifier; -import dev.dsf.fhir.search.parameters.OrganizationName; -import dev.dsf.fhir.search.parameters.OrganizationType; -import dev.dsf.fhir.search.parameters.PatientActive; -import dev.dsf.fhir.search.parameters.PatientIdentifier; -import dev.dsf.fhir.search.parameters.PractitionerActive; -import dev.dsf.fhir.search.parameters.PractitionerIdentifier; -import dev.dsf.fhir.search.parameters.PractitionerRoleActive; -import dev.dsf.fhir.search.parameters.PractitionerRoleIdentifier; -import dev.dsf.fhir.search.parameters.PractitionerRoleOrganization; -import dev.dsf.fhir.search.parameters.PractitionerRolePractitioner; -import dev.dsf.fhir.search.parameters.QuestionnaireDate; -import dev.dsf.fhir.search.parameters.QuestionnaireIdentifier; -import dev.dsf.fhir.search.parameters.QuestionnaireName; -import dev.dsf.fhir.search.parameters.QuestionnaireResponseAuthored; -import dev.dsf.fhir.search.parameters.QuestionnaireResponseIdentifier; -import dev.dsf.fhir.search.parameters.QuestionnaireResponseStatus; -import dev.dsf.fhir.search.parameters.QuestionnaireStatus; -import dev.dsf.fhir.search.parameters.QuestionnaireUrl; -import dev.dsf.fhir.search.parameters.QuestionnaireVersion; -import dev.dsf.fhir.search.parameters.ResearchStudyEnrollment; -import dev.dsf.fhir.search.parameters.ResearchStudyIdentifier; -import dev.dsf.fhir.search.parameters.ResearchStudyPrincipalInvestigator; -import dev.dsf.fhir.search.parameters.ResourceId; -import dev.dsf.fhir.search.parameters.ResourceLastUpdated; -import dev.dsf.fhir.search.parameters.ResourceProfile; -import dev.dsf.fhir.search.parameters.StructureDefinitionDate; -import dev.dsf.fhir.search.parameters.StructureDefinitionIdentifier; -import dev.dsf.fhir.search.parameters.StructureDefinitionName; -import dev.dsf.fhir.search.parameters.StructureDefinitionStatus; -import dev.dsf.fhir.search.parameters.StructureDefinitionUrl; -import dev.dsf.fhir.search.parameters.StructureDefinitionVersion; -import dev.dsf.fhir.search.parameters.SubscriptionCriteria; -import dev.dsf.fhir.search.parameters.SubscriptionPayload; -import dev.dsf.fhir.search.parameters.SubscriptionStatus; -import dev.dsf.fhir.search.parameters.SubscriptionType; -import dev.dsf.fhir.search.parameters.TaskAuthoredOn; -import dev.dsf.fhir.search.parameters.TaskIdentifier; -import dev.dsf.fhir.search.parameters.TaskModified; -import dev.dsf.fhir.search.parameters.TaskRequester; -import dev.dsf.fhir.search.parameters.TaskStatus; -import dev.dsf.fhir.search.parameters.ValueSetDate; -import dev.dsf.fhir.search.parameters.ValueSetIdentifier; -import dev.dsf.fhir.search.parameters.ValueSetName; -import dev.dsf.fhir.search.parameters.ValueSetStatus; -import dev.dsf.fhir.search.parameters.ValueSetUrl; -import dev.dsf.fhir.search.parameters.ValueSetVersion; +import dev.dsf.fhir.search.parameters.*; import dev.dsf.fhir.search.parameters.basic.AbstractSearchParameter; import dev.dsf.fhir.search.parameters.rev.include.AbstractRevIncludeParameter; import dev.dsf.fhir.search.parameters.rev.include.EndpointOrganizationRevInclude; @@ -374,8 +277,9 @@ private CapabilityStatement createCapabilityStatement() List.of(QuestionnaireDate.class, QuestionnaireIdentifier.class, QuestionnaireName.class, QuestionnaireStatus.class, QuestionnaireUrl.class, QuestionnaireVersion.class)); - searchParameters.put(QuestionnaireResponse.class, List.of(QuestionnaireResponseAuthored.class, - QuestionnaireResponseIdentifier.class, QuestionnaireResponseStatus.class)); + searchParameters.put(QuestionnaireResponse.class, + List.of(QuestionnaireResponseAuthor.class, QuestionnaireResponseAuthored.class, + QuestionnaireResponseIdentifier.class, QuestionnaireResponseStatus.class)); searchParameters.put(ResearchStudy.class, List.of(ResearchStudyIdentifier.class, ResearchStudyEnrollment.class, ResearchStudyPrincipalInvestigator.class)); diff --git a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/dsf.css b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/dsf.css index 6e551836c..ea9a6a51c 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/dsf.css +++ b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/dsf.css @@ -508,7 +508,10 @@ pre.lang-html { border-left-color: var(--color-row-border-orange); } -.bundle>#list>table tr[resource-type="QuestionnaireResponse"] td[status="completed"], +.bundle>#list>table tr[resource-type="QuestionnaireResponse"] td[status="completed"] { + border-left-color: var(--color-row-border-grey); +} + .bundle>#list>table tr[resource-type="QuestionnaireResponse"] td[status="amended"] { border-left-color: var(--color-row-border-green); } @@ -740,7 +743,11 @@ pre.lang-html { color: var(--color-info-orange); } -#resource[resource-type="QuestionnaireResponse"][status="completed"] #base-data, +#resource[resource-type="QuestionnaireResponse"][status="completed"] #base-data { + background-color: var(--color-info-background-grey); + color: var(--color-info-grey); +} + #resource[resource-type="QuestionnaireResponse"][status="amended"] #base-data { background-color: var(--color-info-background-green); color: var(--color-info-green); @@ -899,7 +906,10 @@ pre.lang-html { fill: var(--color-info-orange); } -#resource[resource-type="QuestionnaireResponse"][status="completed"] #base-data path, +#resource[resource-type="QuestionnaireResponse"][status="completed"] #base-data path { + fill: var(--color-info-grey); +} + #resource[resource-type="QuestionnaireResponse"][status="amended"] #base-data path { fill: var(--color-info-green); } diff --git a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/form.js b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/form.js index 6346221f6..a202b3be0 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/form.js +++ b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/form.js @@ -38,10 +38,13 @@ function readTaskInputsFromForm() { delete task.meta["lastUpdated"] delete task["identifier"] - // TODO set requester as practitioner-identifier if OIDC or Personal Client-Certificate - //task.requester.type = "Practitioner" - //task.requester.identifier.value = "" - //task.requester.identifier.system = "http://dsf.dev/sid/practitioner-identifier" + const practitionerIdentifierValue = document.querySelector('#practitionerIdentifierValue')?.value + if (practitionerIdentifierValue !== undefined) { + task.requester.type = "Practitioner" + task.requester.identifier.system = "http://dsf.dev/sid/practitioner-identifier" + task.requester.identifier.value = practitionerIdentifierValue + } + // task.requester = local organization is default for draft Task task.status = "requested" task.authoredOn = new Date().toISOString() @@ -222,8 +225,17 @@ function readQuestionnaireResponseAnswersFromForm() { } } }) + + const practitionerIdentifierValue = document.querySelector('#practitionerIdentifierValue')?.value + if (practitionerIdentifierValue !== undefined) { + questionnaireResponse.author.type = "Practitioner" + questionnaireResponse.author.identifier.system = "http://dsf.dev/sid/practitioner-identifier" + questionnaireResponse.author.identifier.value = practitionerIdentifierValue + } + // questionnaireResponse.author = local organization is default for in-progess QuestionnaireResponse questionnaireResponse.status = "completed" + questionnaireResponse.authored = new Date().toISOString() questionnaireResponse.item = newItems return valid ? questionnaireResponse : null diff --git a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/main.html b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/main.html index babd91542..3b88de8d3 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/main.html +++ b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/main.html @@ -21,7 +21,7 @@

- Hello, [[${username}]]Logout + Hello, [[${username}]]Logout Show Help Enable Light Mode Enable Dark Mode diff --git a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceQuestionnaireResponse.html b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceQuestionnaireResponse.html index d94d77427..526ea5f88 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceQuestionnaireResponse.html +++ b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceQuestionnaireResponse.html @@ -9,6 +9,8 @@
  • Questionnaire: [[${questionnaireResponse.questionnaire}]]
  • Business-Key: [[${questionnaireResponse.businessKey}]]
  • User-Task-Id: [[${questionnaireResponse.userTaskId}]]
  • +
  • Authored: [[${questionnaireResponse.authored}]]
  • +
  • Author: [[${questionnaireResponse.author.system}]] | [[${questionnaireResponse.author.value}]]
  • @@ -68,6 +70,7 @@
      +
      diff --git a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceTask.html b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceTask.html index a1cff26e4..c3b52b4af 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceTask.html +++ b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceTask.html @@ -73,6 +73,7 @@

      Input

        +
        diff --git a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/searchsetQuestionnaireResponse.html b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/searchsetQuestionnaireResponse.html index f66f57532..da9123cc5 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/searchsetQuestionnaireResponse.html +++ b/dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/searchsetQuestionnaireResponse.html @@ -2,11 +2,12 @@ - + + diff --git a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/authentication/IdentityProviderTest.java b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/authentication/IdentityProviderTest.java index 89e01534c..0be2fea45 100644 --- a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/authentication/IdentityProviderTest.java +++ b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/authentication/IdentityProviderTest.java @@ -355,12 +355,14 @@ public void testGetPractitionerIdentityByOpenIdCredentials() throws Exception { IdentityProvider provider = createIdentityProvider(List.of(createMappingWithEmail(LOCAL_PRACTITIONER_MAIL))); - when(organizationProvider.getLocalOrganization()).thenReturn(Optional.of(LOCAL_ORGANIZATION)); - when(endpointProvider.getLocalEndpoint()).thenReturn(Optional.of(LOCAL_ENDPOINT)); - + when(credentials.getStringClaimOrDefault("iss", "")).thenReturn("https://iss"); + when(credentials.getStringClaimOrDefault("sub", "")).thenReturn("sub"); + when(credentials.getStringClaimOrDefault("email", "sub.iss@oidc.invalid")).thenReturn(LOCAL_PRACTITIONER_MAIL); when(credentials.getStringClaimOrDefault("family_name", "")).thenReturn(LOCAL_PRACTITIONER_NAME_FAMILY); when(credentials.getStringClaimOrDefault("given_name", "")).thenReturn(LOCAL_PRACTITIONER_NAME_GIVEN); - when(credentials.getStringClaimOrDefault("email", "")).thenReturn(LOCAL_PRACTITIONER_MAIL); + + when(organizationProvider.getLocalOrganization()).thenReturn(Optional.of(LOCAL_ORGANIZATION)); + when(credentials.getIdToken()) .thenReturn(Map.of("realm_access", Map.of("roles", new String[] { TOKEN_ROLE1 }))); when(credentials.getAccessToken()).thenReturn( @@ -382,6 +384,8 @@ public void testGetPractitionerIdentityByOpenIdCredentials() throws Exception .thenReturn(List.of(PRACTIONER_ROLE3)); when(roleConfig.getPractitionerRolesForTokenGroup(TOKEN_GROUP)).thenReturn(List.of(PRACTIONER_ROLE4)); + when(endpointProvider.getLocalEndpoint()).thenReturn(Optional.of(LOCAL_ENDPOINT)); + Identity i = provider.getIdentity(credentials); assertNotNull(i); assertTrue(i instanceof PractitionerIdentity); @@ -449,7 +453,7 @@ public void testGetPractitionerIdentityByOpenIdCredentialsUnknownPractitioner() when(credentials.getStringClaimOrDefault("iss", "")).thenReturn("https://iss"); when(credentials.getStringClaimOrDefault("sub", "")).thenReturn("sub"); - when(credentials.getStringClaimOrDefault("email", "")).thenReturn(LOCAL_PRACTITIONER_MAIL); + when(credentials.getStringClaimOrDefault("email", "sub.iss@oidc.invalid")).thenReturn(LOCAL_PRACTITIONER_MAIL); when(credentials.getStringClaimOrDefault("family_name", "")).thenReturn(LOCAL_PRACTITIONER_NAME_FAMILY); when(credentials.getStringClaimOrDefault("given_name", "")).thenReturn(LOCAL_PRACTITIONER_NAME_GIVEN); when(credentials.getIdToken()) @@ -458,6 +462,7 @@ public void testGetPractitionerIdentityByOpenIdCredentialsUnknownPractitioner() Map.of("resource_access", Map.of(TOKEN_ROLE2_CLIENT, Map.of("roles", new String[] { TOKEN_ROLE2 })), "groups", new String[] { TOKEN_GROUP })); when(credentials.getUserId()).thenReturn("user-id"); + when(organizationProvider.getLocalOrganization()).thenReturn(Optional.of(LOCAL_ORGANIZATION)); Identity i = provider.getIdentity(credentials); @@ -465,7 +470,7 @@ public void testGetPractitionerIdentityByOpenIdCredentialsUnknownPractitioner() verify(credentials).getStringClaimOrDefault("iss", ""); verify(credentials).getStringClaimOrDefault("sub", ""); - verify(credentials).getStringClaimOrDefault("email", ""); + verify(credentials).getStringClaimOrDefault("email", "sub.iss@oidc.invalid"); verify(credentials).getStringClaimOrDefault("family_name", ""); verify(credentials).getStringClaimOrDefault("given_name", ""); verify(credentials).getIdToken(); diff --git a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/AbstractIntegrationTest.java b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/AbstractIntegrationTest.java index 41eb98bb5..84f3236fe 100644 --- a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/AbstractIntegrationTest.java +++ b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/AbstractIntegrationTest.java @@ -114,6 +114,7 @@ public abstract class AbstractIntegrationTest extends AbstractDbTest private static FhirWebserviceClient webserviceClient; private static FhirWebserviceClient externalWebserviceClient; private static FhirWebserviceClient practitionerWebserviceClient; + private static FhirWebserviceClient adminWebserviceClient; private static FhirWebserviceClient minimalWebserviceClient; @BeforeClass @@ -148,6 +149,12 @@ public static void beforeClass() throws Exception certificates.getPractitionerClientCertificate().keyStore(), certificates.getPractitionerClientCertificate().keyStorePassword(), fhirContext, referenceCleaner); + logger.info("Creating admin client ..."); + adminWebserviceClient = createWebserviceClient(apiConnectorChannel.socket().getLocalPort(), + certificates.getAdminClientCertificate().trustStore(), + certificates.getAdminClientCertificate().keyStore(), + certificates.getAdminClientCertificate().keyStorePassword(), fhirContext, referenceCleaner); + logger.info("Creating minimal client ..."); minimalWebserviceClient = createWebserviceClient(apiConnectorChannel.socket().getLocalPort(), certificates.getMinimalClientCertificate().trustStore(), @@ -207,30 +214,37 @@ private static JettyServer startFhirServer(ServerSocketChannel statusConnectorCh initParameters.put("dev.dsf.fhir.client.certificate.private.key.password", String.valueOf(X509Certificates.PASSWORD)); - initParameters.put("dev.dsf.fhir.server.roleConfig", String.format(""" - - practitioner-test-user: - thumbprint: %s - dsf-role: - - CREATE - - READ - - UPDATE - - DELETE - - SEARCH - - HISTORY - practitioner-role: - - http://dsf.dev/fhir/CodeSystem/practitioner-role|DIC_USER - - minimal-test-user: - thumbprint: %s - dsf-role: - - CREATE: [Task] - - READ: &tqqr [Task, Questionnaire, QuestionnaireResponse] - - UPDATE: [QuestionnaireResponse] - - SEARCH: *tqqr - - HISTORY: *tqqr - practitioner-role: - - http://dsf.dev/fhir/CodeSystem/practitioner-role|DIC_USER - """, certificates.getPractitionerClientCertificate().certificateSha512ThumbprintHex(), - certificates.getMinimalClientCertificate().certificateSha512ThumbprintHex())); + initParameters.put("dev.dsf.fhir.server.roleConfig", + String.format(""" + - practitioner-test-user: + thumbprint: %s + dsf-role: + - CREATE + - READ + - UPDATE + - DELETE + - SEARCH + - HISTORY + practitioner-role: + - http://dsf.dev/fhir/CodeSystem/practitioner-role|DIC_USER + - admin-user: + thumbprint: %s + dsf-role: [CREATE, READ, UPDATE, DELETE, SEARCH, HISTORY] + practitioner-role: + - http://dsf.dev/fhir/CodeSystem/practitioner-role|DSF_ADMIN + - minimal-test-user: + thumbprint: %s + dsf-role: + - CREATE: [Task] + - READ: &tqqr [Task, Questionnaire, QuestionnaireResponse] + - UPDATE: [QuestionnaireResponse] + - SEARCH: *tqqr + - HISTORY: *tqqr + practitioner-role: + - http://dsf.dev/fhir/CodeSystem/practitioner-role|DIC_USER + """, certificates.getPractitionerClientCertificate().certificateSha512ThumbprintHex(), + certificates.getAdminClientCertificate().certificateSha512ThumbprintHex(), + certificates.getMinimalClientCertificate().certificateSha512ThumbprintHex())); initParameters.put("dev.dsf.fhir.debug.log.message.dbStatement", "true"); KeyStore clientCertificateTrustStore = KeyStoreCreator @@ -400,6 +414,11 @@ protected static FhirWebserviceClient getPractitionerWebserviceClient() return practitionerWebserviceClient; } + protected static FhirWebserviceClient getAdminWebserviceClient() + { + return adminWebserviceClient; + } + protected static FhirWebserviceClient getMinimalWebserviceClient() { return minimalWebserviceClient; diff --git a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/AbstractQuestionnaireIntegrationTest.java b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/AbstractQuestionnaireIntegrationTest.java index aa2029883..19aba4d03 100644 --- a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/AbstractQuestionnaireIntegrationTest.java +++ b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/AbstractQuestionnaireIntegrationTest.java @@ -13,10 +13,9 @@ import org.hl7.fhir.r4.model.CanonicalType; import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.Meta; -import org.hl7.fhir.r4.model.Organization; import org.hl7.fhir.r4.model.Questionnaire; import org.hl7.fhir.r4.model.QuestionnaireResponse; -import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseStatus; import org.hl7.fhir.r4.model.ResourceType; import org.hl7.fhir.r4.model.StringType; import org.hl7.fhir.r4.model.Type; @@ -38,7 +37,6 @@ public class AbstractQuestionnaireIntegrationTest extends AbstractIntegrationTes protected static final String QUESTIONNAIRE_ITEM_BUSINESS_KEY_LINK = "business-key"; protected static final String QUESTIONNAIRE_ITEM_BUSINESS_KEY_TEXT = "The business-key of the process execution"; - protected static final QuestionnaireResponse.QuestionnaireResponseStatus QUESTIONNAIRE_RESPONSE_STATUS = QuestionnaireResponse.QuestionnaireResponseStatus.INPROGRESS; protected static final Date QUESTIONNAIRE_RESPONSE_DATE = Date .from(LocalDateTime.parse("2022-01-02T00:00:00").toInstant(ZoneOffset.UTC)); @@ -97,26 +95,24 @@ protected void addItem(Questionnaire questionnaire, String linkId, String text, required.ifPresent(item::setRequired); } - protected QuestionnaireResponse createQuestionnaireResponse() + protected QuestionnaireResponse createInProgressQuestionnaireResponse() { OrganizationProvider organizationProvider = getSpringWebApplicationContext() .getBean(OrganizationProvider.class); assertNotNull(organizationProvider); QuestionnaireResponse questionnaireResponse = new QuestionnaireResponse(); - questionnaireResponse.getMeta() - .addProfile("http://dsf.dev/fhir/StructureDefinition/questionnaire-response|1.0.0"); + questionnaireResponse.getMeta().addProfile("http://dsf.dev/fhir/StructureDefinition/questionnaire-response"); questionnaireResponse.getIdentifier().setSystem(TEST_IDENTIFIER_SYSTEM).setValue(TEST_IDENTIFIER_VALUE); questionnaireResponse.setQuestionnaire(QUESTIONNAIRE_URL_VERSION); - questionnaireResponse.setStatus(QUESTIONNAIRE_RESPONSE_STATUS); + questionnaireResponse.setStatus(QuestionnaireResponseStatus.INPROGRESS); questionnaireResponse.setAuthored(QUESTIONNAIRE_RESPONSE_DATE); - Organization localOrganization = organizationProvider.getLocalOrganization().get(); - questionnaireResponse.setSubject(new Reference("Organization/" + localOrganization.getIdElement().getIdPart())); - questionnaireResponse.setAuthor(new Reference().setType(ResourceType.Organization.name()) - .setIdentifier(localOrganization.getIdentifierFirstRep())); + questionnaireResponse.getAuthor().setType(ResourceType.Organization.name()) + .setReferenceElement(organizationProvider.getLocalOrganization().get().getIdElement().toVersionless()) + .getIdentifier().setSystem("http://dsf.dev/sid/organization-identifier").setValue("Test_Organization"); addItem(questionnaireResponse, QUESTIONNAIRE_ITEM_USER_TASK_ID_LINK, QUESTIONNAIRE_ITEM_USER_TASK_ID_TEXT, new StringType(UUID.randomUUID().toString())); @@ -128,7 +124,7 @@ protected QuestionnaireResponse createQuestionnaireResponse() protected QuestionnaireResponse createQuestionnaireResponse(String questionnaireVersion) { - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); String urlVersion = questionnaireResponse.getQuestionnaire().replace(QUESTIONNAIRE_VERSION, questionnaireVersion); return questionnaireResponse.setQuestionnaire(urlVersion); diff --git a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/QuestionnaireResponseIntegrationTest.java b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/QuestionnaireResponseIntegrationTest.java index dec034664..b52ff9247 100644 --- a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/QuestionnaireResponseIntegrationTest.java +++ b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/QuestionnaireResponseIntegrationTest.java @@ -4,17 +4,24 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import java.util.Date; import java.util.List; import java.util.Map; import java.util.UUID; import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.Organization; import org.hl7.fhir.r4.model.Questionnaire; import org.hl7.fhir.r4.model.QuestionnaireResponse; import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseStatus; +import org.hl7.fhir.r4.model.ResourceType; import org.hl7.fhir.r4.model.StringType; import org.junit.Test; @@ -31,7 +38,7 @@ public void testCreateValidByLocalUser() throws Exception QuestionnaireDao questionnaireDao = getSpringWebApplicationContext().getBean(QuestionnaireDao.class); questionnaireDao.create(questionnaire); - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); QuestionnaireResponse created = getWebserviceClient().create(questionnaireResponse); assertNotNull(created); @@ -42,7 +49,7 @@ public void testCreateValidByLocalUser() throws Exception @Test public void testCreateNotAllowedByLocalUserStatusCompleted() throws Exception { - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); questionnaireResponse.setStatus(QuestionnaireResponseStatus.COMPLETED); expectForbidden(() -> getWebserviceClient().create(questionnaireResponse)); @@ -51,7 +58,7 @@ public void testCreateNotAllowedByLocalUserStatusCompleted() throws Exception @Test public void testCreateNotAllowedByLocalUserQuestionnaireDoesNotExists() throws Exception { - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); expectForbidden(() -> getWebserviceClient().create(questionnaireResponse)); } @@ -59,7 +66,7 @@ public void testCreateNotAllowedByLocalUserQuestionnaireDoesNotExists() throws E @Test public void testCreateNotAllowedByRemoteUser() throws Exception { - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); expectForbidden(() -> getExternalWebserviceClient().create(questionnaireResponse)); } @@ -71,7 +78,7 @@ public void testUpdateAllowedByLocalUser() throws Exception QuestionnaireDao questionnaireDao = getSpringWebApplicationContext().getBean(QuestionnaireDao.class); questionnaireDao.create(questionnaire); - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() .getBean(QuestionnaireResponseDao.class); QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse); @@ -89,7 +96,7 @@ public void testUpdateAllowedByLocalUser() throws Exception @Test public void testUpdateNotAllowedByLocalUser() throws Exception { - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() .getBean(QuestionnaireResponseDao.class); QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse); @@ -100,7 +107,7 @@ public void testUpdateNotAllowedByLocalUser() throws Exception @Test public void testUpdateNotAllowedByLocalUserNoUserTaskId() throws Exception { - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() .getBean(QuestionnaireResponseDao.class); QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse); @@ -114,7 +121,7 @@ public void testUpdateNotAllowedByLocalUserNoUserTaskId() throws Exception @Test public void testUpdateNotAllowedByLocalUserChangedUserTaskId() throws Exception { - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() .getBean(QuestionnaireResponseDao.class); QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse); @@ -131,7 +138,7 @@ public void testUpdateNotAllowedByLocalUserChangedUserTaskId() throws Exception @Test public void testSecondUpdateNotAllowedByLocalUser() throws Exception { - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() .getBean(QuestionnaireResponseDao.class); QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse); @@ -144,7 +151,7 @@ public void testSecondUpdateNotAllowedByLocalUser() throws Exception @Test public void testUpdateNotAllowedByRemoteUser() throws Exception { - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() .getBean(QuestionnaireResponseDao.class); QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse); @@ -157,7 +164,7 @@ public void testUpdateNotAllowedByRemoteUser() throws Exception @Test public void testSearchByDate() throws Exception { - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() .getBean(QuestionnaireResponseDao.class); questionnaireResponseDao.create(questionnaireResponse); @@ -180,7 +187,7 @@ public void testSearchByDate() throws Exception @Test public void testSearchByIdentifier() throws Exception { - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() .getBean(QuestionnaireResponseDao.class); questionnaireResponseDao.create(questionnaireResponse); @@ -204,7 +211,7 @@ public void testSearchByIdentifier() throws Exception @Test public void testSearchByIdentifierRemoteUser() throws Exception { - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() .getBean(QuestionnaireResponseDao.class); questionnaireResponseDao.create(questionnaireResponse); @@ -234,7 +241,7 @@ private void testSearchByQuestionnaire(String questionnaireUrl) throws Exception QuestionnaireDao questionnaireDao = getSpringWebApplicationContext().getBean(QuestionnaireDao.class); questionnaireDao.create(questionnaire); - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() .getBean(QuestionnaireResponseDao.class); questionnaireResponseDao.create(questionnaireResponse); @@ -271,7 +278,8 @@ public void testSearchByQuestionnaireNoVersion() throws Exception QuestionnaireDao questionnaireDao = getSpringWebApplicationContext().getBean(QuestionnaireDao.class); questionnaireDao.create(questionnaire); - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse().setQuestionnaire(QUESTIONNAIRE_URL); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse() + .setQuestionnaire(QUESTIONNAIRE_URL); QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() .getBean(QuestionnaireResponseDao.class); questionnaireResponseDao.create(questionnaireResponse); @@ -317,7 +325,7 @@ public void testSearchByQuestionnaireWithoutVersionButMultipleVersionExist() thr Questionnaire questionnaire4 = createQuestionnaireProfileVersion100().setVersion(null); questionnaireDao.create(questionnaire4); - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() .getBean(QuestionnaireResponseDao.class); questionnaireResponseDao.create(questionnaireResponse); @@ -352,13 +360,13 @@ public void testSearchByQuestionnaireWithoutVersionButMultipleVersionExist() thr @Test public void testSearchByStatus() throws Exception { - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() .getBean(QuestionnaireResponseDao.class); questionnaireResponseDao.create(questionnaireResponse); Bundle searchBundle = getWebserviceClient().search(QuestionnaireResponse.class, - Map.of("status", List.of(QUESTIONNAIRE_RESPONSE_STATUS.toCode()))); + Map.of("status", List.of(QuestionnaireResponseStatus.INPROGRESS.toCode()))); assertNotNull(searchBundle.getEntry()); assertEquals(1, searchBundle.getEntry().size()); @@ -369,24 +377,28 @@ public void testSearchByStatus() throws Exception QuestionnaireResponse searchQuestionnaireResponse = (QuestionnaireResponse) searchBundle.getEntry().get(0) .getResource(); assertTrue(searchQuestionnaireResponse.hasStatus()); - assertEquals(QUESTIONNAIRE_RESPONSE_STATUS, searchQuestionnaireResponse.getStatus()); + assertEquals(QuestionnaireResponseStatus.INPROGRESS, searchQuestionnaireResponse.getStatus()); } @Test public void testSearchBySubjectReference() throws Exception { - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); - QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() - .getBean(QuestionnaireResponseDao.class); - questionnaireResponseDao.create(questionnaireResponse); - OrganizationProvider organizationProvider = getSpringWebApplicationContext() .getBean(OrganizationProvider.class); Organization localOrganization = organizationProvider.getLocalOrganization().get(); - String organizationReference = "Organization/" + localOrganization.getIdElement().getIdPart(); - Bundle searchBundle = getWebserviceClient().search(QuestionnaireResponse.class, Map.of("subject", - List.of(organizationReference), "_include", List.of("QuestionnaireResponse:subject:Organization"))); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); + questionnaireResponse.getSubject().setType(ResourceType.Organization.name()) + .setReferenceElement(localOrganization.getIdElement().toVersionless()).getIdentifier() + .setSystem("http://dsf.dev/sid/organization-identifier").setValue("Test_Organization"); + + QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() + .getBean(QuestionnaireResponseDao.class); + questionnaireResponseDao.create(questionnaireResponse); + + Bundle searchBundle = getWebserviceClient().search(QuestionnaireResponse.class, + Map.of("subject", List.of(localOrganization.getIdElement().toUnqualifiedVersionless().toString()), + "_include", List.of("QuestionnaireResponse:subject:Organization"))); assertNotNull(searchBundle.getEntry()); assertEquals(2, searchBundle.getEntry().size()); @@ -397,7 +409,11 @@ public void testSearchBySubjectReference() throws Exception QuestionnaireResponse searchQuestionnaireResponse = (QuestionnaireResponse) searchBundle.getEntry().get(0) .getResource(); assertTrue(searchQuestionnaireResponse.hasStatus()); - assertEquals(organizationReference, searchQuestionnaireResponse.getSubject().getReference()); + assertEquals(localOrganization.getIdentifierFirstRep().getSystem(), + searchQuestionnaireResponse.getSubject().getIdentifier().getSystem()); + assertEquals(localOrganization.getIdentifierFirstRep().getValue(), + searchQuestionnaireResponse.getSubject().getIdentifier().getValue()); + assertNull(searchQuestionnaireResponse.getSubject().getReference()); assertNotNull(searchBundle.getEntry().get(1)); assertNotNull(searchBundle.getEntry().get(1).getResource()); @@ -413,7 +429,7 @@ public void testSearchBySubjectReference() throws Exception @Test public void testDeleteAllowedByLocalUser() throws Exception { - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() .getBean(QuestionnaireResponseDao.class); QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse); @@ -424,7 +440,7 @@ public void testDeleteAllowedByLocalUser() throws Exception @Test public void testDeleteNotAllowedByRemoteUser() throws Exception { - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() .getBean(QuestionnaireResponseDao.class); QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse); @@ -436,7 +452,7 @@ public void testDeleteNotAllowedByRemoteUser() throws Exception @Test public void testReadAllowedByLocalUser() throws Exception { - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() .getBean(QuestionnaireResponseDao.class); QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse); @@ -454,7 +470,7 @@ public void testReadAllowedByLocalUser() throws Exception @Test public void testReadNotAllowedByRemoteUser() throws Exception { - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() .getBean(QuestionnaireResponseDao.class); QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse); @@ -466,7 +482,7 @@ public void testReadNotAllowedByRemoteUser() throws Exception @Test public void testReadNotAllowedByRemoteUserWithVersion() throws Exception { - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() .getBean(QuestionnaireResponseDao.class); QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse); @@ -478,7 +494,7 @@ public void testReadNotAllowedByRemoteUserWithVersion() throws Exception @Test public void testNotModifiedCheckAllowedByLocalUser() throws Exception { - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() .getBean(QuestionnaireResponseDao.class); QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse); @@ -491,7 +507,7 @@ public void testNotModifiedCheckAllowedByLocalUser() throws Exception @Test public void testNotModifiedCheckNotAllowedByRemoteUser() throws Exception { - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() .getBean(QuestionnaireResponseDao.class); QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse); @@ -502,7 +518,7 @@ public void testNotModifiedCheckNotAllowedByRemoteUser() throws Exception @Test public void testNotModifiedCheckAllowedByLocalUserWithModification() throws Exception { - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() .getBean(QuestionnaireResponseDao.class); QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse); @@ -519,7 +535,7 @@ public void testNotModifiedCheckAllowedByLocalUserWithModification() throws Exce @Test public void testNotModifiedCheckNotAllowedByRemoteUserWithModification() throws Exception { - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() .getBean(QuestionnaireResponseDao.class); QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse); @@ -534,7 +550,7 @@ public void testHistory() throws Exception { QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() .getBean(QuestionnaireResponseDao.class); - QuestionnaireResponse created = questionnaireResponseDao.create(createQuestionnaireResponse()); + QuestionnaireResponse created = questionnaireResponseDao.create(createInProgressQuestionnaireResponse()); Bundle historyBundle = getWebserviceClient().history(QuestionnaireResponse.class, created.getIdElement().getIdPart()); @@ -570,7 +586,7 @@ public void testHistoryRemoteUser() throws Exception { QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() .getBean(QuestionnaireResponseDao.class); - QuestionnaireResponse created = questionnaireResponseDao.create(createQuestionnaireResponse()); + QuestionnaireResponse created = questionnaireResponseDao.create(createInProgressQuestionnaireResponse()); Bundle historyBundle = getExternalWebserviceClient().history(QuestionnaireResponse.class, created.getIdElement().getIdPart()); @@ -598,7 +614,7 @@ public void testHistoryRemoteUser() throws Exception @Test public void testDeletePermanentlyAllowedByLocalUser() throws Exception { - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() .getBean(QuestionnaireResponseDao.class); QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse); @@ -610,7 +626,7 @@ public void testDeletePermanentlyAllowedByLocalUser() throws Exception @Test public void testDeletePermanentlyNotAllowedByRemoteUser() throws Exception { - QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse(); + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() .getBean(QuestionnaireResponseDao.class); QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse); @@ -619,4 +635,428 @@ public void testDeletePermanentlyNotAllowedByRemoteUser() throws Exception expectForbidden(() -> getExternalWebserviceClient().deletePermanently(QuestionnaireResponse.class, created.getIdElement().getIdPart())); } + + @Test + public void testUpdateAllowedByMinimalUserWithRole() throws Exception + { + Questionnaire questionnaire = createQuestionnaireProfileVersion100(); + QuestionnaireDao questionnaireDao = getSpringWebApplicationContext().getBean(QuestionnaireDao.class); + questionnaireDao.create(questionnaire); + + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); + Extension authExtension = questionnaireResponse.addExtension() + .setUrl("http://dsf.dev/fhir/StructureDefinition/extension-questionnaire-authorization"); + authExtension.addExtension().setUrl("practitioner-role") + .setValue(new Coding("http://dsf.dev/fhir/CodeSystem/practitioner-role", "DIC_USER", null)); + + QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() + .getBean(QuestionnaireResponseDao.class); + QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse); + + created.setStatus(QuestionnaireResponseStatus.COMPLETED); + created.setAuthored(new Date()); + created.setAuthor(null).getAuthor().setType(ResourceType.Practitioner.name()).getIdentifier() + .setSystem("http://dsf.dev/sid/practitioner-identifier").setValue(X509Certificates.MINIMAL_CLIENT_MAIL); + + QuestionnaireResponse updated = getMinimalWebserviceClient().update(created); + assertNotNull(updated); + assertNotNull(updated.getIdElement().getIdPart()); + assertEquals(created.getIdElement().getIdPart(), updated.getIdElement().getIdPart()); + assertNotNull(updated.getIdElement().getVersionIdPart()); + assertEquals("2", updated.getIdElement().getVersionIdPart()); + } + + @Test + public void testUpdateNotAllowedByMinimalUserWithoutRole() throws Exception + { + Questionnaire questionnaire = createQuestionnaireProfileVersion100(); + QuestionnaireDao questionnaireDao = getSpringWebApplicationContext().getBean(QuestionnaireDao.class); + questionnaireDao.create(questionnaire); + + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); + Extension authExtension = questionnaireResponse.addExtension() + .setUrl("http://dsf.dev/fhir/StructureDefinition/extension-questionnaire-authorization"); + authExtension.addExtension().setUrl("practitioner-role") + .setValue(new Coding("http://dsf.dev/fhir/CodeSystem/practitioner-role", "UAC_USER", null)); + + QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() + .getBean(QuestionnaireResponseDao.class); + QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse); + + created.setStatus(QuestionnaireResponseStatus.COMPLETED); + created.setAuthored(new Date()); + created.setAuthor(null).getAuthor().setType(ResourceType.Practitioner.name()).getIdentifier() + .setSystem("http://dsf.dev/sid/practitioner-identifier").setValue(X509Certificates.MINIMAL_CLIENT_MAIL); + + expectForbidden(() -> getMinimalWebserviceClient().update(created)); + } + + @Test + public void testReadNotAllowedByPractitionerUserWithoutRole() throws Exception + { + Questionnaire questionnaire = createQuestionnaireProfileVersion100(); + QuestionnaireDao questionnaireDao = getSpringWebApplicationContext().getBean(QuestionnaireDao.class); + questionnaireDao.create(questionnaire); + + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); + Extension authExtension = questionnaireResponse.addExtension() + .setUrl("http://dsf.dev/fhir/StructureDefinition/extension-questionnaire-authorization"); + authExtension.addExtension().setUrl("practitioner-role") + .setValue(new Coding("http://dsf.dev/fhir/CodeSystem/practitioner-role", "UAC_USER", null)); + + QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() + .getBean(QuestionnaireResponseDao.class); + QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse); + + expectForbidden(() -> getMinimalWebserviceClient().read(QuestionnaireResponse.class, + created.getIdElement().getIdPart(), created.getIdElement().getVersionIdPart())); + + Bundle searchResult1a = getMinimalWebserviceClient().search(QuestionnaireResponse.class, + Map.of("_id", List.of(created.getIdElement().getIdPart()))); + assertNotNull(searchResult1a); + assertEquals(0, searchResult1a.getTotal()); + assertEquals(0, searchResult1a.getEntry().size()); + + Bundle searchResult1b = getWebserviceClient().search(QuestionnaireResponse.class, + Map.of("_id", List.of(created.getIdElement().getIdPart()))); + assertNotNull(searchResult1b); + assertEquals(1, searchResult1b.getTotal()); + assertEquals(1, searchResult1b.getEntry().size()); + + created.setStatus(QuestionnaireResponseStatus.COMPLETED); + created.setAuthored(new Date()); + created.setAuthor(null).getAuthor().setType(ResourceType.Practitioner.name()).getIdentifier() + .setSystem("http://dsf.dev/sid/practitioner-identifier").setValue(X509Certificates.ADMIN_CLIENT_MAIL); + + QuestionnaireResponse updated = getAdminWebserviceClient().update(created); + assertNotNull(updated); + assertNotNull(updated.getIdElement().getIdPart()); + assertEquals(created.getIdElement().getIdPart(), updated.getIdElement().getIdPart()); + assertNotNull(updated.getIdElement().getVersionIdPart()); + assertEquals("2", updated.getIdElement().getVersionIdPart()); + + expectForbidden(() -> getMinimalWebserviceClient().read(QuestionnaireResponse.class, + updated.getIdElement().getIdPart(), updated.getIdElement().getVersionIdPart())); + + Bundle searchResult2a = getMinimalWebserviceClient().search(QuestionnaireResponse.class, + Map.of("_id", List.of(created.getIdElement().getIdPart()))); + assertNotNull(searchResult2a); + assertEquals(0, searchResult2a.getTotal()); + assertEquals(0, searchResult2a.getEntry().size()); + + Bundle searchResult2b = getAdminWebserviceClient().search(QuestionnaireResponse.class, + Map.of("_id", List.of(created.getIdElement().getIdPart()))); + assertNotNull(searchResult2b); + assertEquals(1, searchResult2b.getTotal()); + assertEquals(1, searchResult2b.getEntry().size()); + } + + @Test + public void testReadAllowedByPractitionerUserWithRole() throws Exception + { + Questionnaire questionnaire = createQuestionnaireProfileVersion100(); + QuestionnaireDao questionnaireDao = getSpringWebApplicationContext().getBean(QuestionnaireDao.class); + questionnaireDao.create(questionnaire); + + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); + Extension authExtension = questionnaireResponse.addExtension() + .setUrl("http://dsf.dev/fhir/StructureDefinition/extension-questionnaire-authorization"); + authExtension.addExtension().setUrl("practitioner-role") + .setValue(new Coding("http://dsf.dev/fhir/CodeSystem/practitioner-role", "DIC_USER", null)); + + QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() + .getBean(QuestionnaireResponseDao.class); + QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse); + + QuestionnaireResponse read1 = getMinimalWebserviceClient().read(QuestionnaireResponse.class, + created.getIdElement().getIdPart(), created.getIdElement().getVersionIdPart()); + assertNotNull(read1); + assertNotNull(read1.getIdElement().getIdPart()); + assertEquals(created.getIdElement().getIdPart(), read1.getIdElement().getIdPart()); + assertNotNull(read1.getIdElement().getVersionIdPart()); + assertEquals("1", read1.getIdElement().getVersionIdPart()); + + Bundle searchResult1a = getMinimalWebserviceClient().search(QuestionnaireResponse.class, + Map.of("_id", List.of(created.getIdElement().getIdPart()))); + assertNotNull(searchResult1a); + assertEquals(1, searchResult1a.getTotal()); + assertEquals(1, searchResult1a.getEntry().size()); + + Bundle searchResult1b = getWebserviceClient().search(QuestionnaireResponse.class, + Map.of("_id", List.of(created.getIdElement().getIdPart()))); + assertNotNull(searchResult1b); + assertEquals(1, searchResult1b.getTotal()); + assertEquals(1, searchResult1b.getEntry().size()); + + created.setStatus(QuestionnaireResponseStatus.COMPLETED); + created.setAuthored(new Date()); + created.setAuthor(null).getAuthor().setType(ResourceType.Practitioner.name()).getIdentifier() + .setSystem("http://dsf.dev/sid/practitioner-identifier").setValue(X509Certificates.MINIMAL_CLIENT_MAIL); + + QuestionnaireResponse updated = getMinimalWebserviceClient().update(created); + assertNotNull(updated); + assertNotNull(updated.getIdElement().getIdPart()); + assertEquals(created.getIdElement().getIdPart(), updated.getIdElement().getIdPart()); + assertNotNull(updated.getIdElement().getVersionIdPart()); + assertEquals("2", updated.getIdElement().getVersionIdPart()); + + QuestionnaireResponse read2 = getMinimalWebserviceClient().read(QuestionnaireResponse.class, + updated.getIdElement().getIdPart(), updated.getIdElement().getVersionIdPart()); + assertNotNull(read2); + assertNotNull(read2.getIdElement().getIdPart()); + assertEquals(updated.getIdElement().getIdPart(), read2.getIdElement().getIdPart()); + assertNotNull(read2.getIdElement().getVersionIdPart()); + assertEquals("2", read2.getIdElement().getVersionIdPart()); + + Bundle searchResult2a = getMinimalWebserviceClient().search(QuestionnaireResponse.class, + Map.of("_id", List.of(created.getIdElement().getIdPart()))); + assertNotNull(searchResult2a); + assertEquals(1, searchResult2a.getTotal()); + assertEquals(1, searchResult2a.getEntry().size()); + + Bundle searchResult2b = getAdminWebserviceClient().search(QuestionnaireResponse.class, + Map.of("_id", List.of(created.getIdElement().getIdPart()))); + assertNotNull(searchResult2b); + assertEquals(1, searchResult2b.getTotal()); + assertEquals(1, searchResult2b.getEntry().size()); + } + + @Test + public void testReadNotAllowedByPractitionerUserWithoutIdentifier() throws Exception + { + Questionnaire questionnaire = createQuestionnaireProfileVersion100(); + QuestionnaireDao questionnaireDao = getSpringWebApplicationContext().getBean(QuestionnaireDao.class); + questionnaireDao.create(questionnaire); + + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); + Extension authExtension = questionnaireResponse.addExtension() + .setUrl("http://dsf.dev/fhir/StructureDefinition/extension-questionnaire-authorization"); + authExtension.addExtension().setUrl("practitioner") + .setValue(new Identifier().setSystem("http://dsf.dev/sid/practitioner-identifier") + .setValue(X509Certificates.PRACTITIONER_CLIENT_MAIL)); + + QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() + .getBean(QuestionnaireResponseDao.class); + QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse); + + expectForbidden(() -> getMinimalWebserviceClient().read(QuestionnaireResponse.class, + created.getIdElement().getIdPart(), created.getIdElement().getVersionIdPart())); + + Bundle searchResult1a = getMinimalWebserviceClient().search(QuestionnaireResponse.class, + Map.of("_id", List.of(created.getIdElement().getIdPart()))); + assertNotNull(searchResult1a); + assertEquals(0, searchResult1a.getTotal()); + assertEquals(0, searchResult1a.getEntry().size()); + + Bundle searchResult1b = getWebserviceClient().search(QuestionnaireResponse.class, + Map.of("_id", List.of(created.getIdElement().getIdPart()))); + assertNotNull(searchResult1b); + assertEquals(1, searchResult1b.getTotal()); + assertEquals(1, searchResult1b.getEntry().size()); + + created.setStatus(QuestionnaireResponseStatus.COMPLETED); + created.setAuthored(new Date()); + created.setAuthor(null).getAuthor().setType(ResourceType.Practitioner.name()).getIdentifier() + .setSystem("http://dsf.dev/sid/practitioner-identifier").setValue(X509Certificates.ADMIN_CLIENT_MAIL); + + QuestionnaireResponse updated = getAdminWebserviceClient().update(created); + assertNotNull(updated); + assertNotNull(updated.getIdElement().getIdPart()); + assertEquals(created.getIdElement().getIdPart(), updated.getIdElement().getIdPart()); + assertNotNull(updated.getIdElement().getVersionIdPart()); + assertEquals("2", updated.getIdElement().getVersionIdPart()); + + expectForbidden(() -> getMinimalWebserviceClient().read(QuestionnaireResponse.class, + updated.getIdElement().getIdPart(), updated.getIdElement().getVersionIdPart())); + + Bundle searchResult2a = getMinimalWebserviceClient().search(QuestionnaireResponse.class, + Map.of("_id", List.of(created.getIdElement().getIdPart()))); + assertNotNull(searchResult2a); + assertEquals(0, searchResult2a.getTotal()); + assertEquals(0, searchResult2a.getEntry().size()); + + Bundle searchResult2b = getAdminWebserviceClient().search(QuestionnaireResponse.class, + Map.of("_id", List.of(created.getIdElement().getIdPart()))); + assertNotNull(searchResult2b); + assertEquals(1, searchResult2b.getTotal()); + assertEquals(1, searchResult2b.getEntry().size()); + } + + @Test + public void testReadAllowedByPractitionerUserWithIdentifier() throws Exception + { + Questionnaire questionnaire = createQuestionnaireProfileVersion100(); + QuestionnaireDao questionnaireDao = getSpringWebApplicationContext().getBean(QuestionnaireDao.class); + questionnaireDao.create(questionnaire); + + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); + Extension authExtension = questionnaireResponse.addExtension() + .setUrl("http://dsf.dev/fhir/StructureDefinition/extension-questionnaire-authorization"); + authExtension.addExtension().setUrl("practitioner") + .setValue(new Identifier().setSystem("http://dsf.dev/sid/practitioner-identifier") + .setValue(X509Certificates.MINIMAL_CLIENT_MAIL)); + + QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() + .getBean(QuestionnaireResponseDao.class); + QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse); + + QuestionnaireResponse read1 = getMinimalWebserviceClient().read(QuestionnaireResponse.class, + created.getIdElement().getIdPart(), created.getIdElement().getVersionIdPart()); + assertNotNull(read1); + assertNotNull(read1.getIdElement().getIdPart()); + assertEquals(created.getIdElement().getIdPart(), read1.getIdElement().getIdPart()); + assertNotNull(read1.getIdElement().getVersionIdPart()); + assertEquals("1", read1.getIdElement().getVersionIdPart()); + + Bundle searchResult1a = getMinimalWebserviceClient().search(QuestionnaireResponse.class, + Map.of("_id", List.of(created.getIdElement().getIdPart()))); + assertNotNull(searchResult1a); + assertEquals(1, searchResult1a.getTotal()); + assertEquals(1, searchResult1a.getEntry().size()); + + Bundle searchResult1b = getWebserviceClient().search(QuestionnaireResponse.class, + Map.of("_id", List.of(created.getIdElement().getIdPart()))); + assertNotNull(searchResult1b); + assertEquals(1, searchResult1b.getTotal()); + assertEquals(1, searchResult1b.getEntry().size()); + + created.setStatus(QuestionnaireResponseStatus.COMPLETED); + created.setAuthored(new Date()); + created.setAuthor(null).getAuthor().setType(ResourceType.Practitioner.name()).getIdentifier() + .setSystem("http://dsf.dev/sid/practitioner-identifier").setValue(X509Certificates.MINIMAL_CLIENT_MAIL); + + QuestionnaireResponse updated = getMinimalWebserviceClient().update(created); + assertNotNull(updated); + assertNotNull(updated.getIdElement().getIdPart()); + assertEquals(created.getIdElement().getIdPart(), updated.getIdElement().getIdPart()); + assertNotNull(updated.getIdElement().getVersionIdPart()); + assertEquals("2", updated.getIdElement().getVersionIdPart()); + + QuestionnaireResponse read2 = getMinimalWebserviceClient().read(QuestionnaireResponse.class, + updated.getIdElement().getIdPart(), updated.getIdElement().getVersionIdPart()); + assertNotNull(read2); + assertNotNull(read2.getIdElement().getIdPart()); + assertEquals(updated.getIdElement().getIdPart(), read2.getIdElement().getIdPart()); + assertNotNull(read2.getIdElement().getVersionIdPart()); + assertEquals("2", read2.getIdElement().getVersionIdPart()); + + Bundle searchResult2a = getMinimalWebserviceClient().search(QuestionnaireResponse.class, + Map.of("_id", List.of(created.getIdElement().getIdPart()))); + assertNotNull(searchResult2a); + assertEquals(1, searchResult2a.getTotal()); + assertEquals(1, searchResult2a.getEntry().size()); + + Bundle searchResult2b = getAdminWebserviceClient().search(QuestionnaireResponse.class, + Map.of("_id", List.of(created.getIdElement().getIdPart()))); + assertNotNull(searchResult2b); + assertEquals(1, searchResult2b.getTotal()); + assertEquals(1, searchResult2b.getEntry().size()); + } + + @Test + public void testUpdateCompletedAmendedAllowedOrganizationUser() throws Exception + { + Questionnaire questionnaire = createQuestionnaireProfileVersion100(); + QuestionnaireDao questionnaireDao = getSpringWebApplicationContext().getBean(QuestionnaireDao.class); + questionnaireDao.create(questionnaire); + + QuestionnaireResponse qr = createInProgressQuestionnaireResponse(); + qr.setStatus(QuestionnaireResponseStatus.COMPLETED); + qr.setAuthored(new Date()); + qr.setAuthor(null).getAuthor().setType(ResourceType.Practitioner.name()).getIdentifier() + .setSystem("http://dsf.dev/sid/practitioner-identifier").setValue(X509Certificates.MINIMAL_CLIENT_MAIL); + QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() + .getBean(QuestionnaireResponseDao.class); + QuestionnaireResponse created = questionnaireResponseDao.create(qr); + + created.setStatus(QuestionnaireResponseStatus.AMENDED); + QuestionnaireResponse updated = getWebserviceClient().update(created); + assertNotNull(updated); + } + + @Test + public void testUpdateCompletedAmendedAllowedDsfAdmin() throws Exception + { + Questionnaire questionnaire = createQuestionnaireProfileVersion100(); + QuestionnaireDao questionnaireDao = getSpringWebApplicationContext().getBean(QuestionnaireDao.class); + questionnaireDao.create(questionnaire); + + QuestionnaireResponse qr = createInProgressQuestionnaireResponse(); + qr.setStatus(QuestionnaireResponseStatus.COMPLETED); + qr.setAuthored(new Date()); + qr.setAuthor(null).getAuthor().setType(ResourceType.Practitioner.name()).getIdentifier() + .setSystem("http://dsf.dev/sid/practitioner-identifier").setValue(X509Certificates.MINIMAL_CLIENT_MAIL); + QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() + .getBean(QuestionnaireResponseDao.class); + QuestionnaireResponse created = questionnaireResponseDao.create(qr); + + created.setStatus(QuestionnaireResponseStatus.AMENDED); + QuestionnaireResponse updated = getAdminWebserviceClient().update(created); + assertNotNull(updated); + } + + @Test + public void testUpdateCompletedAmendedNotAllowed() throws Exception + { + Questionnaire questionnaire = createQuestionnaireProfileVersion100(); + QuestionnaireDao questionnaireDao = getSpringWebApplicationContext().getBean(QuestionnaireDao.class); + questionnaireDao.create(questionnaire); + + QuestionnaireResponse qr = createInProgressQuestionnaireResponse(); + qr.setStatus(QuestionnaireResponseStatus.COMPLETED); + qr.setAuthored(new Date()); + qr.setAuthor(null).getAuthor().setType(ResourceType.Practitioner.name()).getIdentifier() + .setSystem("http://dsf.dev/sid/practitioner-identifier").setValue(X509Certificates.MINIMAL_CLIENT_MAIL); + QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() + .getBean(QuestionnaireResponseDao.class); + QuestionnaireResponse created = questionnaireResponseDao.create(qr); + + created.setStatus(QuestionnaireResponseStatus.AMENDED); + expectForbidden(() -> getMinimalWebserviceClient().update(created)); + expectForbidden(() -> getPractitionerWebserviceClient().update(created)); + expectForbidden(() -> getExternalWebserviceClient().update(created)); + } + + @Test + public void testSearchAllowedByMinimalUserWithRole() throws Exception + { + Questionnaire questionnaire = createQuestionnaireProfileVersion100(); + QuestionnaireDao questionnaireDao = getSpringWebApplicationContext().getBean(QuestionnaireDao.class); + questionnaireDao.create(questionnaire); + + QuestionnaireResponse questionnaireResponse = createInProgressQuestionnaireResponse(); + Extension authExtension = questionnaireResponse.addExtension() + .setUrl("http://dsf.dev/fhir/StructureDefinition/extension-questionnaire-authorization"); + authExtension.addExtension().setUrl("practitioner-role") + .setValue(new Coding("http://dsf.dev/fhir/CodeSystem/practitioner-role", "DIC_USER", null)); + + QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext() + .getBean(QuestionnaireResponseDao.class); + QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse); + + created.setStatus(QuestionnaireResponseStatus.COMPLETED); + created.setAuthored(new Date()); + created.setAuthor(null).getAuthor().setType(ResourceType.Practitioner.name()).getIdentifier() + .setSystem("http://dsf.dev/sid/practitioner-identifier").setValue(X509Certificates.MINIMAL_CLIENT_MAIL); + + QuestionnaireResponse updated = getMinimalWebserviceClient().update(created); + assertNotNull(updated); + assertNotNull(updated.getIdElement().getIdPart()); + assertEquals(created.getIdElement().getIdPart(), updated.getIdElement().getIdPart()); + assertNotNull(updated.getIdElement().getVersionIdPart()); + assertEquals("2", updated.getIdElement().getVersionIdPart()); + + Bundle searchResult = getMinimalWebserviceClient().search(QuestionnaireResponse.class, + Map.of("author:identifier", + List.of("http://dsf.dev/sid/practitioner-identifier|" + X509Certificates.MINIMAL_CLIENT_MAIL))); + assertNotNull(searchResult); + assertEquals(1, searchResult.getTotal()); + assertNotNull(searchResult.getEntry()); + assertEquals(1, searchResult.getEntry().size()); + BundleEntryComponent entry = searchResult.getEntry().get(0); + assertNotNull(entry); + assertNotNull(entry.getResource()); + assertEquals(QuestionnaireResponse.class, entry.getResource().getClass()); + assertEquals(updated.getIdElement().getIdPart(), entry.getResource().getIdElement().getIdPart()); + } } \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/QuestionnaireVsQuestionnaireResponseIntegrationTest.java b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/QuestionnaireVsQuestionnaireResponseIntegrationTest.java index bd6a1bb81..1cd638fbb 100644 --- a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/QuestionnaireVsQuestionnaireResponseIntegrationTest.java +++ b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/QuestionnaireVsQuestionnaireResponseIntegrationTest.java @@ -3,6 +3,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import java.util.Date; import java.util.Optional; import java.util.UUID; @@ -220,6 +221,9 @@ public void testQuestionnaireResponseQuestionnaireDisplayItemChangedWithMinimalU created.getItem().stream().filter(i -> "display-id".equals(i.getLinkId())).findFirst() .ifPresent(i -> i.setText("Response Test Value")); created.setStatus(QuestionnaireResponse.QuestionnaireResponseStatus.COMPLETED); + created.setAuthored(new Date()); + created.getAuthor().setType("Practitioner").getIdentifier() + .setSystem("http://dsf.dev/sid/practitioner-identifier").setValue(X509Certificates.MINIMAL_CLIENT_MAIL); QuestionnaireResponse updated = getMinimalWebserviceClient().update(created); diff --git a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/TaskIntegrationTest.java b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/TaskIntegrationTest.java index 688dc827a..bb3c470cb 100755 --- a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/TaskIntegrationTest.java +++ b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/TaskIntegrationTest.java @@ -57,33 +57,6 @@ public class TaskIntegrationTest extends AbstractIntegrationTest { private static final Logger logger = LoggerFactory.getLogger(TaskIntegrationTest.class); - @Test - public void testCreateTaskStartPingProcessNotAllowedForRemoteUser() throws Exception - { - OrganizationProvider organizationProvider = getSpringWebApplicationContext() - .getBean(OrganizationProvider.class); - assertNotNull(organizationProvider); - - Task t = new Task(); - t.getMeta().addProfile("http://dsf.dev/fhir/StructureDefinition/task-start-ping-process"); - t.setInstantiatesCanonical("http://dsf.dev/bpe/Process/ping|0.3"); - t.setStatus(TaskStatus.REQUESTED); - t.setIntent(TaskIntent.ORDER); - t.setAuthoredOn(new Date()); - - Reference requester = new Reference().setType("Organization"); - requester.getIdentifier().setSystem("http://dsf.dev/sid/organization-identifier") - .setValue("External_Test_Organization"); - t.setRequester(requester); - - t.getRestriction().addRecipient(new Reference(organizationProvider.getLocalOrganization().get())); - t.getInputFirstRep().getType().getCodingFirstRep().setSystem("http://dsf.dev/fhir/CodeSystem/bpmn-message") - .setCode("message-name"); - t.getInputFirstRep().setValue(new StringType("startPingProcessMessage")); - - expectForbidden(() -> getExternalWebserviceClient().create(t)); - } - @Test public void testCreateForbiddenLocalUserIllegalStatus() throws Exception { @@ -153,7 +126,7 @@ public void testCreateForbiddenExternalUserIllegalStatus() throws Exception } @Test - public void testCreateForbiddenLocalUserNotPartOfRequesterOrganization() throws Exception + public void testCreateDarftTaskForbiddenLocalUserNotPartOfRequesterOrganization() throws Exception { OrganizationProvider organizationProvider = getSpringWebApplicationContext() .getBean(OrganizationProvider.class); @@ -161,6 +134,7 @@ public void testCreateForbiddenLocalUserNotPartOfRequesterOrganization() throws Task t = new Task(); t.getMeta().addProfile("http://dsf.dev/fhir/StructureDefinition/task-start-ping-process"); + t.addIdentifier().setSystem("http://dsf.dev/sid/task-identifier").setValue("test"); t.setInstantiatesCanonical("http://dsf.dev/bpe/Process/ping|0.3"); t.setIntent(TaskIntent.ORDER); t.setStatus(TaskStatus.DRAFT); @@ -190,42 +164,55 @@ public void testCreateForbiddenLocalUserNotPartOfRequesterOrganization() throws } @Test - public void testCreateForbiddenExternalUserNotPartOfRequesterOrganization() throws Exception + public void testCreateDarftTaskForbiddenExternalUserOrNonAdminPractitioner() throws Exception { OrganizationProvider organizationProvider = getSpringWebApplicationContext() .getBean(OrganizationProvider.class); assertNotNull(organizationProvider); Task t = new Task(); - t.getMeta().addProfile("http://dsf.dev/fhir/StructureDefinition/task-ping"); - t.setInstantiatesCanonical("http://dsf.dev/bpe/Process/pong|0.3"); - t.setIntent(TaskIntent.ORDER); t.setStatus(TaskStatus.DRAFT); - t.setAuthoredOn(new Date()); - t.getRestriction().addRecipient(new Reference(organizationProvider.getLocalOrganization().get())); - t.getInputFirstRep().getType().getCodingFirstRep().setSystem("http://dsf.dev/fhir/CodeSystem/bpmn-message") - .setCode("message-name"); - t.getInputFirstRep().setValue(new StringType("pingMessage")); - t.setRequester(null); expectForbidden(() -> getExternalWebserviceClient().create(t)); + expectForbidden(() -> getMinimalWebserviceClient().create(t)); + expectForbidden(() -> getPractitionerWebserviceClient().create(t)); - t.setRequester(new Reference()); + } + + @Test + public void testCreateTaskForbiddenExternalUserNotPartOfRequesterOrganization() throws Exception + { + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition2-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); + + StructureDefinition testTaskProfile = readTestTaskProfile(); + StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); + assertNotNull(createdTestTaskProfile); + assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); + + Task t = readTestTask("Test_Organization", null, "Test_Organization"); + // Task.requester current identity not part of referenced organization expectForbidden(() -> getExternalWebserviceClient().create(t)); - Reference requester1 = new Reference() - .setReferenceElement(organizationProvider.getLocalOrganization().get().getIdElement().toVersionless()); - t.setRequester(requester1); + t.setRequester(null); + // Task.requester missing + expectForbidden(() -> getExternalWebserviceClient().create(t)); + + t.setRequester(new Reference()); + // Task.requester missing expectForbidden(() -> getExternalWebserviceClient().create(t)); Reference requester2 = new Reference() .setReference("http://foo.test/fhir/Organization/" + UUID.randomUUID().toString()); t.setRequester(requester2); + // Task.requester.identifier missing expectForbidden(() -> getExternalWebserviceClient().create(t)); } @Test - public void testCreateForbiddenLocalUserRestrictionRecipientNotValidByLocalUser() throws Exception + public void testCreateDraftTaskForbiddenLocalUserRestrictionRecipientNotValidByLocalUser() throws Exception { OrganizationProvider organizationProvider = getSpringWebApplicationContext() .getBean(OrganizationProvider.class); @@ -233,6 +220,7 @@ public void testCreateForbiddenLocalUserRestrictionRecipientNotValidByLocalUser( Task t = new Task(); t.getMeta().addProfile("http://dsf.dev/fhir/StructureDefinition/task-start-ping-process"); + t.addIdentifier().setSystem("http://dsf.dev/sid/task-identifier").setValue("test"); t.setInstantiatesCanonical("http://dsf.dev/bpe/Process/ping|0.3"); t.setIntent(TaskIntent.ORDER); t.setStatus(TaskStatus.DRAFT); @@ -278,114 +266,99 @@ public void testCreateForbiddenLocalUserRestrictionRecipientNotValidByLocalUser( } @Test - public void testCreateForbiddenLocalUserRestrictionRecipientNotValidByExternalUser() throws Exception + public void testCreateForbiddenExternalUserRestrictionRecipientNotValid() throws Exception { - OrganizationProvider organizationProvider = getSpringWebApplicationContext() - .getBean(OrganizationProvider.class); - assertNotNull(organizationProvider); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition2-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); - Task t = new Task(); - t.getMeta().addProfile("http://dsf.dev/fhir/StructureDefinition/task-ping"); - t.setInstantiatesCanonical("http://dsf.dev/bpe/Process/pong|0.3"); - t.setIntent(TaskIntent.ORDER); - t.setStatus(TaskStatus.DRAFT); - t.setAuthoredOn(new Date()); - Reference requester = new Reference().setType("Organization"); - requester.getIdentifier().setSystem("http://dsf.dev/sid/organization-identifier") - .setValue("External_Test_Organization"); - t.setRequester(requester); - t.getInputFirstRep().getType().getCodingFirstRep().setSystem("http://dsf.dev/fhir/CodeSystem/bpmn-message") - .setCode("message-name"); - t.getInputFirstRep().setValue(new StringType("pingMessage")); + StructureDefinition testTaskProfile = readTestTaskProfile(); + StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); + assertNotNull(createdTestTaskProfile); + assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); + Task t = readTestTask("External_Test_Organization", null, "External_Test_Organization"); t.setRestriction(null); + // Task.restriction not defined expectForbidden(() -> getExternalWebserviceClient().create(t)); t.getRestriction().addExtension().setUrl("test"); + // Task.restriction not defined expectForbidden(() -> getExternalWebserviceClient().create(t)); - Reference requester0 = new Reference().setReference("Organization/" + UUID.randomUUID().toString()); + Reference recipient0 = new Reference().setReference("Organization/" + UUID.randomUUID().toString()); t.setRestriction(new TaskRestrictionComponent()); - t.getRestriction().addRecipient(requester0); + t.getRestriction().addRecipient(recipient0); + // Task.restriction.recipient could not be resolved expectForbidden(() -> getExternalWebserviceClient().create(t)); - Reference requester1 = new Reference().setType("Organization"); - requester1.getIdentifier().setSystem("http://dsf.dev/sid/organization-identifier") + Reference recipient1 = new Reference().setType("Organization"); + recipient1.getIdentifier().setSystem("http://dsf.dev/sid/organization-identifier") .setValue("External_Test_Organization"); t.setRestriction(new TaskRestrictionComponent()); - t.getRestriction().addRecipient(requester1); + t.getRestriction().addRecipient(recipient1); + // Task.restriction.recipient not local organization expectForbidden(() -> getExternalWebserviceClient().create(t)); - Reference requester2 = new Reference() + Reference recipient2 = new Reference() .setReference("http://foo.test/fhir/Organization/" + UUID.randomUUID().toString()); t.setRestriction(new TaskRestrictionComponent()); - t.getRestriction().addRecipient(requester2); - expectForbidden(() -> getExternalWebserviceClient().create(t)); - - t.setRestriction(new TaskRestrictionComponent()); - t.getRestriction().addRecipient(requester1).addRecipient(requester2); + t.getRestriction().addRecipient(recipient2); + // Task.restriction.recipient could not be resolved expectForbidden(() -> getExternalWebserviceClient().create(t)); t.setRestriction(new TaskRestrictionComponent()); - t.getRestriction().addRecipient(new Reference(organizationProvider.getLocalOrganization().get())) - .addRecipient(requester0); + t.getRestriction().addRecipient(recipient1).addRecipient(recipient2); + // Task.restriction.recipient missing or more than one expectForbidden(() -> getExternalWebserviceClient().create(t)); } @Test - public void testCreateForbiddenInstantiatesUriNotValidByLocalUser() throws Exception + public void testCreateForbiddenLocalUserInstantiatesUriNotValid() throws Exception { - OrganizationProvider organizationProvider = getSpringWebApplicationContext() - .getBean(OrganizationProvider.class); - assertNotNull(organizationProvider); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition1-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); - Task t = new Task(); - t.getMeta().addProfile("http://dsf.dev/fhir/StructureDefinition/task-start-ping-process"); - // t.setInstantiatesCanonical("http://dsf.dev/bpe/Process/ping|0.3"); - t.setIntent(TaskIntent.ORDER); - t.setStatus(TaskStatus.DRAFT); - t.setAuthoredOn(new Date()); - Reference localOrg = new Reference(organizationProvider.getLocalOrganization().get()); - t.setRequester(localOrg); - t.getRestriction().addRecipient(localOrg); - t.getInputFirstRep().getType().getCodingFirstRep().setSystem("http://dsf.dev/fhir/CodeSystem/bpmn-message") - .setCode("message-name"); - t.getInputFirstRep().setValue(new StringType("startPingProcessMessage")); + StructureDefinition testTaskProfile = readTestTaskProfile(); + StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); + assertNotNull(createdTestTaskProfile); + assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); + + Task t = readTestTask("Test_Organization", null, "Test_Organization"); t.setInstantiatesCanonical(null); + // Task.instantiatesCanonical not defined expectForbidden(() -> getWebserviceClient().create(t)); t.setInstantiatesCanonical("not-a-valid-pattern"); + // Task.instantiatesCanonical not matching ... pattern expectForbidden(() -> getWebserviceClient().create(t)); } @Test - public void testCreateForbiddenInstantiatesUriNotValidByExternalUser() throws Exception + public void testCreateForbiddenExternalUserInstantiatesUriNotValid() throws Exception { - OrganizationProvider organizationProvider = getSpringWebApplicationContext() - .getBean(OrganizationProvider.class); - assertNotNull(organizationProvider); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition2-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); - Task t = new Task(); - t.getMeta().addProfile("http://dsf.dev/fhir/StructureDefinition/task-ping"); - // t.setInstantiatesCanonical("http://dsf.dev/bpe/Process/pong|0.3"); - t.setIntent(TaskIntent.ORDER); - t.setStatus(TaskStatus.DRAFT); - t.setAuthoredOn(new Date()); - Reference requester = new Reference().setType("Organization"); - requester.getIdentifier().setSystem("http://dsf.dev/sid/organization-identifier") - .setValue("External_Test_Organization"); - t.setRequester(requester); - Reference localOrg = new Reference(organizationProvider.getLocalOrganization().get()); - t.getRestriction().addRecipient(localOrg); - t.getInputFirstRep().getType().getCodingFirstRep().setSystem("http://dsf.dev/fhir/CodeSystem/bpmn-message") - .setCode("message-name"); - t.getInputFirstRep().setValue(new StringType("pingMessage")); + StructureDefinition testTaskProfile = readTestTaskProfile(); + StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); + assertNotNull(createdTestTaskProfile); + assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); + + Task t = readTestTask("External_Test_Organization", null, "Test_Organization"); t.setInstantiatesCanonical(null); + // Task.instantiatesCanonical not defined expectForbidden(() -> getExternalWebserviceClient().create(t)); t.setInstantiatesCanonical("not-a-valid-pattern"); + // Task.instantiatesCanonical not matching ... pattern expectForbidden(() -> getExternalWebserviceClient().create(t)); } @@ -398,6 +371,7 @@ public void testCreateForbiddenInputNotValidByLocalUser() throws Exception Task t = new Task(); t.getMeta().addProfile("http://dsf.dev/fhir/StructureDefinition/task-start-ping-process"); + t.addIdentifier().setSystem("http://dsf.dev/sid/task-identifier").setValue("test"); t.setInstantiatesCanonical("http://dsf.dev/bpe/Process/ping|0.3"); t.setIntent(TaskIntent.ORDER); t.setStatus(TaskStatus.DRAFT); @@ -453,34 +427,29 @@ public void testCreateForbiddenInputNotValidByLocalUser() throws Exception } @Test - public void testCreateForbiddenInputNotValidByExternalUser() throws Exception + public void testCreateForbiddenExternalUserInputNotValid() throws Exception { - OrganizationProvider organizationProvider = getSpringWebApplicationContext() - .getBean(OrganizationProvider.class); - assertNotNull(organizationProvider); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition2-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); - Task t = new Task(); - t.getMeta().addProfile("http://dsf.dev/fhir/StructureDefinition/task-ping"); - t.setInstantiatesCanonical("http://dsf.dev/bpe/Process/pong|0.3"); - t.setIntent(TaskIntent.ORDER); - t.setStatus(TaskStatus.DRAFT); - t.setAuthoredOn(new Date()); - Reference requester = new Reference().setType("Organization"); - requester.getIdentifier().setSystem("http://dsf.dev/sid/organization-identifier") - .setValue("External_Test_Organization"); - t.setRequester(requester); - Reference localOrg = new Reference(organizationProvider.getLocalOrganization().get()); - t.getRestriction().addRecipient(localOrg); - // t.getInputFirstRep().getType().getCodingFirstRep().setSystem("http://dsf.dev/fhir/CodeSystem/bpmn-message") - // .setCode("message-name"); - // t.getInputFirstRep().setValue(new StringType("pingMessage")); + StructureDefinition testTaskProfile = readTestTaskProfile(); + StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); + assertNotNull(createdTestTaskProfile); + assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); + + Task t = readTestTask("External_Test_Organization", null, "Test_Organization"); t.setInput(null); + // Task.input empty expectForbidden(() -> getExternalWebserviceClient().create(t)); t.setInput(null); t.getInputFirstRep().getType().getCodingFirstRep().setSystem("system").setCode("code"); t.getInputFirstRep().setValue(new StringType("value")); + // Task.input with system http://dsf.dev/fhir/CodeSystem/bpmn-message and code message-name with non empty + // string value not defined or more than one expectForbidden(() -> getExternalWebserviceClient().create(t)); t.setInput(null); @@ -492,29 +461,39 @@ public void testCreateForbiddenInputNotValidByExternalUser() throws Exception in2.getType().getCodingFirstRep().setSystem("http://dsf.dev/fhir/CodeSystem/bpmn-message") .setCode("message-name"); in2.setValue(new StringType("startPingProcessMessage")); + // Task.input with system http://dsf.dev/fhir/CodeSystem/bpmn-message and code message-name with non empty + // string value not defined or more than one expectForbidden(() -> getExternalWebserviceClient().create(t)); t.setInput(null); t.getInputFirstRep().getType().getCodingFirstRep().setSystem("http://dsf.dev/fhir/CodeSystem/bpmn-message") .setCode("message-name"); + // Task.input with system http://dsf.dev/fhir/CodeSystem/bpmn-message and code message-name with non empty + // string value not defined or more than one expectForbidden(() -> getExternalWebserviceClient().create(t)); t.setInput(null); t.getInputFirstRep().getType().getCodingFirstRep().setSystem("http://dsf.dev/fhir/CodeSystem/bpmn-message") .setCode(""); t.getInputFirstRep().setValue(new StringType("pingMessage")); + // Task.input with system http://dsf.dev/fhir/CodeSystem/bpmn-message and code message-name with non empty + // string value not defined or more than one expectForbidden(() -> getExternalWebserviceClient().create(t)); t.setInput(null); t.getInputFirstRep().getType().getCodingFirstRep().setSystem("http://dsf.dev/fhir/CodeSystem/bpmn-message") .setCode("message-name"); t.getInputFirstRep().setValue(new StringType("")); + // Task.input with system http://dsf.dev/fhir/CodeSystem/bpmn-message and code message-name with non empty + // string value not defined or more than one expectForbidden(() -> getExternalWebserviceClient().create(t)); t.setInput(null); t.getInputFirstRep().getType().getCodingFirstRep().setSystem("http://dsf.dev/fhir/CodeSystem/bpmn-message") .setCode("message-name"); t.getInputFirstRep().setValue(new Coding().setSystem("system").setCode("code")); + // Task.input with system http://dsf.dev/fhir/CodeSystem/bpmn-message and code message-name with non empty + // string value not defined or more than one expectForbidden(() -> getExternalWebserviceClient().create(t)); } @@ -527,6 +506,7 @@ public void testCreateForbiddenOutputNotValidByLocalUser() throws Exception Task t = new Task(); t.getMeta().addProfile("http://dsf.dev/fhir/StructureDefinition/task-start-ping-process"); + t.addIdentifier().setSystem("http://dsf.dev/sid/task-identifier").setValue("test"); t.setInstantiatesCanonical("http://dsf.dev/bpe/Process/ping|0.3"); t.setIntent(TaskIntent.ORDER); t.setStatus(TaskStatus.DRAFT); @@ -544,30 +524,23 @@ public void testCreateForbiddenOutputNotValidByLocalUser() throws Exception } @Test - public void testCreateForbiddenOutputNotValidByExternalUser() throws Exception + public void testCreateForbiddenExternalUserOutputNotValid() throws Exception { - OrganizationProvider organizationProvider = getSpringWebApplicationContext() - .getBean(OrganizationProvider.class); - assertNotNull(organizationProvider); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition2-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); - Task t = new Task(); - t.getMeta().addProfile("http://dsf.dev/fhir/StructureDefinition/task-ping"); - t.setInstantiatesCanonical("http://dsf.dev/bpe/Process/pong|0.3"); - t.setIntent(TaskIntent.ORDER); - t.setStatus(TaskStatus.DRAFT); - t.setAuthoredOn(new Date()); - Reference requester = new Reference().setType("Organization"); - requester.getIdentifier().setSystem("http://dsf.dev/sid/organization-identifier") - .setValue("External_Test_Organization"); - t.setRequester(requester); - Reference localOrg = new Reference(organizationProvider.getLocalOrganization().get()); - t.getRestriction().addRecipient(localOrg); - t.getInputFirstRep().getType().getCodingFirstRep().setSystem("http://dsf.dev/fhir/CodeSystem/bpmn-message") - .setCode("message-name"); - t.getInputFirstRep().setValue(new StringType("pingMessage")); + StructureDefinition testTaskProfile = readTestTaskProfile(); + StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); + assertNotNull(createdTestTaskProfile); + assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); + + Task t = readTestTask("External_Test_Organization", null, "Test_Organization"); t.getOutputFirstRep().getType().getCodingFirstRep().setSystem("system").setCode("code"); t.getOutputFirstRep().setValue(new StringType("value")); + // Task.output not empty expectForbidden(() -> getExternalWebserviceClient().create(t)); } @@ -646,17 +619,25 @@ private StructureDefinition readTestTaskProfile(String fileName) throws IOExcept } } - private Task readTestTask(String requester, String recipient) throws IOException + private Task readTestTask(String requesterOrganization, String requesterPractitioner, String recipient) + throws IOException { try (InputStream in = Files .newInputStream(Paths.get("src/test/resources/integration/task/dsf-test-task-1.0.xml"))) { Task task = fhirContext.newXmlParser().parseResource(Task.class, in); task.setAuthoredOn(new Date()); - task.getRequester().setType("Organization").getIdentifier() - .setSystem("http://dsf.dev/sid/organization-identifier").setValue(requester); + + if (requesterOrganization != null) + task.getRequester().setType("Organization").getIdentifier() + .setSystem("http://dsf.dev/sid/organization-identifier").setValue(requesterOrganization); + else if (requesterPractitioner != null) + task.getRequester().setType("Practitioner").getIdentifier() + .setSystem("http://dsf.dev/sid/practitioner-identifier").setValue(requesterPractitioner); + task.getRestriction().getRecipientFirstRep().setType("Organization").getIdentifier() .setSystem("http://dsf.dev/sid/organization-identifier").setValue(recipient); + return task; } } @@ -679,17 +660,17 @@ private Task readTestTaskBinary(String requester, String recipient) throws IOExc @Test public void testCreateTaskAllowedLocalUser() throws Exception { - ActivityDefinition ad1 = readActivityDefinition("dsf-test-activity-definition1-1.0.xml"); - ActivityDefinition createdAd1 = getWebserviceClient().create(ad1); - assertNotNull(createdAd1); - assertNotNull(createdAd1.getIdElement().getIdPart()); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition1-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); StructureDefinition testTaskProfile = readTestTaskProfile(); StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task task = readTestTask("Test_Organization", "Test_Organization"); + Task task = readTestTask("Test_Organization", null, "Test_Organization"); Task createdTask = getWebserviceClient().create(task); assertNotNull(createdTask); assertNotNull(createdTask.getIdElement().getIdPart()); @@ -698,13 +679,13 @@ public void testCreateTaskAllowedLocalUser() throws Exception @Test public void testCreateTaskAllowedMinimalUser() throws Exception { - ActivityDefinition ad5 = readActivityDefinition("dsf-test-activity-definition5-1.0.xml"); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition5-1.0.xml"); - expectForbidden(() -> getMinimalWebserviceClient().create(ad5)); + expectForbidden(() -> getMinimalWebserviceClient().create(ad)); - ActivityDefinition createdAd1 = getWebserviceClient().create(ad5); - assertNotNull(createdAd1); - assertNotNull(createdAd1.getIdElement().getIdPart()); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); StructureDefinition testTaskProfile = readTestTaskProfile(); @@ -714,7 +695,7 @@ public void testCreateTaskAllowedMinimalUser() throws Exception assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task task = readTestTask("Test_Organization", "Test_Organization"); + Task task = readTestTask(null, X509Certificates.MINIMAL_CLIENT_MAIL, "Test_Organization"); Task createdTask = getMinimalWebserviceClient().create(task); assertNotNull(createdTask); assertNotNull(createdTask.getIdElement().getIdPart()); @@ -723,106 +704,125 @@ public void testCreateTaskAllowedMinimalUser() throws Exception @Test public void testCreateTaskAllowedLocalUserWithRole() throws Exception { - ActivityDefinition ad1 = readActivityDefinition("dsf-test-activity-definition5-1.0.xml"); - ActivityDefinition createdAd1 = getWebserviceClient().create(ad1); - assertNotNull(createdAd1); - assertNotNull(createdAd1.getIdElement().getIdPart()); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition5-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); StructureDefinition testTaskProfile = readTestTaskProfile(); StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task task = readTestTask("Test_Organization", "Test_Organization"); + Task task = readTestTask(null, X509Certificates.PRACTITIONER_CLIENT_MAIL, "Test_Organization"); Task createdTask = getPractitionerWebserviceClient().create(task); assertNotNull(createdTask); assertNotNull(createdTask.getIdElement().getIdPart()); } @Test - public void testCreateTaskNotAllowedLocalUserWithoutRole() throws Exception + public void testCreateTaskNotAllowedLocalUserWrongRole() throws Exception { - ActivityDefinition ad1 = readActivityDefinition("dsf-test-activity-definition6-1.0.xml"); - ActivityDefinition createdAd1 = getWebserviceClient().create(ad1); - assertNotNull(createdAd1); - assertNotNull(createdAd1.getIdElement().getIdPart()); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition6-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); StructureDefinition testTaskProfile = readTestTaskProfile(); StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task task = readTestTask("Test_Organization", "Test_Organization"); + Task task = readTestTask(null, X509Certificates.PRACTITIONER_CLIENT_MAIL, "Test_Organization"); expectForbidden(() -> getPractitionerWebserviceClient().create(task)); } @Test public void testCreateTaskAllowedLocalUserWithRole2() throws Exception { - ActivityDefinition ad1 = readActivityDefinition("dsf-test-activity-definition7-1.0.xml"); - ActivityDefinition createdAd1 = getWebserviceClient().create(ad1); - assertNotNull(createdAd1); - assertNotNull(createdAd1.getIdElement().getIdPart()); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition7-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); StructureDefinition testTaskProfile = readTestTaskProfile(); StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task task = readTestTask("Test_Organization", "Test_Organization"); + Task task = readTestTask(null, X509Certificates.PRACTITIONER_CLIENT_MAIL, "Test_Organization"); Task createdTask = getPractitionerWebserviceClient().create(task); assertNotNull(createdTask); assertNotNull(createdTask.getIdElement().getIdPart()); } @Test - public void testCreateTaskNotAllowedLocalUserWithoutRole2() throws Exception + public void testCreateTaskAllowedLocalUserWithAdminRole() throws Exception { - ActivityDefinition ad1 = readActivityDefinition("dsf-test-activity-definition8-1.0.xml"); - ActivityDefinition createdAd1 = getWebserviceClient().create(ad1); - assertNotNull(createdAd1); - assertNotNull(createdAd1.getIdElement().getIdPart()); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition7-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); StructureDefinition testTaskProfile = readTestTaskProfile(); StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task task = readTestTask("Test_Organization", "Test_Organization"); + Task task = readTestTask(null, X509Certificates.ADMIN_CLIENT_MAIL, "Test_Organization"); + Task createdTask = getAdminWebserviceClient().create(task); + assertNotNull(createdTask); + assertNotNull(createdTask.getIdElement().getIdPart()); + } + + @Test + public void testCreateTaskNotAllowedLocalUserWithoutCosUserRole() throws Exception + { + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition8-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); + + StructureDefinition testTaskProfile = readTestTaskProfile(); + StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); + assertNotNull(createdTestTaskProfile); + assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); + + Task task = readTestTask(null, X509Certificates.PRACTITIONER_CLIENT_MAIL, "Test_Organization"); expectForbidden(() -> getPractitionerWebserviceClient().create(task)); } @Test - public void testCreateTaskNotAllowedLocalOrganizationWithoutRole2() throws Exception + public void testCreateTaskNotAllowedLocalOrganizationNotPractitionerWithCosUserRole() throws Exception { - ActivityDefinition ad1 = readActivityDefinition("dsf-test-activity-definition8-1.0.xml"); - ActivityDefinition createdAd1 = getWebserviceClient().create(ad1); - assertNotNull(createdAd1); - assertNotNull(createdAd1.getIdElement().getIdPart()); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition8-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); StructureDefinition testTaskProfile = readTestTaskProfile(); StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task task = readTestTask("Test_Organization", "Test_Organization"); + Task task = readTestTask("Test_Organization", null, "Test_Organization"); expectForbidden(() -> getWebserviceClient().create(task)); } @Test - public void testCreateTaskAllowedLocalUserWithRole3() throws Exception + public void testCreateTaskAllowedLocalUserWithDicUserRoleAndLocalOrganizationIsDic() throws Exception { - ActivityDefinition ad1 = readActivityDefinition("dsf-test-activity-definition9-1.0.xml"); - ActivityDefinition createdAd1 = getWebserviceClient().create(ad1); - assertNotNull(createdAd1); - assertNotNull(createdAd1.getIdElement().getIdPart()); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition9-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); StructureDefinition testTaskProfile = readTestTaskProfile(); StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task task = readTestTask("Test_Organization", "Test_Organization"); + Task task = readTestTask(null, X509Certificates.PRACTITIONER_CLIENT_MAIL, "Test_Organization"); Task createdTask = getPractitionerWebserviceClient().create(task); assertNotNull(createdTask); assertNotNull(createdTask.getIdElement().getIdPart()); @@ -831,51 +831,51 @@ public void testCreateTaskAllowedLocalUserWithRole3() throws Exception @Test public void testCreateTaskNotAllowedLocalUserWithoutRole3() throws Exception { - ActivityDefinition ad1 = readActivityDefinition("dsf-test-activity-definition10-1.0.xml"); - ActivityDefinition createdAd1 = getWebserviceClient().create(ad1); - assertNotNull(createdAd1); - assertNotNull(createdAd1.getIdElement().getIdPart()); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition10-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); StructureDefinition testTaskProfile = readTestTaskProfile(); StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task task = readTestTask("Test_Organization", "Test_Organization"); + Task task = readTestTask("Test_Organization", null, "Test_Organization"); expectForbidden(() -> getPractitionerWebserviceClient().create(task)); } @Test public void testCreateTaskNotAllowedLocalOrganizationWithoutRole3() throws Exception { - ActivityDefinition ad1 = readActivityDefinition("dsf-test-activity-definition10-1.0.xml"); - ActivityDefinition createdAd1 = getWebserviceClient().create(ad1); - assertNotNull(createdAd1); - assertNotNull(createdAd1.getIdElement().getIdPart()); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition10-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); StructureDefinition testTaskProfile = readTestTaskProfile(); StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task task = readTestTask("Test_Organization", "Test_Organization"); + Task task = readTestTask("Test_Organization", null, "Test_Organization"); expectForbidden(() -> getWebserviceClient().create(task)); } @Test public void testCreateTaskAllowedLocalUserVersionSpecificProfile() throws Exception { - ActivityDefinition ad1 = readActivityDefinition("dsf-test-activity-definition1-1.0.xml"); - ActivityDefinition createdAd1 = getWebserviceClient().create(ad1); - assertNotNull(createdAd1); - assertNotNull(createdAd1.getIdElement().getIdPart()); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition1-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); StructureDefinition testTaskProfile = readTestTaskProfile(); StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task task = readTestTask("Test_Organization", "Test_Organization"); + Task task = readTestTask("Test_Organization", null, "Test_Organization"); CanonicalType profile = task.getMeta().getProfile().get(0); profile.setValue(profile.getValue() + "|1.0"); Task createdTask = getWebserviceClient().create(task); @@ -886,17 +886,17 @@ public void testCreateTaskAllowedLocalUserVersionSpecificProfile() throws Except @Test public void testCreateTaskAllowedLocalUserVersionSpecificProfileBadVersion() throws Exception { - ActivityDefinition ad1 = readActivityDefinition("dsf-test-activity-definition1-1.0.xml"); - ActivityDefinition createdAd1 = getWebserviceClient().create(ad1); - assertNotNull(createdAd1); - assertNotNull(createdAd1.getIdElement().getIdPart()); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition1-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); StructureDefinition testTaskProfile = readTestTaskProfile(); StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task task = readTestTask("Test_Organization", "Test_Organization"); + Task task = readTestTask("Test_Organization", null, "Test_Organization"); CanonicalType profile = task.getMeta().getProfile().get(0); profile.setValue(profile.getValue() + "|0.x"); @@ -906,51 +906,51 @@ public void testCreateTaskAllowedLocalUserVersionSpecificProfileBadVersion() thr @Test public void testCreateTaskNotAllowedRemoteUser() throws Exception { - ActivityDefinition ad1 = readActivityDefinition("dsf-test-activity-definition1-1.0.xml"); - ActivityDefinition createdAd1 = getWebserviceClient().create(ad1); - assertNotNull(createdAd1); - assertNotNull(createdAd1.getIdElement().getIdPart()); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition1-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); StructureDefinition testTaskProfile = readTestTaskProfile(); StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task task = readTestTask("External_Test_Organization", "Test_Organization"); + Task task = readTestTask("External_Test_Organization", null, "Test_Organization"); expectForbidden(() -> getExternalWebserviceClient().create(task)); } @Test public void testCreateTaskNotAllowedPractitioner1() throws Exception { - ActivityDefinition ad1 = readActivityDefinition("dsf-test-activity-definition1-1.0.xml"); - ActivityDefinition createdAd1 = getWebserviceClient().create(ad1); - assertNotNull(createdAd1); - assertNotNull(createdAd1.getIdElement().getIdPart()); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition1-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); StructureDefinition testTaskProfile = readTestTaskProfile(); StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task task = readTestTask("Test_Organization", "Test_Organization"); + Task task = readTestTask("Test_Organization", null, "Test_Organization"); expectForbidden(() -> getPractitionerWebserviceClient().create(task)); } @Test public void testCreateTaskAllowedLocalUser11() throws Exception { - ActivityDefinition ad1 = readActivityDefinition("dsf-test-activity-definition11-1.0.xml"); - ActivityDefinition createdAd1 = getWebserviceClient().create(ad1); - assertNotNull(createdAd1); - assertNotNull(createdAd1.getIdElement().getIdPart()); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition11-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); StructureDefinition testTaskProfile = readTestTaskProfile(); StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task task = readTestTask("Test_Organization", "Test_Organization"); + Task task = readTestTask("Test_Organization", null, "Test_Organization"); Task createdTask = getWebserviceClient().create(task); assertNotNull(createdTask); assertNotNull(createdTask.getIdElement().getIdPart()); @@ -959,34 +959,34 @@ public void testCreateTaskAllowedLocalUser11() throws Exception @Test public void testCreateTaskNotAllowedPractitioner11() throws Exception { - ActivityDefinition ad1 = readActivityDefinition("dsf-test-activity-definition11-1.0.xml"); - ActivityDefinition createdAd1 = getWebserviceClient().create(ad1); - assertNotNull(createdAd1); - assertNotNull(createdAd1.getIdElement().getIdPart()); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition11-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); StructureDefinition testTaskProfile = readTestTaskProfile(); StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task task = readTestTask("Test_Organization", "Test_Organization"); + Task task = readTestTask("Test_Organization", null, "Test_Organization"); expectForbidden(() -> getPractitionerWebserviceClient().create(task)); } @Test public void testCreateTaskAllowedLocalUser12() throws Exception { - ActivityDefinition ad1 = readActivityDefinition("dsf-test-activity-definition12-1.0.xml"); - ActivityDefinition createdAd1 = getWebserviceClient().create(ad1); - assertNotNull(createdAd1); - assertNotNull(createdAd1.getIdElement().getIdPart()); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition12-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); StructureDefinition testTaskProfile = readTestTaskProfile(); StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task task = readTestTask("Test_Organization", "Test_Organization"); + Task task = readTestTask("Test_Organization", null, "Test_Organization"); Task createdTask = getWebserviceClient().create(task); assertNotNull(createdTask); assertNotNull(createdTask.getIdElement().getIdPart()); @@ -995,34 +995,34 @@ public void testCreateTaskAllowedLocalUser12() throws Exception @Test public void testCreateTaskNotAllowedPractitioner12() throws Exception { - ActivityDefinition ad1 = readActivityDefinition("dsf-test-activity-definition12-1.0.xml"); - ActivityDefinition createdAd1 = getWebserviceClient().create(ad1); - assertNotNull(createdAd1); - assertNotNull(createdAd1.getIdElement().getIdPart()); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition12-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); StructureDefinition testTaskProfile = readTestTaskProfile(); StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task task = readTestTask("Test_Organization", "Test_Organization"); + Task task = readTestTask("Test_Organization", null, "Test_Organization"); expectForbidden(() -> getPractitionerWebserviceClient().create(task)); } @Test public void testCreateTaskAllowedLocalUser13() throws Exception { - ActivityDefinition ad1 = readActivityDefinition("dsf-test-activity-definition13-1.0.xml"); - ActivityDefinition createdAd1 = getWebserviceClient().create(ad1); - assertNotNull(createdAd1); - assertNotNull(createdAd1.getIdElement().getIdPart()); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition13-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); StructureDefinition testTaskProfile = readTestTaskProfile(); StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task task = readTestTask("Test_Organization", "Test_Organization"); + Task task = readTestTask("Test_Organization", null, "Test_Organization"); Task createdTask = getWebserviceClient().create(task); assertNotNull(createdTask); assertNotNull(createdTask.getIdElement().getIdPart()); @@ -1031,17 +1031,17 @@ public void testCreateTaskAllowedLocalUser13() throws Exception @Test public void testCreateTaskAllowedPractitioner13() throws Exception { - ActivityDefinition ad1 = readActivityDefinition("dsf-test-activity-definition13-1.0.xml"); - ActivityDefinition createdAd1 = getWebserviceClient().create(ad1); - assertNotNull(createdAd1); - assertNotNull(createdAd1.getIdElement().getIdPart()); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition13-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); StructureDefinition testTaskProfile = readTestTaskProfile(); StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task task = readTestTask("Test_Organization", "Test_Organization"); + Task task = readTestTask(null, X509Certificates.PRACTITIONER_CLIENT_MAIL, "Test_Organization"); Task createdTask = getPractitionerWebserviceClient().create(task); assertNotNull(createdTask); assertNotNull(createdTask.getIdElement().getIdPart()); @@ -1050,34 +1050,34 @@ public void testCreateTaskAllowedPractitioner13() throws Exception @Test public void testCreateTaskNotAllowedLocalUser() throws Exception { - ActivityDefinition ad2 = readActivityDefinition("dsf-test-activity-definition2-1.0.xml"); - ActivityDefinition createdAd2 = getWebserviceClient().create(ad2); - assertNotNull(createdAd2); - assertNotNull(createdAd2.getIdElement().getIdPart()); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition2-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); StructureDefinition testTaskProfile = readTestTaskProfile(); StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task task = readTestTask("Test_Organization", "Test_Organization"); + Task task = readTestTask("Test_Organization", null, "Test_Organization"); expectForbidden(() -> getWebserviceClient().create(task)); } @Test public void testCreateTaskAllowedRemoteUser() throws Exception { - ActivityDefinition ad2 = readActivityDefinition("dsf-test-activity-definition2-1.0.xml"); - ActivityDefinition createdAd2 = getWebserviceClient().create(ad2); - assertNotNull(createdAd2); - assertNotNull(createdAd2.getIdElement().getIdPart()); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition2-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); StructureDefinition testTaskProfile = readTestTaskProfile(); StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task task = readTestTask("External_Test_Organization", "Test_Organization"); + Task task = readTestTask("External_Test_Organization", null, "Test_Organization"); Task createdTask = getExternalWebserviceClient().create(task); assertNotNull(createdTask); assertNotNull(createdTask.getIdElement().getIdPart()); @@ -1086,34 +1086,34 @@ public void testCreateTaskAllowedRemoteUser() throws Exception @Test public void testCreateTaskNotAllowedLocalUser2() throws Exception { - ActivityDefinition ad3 = readActivityDefinition("dsf-test-activity-definition3-1.0.xml"); - ActivityDefinition createdAd3 = getWebserviceClient().create(ad3); - assertNotNull(createdAd3); - assertNotNull(createdAd3.getIdElement().getIdPart()); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition3-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); StructureDefinition testTaskProfile = readTestTaskProfile(); StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task task = readTestTask("Test_Organization", "Test_Organization"); + Task task = readTestTask("Test_Organization", null, "Test_Organization"); expectForbidden(() -> getWebserviceClient().create(task)); } @Test public void testCreateTaskAllowedRemoteUser2() throws Exception { - ActivityDefinition ad3 = readActivityDefinition("dsf-test-activity-definition3-1.0.xml"); - ActivityDefinition createdAd3 = getWebserviceClient().create(ad3); - assertNotNull(createdAd3); - assertNotNull(createdAd3.getIdElement().getIdPart()); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition3-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); StructureDefinition testTaskProfile = readTestTaskProfile(); StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task task = readTestTask("External_Test_Organization", "Test_Organization"); + Task task = readTestTask("External_Test_Organization", null, "Test_Organization"); Task createdTask = getExternalWebserviceClient().create(task); assertNotNull(createdTask); assertNotNull(createdTask.getIdElement().getIdPart()); @@ -1122,8 +1122,8 @@ public void testCreateTaskAllowedRemoteUser2() throws Exception @Test public void testCreateTaskNotAllowedRemoteUser2() throws Exception { - ActivityDefinition ad3 = readActivityDefinition("dsf-test-activity-definition3-1.0.xml"); - Coding recipient = (Coding) ad3 + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition3-1.0.xml"); + Coding recipient = (Coding) ad .getExtensionByUrl("http://dsf.dev/fhir/StructureDefinition/extension-process-authorization") .getExtensionByUrl("recipient").getValue(); Coding role = (Coding) recipient.getExtensionByUrl( @@ -1131,7 +1131,7 @@ public void testCreateTaskNotAllowedRemoteUser2() throws Exception .getExtensionByUrl("organization-role").getValue(); role.setCode("TTP"); - ActivityDefinition createdAd3 = getWebserviceClient().create(ad3); + ActivityDefinition createdAd3 = getWebserviceClient().create(ad); assertNotNull(createdAd3); assertNotNull(createdAd3.getIdElement().getIdPart()); @@ -1140,24 +1140,24 @@ public void testCreateTaskNotAllowedRemoteUser2() throws Exception assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task t = readTestTask("External_Test_Organization", "Test_Organization"); + Task t = readTestTask("External_Test_Organization", null, "Test_Organization"); expectForbidden(() -> getExternalWebserviceClient().create(t)); } @Test public void testCreateTaskAllowedRemoteUser3() throws Exception { - ActivityDefinition ad3 = readActivityDefinition("dsf-test-activity-definition4-1.0.xml"); - ActivityDefinition createdAd3 = getWebserviceClient().create(ad3); - assertNotNull(createdAd3); - assertNotNull(createdAd3.getIdElement().getIdPart()); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition4-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); StructureDefinition testTaskProfile = readTestTaskProfile(); StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task task = readTestTask("External_Test_Organization", "Test_Organization"); + Task task = readTestTask("External_Test_Organization", null, "Test_Organization"); Task createdTask = getExternalWebserviceClient().create(task); assertNotNull(createdTask); assertNotNull(createdTask.getIdElement().getIdPart()); @@ -1193,7 +1193,7 @@ public void testCreateViaBundleNotValid() throws Exception @Test public void testDeletePermanentlyByLocalDeletionUser() throws Exception { - Task task = readTestTask("External_Test_Organization", "Test_Organization"); + Task task = readTestTask("External_Test_Organization", null, "Test_Organization"); task.setStatus(TaskStatus.DRAFT); readAccessHelper.addLocal(task); @@ -1210,7 +1210,7 @@ public void testDeletePermanentlyByLocalDeletionUser() throws Exception @Test public void testDeletePermanentlyByLocalDeletionUserNotMarkedAsDeleted() throws Exception { - Task task = readTestTask("External_Test_Organization", "Test_Organization"); + Task task = readTestTask("External_Test_Organization", null, "Test_Organization"); task.setStatus(TaskStatus.DRAFT); readAccessHelper.addLocal(task); @@ -1223,7 +1223,7 @@ public void testDeletePermanentlyByLocalDeletionUserNotMarkedAsDeleted() throws @Test public void testDeletePermanentlyByExternalUser() throws Exception { - Task task = readTestTask("External_Test_Organization", "Test_Organization"); + Task task = readTestTask("External_Test_Organization", null, "Test_Organization"); readAccessHelper.addLocal(task); TaskDao taskDao = getSpringWebApplicationContext().getBean(TaskDao.class); String taskId = taskDao.create(task).getIdElement().getIdPart(); @@ -1234,17 +1234,17 @@ public void testDeletePermanentlyByExternalUser() throws Exception @Test public void testHistoryLiteralReferenceClean() throws Exception { - ActivityDefinition ad1 = readActivityDefinition("dsf-test-activity-definition1-1.0.xml"); - ActivityDefinition createdAd1 = getWebserviceClient().create(ad1); - assertNotNull(createdAd1); - assertNotNull(createdAd1.getIdElement().getIdPart()); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition1-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); StructureDefinition testTaskProfile = readTestTaskProfile(); StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task task = readTestTask("Test_Organization", "Test_Organization"); + Task task = readTestTask("Test_Organization", null, "Test_Organization"); assertFalse(task.getRequester().hasReference()); assertTrue(task.getRequester().hasType()); assertTrue(task.getRequester().hasIdentifier()); @@ -1576,17 +1576,17 @@ public void testUpdateTaskFromRequestedToFailedWithNonExistingInputReferenceToEx @Test public void testCreateAllowViaDraftNotAllowedAsRequestedLocal() throws Exception { - ActivityDefinition ad14 = readActivityDefinition("dsf-test-activity-definition14-1.0.xml"); - ActivityDefinition createdAd14 = getWebserviceClient().create(ad14); - assertNotNull(createdAd14); - assertNotNull(createdAd14.getIdElement().getIdPart()); + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition14-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); StructureDefinition testTaskProfile = readTestTaskProfile(); StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); assertNotNull(createdTestTaskProfile); assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); - Task task = readTestTask("Test_Organization", "Test_Organization"); + Task task = readTestTask("Test_Organization", null, "Test_Organization"); task.addIdentifier().setSystem("http://dsf.dev/sid/task-identifier").setValue("test"); task.setStatus(TaskStatus.DRAFT); @@ -1603,7 +1603,7 @@ public void testCreateAllowViaDraftNotAllowedAsRequestedLocal() throws Exception @Test public void testCreateForbiddenDraftTaskExternalOrganization() throws Exception { - Task task = readTestTask("External_Test_Organization", "Test_Organization"); + Task task = readTestTask("External_Test_Organization", null, "Test_Organization"); task.setStatus(TaskStatus.DRAFT); expectForbidden(() -> getExternalWebserviceClient().create(task)); @@ -1612,7 +1612,7 @@ public void testCreateForbiddenDraftTaskExternalOrganization() throws Exception @Test public void testCreateForbiddenDraftTaskPractitionerIdentity() throws Exception { - Task task = readTestTask("Test_Organization", "Test_Organization"); + Task task = readTestTask("Test_Organization", null, "Test_Organization"); task.setStatus(TaskStatus.DRAFT); expectForbidden(() -> getPractitionerWebserviceClient().create(task)); @@ -1621,7 +1621,7 @@ public void testCreateForbiddenDraftTaskPractitionerIdentity() throws Exception @Test public void testUpdateRequestedToInProgressForbiddenForExternal() throws Exception { - Task task = readTestTask("Test_Organization", "Test_Organization"); + Task task = readTestTask("Test_Organization", null, "Test_Organization"); task.setStatus(TaskStatus.REQUESTED); TaskDao dao = getSpringWebApplicationContext().getBean(TaskDao.class); Task createdTask = dao.create(task); @@ -1629,4 +1629,653 @@ public void testUpdateRequestedToInProgressForbiddenForExternal() throws Excepti createdTask.setStatus(TaskStatus.INPROGRESS); expectForbidden(() -> getExternalWebserviceClient().update(createdTask)); } + + @Test + public void testReadSearchLocalOrganizationDraftTask() throws Exception + { + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition14-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); + + StructureDefinition p = readTestTaskProfile(); + StructureDefinition createdP = getWebserviceClient().create(p); + assertNotNull(createdP); + assertNotNull(createdP.getIdElement().getIdPart()); + + Task t = readTestTask("Test_Organization", null, "Test_Organization"); + t.addIdentifier().setSystem("http://dsf.dev/sid/task-identifier").setValue("test"); + t.setStatus(TaskStatus.DRAFT); + Task createdT = getWebserviceClient().create(t); + assertNotNull(createdT); + assertNotNull(createdT.getIdElement().getIdPart()); + + Task readResult = getWebserviceClient().read(Task.class, createdT.getIdElement().getIdPart()); + assertNotNull(readResult); + assertEquals(createdT.getIdElement().getIdPart(), readResult.getIdElement().getIdPart()); + + Bundle searchResult = getWebserviceClient().search(Task.class, Map.of()); + assertNotNull(searchResult); + assertEquals(1, searchResult.getTotal()); + assertNotNull(searchResult.getEntry()); + assertEquals(1, searchResult.getEntry().size()); + assertTrue(searchResult.getEntry().get(0).hasResource()); + assertNotNull(searchResult.getEntry().get(0).getResource()); + assertEquals(Task.class, searchResult.getEntry().get(0).getResource().getClass()); + Task searchResultTask = (Task) searchResult.getEntry().get(0).getResource(); + assertEquals(createdT.getIdElement().getIdPart(), searchResultTask.getIdElement().getIdPart()); + } + + @Test + public void testReadSearchExternalOrganizationDraftTask() throws Exception + { + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition14-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); + + StructureDefinition p = readTestTaskProfile(); + StructureDefinition createdP = getWebserviceClient().create(p); + assertNotNull(createdP); + assertNotNull(createdP.getIdElement().getIdPart()); + + Task t = readTestTask("Test_Organization", null, "Test_Organization"); + t.addIdentifier().setSystem("http://dsf.dev/sid/task-identifier").setValue("test"); + t.setStatus(TaskStatus.DRAFT); + Task createdT = getWebserviceClient().create(t); + assertNotNull(createdT); + assertNotNull(createdT.getIdElement().getIdPart()); + + expectForbidden(() -> getExternalWebserviceClient().read(Task.class, createdT.getIdElement().getIdPart())); + + Bundle searchResult = getExternalWebserviceClient().search(Task.class, Map.of()); + assertNotNull(searchResult); + assertEquals(0, searchResult.getTotal()); + assertNotNull(searchResult.getEntry()); + assertEquals(0, searchResult.getEntry().size()); + } + + @Test + public void testReadSearchPractitionerDsfAdminDraftTask() throws Exception + { + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition14-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); + + StructureDefinition p = readTestTaskProfile(); + StructureDefinition createdP = getWebserviceClient().create(p); + assertNotNull(createdP); + assertNotNull(createdP.getIdElement().getIdPart()); + + Task t = readTestTask("Test_Organization", null, "Test_Organization"); + t.addIdentifier().setSystem("http://dsf.dev/sid/task-identifier").setValue("test"); + t.setStatus(TaskStatus.DRAFT); + Task createdT = getWebserviceClient().create(t); + assertNotNull(createdT); + assertNotNull(createdT.getIdElement().getIdPart()); + + Task readResult = getAdminWebserviceClient().read(Task.class, createdT.getIdElement().getIdPart()); + assertNotNull(readResult); + assertEquals(createdT.getIdElement().getIdPart(), readResult.getIdElement().getIdPart()); + + Bundle searchResult = getAdminWebserviceClient().search(Task.class, Map.of()); + assertNotNull(searchResult); + assertEquals(1, searchResult.getTotal()); + assertNotNull(searchResult.getEntry()); + assertEquals(1, searchResult.getEntry().size()); + assertTrue(searchResult.getEntry().get(0).hasResource()); + assertNotNull(searchResult.getEntry().get(0).getResource()); + assertEquals(Task.class, searchResult.getEntry().get(0).getResource().getClass()); + Task searchResultTask = (Task) searchResult.getEntry().get(0).getResource(); + assertEquals(createdT.getIdElement().getIdPart(), searchResultTask.getIdElement().getIdPart()); + } + + @Test + public void testReadSearchPractitionerDicUserDraftTask() throws Exception + { + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition14-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); + + StructureDefinition p = readTestTaskProfile(); + StructureDefinition createdP = getWebserviceClient().create(p); + assertNotNull(createdP); + assertNotNull(createdP.getIdElement().getIdPart()); + + Task t = readTestTask("Test_Organization", null, "Test_Organization"); + t.addIdentifier().setSystem("http://dsf.dev/sid/task-identifier").setValue("test"); + t.setStatus(TaskStatus.DRAFT); + Task createdT = getWebserviceClient().create(t); + assertNotNull(createdT); + assertNotNull(createdT.getIdElement().getIdPart()); + + Task readResult = getPractitionerWebserviceClient().read(Task.class, createdT.getIdElement().getIdPart()); + assertNotNull(readResult); + assertEquals(createdT.getIdElement().getIdPart(), readResult.getIdElement().getIdPart()); + + Bundle searchResult = getPractitionerWebserviceClient().search(Task.class, Map.of()); + assertNotNull(searchResult); + assertEquals(1, searchResult.getTotal()); + assertNotNull(searchResult.getEntry()); + assertEquals(1, searchResult.getEntry().size()); + assertTrue(searchResult.getEntry().get(0).hasResource()); + assertNotNull(searchResult.getEntry().get(0).getResource()); + assertEquals(Task.class, searchResult.getEntry().get(0).getResource().getClass()); + Task searchResultTask = (Task) searchResult.getEntry().get(0).getResource(); + assertEquals(createdT.getIdElement().getIdPart(), searchResultTask.getIdElement().getIdPart()); + } + + @Test + public void testReadSearchPractitionerDicUserMinimalDraftTask() throws Exception + { + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition14-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); + + StructureDefinition p = readTestTaskProfile(); + StructureDefinition createdP = getWebserviceClient().create(p); + assertNotNull(createdP); + assertNotNull(createdP.getIdElement().getIdPart()); + + Task t = readTestTask("Test_Organization", null, "Test_Organization"); + t.addIdentifier().setSystem("http://dsf.dev/sid/task-identifier").setValue("test"); + t.setStatus(TaskStatus.DRAFT); + Task createdT = getWebserviceClient().create(t); + assertNotNull(createdT); + assertNotNull(createdT.getIdElement().getIdPart()); + + Task readResult = getMinimalWebserviceClient().read(Task.class, createdT.getIdElement().getIdPart()); + assertNotNull(readResult); + assertEquals(createdT.getIdElement().getIdPart(), readResult.getIdElement().getIdPart()); + + Bundle searchResult = getMinimalWebserviceClient().search(Task.class, Map.of()); + assertNotNull(searchResult); + assertEquals(1, searchResult.getTotal()); + assertNotNull(searchResult.getEntry()); + assertEquals(1, searchResult.getEntry().size()); + assertTrue(searchResult.getEntry().get(0).hasResource()); + assertNotNull(searchResult.getEntry().get(0).getResource()); + assertEquals(Task.class, searchResult.getEntry().get(0).getResource().getClass()); + Task searchResultTask = (Task) searchResult.getEntry().get(0).getResource(); + assertEquals(createdT.getIdElement().getIdPart(), searchResultTask.getIdElement().getIdPart()); + } + + @Test + public void testReadSearchLocalOrganizationUserIsRequester() throws Exception + { + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition1-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); + + StructureDefinition p = readTestTaskProfile(); + StructureDefinition createdP = getWebserviceClient().create(p); + assertNotNull(createdP); + assertNotNull(createdP.getIdElement().getIdPart()); + + Task t = readTestTask("Test_Organization", null, "Test_Organization"); + Task createdT = getWebserviceClient().create(t); + assertNotNull(createdT); + assertNotNull(createdT.getIdElement().getIdPart()); + + Task readResult = getWebserviceClient().read(Task.class, createdT.getIdElement().getIdPart()); + assertNotNull(readResult); + assertEquals(createdT.getIdElement().getIdPart(), readResult.getIdElement().getIdPart()); + + Bundle searchResult = getWebserviceClient().search(Task.class, Map.of()); + assertNotNull(searchResult); + assertEquals(1, searchResult.getTotal()); + assertNotNull(searchResult.getEntry()); + assertEquals(1, searchResult.getEntry().size()); + assertTrue(searchResult.getEntry().get(0).hasResource()); + assertNotNull(searchResult.getEntry().get(0).getResource()); + assertEquals(Task.class, searchResult.getEntry().get(0).getResource().getClass()); + Task searchResultTask = (Task) searchResult.getEntry().get(0).getResource(); + assertEquals(createdT.getIdElement().getIdPart(), searchResultTask.getIdElement().getIdPart()); + } + + @Test + public void testReadSearchExternalOrganizationUserIsRequester() throws Exception + { + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition2-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); + + StructureDefinition p = readTestTaskProfile(); + StructureDefinition createdP = getWebserviceClient().create(p); + assertNotNull(createdP); + assertNotNull(createdP.getIdElement().getIdPart()); + + Task t = readTestTask("External_Test_Organization", null, "Test_Organization"); + Task createdT = getExternalWebserviceClient().create(t); + assertNotNull(createdT); + assertNotNull(createdT.getIdElement().getIdPart()); + + Task readResult = getExternalWebserviceClient().read(Task.class, createdT.getIdElement().getIdPart()); + assertNotNull(readResult); + assertEquals(createdT.getIdElement().getIdPart(), readResult.getIdElement().getIdPart()); + + Bundle searchResult = getExternalWebserviceClient().search(Task.class, Map.of()); + assertNotNull(searchResult); + assertEquals(1, searchResult.getTotal()); + assertNotNull(searchResult.getEntry()); + assertEquals(1, searchResult.getEntry().size()); + assertTrue(searchResult.getEntry().get(0).hasResource()); + assertNotNull(searchResult.getEntry().get(0).getResource()); + assertEquals(Task.class, searchResult.getEntry().get(0).getResource().getClass()); + Task searchResultTask = (Task) searchResult.getEntry().get(0).getResource(); + assertEquals(createdT.getIdElement().getIdPart(), searchResultTask.getIdElement().getIdPart()); + } + + @Test + public void testReadSearchPractitionerDsfAdminUserIsRequester() throws Exception + { + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition1-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); + + StructureDefinition p = readTestTaskProfile(); + StructureDefinition createdP = getWebserviceClient().create(p); + assertNotNull(createdP); + assertNotNull(createdP.getIdElement().getIdPart()); + + Task t = readTestTask(null, X509Certificates.ADMIN_CLIENT_MAIL, "Test_Organization"); + Task createdT = getAdminWebserviceClient().create(t); + assertNotNull(createdT); + assertNotNull(createdT.getIdElement().getIdPart()); + + Task readResult = getAdminWebserviceClient().read(Task.class, createdT.getIdElement().getIdPart()); + assertNotNull(readResult); + assertEquals(createdT.getIdElement().getIdPart(), readResult.getIdElement().getIdPart()); + + Bundle searchResult = getAdminWebserviceClient().search(Task.class, Map.of()); + assertNotNull(searchResult); + assertEquals(1, searchResult.getTotal()); + assertNotNull(searchResult.getEntry()); + assertEquals(1, searchResult.getEntry().size()); + assertTrue(searchResult.getEntry().get(0).hasResource()); + assertNotNull(searchResult.getEntry().get(0).getResource()); + assertEquals(Task.class, searchResult.getEntry().get(0).getResource().getClass()); + Task searchResultTask = (Task) searchResult.getEntry().get(0).getResource(); + assertEquals(createdT.getIdElement().getIdPart(), searchResultTask.getIdElement().getIdPart()); + } + + @Test + public void testReadSearchPractitionerDicUserUserIsRequester() throws Exception + { + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition5-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); + + StructureDefinition p = readTestTaskProfile(); + StructureDefinition createdP = getWebserviceClient().create(p); + assertNotNull(createdP); + assertNotNull(createdP.getIdElement().getIdPart()); + + Task t = readTestTask(null, X509Certificates.PRACTITIONER_CLIENT_MAIL, "Test_Organization"); + Task createdT = getPractitionerWebserviceClient().create(t); + assertNotNull(createdT); + assertNotNull(createdT.getIdElement().getIdPart()); + + Task readResult = getPractitionerWebserviceClient().read(Task.class, createdT.getIdElement().getIdPart()); + assertNotNull(readResult); + assertEquals(createdT.getIdElement().getIdPart(), readResult.getIdElement().getIdPart()); + + Bundle searchResult = getPractitionerWebserviceClient().search(Task.class, Map.of()); + assertNotNull(searchResult); + assertEquals(1, searchResult.getTotal()); + assertNotNull(searchResult.getEntry()); + assertEquals(1, searchResult.getEntry().size()); + assertTrue(searchResult.getEntry().get(0).hasResource()); + assertNotNull(searchResult.getEntry().get(0).getResource()); + assertEquals(Task.class, searchResult.getEntry().get(0).getResource().getClass()); + Task searchResultTask = (Task) searchResult.getEntry().get(0).getResource(); + assertEquals(createdT.getIdElement().getIdPart(), searchResultTask.getIdElement().getIdPart()); + } + + @Test + public void testReadSearchPractitionerDicUserMinimalUserIsRequester() throws Exception + { + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition5-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); + + StructureDefinition p = readTestTaskProfile(); + StructureDefinition createdP = getWebserviceClient().create(p); + assertNotNull(createdP); + assertNotNull(createdP.getIdElement().getIdPart()); + + Task t = readTestTask(null, X509Certificates.MINIMAL_CLIENT_MAIL, "Test_Organization"); + Task createdT = getMinimalWebserviceClient().create(t); + assertNotNull(createdT); + assertNotNull(createdT.getIdElement().getIdPart()); + + Task readResult = getMinimalWebserviceClient().read(Task.class, createdT.getIdElement().getIdPart()); + assertNotNull(readResult); + assertEquals(createdT.getIdElement().getIdPart(), readResult.getIdElement().getIdPart()); + + Bundle searchResult = getMinimalWebserviceClient().search(Task.class, Map.of()); + assertNotNull(searchResult); + assertEquals(1, searchResult.getTotal()); + assertNotNull(searchResult.getEntry()); + assertEquals(1, searchResult.getEntry().size()); + assertTrue(searchResult.getEntry().get(0).hasResource()); + assertNotNull(searchResult.getEntry().get(0).getResource()); + assertEquals(Task.class, searchResult.getEntry().get(0).getResource().getClass()); + Task searchResultTask = (Task) searchResult.getEntry().get(0).getResource(); + assertEquals(createdT.getIdElement().getIdPart(), searchResultTask.getIdElement().getIdPart()); + } + + @Test + public void testReadSearchLocalOrganizationUserIsNotRequester() throws Exception + { + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition2-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); + + StructureDefinition p = readTestTaskProfile(); + StructureDefinition createdP = getWebserviceClient().create(p); + assertNotNull(createdP); + assertNotNull(createdP.getIdElement().getIdPart()); + + Task t = readTestTask("External_Test_Organization", null, "Test_Organization"); + Task createdT = getExternalWebserviceClient().create(t); + assertNotNull(createdT); + assertNotNull(createdT.getIdElement().getIdPart()); + + Task readResult = getWebserviceClient().read(Task.class, createdT.getIdElement().getIdPart()); + assertNotNull(readResult); + assertEquals(createdT.getIdElement().getIdPart(), readResult.getIdElement().getIdPart()); + + Bundle searchResult = getWebserviceClient().search(Task.class, Map.of()); + assertNotNull(searchResult); + assertEquals(1, searchResult.getTotal()); + assertNotNull(searchResult.getEntry()); + assertEquals(1, searchResult.getEntry().size()); + assertTrue(searchResult.getEntry().get(0).hasResource()); + assertNotNull(searchResult.getEntry().get(0).getResource()); + assertEquals(Task.class, searchResult.getEntry().get(0).getResource().getClass()); + Task searchResultTask = (Task) searchResult.getEntry().get(0).getResource(); + assertEquals(createdT.getIdElement().getIdPart(), searchResultTask.getIdElement().getIdPart()); + } + + @Test + public void testReadSearchExternalOrganizationUserIsNotRequester() throws Exception + { + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition1-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); + + StructureDefinition p = readTestTaskProfile(); + StructureDefinition createdP = getWebserviceClient().create(p); + assertNotNull(createdP); + assertNotNull(createdP.getIdElement().getIdPart()); + + Task t = readTestTask("Test_Organization", null, "Test_Organization"); + Task createdT = getWebserviceClient().create(t); + assertNotNull(createdT); + assertNotNull(createdT.getIdElement().getIdPart()); + + expectForbidden(() -> getExternalWebserviceClient().read(Task.class, createdT.getIdElement().getIdPart())); + + Bundle searchResult = getExternalWebserviceClient().search(Task.class, Map.of()); + assertNotNull(searchResult); + assertEquals(0, searchResult.getTotal()); + assertNotNull(searchResult.getEntry()); + assertEquals(0, searchResult.getEntry().size()); + } + + @Test + public void testReadSearchPractitionerDsfAdminUserIsNotRequester() throws Exception + { + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition2-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); + + StructureDefinition p = readTestTaskProfile(); + StructureDefinition createdP = getWebserviceClient().create(p); + assertNotNull(createdP); + assertNotNull(createdP.getIdElement().getIdPart()); + + Task t = readTestTask("External_Test_Organization", null, "Test_Organization"); + Task createdT = getExternalWebserviceClient().create(t); + assertNotNull(createdT); + assertNotNull(createdT.getIdElement().getIdPart()); + + Task readResult = getAdminWebserviceClient().read(Task.class, createdT.getIdElement().getIdPart()); + assertNotNull(readResult); + assertEquals(createdT.getIdElement().getIdPart(), readResult.getIdElement().getIdPart()); + + Bundle searchResult = getAdminWebserviceClient().search(Task.class, Map.of()); + assertNotNull(searchResult); + assertEquals(1, searchResult.getTotal()); + assertNotNull(searchResult.getEntry()); + assertEquals(1, searchResult.getEntry().size()); + assertTrue(searchResult.getEntry().get(0).hasResource()); + assertNotNull(searchResult.getEntry().get(0).getResource()); + assertEquals(Task.class, searchResult.getEntry().get(0).getResource().getClass()); + Task searchResultTask = (Task) searchResult.getEntry().get(0).getResource(); + assertEquals(createdT.getIdElement().getIdPart(), searchResultTask.getIdElement().getIdPart()); + } + + @Test + public void testReadSearchPractitionerDicUserUserIsNotRequester() throws Exception + { + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition1-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); + + StructureDefinition p = readTestTaskProfile(); + StructureDefinition createdP = getWebserviceClient().create(p); + assertNotNull(createdP); + assertNotNull(createdP.getIdElement().getIdPart()); + + Task t = readTestTask("Test_Organization", null, "Test_Organization"); + Task createdT = getWebserviceClient().create(t); + assertNotNull(createdT); + assertNotNull(createdT.getIdElement().getIdPart()); + + expectForbidden(() -> getPractitionerWebserviceClient().read(Task.class, createdT.getIdElement().getIdPart())); + + Bundle searchResult = getPractitionerWebserviceClient().search(Task.class, Map.of()); + assertNotNull(searchResult); + assertEquals(0, searchResult.getTotal()); + assertNotNull(searchResult.getEntry()); + assertEquals(0, searchResult.getEntry().size()); + } + + @Test + public void testReadSearchPractitionerDicUserMinimalUserIsNotRequester() throws Exception + { + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition1-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); + + StructureDefinition p = readTestTaskProfile(); + StructureDefinition createdP = getWebserviceClient().create(p); + assertNotNull(createdP); + assertNotNull(createdP.getIdElement().getIdPart()); + + Task t = readTestTask("Test_Organization", null, "Test_Organization"); + Task createdT = getWebserviceClient().create(t); + assertNotNull(createdT); + assertNotNull(createdT.getIdElement().getIdPart()); + + expectForbidden(() -> getMinimalWebserviceClient().read(Task.class, createdT.getIdElement().getIdPart())); + + Bundle searchResult = getMinimalWebserviceClient().search(Task.class, Map.of()); + assertNotNull(searchResult); + assertEquals(0, searchResult.getTotal()); + assertNotNull(searchResult.getEntry()); + assertEquals(0, searchResult.getEntry().size()); + } + + @Test + public void testUpdateDraftTaskLocalOrganization() throws Exception + { + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition14-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); + + StructureDefinition p = readTestTaskProfile(); + StructureDefinition createdP = getWebserviceClient().create(p); + assertNotNull(createdP); + assertNotNull(createdP.getIdElement().getIdPart()); + + Task t = readTestTask("Test_Organization", null, "Test_Organization"); + t.addIdentifier().setSystem("http://dsf.dev/sid/task-identifier").setValue("test"); + t.setStatus(TaskStatus.DRAFT); + Task createdT = getWebserviceClient().create(t); + assertNotNull(createdT); + assertNotNull(createdT.getIdElement().getIdPart()); + + Task updatedT = getWebserviceClient().update(createdT); + assertNotNull(updatedT); + assertNotNull(updatedT.getIdElement().getIdPart()); + assertEquals(createdT.getIdElement().getIdPart(), updatedT.getIdElement().getIdPart()); + assertEquals(createdT.getIdElement().getVersionIdPartAsLong() + 1, + (long) updatedT.getIdElement().getVersionIdPartAsLong()); + } + + @Test + public void testUpdateDraftTaskExternalOrganization() throws Exception + { + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition14-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); + + StructureDefinition p = readTestTaskProfile(); + StructureDefinition createdP = getWebserviceClient().create(p); + assertNotNull(createdP); + assertNotNull(createdP.getIdElement().getIdPart()); + + Task t = readTestTask("Test_Organization", null, "Test_Organization"); + t.addIdentifier().setSystem("http://dsf.dev/sid/task-identifier").setValue("test"); + t.setStatus(TaskStatus.DRAFT); + Task createdT = getWebserviceClient().create(t); + assertNotNull(createdT); + assertNotNull(createdT.getIdElement().getIdPart()); + + expectForbidden(() -> getExternalWebserviceClient().update(createdT)); + } + + @Test + public void testUpdateDraftTaskPractitionerDsfAdmin() throws Exception + { + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition14-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); + + StructureDefinition p = readTestTaskProfile(); + StructureDefinition createdP = getWebserviceClient().create(p); + assertNotNull(createdP); + assertNotNull(createdP.getIdElement().getIdPart()); + + Task t = readTestTask("Test_Organization", null, "Test_Organization"); + t.addIdentifier().setSystem("http://dsf.dev/sid/task-identifier").setValue("test"); + t.setStatus(TaskStatus.DRAFT); + Task createdT = getWebserviceClient().create(t); + assertNotNull(createdT); + assertNotNull(createdT.getIdElement().getIdPart()); + + Task updatedT = getAdminWebserviceClient().update(createdT); + assertNotNull(updatedT); + assertNotNull(updatedT.getIdElement().getIdPart()); + assertEquals(createdT.getIdElement().getIdPart(), updatedT.getIdElement().getIdPart()); + assertEquals(createdT.getIdElement().getVersionIdPartAsLong() + 1, + (long) updatedT.getIdElement().getVersionIdPartAsLong()); + } + + @Test + public void testUpdateDraftTaskPractitionerDicUser() throws Exception + { + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition14-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); + + StructureDefinition p = readTestTaskProfile(); + StructureDefinition createdP = getWebserviceClient().create(p); + assertNotNull(createdP); + assertNotNull(createdP.getIdElement().getIdPart()); + + Task t = readTestTask("Test_Organization", null, "Test_Organization"); + t.addIdentifier().setSystem("http://dsf.dev/sid/task-identifier").setValue("test"); + t.setStatus(TaskStatus.DRAFT); + Task createdT = getWebserviceClient().create(t); + assertNotNull(createdT); + assertNotNull(createdT.getIdElement().getIdPart()); + + expectForbidden(() -> getPractitionerWebserviceClient().update(createdT)); + } + + @Test + public void testUpdateDraftTaskPractitionerDicUserMinimal() throws Exception + { + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition14-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); + + StructureDefinition p = readTestTaskProfile(); + StructureDefinition createdP = getWebserviceClient().create(p); + assertNotNull(createdP); + assertNotNull(createdP.getIdElement().getIdPart()); + + Task t = readTestTask("Test_Organization", null, "Test_Organization"); + t.addIdentifier().setSystem("http://dsf.dev/sid/task-identifier").setValue("test"); + t.setStatus(TaskStatus.DRAFT); + Task createdT = getWebserviceClient().create(t); + assertNotNull(createdT); + assertNotNull(createdT.getIdElement().getIdPart()); + + expectForbidden(() -> getMinimalWebserviceClient().update(createdT)); + } + + @Test + public void testSerachTaskWithPractitionerUserByRequester() throws Exception + { + ActivityDefinition ad = readActivityDefinition("dsf-test-activity-definition5-1.0.xml"); + ActivityDefinition createdAd = getWebserviceClient().create(ad); + assertNotNull(createdAd); + assertNotNull(createdAd.getIdElement().getIdPart()); + + StructureDefinition testTaskProfile = readTestTaskProfile(); + StructureDefinition createdTestTaskProfile = getWebserviceClient().create(testTaskProfile); + assertNotNull(createdTestTaskProfile); + assertNotNull(createdTestTaskProfile.getIdElement().getIdPart()); + + Task task = readTestTask(null, X509Certificates.PRACTITIONER_CLIENT_MAIL, "Test_Organization"); + Task createdTask = getPractitionerWebserviceClient().create(task); + assertNotNull(createdTask); + assertNotNull(createdTask.getIdElement().getIdPart()); + + + Bundle searchResult = getPractitionerWebserviceClient().search(Task.class, Map.of("requester:identifier", + List.of("http://dsf.dev/sid/practitioner-identifier|" + X509Certificates.PRACTITIONER_CLIENT_MAIL))); + assertNotNull(searchResult); + assertEquals(1, searchResult.getTotal()); + assertNotNull(searchResult.getEntry()); + assertEquals(1, searchResult.getEntry().size()); + BundleEntryComponent entry = searchResult.getEntry().get(0); + assertNotNull(entry); + assertNotNull(entry.getResource()); + assertEquals(Task.class, entry.getResource().getClass()); + assertEquals(createdTask.getIdElement().getIdPart(), entry.getResource().getIdElement().getIdPart()); + } } diff --git a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/X509Certificates.java b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/X509Certificates.java index 65368db2b..8170e298c 100755 --- a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/X509Certificates.java +++ b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/X509Certificates.java @@ -32,6 +32,10 @@ public class X509Certificates extends ExternalResource { + public static final String PRACTITIONER_CLIENT_MAIL = "practitioner@invalid"; + public static final String MINIMAL_CLIENT_MAIL = "minimal@invalid"; + public static final String ADMIN_CLIENT_MAIL = "admin@invalid"; + public static record CertificateAndPrivateKey(X509Certificate caCertificate, X509Certificate certificate, PrivateKey privateKey) { @@ -72,6 +76,7 @@ public String certificateSha512ThumbprintHex() private CertificateAndPrivateKey serverCertificate; private CertificateAndPrivateKey clientCertificate; private CertificateAndPrivateKey practitionerClientCertificate; + private CertificateAndPrivateKey adminClientCertificate; private CertificateAndPrivateKey minimalClientCertificate; private CertificateAndPrivateKey externalClientCertificate; @@ -82,6 +87,8 @@ public String certificateSha512ThumbprintHex() private Path externalClientCertificatePrivateKeyFile; private Path practitionerClientCertificateFile; private Path practitionerClientCertificatePrivateKeyFile; + private Path adminClientCertificateFile; + private Path adminClientCertificatePrivateKeyFile; private Path minimalClientCertificateFile; private Path minimalClientCertificatePrivateKeyFile; @@ -119,6 +126,11 @@ public CertificateAndPrivateKey getPractitionerClientCertificate() return practitionerClientCertificate; } + public CertificateAndPrivateKey getAdminClientCertificate() + { + return adminClientCertificate; + } + public CertificateAndPrivateKey getMinimalClientCertificate() { return minimalClientCertificate; @@ -164,6 +176,16 @@ public Path getPractitionerClientCertificatePrivateKeyFile() return practitionerClientCertificatePrivateKeyFile; } + public Path getAdminClientCertificateFile() + { + return adminClientCertificateFile; + } + + public Path getAdminClientCertificatePrivateKeyFile() + { + return adminClientCertificatePrivateKeyFile; + } + public Path getMinimalClientCertificateFile() { return minimalClientCertificateFile; @@ -186,6 +208,8 @@ private void createX509Certificates() throws InvalidKeyException, NoSuchAlgorith Path externalClientCertificatePrivateKeyFile = Paths.get("target", UUID.randomUUID().toString() + ".pem"); Path practitionerClientCertificateFile = Paths.get("target", UUID.randomUUID().toString() + ".pem"); Path practitionerClientCertificatePrivateKeyFile = Paths.get("target", UUID.randomUUID().toString() + ".pem"); + Path adminClientCertificateFile = Paths.get("target", UUID.randomUUID().toString() + ".pem"); + Path adminClientCertificatePrivateKeyFile = Paths.get("target", UUID.randomUUID().toString() + ".pem"); Path minimalClientCertificateFile = Paths.get("target", UUID.randomUUID().toString() + ".pem"); Path minimalClientCertificatePrivateKeyFile = Paths.get("target", UUID.randomUUID().toString() + ".pem"); @@ -223,16 +247,27 @@ private void createX509Certificates() throws InvalidKeyException, NoSuchAlgorith // -- practitioner client CertificationRequestAndPrivateKey practitionerClientRequest = CertificationRequest .builder(ca, "DE", null, null, null, null, "practitioner-client").generateKeyPair() - .setEmail("practitioner@test.org").build(); + .setEmail(PRACTITIONER_CLIENT_MAIL).build(); X509Certificate practitionerClientCertificate = ca.signClientCertificate(practitionerClientRequest); PemWriter.writeCertificate(practitionerClientCertificate, practitionerClientCertificateFile); PemWriter.writePrivateKey(practitionerClientRequest.getPrivateKey()).asPkcs8().encryptedAes128(PASSWORD) .toFile(practitionerClientCertificatePrivateKeyFile); // practitioner client -- + // -- admin client + CertificationRequestAndPrivateKey adminClientRequest = CertificationRequest + .builder(ca, "DE", null, null, null, null, "admin-client").generateKeyPair().setEmail(ADMIN_CLIENT_MAIL) + .build(); + X509Certificate adminClientCertificate = ca.signClientCertificate(adminClientRequest, Period.ofDays(1)); + PemWriter.writeCertificate(adminClientCertificate, adminClientCertificateFile); + PemWriter.writePrivateKey(adminClientRequest.getPrivateKey()).asPkcs8().encryptedAes128(PASSWORD) + .toFile(adminClientCertificatePrivateKeyFile); + // admin client -- + // -- minimal client CertificationRequestAndPrivateKey minimalClientRequest = CertificationRequest - .builder(ca, "DE", null, null, null, null, "minimal-client").generateKeyPair().build(); + .builder(ca, "DE", null, null, null, null, "minimal-client").generateKeyPair() + .setEmail(MINIMAL_CLIENT_MAIL).build(); X509Certificate minimalClientCertificate = ca.signClientCertificate(minimalClientRequest, Period.ofDays(1)); PemWriter.writeCertificate(minimalClientCertificate, minimalClientCertificateFile); PemWriter.writePrivateKey(minimalClientRequest.getPrivateKey()).asPkcs8().encryptedAes128(PASSWORD) @@ -248,6 +283,8 @@ private void createX509Certificates() throws InvalidKeyException, NoSuchAlgorith externalClientRequest.getPrivateKey()); this.practitionerClientCertificate = new CertificateAndPrivateKey(caCertificate, practitionerClientCertificate, practitionerClientRequest.getPrivateKey()); + this.adminClientCertificate = new CertificateAndPrivateKey(caCertificate, adminClientCertificate, + adminClientRequest.getPrivateKey()); this.minimalClientCertificate = new CertificateAndPrivateKey(caCertificate, minimalClientCertificate, minimalClientRequest.getPrivateKey()); @@ -258,13 +295,16 @@ private void createX509Certificates() throws InvalidKeyException, NoSuchAlgorith this.externalClientCertificatePrivateKeyFile = externalClientCertificatePrivateKeyFile; this.practitionerClientCertificateFile = practitionerClientCertificateFile; this.practitionerClientCertificatePrivateKeyFile = practitionerClientCertificatePrivateKeyFile; + this.adminClientCertificateFile = adminClientCertificateFile; + this.adminClientCertificatePrivateKeyFile = adminClientCertificatePrivateKeyFile; this.minimalClientCertificateFile = minimalClientCertificateFile; this.minimalClientCertificatePrivateKeyFile = minimalClientCertificatePrivateKeyFile; filesToDelete = List.of(caCertificateFile, clientCertificateFile, clientCertificatePrivateKeyFile, externalClientCertificateFile, externalClientCertificatePrivateKeyFile, practitionerClientCertificateFile, practitionerClientCertificatePrivateKeyFile, - minimalClientCertificateFile, minimalClientCertificatePrivateKeyFile); + adminClientCertificateFile, adminClientCertificatePrivateKeyFile, minimalClientCertificateFile, + minimalClientCertificatePrivateKeyFile); } private void deleteX509Certificates() diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-activity-definition-1.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-activity-definition-1.0.0.xml deleted file mode 100644 index 78a5899ff..000000000 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-activity-definition-1.0.0.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-activity-definition-2.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-activity-definition-2.0.0.xml new file mode 100644 index 000000000..610430191 --- /dev/null +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-activity-definition-2.0.0.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-activity-definition-1.0.0.xml.post b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-activity-definition-2.0.0.xml.post similarity index 83% rename from dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-activity-definition-1.0.0.xml.post rename to dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-activity-definition-2.0.0.xml.post index 9f25fda92..bbc97eb5a 100644 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-activity-definition-1.0.0.xml.post +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-activity-definition-2.0.0.xml.post @@ -1 +1 @@ -url=http://dsf.dev/fhir/StructureDefinition/activity-definition&version=1.0.0 \ No newline at end of file +url=http://dsf.dev/fhir/StructureDefinition/activity-definition&version=2.0.0 \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-all-1.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-all-1.0.0.xml deleted file mode 100644 index 1b26a1c37..000000000 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-all-1.0.0.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-all-2.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-all-2.0.0.xml new file mode 100644 index 000000000..9571ae392 --- /dev/null +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-all-2.0.0.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-all-1.0.0.xml.post b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-all-2.0.0.xml.post similarity index 66% rename from dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-all-1.0.0.xml.post rename to dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-all-2.0.0.xml.post index ecd7d51c8..0a2d7410b 100644 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-all-1.0.0.xml.post +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-all-2.0.0.xml.post @@ -1 +1 @@ -url=http://dsf.dev/fhir/StructureDefinition/coding-process-authorization-local-all&version=1.0.0 \ No newline at end of file +url=http://dsf.dev/fhir/StructureDefinition/coding-process-authorization-local-all&version=2.0.0 \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-all-practitioner-1.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-all-practitioner-1.0.0.xml deleted file mode 100644 index 2be59526a..000000000 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-all-practitioner-1.0.0.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-all-practitioner-2.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-all-practitioner-2.0.0.xml new file mode 100644 index 000000000..83d8158ad --- /dev/null +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-all-practitioner-2.0.0.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-all-practitioner-1.0.0.xml.post b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-all-practitioner-2.0.0.xml.post similarity index 58% rename from dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-all-practitioner-1.0.0.xml.post rename to dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-all-practitioner-2.0.0.xml.post index 3191d59f0..974a0f102 100644 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-all-practitioner-1.0.0.xml.post +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-all-practitioner-2.0.0.xml.post @@ -1 +1 @@ -url=http://dsf.dev/fhir/StructureDefinition/coding-process-authorization-local-all-practitioner&version=1.0.0 \ No newline at end of file +url=http://dsf.dev/fhir/StructureDefinition/coding-process-authorization-local-all-practitioner&version=2.0.0 \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-organization-1.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-organization-1.0.0.xml deleted file mode 100644 index e0b5485ae..000000000 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-organization-1.0.0.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-organization-2.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-organization-2.0.0.xml new file mode 100644 index 000000000..d50866872 --- /dev/null +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-organization-2.0.0.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-organization-1.0.0.xml.post b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-organization-2.0.0.xml.post similarity index 60% rename from dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-organization-1.0.0.xml.post rename to dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-organization-2.0.0.xml.post index 557a5e960..035f6f7b2 100644 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-organization-1.0.0.xml.post +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-organization-2.0.0.xml.post @@ -1 +1 @@ -url=http://dsf.dev/fhir/StructureDefinition/coding-process-authorization-local-organization&version=1.0.0 \ No newline at end of file +url=http://dsf.dev/fhir/StructureDefinition/coding-process-authorization-local-organization&version=2.0.0 \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-organization-practitioner-1.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-organization-practitioner-1.0.0.xml deleted file mode 100644 index 8fed9c240..000000000 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-organization-practitioner-1.0.0.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-organization-practitioner-2.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-organization-practitioner-2.0.0.xml new file mode 100644 index 000000000..4b3be02ce --- /dev/null +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-organization-practitioner-2.0.0.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-organization-practitioner-1.0.0.xml.post b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-organization-practitioner-2.0.0.xml.post similarity index 54% rename from dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-organization-practitioner-1.0.0.xml.post rename to dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-organization-practitioner-2.0.0.xml.post index 26d7d6bbb..3cc946911 100644 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-organization-practitioner-1.0.0.xml.post +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-organization-practitioner-2.0.0.xml.post @@ -1 +1 @@ -url=http://dsf.dev/fhir/StructureDefinition/coding-process-authorization-local-organization-practitioner&version=1.0.0 \ No newline at end of file +url=http://dsf.dev/fhir/StructureDefinition/coding-process-authorization-local-organization-practitioner&version=2.0.0 \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-parent-organization-role-1.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-parent-organization-role-1.0.0.xml deleted file mode 100644 index b7414df61..000000000 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-parent-organization-role-1.0.0.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-parent-organization-role-2.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-parent-organization-role-2.0.0.xml new file mode 100644 index 000000000..8a78ab110 --- /dev/null +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-parent-organization-role-2.0.0.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-parent-organization-role-1.0.0.xml.post b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-parent-organization-role-2.0.0.xml.post similarity index 54% rename from dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-parent-organization-role-1.0.0.xml.post rename to dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-parent-organization-role-2.0.0.xml.post index 6c8fc33d3..447f89236 100644 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-parent-organization-role-1.0.0.xml.post +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-parent-organization-role-2.0.0.xml.post @@ -1 +1 @@ -url=http://dsf.dev/fhir/StructureDefinition/coding-process-authorization-local-parent-organization-role&version=1.0.0 \ No newline at end of file +url=http://dsf.dev/fhir/StructureDefinition/coding-process-authorization-local-parent-organization-role&version=2.0.0 \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-parent-organization-role-practitioner-1.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-parent-organization-role-practitioner-1.0.0.xml deleted file mode 100644 index 601e1c9a8..000000000 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-parent-organization-role-practitioner-1.0.0.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-parent-organization-role-practitioner-2.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-parent-organization-role-practitioner-2.0.0.xml new file mode 100644 index 000000000..ca44bc417 --- /dev/null +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-parent-organization-role-practitioner-2.0.0.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-parent-organization-role-practitioner-1.0.0.xml.post b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-parent-organization-role-practitioner-2.0.0.xml.post similarity index 50% rename from dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-parent-organization-role-practitioner-1.0.0.xml.post rename to dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-parent-organization-role-practitioner-2.0.0.xml.post index f18f1ae96..ff0141c17 100644 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-parent-organization-role-practitioner-1.0.0.xml.post +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-local-parent-organization-role-practitioner-2.0.0.xml.post @@ -1 +1 @@ -url=http://dsf.dev/fhir/StructureDefinition/coding-process-authorization-local-parent-organization-role-practitioner&version=1.0.0 \ No newline at end of file +url=http://dsf.dev/fhir/StructureDefinition/coding-process-authorization-local-parent-organization-role-practitioner&version=2.0.0 \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-all-1.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-all-1.0.0.xml deleted file mode 100644 index 09658d3ad..000000000 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-all-1.0.0.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-all-2.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-all-2.0.0.xml new file mode 100644 index 000000000..7350120a5 --- /dev/null +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-all-2.0.0.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-all-1.0.0.xml.post b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-all-2.0.0.xml.post similarity index 65% rename from dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-all-1.0.0.xml.post rename to dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-all-2.0.0.xml.post index a67c3cd71..0794ef6a6 100644 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-all-1.0.0.xml.post +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-all-2.0.0.xml.post @@ -1 +1 @@ -url=http://dsf.dev/fhir/StructureDefinition/coding-process-authorization-remote-all&version=1.0.0 \ No newline at end of file +url=http://dsf.dev/fhir/StructureDefinition/coding-process-authorization-remote-all&version=2.0.0 \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-organization-1.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-organization-1.0.0.xml deleted file mode 100644 index 4ae739811..000000000 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-organization-1.0.0.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-organization-2.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-organization-2.0.0.xml new file mode 100644 index 000000000..12edaf8d8 --- /dev/null +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-organization-2.0.0.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-organization-1.0.0.xml.post b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-organization-2.0.0.xml.post similarity index 60% rename from dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-organization-1.0.0.xml.post rename to dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-organization-2.0.0.xml.post index 0d57434f6..47f0dd424 100644 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-organization-1.0.0.xml.post +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-organization-2.0.0.xml.post @@ -1 +1 @@ -url=http://dsf.dev/fhir/StructureDefinition/coding-process-authorization-remote-organization&version=1.0.0 \ No newline at end of file +url=http://dsf.dev/fhir/StructureDefinition/coding-process-authorization-remote-organization&version=2.0.0 \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-parent-organization-role-1.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-parent-organization-role-1.0.0.xml deleted file mode 100644 index 4e5461a3a..000000000 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-parent-organization-role-1.0.0.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-parent-organization-role-1.0.0.xml.post b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-parent-organization-role-1.0.0.xml.post deleted file mode 100644 index 40f01c512..000000000 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-parent-organization-role-1.0.0.xml.post +++ /dev/null @@ -1 +0,0 @@ -url=http://dsf.dev/fhir/StructureDefinition/coding-process-authorization-remote-parent-organization-role&version=1.0.0 \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-parent-organization-role-2.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-parent-organization-role-2.0.0.xml new file mode 100644 index 000000000..1dbfdef94 --- /dev/null +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-parent-organization-role-2.0.0.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-parent-organization-role-2.0.0.xml.post b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-parent-organization-role-2.0.0.xml.post new file mode 100644 index 000000000..b7c42bb26 --- /dev/null +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-coding-process-authorization-remote-parent-organization-role-2.0.0.xml.post @@ -0,0 +1 @@ +url=http://dsf.dev/fhir/StructureDefinition/coding-process-authorization-remote-parent-organization-role&version=2.0.0 \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-1.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-1.0.0.xml deleted file mode 100644 index 7fc2dd741..000000000 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-1.0.0.xml +++ /dev/null @@ -1,136 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-2.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-2.0.0.xml new file mode 100644 index 000000000..97522b3f1 --- /dev/null +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-2.0.0.xml @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-1.0.0.xml.post b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-2.0.0.xml.post similarity index 71% rename from dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-1.0.0.xml.post rename to dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-2.0.0.xml.post index a7f64aa63..2c4a2ed29 100644 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-1.0.0.xml.post +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-2.0.0.xml.post @@ -1 +1 @@ -url=http://dsf.dev/fhir/StructureDefinition/extension-process-authorization&version=1.0.0 \ No newline at end of file +url=http://dsf.dev/fhir/StructureDefinition/extension-process-authorization&version=2.0.0 \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-organization-1.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-organization-1.0.0.xml deleted file mode 100644 index 285a023ca..000000000 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-organization-1.0.0.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-organization-2.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-organization-2.0.0.xml new file mode 100644 index 000000000..b7e272be5 --- /dev/null +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-organization-2.0.0.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-organization-1.0.0.xml.post b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-organization-2.0.0.xml.post similarity index 62% rename from dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-organization-1.0.0.xml.post rename to dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-organization-2.0.0.xml.post index 4d82ef5b9..988400cbc 100644 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-organization-1.0.0.xml.post +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-organization-2.0.0.xml.post @@ -1 +1 @@ -url=http://dsf.dev/fhir/StructureDefinition/extension-process-authorization-organization&version=1.0.0 \ No newline at end of file +url=http://dsf.dev/fhir/StructureDefinition/extension-process-authorization-organization&version=2.0.0 \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-organization-practitioner-1.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-organization-practitioner-1.0.0.xml deleted file mode 100644 index a316e5ced..000000000 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-organization-practitioner-1.0.0.xml +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-organization-practitioner-2.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-organization-practitioner-2.0.0.xml new file mode 100644 index 000000000..207cb25d0 --- /dev/null +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-organization-practitioner-2.0.0.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-organization-practitioner-1.0.0.xml.post b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-organization-practitioner-2.0.0.xml.post similarity index 55% rename from dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-organization-practitioner-1.0.0.xml.post rename to dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-organization-practitioner-2.0.0.xml.post index b98fa22cb..1881dee93 100644 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-organization-practitioner-1.0.0.xml.post +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-organization-practitioner-2.0.0.xml.post @@ -1 +1 @@ -url=http://dsf.dev/fhir/StructureDefinition/extension-process-authorization-organization-practitioner&version=1.0.0 \ No newline at end of file +url=http://dsf.dev/fhir/StructureDefinition/extension-process-authorization-organization-practitioner&version=2.0.0 \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-parent-organization-role-1.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-parent-organization-role-1.0.0.xml deleted file mode 100644 index f31297f62..000000000 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-parent-organization-role-1.0.0.xml +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-parent-organization-role-2.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-parent-organization-role-2.0.0.xml new file mode 100644 index 000000000..3fb6771b5 --- /dev/null +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-parent-organization-role-2.0.0.xml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-parent-organization-role-1.0.0.xml.post b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-parent-organization-role-2.0.0.xml.post similarity index 56% rename from dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-parent-organization-role-1.0.0.xml.post rename to dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-parent-organization-role-2.0.0.xml.post index 2027e393f..c4a8d7c7f 100644 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-parent-organization-role-1.0.0.xml.post +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-parent-organization-role-2.0.0.xml.post @@ -1 +1 @@ -url=http://dsf.dev/fhir/StructureDefinition/extension-process-authorization-parent-organization-role&version=1.0.0 \ No newline at end of file +url=http://dsf.dev/fhir/StructureDefinition/extension-process-authorization-parent-organization-role&version=2.0.0 \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-parent-organization-role-practitioner-1.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-parent-organization-role-practitioner-1.0.0.xml deleted file mode 100644 index 91689e24c..000000000 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-parent-organization-role-practitioner-1.0.0.xml +++ /dev/null @@ -1,127 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-parent-organization-role-practitioner-1.0.0.xml.post b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-parent-organization-role-practitioner-1.0.0.xml.post deleted file mode 100644 index 66b2fd5e7..000000000 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-parent-organization-role-practitioner-1.0.0.xml.post +++ /dev/null @@ -1 +0,0 @@ -url=http://dsf.dev/fhir/StructureDefinition/extension-process-authorization-parent-organization-role-practitioner&version=1.0.0 \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-parent-organization-role-practitioner-2.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-parent-organization-role-practitioner-2.0.0.xml new file mode 100644 index 000000000..f9a30ea15 --- /dev/null +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-parent-organization-role-practitioner-2.0.0.xml @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-parent-organization-role-practitioner-2.0.0.xml.post b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-parent-organization-role-practitioner-2.0.0.xml.post new file mode 100644 index 000000000..ff515421d --- /dev/null +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-parent-organization-role-practitioner-2.0.0.xml.post @@ -0,0 +1 @@ +url=http://dsf.dev/fhir/StructureDefinition/extension-process-authorization-parent-organization-role-practitioner&version=2.0.0 \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-practitioner-1.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-practitioner-1.0.0.xml deleted file mode 100644 index 5f4caf13d..000000000 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-practitioner-1.0.0.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-practitioner-2.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-practitioner-2.0.0.xml new file mode 100644 index 000000000..ca3a97114 --- /dev/null +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-practitioner-2.0.0.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-practitioner-1.0.0.xml.post b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-practitioner-2.0.0.xml.post similarity index 62% rename from dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-practitioner-1.0.0.xml.post rename to dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-practitioner-2.0.0.xml.post index 086f697b3..354540b47 100644 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-practitioner-1.0.0.xml.post +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-process-authorization-practitioner-2.0.0.xml.post @@ -1 +1 @@ -url=http://dsf.dev/fhir/StructureDefinition/extension-process-authorization-practitioner&version=1.0.0 \ No newline at end of file +url=http://dsf.dev/fhir/StructureDefinition/extension-process-authorization-practitioner&version=2.0.0 \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-questionnaire-authorization-2.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-questionnaire-authorization-2.0.0.xml new file mode 100644 index 000000000..b8ea4cccd --- /dev/null +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-questionnaire-authorization-2.0.0.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-questionnaire-authorization-2.0.0.xml.post b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-questionnaire-authorization-2.0.0.xml.post new file mode 100644 index 000000000..aebf2449f --- /dev/null +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-extension-questionnaire-authorization-2.0.0.xml.post @@ -0,0 +1 @@ +url=http://dsf.dev/fhir/StructureDefinition/extension-questionnaire-authorization&version=2.0.0 \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-response-1.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-response-1.0.0.xml deleted file mode 100644 index cb97156b9..000000000 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-response-1.0.0.xml +++ /dev/null @@ -1,196 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-response-2.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-response-2.0.0.xml new file mode 100644 index 000000000..f95ca0b07 --- /dev/null +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-response-2.0.0.xml @@ -0,0 +1,257 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-response-1.0.0.xml.post b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-response-2.0.0.xml.post similarity index 80% rename from dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-response-1.0.0.xml.post rename to dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-response-2.0.0.xml.post index 8655a857c..9025247d5 100644 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-response-1.0.0.xml.post +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-questionnaire-response-2.0.0.xml.post @@ -1 +1 @@ -url=http://dsf.dev/fhir/StructureDefinition/questionnaire-response&version=1.0.0 \ No newline at end of file +url=http://dsf.dev/fhir/StructureDefinition/questionnaire-response&version=2.0.0 \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-task-base-2.0.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-task-base-2.0.0.xml index 496538c79..4e5225181 100644 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-task-base-2.0.0.xml +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/dsf-task-base-2.0.0.xml @@ -55,6 +55,18 @@ + + + + + + + + + + + + @@ -67,7 +79,12 @@ - + + + + + + @@ -85,6 +102,12 @@ + + + + + + @@ -104,9 +127,6 @@ - - - @@ -122,9 +142,6 @@ - - - @@ -133,9 +150,6 @@ - - - @@ -162,9 +176,6 @@ - - - @@ -172,9 +183,6 @@ - - - @@ -201,9 +209,6 @@ - - - @@ -211,9 +216,6 @@ - - - @@ -260,9 +262,6 @@ - - - diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/resources.delete b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/resources.delete index d18d9e8f6..4865bff22 100644 --- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/resources.delete +++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/resources.delete @@ -1,7 +1,24 @@ CodeSystem?url=urn:ietf:bcp:13&version=4.0.1&date=eq2021-07-28 +StructureDefinition?url=http://dsf.dev/fhir/StructureDefinition/activity-definition&version=1.0.0 +StructureDefinition?url=http://dsf.dev/fhir/StructureDefinition/coding-process-authorization-local-all&version=1.0.0 +StructureDefinition?url=http://dsf.dev/fhir/StructureDefinition/coding-process-authorization-local-all-practitioner&version=1.0.0 +StructureDefinition?url=http://dsf.dev/fhir/StructureDefinition/coding-process-authorization-local-organization&version=1.0.0 +StructureDefinition?url=http://dsf.dev/fhir/StructureDefinition/coding-process-authorization-local-organization-practitioner&version=1.0.0 +StructureDefinition?url=http://dsf.dev/fhir/StructureDefinition/coding-process-authorization-local-parent-organization-role&version=1.0.0 +StructureDefinition?url=http://dsf.dev/fhir/StructureDefinition/coding-process-authorization-local-parent-organization-role-practitioner&version=1.0.0 +StructureDefinition?url=http://dsf.dev/fhir/StructureDefinition/coding-process-authorization-remote-all&version=1.0.0 +StructureDefinition?url=http://dsf.dev/fhir/StructureDefinition/coding-process-authorization-remote-organization&version=1.0.0 +StructureDefinition?url=http://dsf.dev/fhir/StructureDefinition/coding-process-authorization-remote-parent-organization-role&version=1.0.0 StructureDefinition?url=http://dsf.dev/fhir/StructureDefinition/endpoint&version=1.0.0 StructureDefinition?url=http://dsf.dev/fhir/StructureDefinition/extension-certificate-thumbprint&version=1.0.0 +StructureDefinition?url=http://dsf.dev/fhir/StructureDefinition/extension-process-authorization&version=1.0.0 +StructureDefinition?url=http://dsf.dev/fhir/StructureDefinition/extension-process-authorization-organization&version=1.0.0 +StructureDefinition?url=http://dsf.dev/fhir/StructureDefinition/extension-process-authorization-organization-practitioner&version=1.0.0 +StructureDefinition?url=http://dsf.dev/fhir/StructureDefinition/extension-process-authorization-parent-organization-role&version=1.0.0 +StructureDefinition?url=http://dsf.dev/fhir/StructureDefinition/extension-process-authorization-parent-organization-role-practitioner&version=1.0.0 +StructureDefinition?url=http://dsf.dev/fhir/StructureDefinition/extension-process-authorization-practitioner&version=1.0.0 StructureDefinition?url=http://dsf.dev/fhir/StructureDefinition/organization&version=1.0.0 StructureDefinition?url=http://dsf.dev/fhir/StructureDefinition/organization-affiliation&version=1.0.0 StructureDefinition?url=http://dsf.dev/fhir/StructureDefinition/organization-parent&version=1.0.0 -StructureDefinition?url=http://dsf.dev/fhir/StructureDefinition/task-base&version=1.0.0 \ No newline at end of file +StructureDefinition?url=http://dsf.dev/fhir/StructureDefinition/task-base&version=1.0.0 +StructureDefinition?url=http://dsf.dev/fhir/StructureDefinition/questionnaire-response&version=1.0.0 \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-validation/src/test/java/dev/dsf/fhir/profiles/ActivityDefinitionProfileTest.java b/dsf-fhir/dsf-fhir-validation/src/test/java/dev/dsf/fhir/profiles/ActivityDefinitionProfileTest.java index 4016beda3..03300f980 100644 --- a/dsf-fhir/dsf-fhir-validation/src/test/java/dev/dsf/fhir/profiles/ActivityDefinitionProfileTest.java +++ b/dsf-fhir/dsf-fhir-validation/src/test/java/dev/dsf/fhir/profiles/ActivityDefinitionProfileTest.java @@ -31,21 +31,21 @@ public class ActivityDefinitionProfileTest @ClassRule public static final ValidationSupportRule validationRule = new ValidationSupportRule( - List.of("dsf-activity-definition-1.0.0.xml", "dsf-extension-process-authorization-1.0.0.xml", - "dsf-extension-process-authorization-practitioner-1.0.0.xml", - "dsf-extension-process-authorization-organization-1.0.0.xml", - "dsf-extension-process-authorization-organization-practitioner-1.0.0.xml", - "dsf-extension-process-authorization-parent-organization-role-1.0.0.xml", - "dsf-extension-process-authorization-parent-organization-role-practitioner-1.0.0.xml", - "dsf-coding-process-authorization-local-all-1.0.0.xml", - "dsf-coding-process-authorization-local-all-practitioner-1.0.0.xml", - "dsf-coding-process-authorization-local-organization-1.0.0.xml", - "dsf-coding-process-authorization-local-organization-practitioner-1.0.0.xml", - "dsf-coding-process-authorization-local-parent-organization-role-1.0.0.xml", - "dsf-coding-process-authorization-local-parent-organization-role-practitioner-1.0.0.xml", - "dsf-coding-process-authorization-remote-all-1.0.0.xml", - "dsf-coding-process-authorization-remote-organization-1.0.0.xml", - "dsf-coding-process-authorization-remote-parent-organization-role-1.0.0.xml"), + List.of("dsf-activity-definition-2.0.0.xml", "dsf-extension-process-authorization-2.0.0.xml", + "dsf-extension-process-authorization-practitioner-2.0.0.xml", + "dsf-extension-process-authorization-organization-2.0.0.xml", + "dsf-extension-process-authorization-organization-practitioner-2.0.0.xml", + "dsf-extension-process-authorization-parent-organization-role-2.0.0.xml", + "dsf-extension-process-authorization-parent-organization-role-practitioner-2.0.0.xml", + "dsf-coding-process-authorization-local-all-2.0.0.xml", + "dsf-coding-process-authorization-local-all-practitioner-2.0.0.xml", + "dsf-coding-process-authorization-local-organization-2.0.0.xml", + "dsf-coding-process-authorization-local-organization-practitioner-2.0.0.xml", + "dsf-coding-process-authorization-local-parent-organization-role-2.0.0.xml", + "dsf-coding-process-authorization-local-parent-organization-role-practitioner-2.0.0.xml", + "dsf-coding-process-authorization-remote-all-2.0.0.xml", + "dsf-coding-process-authorization-remote-organization-2.0.0.xml", + "dsf-coding-process-authorization-remote-parent-organization-role-2.0.0.xml"), List.of("dsf-read-access-tag-1.0.0.xml", "dsf-organization-role-1.0.0.xml", "dsf-practitioner-role-1.0.0.xml", "dsf-process-authorization-1.0.0.xml"), List.of("dsf-read-access-tag-1.0.0.xml", "dsf-organization-role-1.0.0.xml", diff --git a/dsf-fhir/dsf-fhir-validation/src/test/java/dev/dsf/fhir/profiles/QuestionnaireResponseProfileTest.java b/dsf-fhir/dsf-fhir-validation/src/test/java/dev/dsf/fhir/profiles/QuestionnaireResponseProfileTest.java index b1b580ab9..67257f929 100644 --- a/dsf-fhir/dsf-fhir-validation/src/test/java/dev/dsf/fhir/profiles/QuestionnaireResponseProfileTest.java +++ b/dsf-fhir/dsf-fhir-validation/src/test/java/dev/dsf/fhir/profiles/QuestionnaireResponseProfileTest.java @@ -7,12 +7,15 @@ import java.util.UUID; import org.hl7.fhir.r4.model.BooleanType; +import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.DateTimeType; import org.hl7.fhir.r4.model.DateType; import org.hl7.fhir.r4.model.DecimalType; +import org.hl7.fhir.r4.model.Extension; import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.IntegerType; import org.hl7.fhir.r4.model.QuestionnaireResponse; +import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseStatus; import org.hl7.fhir.r4.model.Reference; import org.hl7.fhir.r4.model.StringType; import org.hl7.fhir.r4.model.TimeType; @@ -35,188 +38,268 @@ public class QuestionnaireResponseProfileTest @ClassRule public static final ValidationSupportRule validationRule = new ValidationSupportRule( - List.of("dsf-questionnaire-response-1.0.0.xml"), List.of(), List.of()); + List.of("dsf-questionnaire-response-2.0.0.xml", "dsf-extension-questionnaire-authorization-2.0.0.xml"), + List.of("dsf-practitioner-role-1.0.0.xml"), List.of("dsf-practitioner-role-1.0.0.xml")); private final ResourceValidator resourceValidator = new ResourceValidatorImpl(validationRule.getFhirContext(), validationRule.getValidationSupport()); @Test - public void testQuestionnaireResponseValidTypeString() + public void testQuestionnaireResponseInProgressValidTypeString() { - testQuestionnaireResponseValidType(new StringType("foo")); + QuestionnaireResponse r = createQuestionnaireResponseInProgress(new StringType("foo")); + testQuestionnaireResponse(r); + + Extension auth = r.addExtension() + .setUrl("http://dsf.dev/fhir/StructureDefinition/extension-questionnaire-authorization"); + auth.addExtension("practitioner", new Identifier().setSystem("http://dsf.dev/sid/practitioner-identifier") + .setValue("practitioner1@organization.com")); + auth.addExtension("practitioner", new Identifier().setSystem("http://dsf.dev/sid/practitioner-identifier") + .setValue("practitioner2@organization.com")); + auth.addExtension("practitioner-role", + new Coding().setSystem("http://dsf.dev/fhir/CodeSystem/practitioner-role").setCode("DIC_USER")); + auth.addExtension("practitioner-role", + new Coding().setSystem("http://organization.com/fhir/CodeSystem/my-role").setCode("SOMETHING")); + testQuestionnaireResponse(r); + + r.getAuthor().setType("Practitioner").getIdentifier().setSystem("http://dsf.dev/sid/practitioner-identifier") + .setValue("practitioner@organization.com"); + testQuestionnaireResponse(r); } @Test - public void testQuestionnaireResponseValidTypeInteger() + public void testQuestionnaireResponseCompletedValidTypeString() { - testQuestionnaireResponseValidType(new IntegerType(-1)); + testQuestionnaireResponse(createQuestionnaireResponseCompleted(new StringType("foo"), null), 2); + + QuestionnaireResponse r = createQuestionnaireResponseCompleted(new StringType("foo"), + "practitioner@organization.com"); + testQuestionnaireResponse(r); + + Extension auth = r.addExtension() + .setUrl("http://dsf.dev/fhir/StructureDefinition/extension-questionnaire-authorization"); + auth.addExtension("practitioner", new Identifier().setSystem("http://dsf.dev/sid/practitioner-identifier") + .setValue("practitioner@organization.com")); + testQuestionnaireResponse(r); } @Test - public void testQuestionnaireResponseValidTypeDecimal() + public void testQuestionnaireResponseInProgressValidTypeInteger() { - testQuestionnaireResponseValidType(new DecimalType(-1)); + QuestionnaireResponse r = createQuestionnaireResponseInProgress(new IntegerType(-1)); + + r.getAuthor().setType("Practitioner").getIdentifier().setSystem("http://dsf.dev/sid/practitioner-identifier") + .setValue("practitioner@organization.com"); + + testQuestionnaireResponse(r); } @Test - public void testQuestionnaireResponseValidTypeBoolean() + public void testQuestionnaireResponseInProgressValidTypeDecimal() { - testQuestionnaireResponseValidType(new BooleanType(false)); + QuestionnaireResponse r = createQuestionnaireResponseInProgress(new DecimalType(-1)); + testQuestionnaireResponse(r); } @Test - public void testQuestionnaireResponseValidTypeDate() + public void testQuestionnaireResponseInProgressValidTypeBoolean() { - testQuestionnaireResponseValidType(new DateType("1900-01-01")); + QuestionnaireResponse r = createQuestionnaireResponseInProgress(new BooleanType(false)); + testQuestionnaireResponse(r); } @Test - public void testQuestionnaireResponseValidTypeTime() + public void testQuestionnaireResponseInProgressValidTypeDate() { - testQuestionnaireResponseValidType(new TimeType("00:00:00")); + QuestionnaireResponse r = createQuestionnaireResponseInProgress(new DateType("1900-01-01")); + testQuestionnaireResponse(r); } @Test - public void testQuestionnaireResponseValidTypeDateTime() + public void testQuestionnaireResponseInProgressValidTypeTime() { - testQuestionnaireResponseValidType(new DateTimeType("1900-01-01T00:00:00.000Z")); + QuestionnaireResponse r = createQuestionnaireResponseInProgress(new TimeType("00:00:00")); + testQuestionnaireResponse(r); } @Test - public void testQuestionnaireResponseValidTypeUri() + public void testQuestionnaireResponseInProgressValidTypeDateTime() { - testQuestionnaireResponseValidType(new UriType("http://example.de/foo")); + QuestionnaireResponse r = createQuestionnaireResponseInProgress(new DateTimeType("1900-01-01T00:00:00.000Z")); + testQuestionnaireResponse(r); } @Test - public void testQuestionnaireResponseValidTypeReference() + public void testQuestionnaireResponseInProgressValidTypeUri() { - testQuestionnaireResponseValidType(new Reference("Observation/foo")); + QuestionnaireResponse r = createQuestionnaireResponseInProgress(new UriType("http://example.de/foo")); + testQuestionnaireResponse(r); } @Test - public void testQuestionnaireResponseValidTypeReferenceWithBusinessKey() + public void testQuestionnaireResponseInProgressValidTypeReference() { - testQuestionnaireResponseValidTypeWithBusinessKey(new Reference("Observation/foo")); + QuestionnaireResponse r = createQuestionnaireResponseInProgress(new Reference("Observation/foo")); + testQuestionnaireResponse(r); } - private void testQuestionnaireResponseValidTypeWithBusinessKey(Type type) + @Test + public void testQuestionnaireResponseInProgressValidTypeReferenceWithBusinessKey() { - QuestionnaireResponse res = createQuestionnaireResponseWithBusinessKey(type); - testQuestionnaireResponse(res); + QuestionnaireResponse r = createQuestionnaireInProgressResponseWithBusinessKey( + new Reference("Observation/foo")); + testQuestionnaireResponse(r); } - private void testQuestionnaireResponseValidType(Type type) + private void testQuestionnaireResponse(QuestionnaireResponse r) { - QuestionnaireResponse res = createQuestionnaireResponse(type); - testQuestionnaireResponse(res); + testQuestionnaireResponse(r, 0); } - private void testQuestionnaireResponse(QuestionnaireResponse res) + private void testQuestionnaireResponse(QuestionnaireResponse r, int numberOfExpectedErrors) { - ValidationResult result = resourceValidator.validate(res); + ValidationResult result = resourceValidator.validate(r); result.getMessages().stream().map(m -> m.getLocationString() + " " + m.getLocationLine() + ":" + m.getLocationCol() + " - " + m.getSeverity() + ": " + m.getMessage()).forEach(logger::info); - assertEquals(0, result.getMessages().stream().filter(m -> ResultSeverityEnum.ERROR.equals(m.getSeverity()) - || ResultSeverityEnum.FATAL.equals(m.getSeverity())).count()); + assertEquals(numberOfExpectedErrors, + result.getMessages().stream().filter(m -> ResultSeverityEnum.ERROR.equals(m.getSeverity()) + || ResultSeverityEnum.FATAL.equals(m.getSeverity())).count()); } + // TODO: activate after HAPI validator is fixed: https://github.com/hapifhir/org.hl7.fhir.core/issues/193 + // @Test + // public void testQuestionnaireResponseInvalidType() + // { + // QuestionnaireResponse res = createValidQuestionnaireResponse(new + // Coding().setSystem("http://system.foo").setCode("code")); + // + // ValidationResult result = resourceValidator.validate(res); + // result.getMessages().stream() + // .map(m -> m.getLocationString() + " " + m.getLocationLine() + ":" + m.getLocationCol() + " - " + // + m.getSeverity() + ": " + m.getMessage()).forEach(logger::info); + // + // assertEquals(1, result.getMessages().stream() + // .filter(m -> ResultSeverityEnum.ERROR.equals(m.getSeverity()) || ResultSeverityEnum.FATAL.equals( + // m.getSeverity())).count()); + // } + @Test - public void testQuestionnaireResponseInvalidType() + public void testQuestionnaireResponseValidCompletedAuthorOrganization() { - // TODO: activate after HAPI validator is fixed: https://github.com/hapifhir/org.hl7.fhir.core/issues/193 + QuestionnaireResponse r = createQuestionnaireResponseInProgress(new StringType("foo")); + r.setStatus(QuestionnaireResponse.QuestionnaireResponseStatus.COMPLETED); + r.setAuthored(new Date()); + r.setAuthor(new Reference().setType("Organization").setIdentifier( + new Identifier().setSystem("http://dsf.dev/sid/organization-identifier").setValue("foo.de"))); - // QuestionnaireResponse res = createValidQuestionnaireResponse(new - // Coding().setSystem("http://system.foo").setCode("code")); - // - // ValidationResult result = resourceValidator.validate(res); - // result.getMessages().stream() - // .map(m -> m.getLocationString() + " " + m.getLocationLine() + ":" + m.getLocationCol() + " - " - // + m.getSeverity() + ": " + m.getMessage()).forEach(logger::info); - // - // assertEquals(1, result.getMessages().stream() - // .filter(m -> ResultSeverityEnum.ERROR.equals(m.getSeverity()) || ResultSeverityEnum.FATAL.equals( - // m.getSeverity())).count()); + testQuestionnaireResponse(r); } @Test - public void testQuestionnaireResponseValidCompleted() + public void testQuestionnaireResponseValidCompletedAuthorPractitioner() { - QuestionnaireResponse res = createQuestionnaireResponse(new StringType("foo")); - res.setStatus(QuestionnaireResponse.QuestionnaireResponseStatus.COMPLETED); - res.setAuthored(new Date()); - res.setAuthor(new Reference().setIdentifier( - new Identifier().setSystem("http://dsf.dev/sid/organization-identifier").setValue("foo.de"))); + QuestionnaireResponse r = createQuestionnaireResponseInProgress(new StringType("foo")); + r.setStatus(QuestionnaireResponse.QuestionnaireResponseStatus.COMPLETED); + r.setAuthored(new Date()); + r.setAuthor(new Reference().setType("Practitioner").setIdentifier(new Identifier() + .setSystem("http://dsf.dev/sid/practitioner-identifier").setValue("practitioner@foo.de"))); + + testQuestionnaireResponse(r); + } - ValidationResult result = resourceValidator.validate(res); + @Test + public void testQuestionnaireResponseInvalidCompletedNoAuthorAndNoAuthored() + { + QuestionnaireResponse r = createQuestionnaireResponseInProgress(new StringType("foo")); + r.setStatus(QuestionnaireResponse.QuestionnaireResponseStatus.COMPLETED); + + ValidationResult result = resourceValidator.validate(r); result.getMessages().stream().map(m -> m.getLocationString() + " " + m.getLocationLine() + ":" + m.getLocationCol() + " - " + m.getSeverity() + ": " + m.getMessage()).forEach(logger::info); - assertEquals(0, result.getMessages().stream().filter(m -> ResultSeverityEnum.ERROR.equals(m.getSeverity()) - || ResultSeverityEnum.FATAL.equals(m.getSeverity())).count()); + assertEquals(2, result.getMessages().stream() + .filter(m -> ResultSeverityEnum.ERROR.equals(m.getSeverity()) + || ResultSeverityEnum.FATAL.equals(m.getSeverity())) + .filter(m -> m.getMessage() != null) + .filter(m -> m.getMessage().startsWith("Constraint failed: authored-if-completed-or-amended") + || m.getMessage().startsWith("Constraint failed: author-if-completed-or-amended")) + .count()); } @Test - public void testQuestionnaireResponseInvalidCompletedNoAuthorAndNoAuthored() + public void testQuestionnaireResponseInvalidAmendedNoAuthorAndNoAuthored() { - QuestionnaireResponse res = createQuestionnaireResponse(new StringType("foo")); - res.setStatus(QuestionnaireResponse.QuestionnaireResponseStatus.COMPLETED); + QuestionnaireResponse r = createQuestionnaireResponseInProgress(new StringType("foo")); + r.setStatus(QuestionnaireResponse.QuestionnaireResponseStatus.AMENDED); - ValidationResult result = resourceValidator.validate(res); + ValidationResult result = resourceValidator.validate(r); result.getMessages().stream().map(m -> m.getLocationString() + " " + m.getLocationLine() + ":" + m.getLocationCol() + " - " + m.getSeverity() + ": " + m.getMessage()).forEach(logger::info); - assertEquals(2, - result.getMessages().stream() - .filter(m -> ResultSeverityEnum.ERROR.equals(m.getSeverity()) - || ResultSeverityEnum.FATAL.equals(m.getSeverity())) - .filter(m -> m.getMessage() != null) - .filter(m -> m.getMessage().startsWith("Constraint failed: authored-if-completed") - || m.getMessage().startsWith("Constraint failed: author-if-completed")) - .count()); + assertEquals(2, result.getMessages().stream() + .filter(m -> ResultSeverityEnum.ERROR.equals(m.getSeverity()) + || ResultSeverityEnum.FATAL.equals(m.getSeverity())) + .filter(m -> m.getMessage() != null) + .filter(m -> m.getMessage().startsWith("Constraint failed: authored-if-completed-or-amended") + || m.getMessage().startsWith("Constraint failed: author-if-completed-or-amended")) + .count()); } @Test public void testQuestionnaireResponseInvalidCompletedWithAuthorReferenceAndAuthored() { - QuestionnaireResponse res = createQuestionnaireResponse(new StringType("foo")); - res.setStatus(QuestionnaireResponse.QuestionnaireResponseStatus.COMPLETED); - res.setAuthored(new Date()); - res.setAuthor(new Reference("Organization/" + UUID.randomUUID().toString())); + QuestionnaireResponse r = createQuestionnaireResponseInProgress(new StringType("foo")); + r.setStatus(QuestionnaireResponse.QuestionnaireResponseStatus.COMPLETED); + r.setAuthored(new Date()); + r.setAuthor(new Reference("Organization/" + UUID.randomUUID().toString())); - ValidationResult result = resourceValidator.validate(res); + ValidationResult result = resourceValidator.validate(r); result.getMessages().stream().map(m -> m.getLocationString() + " " + m.getLocationLine() + ":" + m.getLocationCol() + " - " + m.getSeverity() + ": " + m.getMessage()).forEach(logger::info); - assertEquals(1, - result.getMessages().stream() - .filter(m -> ResultSeverityEnum.ERROR.equals(m.getSeverity()) - || ResultSeverityEnum.FATAL.equals(m.getSeverity())) - .filter(m -> m.getMessage() != null) - .filter(m -> m.getMessage().startsWith("Constraint failed: author-if-completed")).count()); + assertEquals(1, result.getMessages().stream() + .filter(m -> ResultSeverityEnum.ERROR.equals(m.getSeverity()) + || ResultSeverityEnum.FATAL.equals(m.getSeverity())) + .filter(m -> m.getMessage() != null) + .filter(m -> m.getMessage().startsWith("Constraint failed: author-if-completed-or-amended")).count()); } - private QuestionnaireResponse createQuestionnaireResponseWithBusinessKey(Type type) + private QuestionnaireResponse createQuestionnaireInProgressResponseWithBusinessKey(Type type) { - QuestionnaireResponse res = createQuestionnaireResponse(type); - res.addItem().setLinkId("business-key").setText("The business-key of the process execution").addAnswer() + QuestionnaireResponse r = createQuestionnaireResponseInProgress(type); + r.addItem().setLinkId("business-key").setText("The business-key of the process execution").addAnswer() .setValue(new StringType(UUID.randomUUID().toString())); - return res; + return r; + } + + private QuestionnaireResponse createQuestionnaireResponseCompleted(Type type, String practitionerIdentifierValue) + { + QuestionnaireResponse r = createQuestionnaireResponseInProgress(type); + r.setStatus(QuestionnaireResponseStatus.COMPLETED); + + if (practitionerIdentifierValue != null) + { + r.setAuthored(new Date()); + r.getAuthor().setType("Practitioner").getIdentifier() + .setSystem("http://dsf.dev/sid/practitioner-identifier").setValue(practitionerIdentifierValue); + } + + return r; } - private QuestionnaireResponse createQuestionnaireResponse(Type type) + private QuestionnaireResponse createQuestionnaireResponseInProgress(Type type) { - QuestionnaireResponse res = new QuestionnaireResponse(); - res.getMeta().addProfile("http://dsf.dev/fhir/StructureDefinition/questionnaire-response"); - res.setQuestionnaire("http://dsf.dev/fhir/Questionnaire/hello-world|0.1.0"); - res.setStatus(QuestionnaireResponse.QuestionnaireResponseStatus.INPROGRESS); - res.addItem().setLinkId("user-task-id").setText("The user-task-id of the process execution").addAnswer() + QuestionnaireResponse r = new QuestionnaireResponse(); + r.getMeta().addProfile("http://dsf.dev/fhir/StructureDefinition/questionnaire-response"); + r.setQuestionnaire("http://dsf.dev/fhir/Questionnaire/hello-world|0.1.0"); + r.setStatus(QuestionnaireResponse.QuestionnaireResponseStatus.INPROGRESS); + r.addItem().setLinkId("user-task-id").setText("The user-task-id of the process execution").addAnswer() .setValue(new StringType("1")); - res.addItem().setLinkId("valid-display").setText("valid-display"); - res.addItem().setLinkId("valid-answer").setText("valid answer").addAnswer().setValue(type); + r.addItem().setLinkId("valid-display").setText("valid-display"); + r.addItem().setLinkId("valid-answer").setText("valid answer").addAnswer().setValue(type); - return res; + return r; } } diff --git a/dsf-fhir/dsf-fhir-validation/src/test/java/dev/dsf/fhir/profiles/TaskProfileTest.java b/dsf-fhir/dsf-fhir-validation/src/test/java/dev/dsf/fhir/profiles/TaskProfileTest.java index e14cf9f54..b43daa79d 100755 --- a/dsf-fhir/dsf-fhir-validation/src/test/java/dev/dsf/fhir/profiles/TaskProfileTest.java +++ b/dsf-fhir/dsf-fhir-validation/src/test/java/dev/dsf/fhir/profiles/TaskProfileTest.java @@ -41,7 +41,14 @@ public class TaskProfileTest @Test public void testTaskValidRequestedWithoutBusinessKey() { - Task task = createTask(Task.TaskStatus.REQUESTED); + Task task = createTaskForOrganizationRequester(Task.TaskStatus.REQUESTED); + testTaskValid(task); + } + + @Test + public void testTaskValidRequestedWithoutBusinessKeyForPractitioner() + { + Task task = createTaskForPractitionerRequester(Task.TaskStatus.REQUESTED); testTaskValid(task); } @@ -62,7 +69,7 @@ public void testTaskValidInProgressWithBusinessKey() @Test public void testTaskInvalidInProgressMissingBusinessKey() { - Task task = createTask(Task.TaskStatus.INPROGRESS); + Task task = createTaskForOrganizationRequester(Task.TaskStatus.INPROGRESS); testTaskInvalidMissingBusinessKey(task); } @@ -76,7 +83,7 @@ public void testTaskValidCompletedWithBusinessKey() @Test public void testTaskInvalidCompletedMissingBusinessKey() { - Task task = createTask(Task.TaskStatus.COMPLETED); + Task task = createTaskForOrganizationRequester(Task.TaskStatus.COMPLETED); testTaskInvalidMissingBusinessKey(task); } @@ -90,19 +97,37 @@ public void testTaskValidFailedWithBusinessKey() @Test public void testTaskInvalidFailedMissingBusinessKey() { - Task task = createTask(Task.TaskStatus.FAILED); + Task task = createTaskForOrganizationRequester(Task.TaskStatus.FAILED); testTaskInvalidMissingBusinessKey(task); } private Task createTaskWithBusinessKey(Task.TaskStatus status) { - Task task = createTask(status); + Task task = createTaskForOrganizationRequester(status); task.addInput().setValue(new StringType(UUID.randomUUID().toString())).getType().addCoding() .setSystem("http://dsf.dev/fhir/CodeSystem/bpmn-message").setCode("business-key"); return task; } + private Task createTaskForOrganizationRequester(Task.TaskStatus status) + { + Task task = createTask(status); + task.getRequester().setType(ResourceType.Organization.name()).getIdentifier() + .setSystem("http://dsf.dev/sid/organization-identifier").setValue("Test_DIC"); + + return task; + } + + private Task createTaskForPractitionerRequester(Task.TaskStatus status) + { + Task task = createTask(status); + task.getRequester().setType(ResourceType.Practitioner.name()).getIdentifier() + .setSystem("http://dsf.dev/sid/practitioner-identifier").setValue("foo@org.com"); + + return task; + } + private Task createTask(Task.TaskStatus status) { Task task = new Task(); @@ -111,8 +136,6 @@ private Task createTask(Task.TaskStatus status) task.setStatus(status); task.setIntent(Task.TaskIntent.ORDER); task.setAuthoredOn(new Date()); - task.getRequester().setType(ResourceType.Organization.name()).getIdentifier() - .setSystem("http://dsf.dev/sid/organization-identifier").setValue("Test_DIC"); task.getRestriction().addRecipient().setType(ResourceType.Organization.name()).getIdentifier() .setSystem("http://dsf.dev/sid/organization-identifier").setValue("Test_DIC"); diff --git a/dsf-maven/dsf-maven-plugin/pom.xml b/dsf-maven/dsf-maven-plugin/pom.xml index 4df720c21..ea919db7a 100644 --- a/dsf-maven/dsf-maven-plugin/pom.xml +++ b/dsf-maven/dsf-maven-plugin/pom.xml @@ -113,6 +113,9 @@ org.apache.maven.plugins maven-plugin-plugin + + ${maven.compiler.target} + helpmojo diff --git a/dsf-maven/dsf-maven-plugin/src/main/java/dev/dsf/maven/dev/Cert.java b/dsf-maven/dsf-maven-plugin/src/main/java/dev/dsf/maven/dev/Cert.java index 657c41df8..71b213116 100644 --- a/dsf-maven/dsf-maven-plugin/src/main/java/dev/dsf/maven/dev/Cert.java +++ b/dsf-maven/dsf-maven-plugin/src/main/java/dev/dsf/maven/dev/Cert.java @@ -17,6 +17,7 @@ public static enum Type } private String cn; + private String email; private List sans; private Type type; private List targets; @@ -26,6 +27,11 @@ public String getCn() return cn; } + public String getEmail() + { + return email; + } + public List getSans() { return sans; @@ -44,8 +50,9 @@ public List getTargets() @Override public String toString() { - return "Cert [" + (cn != null ? "cn=" + cn + ", " : "") + (sans != null ? "sans=" + sans + ", " : "") - + (type != null ? "type=" + type + ", " : "") + (targets != null ? "targets=" + targets : "") + "]"; + return "Cert [" + (cn != null ? "cn=" + cn + ", " : "") + (email != null ? "email=" + email + ", " : "") + + (sans != null ? "sans=" + sans + ", " : "") + (type != null ? "type=" + type + ", " : "") + + (targets != null ? "targets=" + targets : "") + "]"; } public CertificationRequestConfig toCertificationRequestConfig() @@ -59,6 +66,6 @@ public CertificationRequestConfig toCertificationRequestConfig() default -> throw new IllegalArgumentException("Unexpected value: " + type); }; - return new CertificationRequestConfig(signer, cn, sans == null ? List.of() : sans); + return new CertificationRequestConfig(signer, cn, email, sans == null ? List.of() : sans); } } diff --git a/dsf-maven/dsf-maven-plugin/src/main/java/dev/dsf/maven/dev/CertificateGenerator.java b/dsf-maven/dsf-maven-plugin/src/main/java/dev/dsf/maven/dev/CertificateGenerator.java index 76bea89cb..f54c7a2ac 100755 --- a/dsf-maven/dsf-maven-plugin/src/main/java/dev/dsf/maven/dev/CertificateGenerator.java +++ b/dsf-maven/dsf-maven-plugin/src/main/java/dev/dsf/maven/dev/CertificateGenerator.java @@ -33,6 +33,7 @@ import de.hsheilbronn.mi.utils.crypto.ca.CertificateAuthority; import de.hsheilbronn.mi.utils.crypto.ca.CertificationRequest; import de.hsheilbronn.mi.utils.crypto.ca.CertificationRequest.CertificationRequestAndPrivateKey; +import de.hsheilbronn.mi.utils.crypto.ca.CertificationRequest.CertificationRequestBuilder; import de.hsheilbronn.mi.utils.crypto.io.PemReader; import de.hsheilbronn.mi.utils.crypto.io.PemWriter; import de.hsheilbronn.mi.utils.crypto.keypair.KeyPairValidator; @@ -60,26 +61,32 @@ public static record CertificateAndPrivateKey(X509Certificate certificate, Priva public static record CertificationRequestConfig( BiFunction signer, String commonName, - List dnsNames) + String mail, List dnsNames) { public CertificationRequestConfig( BiFunction signer, String commonName, - String... dnsNames) + String mail, String... dnsNames) { - this(signer, commonName, List.of(dnsNames)); + this(signer, commonName, mail, List.of(dnsNames)); } public CertificationRequestConfig( - BiFunction signer, String commonName) + BiFunction signer, String commonName, + String mail) { - this(signer, commonName, List.of()); + this(signer, commonName, mail, List.of()); } public CertificateAndPrivateKey sign(CertificateAuthority ca) { - CertificationRequestAndPrivateKey req = CertificationRequest + CertificationRequestBuilder reqBuilder = CertificationRequest .builder(ca, SUBJECT_C, null, null, SUBJECT_O, null, commonName).generateKeyPair() - .setDnsNames(dnsNames).build(); + .setDnsNames(dnsNames); + + if (mail != null && !mail.isBlank()) + reqBuilder.setEmail(mail); + + CertificationRequestAndPrivateKey req = reqBuilder.build(); X509Certificate crt = signer.apply(ca, req); @@ -97,7 +104,7 @@ public CertificateAndPrivateKey sign(CertificateAuthority ca) public static final String SUBJECT_CN_ISSUING_CA = "DSF Dev Issuing CA"; private static final CertificationRequestConfig CERTIFICATION_REQUEST_ISSUING_CA = new CertificationRequestConfig( - CertificateAuthority::signClientServerIssuingCaCertificate, SUBJECT_CN_ISSUING_CA); + CertificateAuthority::signClientServerIssuingCaCertificate, SUBJECT_CN_ISSUING_CA, null); private final Path certDir; private final char[] privateKeyPassword; diff --git a/pom.xml b/pom.xml index e10d5e64a..4ce6788e4 100755 --- a/pom.xml +++ b/pom.xml @@ -523,7 +523,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.11.3 + 3.12.0 org.apache.maven.plugins @@ -630,6 +630,7 @@ Webbrowser Test User + webbrowser.test.user@invalid CLIENT cert/Webbrowser_Test_User.p12
        IDStatusQuestionnaireBusiness-KeyLast Updated
        IDStatusQuestionnaireAuthorBusiness-KeyLast Updated
        id in-progress questionnaireauthor businesKey lastUpdated