# access control for openEHR 'resources' **Category:** [Implementation](https://discourse.openehr.org/c/implem/39) **Created:** 2024-04-26 11:00 UTC **Views:** 735 **Replies:** 27 **URL:** https://discourse.openehr.org/t/access-control-for-openehr-resources/5144 --- ## Post #1 by @joostholslag continuation of: https://discourse.openehr.org/t/karolinska-stockholm-procurement-of-digital-health-platform-cdr-tools-services-consultants/4457/17?u=joostholslag @Daniel.Alomar @birger.haarbrandt @erik.sundvall @tangit86 @sebastian.iancu and @Sidharth_Ramesh are probably interested. Background: Currently access control has limited support in the openEHR spec itself. The only thing there is an [EHR_ACCESS class](https://specifications.openehr.org/releases/RM/latest/ehr.html#_ehr_access_class), which has an abstract ACCESS_CONTROL_SETTINGS which is unconstrained and lives at the EHR level. So basically a place to record access control rules on the specific EHR in any format. And there is a general statement that [the composition is the atomic unit of commit](https://specifications.openehr.org/releases/BASE/latest/architecture_overview.html#_top_level_information_structures). And ["An `ENTRY` is also the minimal unit of information any query should return"](https://specifications.openehr.org/releases/RM/latest/ehr.html#_entry_class) . Thus you should only set read access at the entry level (not smaller) and only write access if you can edit (and thus read) the entire composition. As described in the [SMART on openEHR](https://specifications.openehr.org/releases/ITS-REST/latest/smart_app_launch.html) there are many more [scopes](https://specifications.openehr.org/releases/ITS-REST/latest/smart_app_launch.html#_scopes_for_openehr_rest_api) that are relevant to define access control for: e.g. doctors can create compositions that are instances of template composition.acp, or nurses can execute stored AQL with name "patient_history". etc. This is a generic topic to share requirements and solutions and architectural thoughts. My specific requirement is how to define access policies to openEHR resource expression (templates, specific composition etc as defined in smart on openEHR spec) for a network of care organisations, without a single source of trust. E.g. all doctors in the netword can create composition.ACP instances. [More info](https://github.com/jorritspee/openEHRxNuts/issues/11#issuecomment-2079018841): --- ## Post #2 by @joostholslag [quote="birger.haarbrandt, post:20, topic:4457, full:true"] @joostholslag please keep us in the loop how things are going for OPAL. Our architects made an evaluation and then decided to go with XACML 3.0 instead of OPAL, but I don’t have the details. [/quote] Word on the street is XACML is dead. (source listed in GitHub issue) I'm curious for the considerations of your architects. --- ## Post #3 by @birger.haarbrandt the rumors did not spread to Germany and basically all IHE XDS implementations are based on it. So maybe a bit dated but well tested ;) --- ## Post #4 by @joostholslag At the risk of deviating from the topic: how do you see the relation between XDS and openEHR? XDS only works in a network is present, this is not feasible for my usecase. Also xacml isn’t human readable imho, so it’s not a good fit for what I’m trying to do. But interesting nonetheless. Do you have an example xacml policy for access to openehr data? Does xaxml allows policies to be defined by an external artefact like a yaml file? --- ## Post #5 by @birger.haarbrandt I think openEHR and XDS deserves its own discussion. Depending on the use-case they can be complements, but openEHR can also replace XDS (though when we talk about distributed querying, you will end up with similar patterns with record locators/registries etc). I think you will need to put some domain specific language on top of any access control technology to make it user friendly for administrators in healthcare. XACML is surely verbose. So while we are doing this directly on XACML for now, we could also think about some higher abstraction to make this a bit more digestible (or build custom UIs). I will check for examples, though not sure if I'm allowed to share publicly for now. --- ## Post #6 by @ian.mcnicoll XDS and openEHR work really well together -see Slovenia for a prime example. @borut.fabjan is the expert here. We are using his XDS archetype in the UK to tag all compositions with at least a 'document type' code to allow discovery via XDS or the English record locator service. https://ckm.apperta.org/ckm/archetypes/1051.32.672 https://discourse.openehr.org/t/ihe-and-openehr/1278 --- ## Post #7 by @joostholslag [quote="birger.haarbrandt, post:5, topic:5144"] I think you will need to put some domain specific language on top of any access control technology to make it user friendly for administrators in healthcare. [/quote] This is exactly what I’m trying to achieve. My current idea is to expres the openEHR resource scope in a json schema. Then an access policy will be a json/yaml instance confirming to the schema. Rego/opa seem able to link variables and data in a generic policy to json data based on json schema. Thus to set a specific policy, the editor needs to edit the json. Now if there’s a json schema available it should be trivial to create a ui for that. Now if XACML can do the same for the same json instance and json schema, we have a ‘dsl’ and policies that are actually machine interpretable and executable in an implementation (rego/xacml) agnostic way and are ui editable and human readable. that sounds really exciting to me. My gut feeling says it’s also very doable, but the proof of the pudding.. --- ## Post #8 by @thomas.beale [quote="joostholslag, post:1, topic:5144"] And there is a general statement that [the composition is the atomic unit of commit](https://specifications.openehr.org/releases/BASE/latest/architecture_overview.html#_top_level_information_structures). And [“An `ENTRY` is also the minimal unit of information any query should return”](https://specifications.openehr.org/releases/RM/latest/ehr.html#_entry_class) . [/quote] We should modify that a bit to say that an Entry is the minimal unit of information that should be returned outside of a querying or other retrieval context that already knows the context information of the Entry/ies in question. For example, obtaining just the systolic pressures + times via a query that establishes that they are coming from Observations based on OBSERVATION.blood_pressure, created in the last 3 month (usually via comparing /context/start_time in the WHERE clause) is perfectly sound, because the query establishes the context and domain type (the OBS.blood_pressure). What is not sound is plucking out just an ELEMENT containing a Quantity containing a systolic pressure, because you don't know if you are getting it from an OBS or say, an EVALUATION that contains a target BP. I think everyone understands this intuitively, but may be worth stating more clearly in the specs. --- ## Post #9 by @joostholslag I agree it’s fine to be more selective in the query than the entry. But regarding the access control, you need access to the entire composition (or definitely to the entry level) to be able to correctly interpret the result of the query and to build the right filters in the where clause. E.g. in order to guarantee the correctness of a systolic bloodpressure, you probably should check the diastolic bloodpressure in that entry in that composition is lower than the systolic. To guarantee access to other elements in the entry, we shouldn’t allow access control at element level. --- ## Post #10 by @joostholslag FYI: here’s a nice blog, discussion and upcoming meetup about access control in FHIR: https://www.linkedin.com/posts/andrejcgasper_fhir-healthit-digitalhealth-activity-7188450301970898948-NhPf?utm_source=share&utm_medium=member_ios --- ## Post #11 by @chunlan.ma The topic below is also relevant: https://discourse.openehr.org/t/security-privacy-and-patient-identity/2380/10 --- ## Post #12 by @joostholslag One of the questions I have is whether it's useful to define AQL as a scope. My feeling is it's only useful to set read access at the 'entry' level. Because doing a smaller read authorisation violates the specification that entry is the smallest unit a query should return. And assuming the maximum scope of an AQL is a single entry (and containing compositions) this should be functionally equivalent. If access should be definable on a combination of archetypes, we need this anyways. My gut feeling is also that it will be really convenient to be able to authorise AQLs. So I'm curious about your thoughts. --- ## Post #13 by @birger.haarbrandt Apologies if I missed a nuance of what you are saying. One of the issues is that entry-level is not sufficient because it won't allow you to filter for terminologies. Classic example will be lab values, diagnoses, medications etc., where you will likely depend on whitelisting/blacklisting defined codes. In reality, we also have many implementers going way more granular than entry level (as Tom explained, we sometimes can assume the context well enough or it does not matter for a particular use-case). --- ## Post #14 by @damoca This week I attended to an HL7 webinar about FHIR Access Control. We are all facing the same problems, since this is a complex topic. They are working around several proposal that might be of interest for openEHR. You can see the videos of the webinar here: https://www.youtube.com/playlist?list=PLEOOqZS1NtwalGOditsQzRxwlw53aPwZE --- ## Post #15 by @joostholslag My main point was that if a user is allowed to read one element in an entry, they must be allowed to see all the others in that entry. But interesting point with lab data. It might be valid access policy that a nurse should be able to see obs.lab test result for eg HbA1c (diabetes) but not for ANCA (Rheuma). But I may incline to enforce that at the composition instance level access policy. --- ## Post #16 by @birger.haarbrandt Thanks for clarification, I agree that showing (or not showing) the whole entry (based on a more granular criterium) is likely applicable. And yes, it might also depend on your role or association to a healthcare organization what data you might be able to see. --- ## Post #17 by @thomas.beale [quote="damoca, post:14, topic:5144"] You can see the videos of the webinar here: https://www.youtube.com/playlist?list=PLEOOqZS1NtwalGOditsQzRxwlw53aPwZE [/quote] Does there exist any summary or documents of that? Finding 2h to burn on a video is not easy these days! --- ## Post #18 by @thomas.beale [quote="joostholslag, post:15, topic:5144"] My main point was that if a user is allowed to read one element in an entry, they must be allowed to see all the others in that entry. [/quote] Is there anything wrong with querying a lab panel and extracting just the serum K and not the other analytes? It's the context that matters. If your client application only asks for serum K (maybe over time), then it presumably knows what it's doing... --- ## Post #19 by @ian.mcnicoll Couple of things... It is generally good advice that a query should at least take account of te ENTRY context, but that is different from saying that the SELECT should only return ENTRY-level data. I don't think that is what is being argued but it might be mis-understood. Thee will be many use-cases for simple Resultsets at Element level Thisis a good example #### AQL ```sql SELECT w/data[at0001]/items[at0002]/value/value AS Safeguarding_status FROM EHR e [ehrId/value = :ehrId] CONTAINS COMPOSITION c[openEHR-EHR-COMPOSITION.report.v1] CONTAINS EVALUATION w[openEHR-EHR-EVALUATION.safeguarding_concern_uk.v0] WHERE c/name/value='OL - Safeguarding' AND w/data[at0001]/items[at0002]/value/defining_code/code_string = '1010196001' AND w/data[at0001]/items[at0002]/value/defining_code/terminologyId = 'SNOMED-CT' ``` If a match is found, in Ehrscape AQL returns.... ```JSON [ { "compositionId": "79ce9194-0907-4224-9240-3a8f35892742::11ed4f4e-6a7f-4849-a03c-2c41333a15ec::1", "safeguardingStatusDateTime": "2024-02-20T13:32:47.887Z", "safeguardingStatusText": "Safeguarding concern" } ] ``` The other issue is that while, in theory, we might need to apply highly granular access controls, in practice this becomes really tricky, not just in terms of applying the rules technically but in humanly managing them. I agree this is an area where we really want to be involved in those wider FHIR/IHE conversations, and I think at the last f2f SEC meeting there was I think broad consensus that the EHR_ACCESS placeholder was probably not the place to hold access information. [quote="joostholslag, post:15, topic:5144"] But interesting point with lab data. It might be valid access policy that a nurse should be able to see obs.lab test result for eg HbA1c (diabetes) but not for ANCA (Rheuma). [/quote] That is an example of where at some level, this might seem respectful of a patient's privacy need but my feeling now is that trying to meet this level of expectation (rarely required or wanted by the majority) is just going to tie us in knots. Better to work at composition level in the main, with occasional exceptions. This is already having a bit of design impact on the way we put together template compositions trying to keep the scope of the content fairly tight. e.g .do not mix housing information with safeguarding information, even through both are 'social circumstances'. Better to use 2 different templates/compositions. --- ## Post #20 by @pablo [quote="thomas.beale, post:18, topic:5144"] It’s the context that matters. If your client application only asks for serum K (maybe over time), then it presumably knows what it’s doing… [/quote] We need to differentiate the end user that an application provides information too, from the application that extracts information from the repository. The application has an extra context that each individual query can't access, but the user interface already has, so the user, if the application is well designed, already has the context. Considering this, the RBAC or any other solution for checking permissions can be at different levels: in the repository itself, between the repository and the application, and in the application. If we get too restrictive and fine grained at the repository, we will affect performance, and might not have all the information the application or the Middleware has in order to check fine grained permissions. So IMO saying openEHR CDRs could support permission checking at the entry level, the any data point retrieved should be checked against is container entry permissions. But if we add permissions at the element level, it can get ugly fast. It's also good to say to " at least check permissions at the composition level", again each element retrieved should be checked against its container compo permisions. --- ## Post #21 by @damoca None that I'm aware of. Videos 2 to 5 were the presentations, that might be the most interesting (just 1h 15m :sweat_smile:). The rest of the time were discussions. --- ## Post #22 by @joostholslag [quote="thomas.beale, post:18, topic:5144"] Is there anything wrong with querying a lab panel and extracting just the serum K and not the other analytes? [/quote] No, there isn’t. But there is something wrong with an access policy that lets you query lab result analyte value, but not lab result point of care test. [quote="ian.mcnicoll, post:19, topic:5144"] It is generally good advice that a query should at least take account of te ENTRY context, but that is different from saying that the SELECT should only return ENTRY-level data. I don’t think that is what is being argued but it might be mis-understood. [/quote] Sorry if I expressed myself badly. I indeed argue for the first, not the second. --- ## Post #23 by @joostholslag [quote="ian.mcnicoll, post:19, topic:5144"] That is an example of where at some level, this might seem respectful of a patient’s privacy need but my feeling now is that trying to meet this level of expectation (rarely required or wanted by the majority) is just going to tie us in knots. Better to work at composition level in the main, with occasional exceptions. This is already having a bit of design impact on the way we put together template compositions trying to keep the scope of the content fairly tight. e.g .do not mix housing information with safeguarding information, even through both are ‘social circumstances’. Better to use 2 different templates/compositions. [/quote] Yes, I’m trying to argue for limiting acces control scope at the composition level and the entry level (depending on context like read/write, AQL/GET composition, others?). Which is different from the current smart on openEHR spec, which supports authorising specific AQLs. I also recognise there are niche use cases where more granular acces control is implemented. But I’d like to update the spec (both the smart and the entry and the ehr access) to make general recommendations. (Currently I’m only in the orientation phase, I may sound convinced of a position, but this is mostly to challenge to show me where I’m wrong. Already I’m confined there should be exceptions allowed). And also yes, I think we should take these access control principles into account when designing archetypes and templates. I’ve found in designing templates for a (problem-goal-action) care plan this adds some complexity initially. Because it forces you to split it at a composition (instance) per problem (and tying those together using an indexing compositon with DV_EHR_URIs). But in the longer term I’m convinced it adds many benefits, other than just access control. --- ## Post #24 by @ian.mcnicoll [quote="joostholslag, post:22, topic:5144"] Sorry if I expressed myself badly. I indeed argue for the first, not the second. [/quote] It is actually the specs that are potentially a bit misleading here, not you!! [quote="joostholslag, post:23, topic:5144"] Yes, I’m trying to argue for limiting acces control scope at the composition level and the entry level (depending on context like read/write, AQL/GET composition, others?). Which is different from the current smart on openEHR spec, which supports authorising specific AQLs. [/quote] I think there is room for all of these, including AQL authorisation, but I agree life is a hack of a lot easier if we can largely use compositions as the basis for access control, especially for create/update/delete. --- ## Post #25 by @pablo [quote="joostholslag, post:23, topic:5144"] Yes, I’m trying to argue for limiting acces control scope at the composition level and the entry level (depending on context like read/write, AQL/GET composition, others?). Which is different from the current smart on openEHR spec, which supports authorising specific AQLs. [/quote] I also think access control / permission checking should be limited by component (we need to define what a component is), and consider the whole architecture to provide a full solution, while defining the scope for the openEHR specs (we might not be able to define a full access control architecture). When I say components, an example could be: - end user application - middleware (stuff in the middle of the end user app and the openEHR API implementation) - openEHR API - normal GET methods - query - POST/PUT/DELETE methods - repository / backend / archive (stuff behind the API) Then we can say, at the end user application we should be able to say "this user, role or group doesn't have access to this patient or EHR" or "has access" or "has access under certain conditions", or "has access to partial information, like compositions of certain types or with certain setting code", or "has access to information defined by certain archetype (compo, entry, cluster, element)". We know the application could just do all the filtering since it knows the user, it's role/group, etc. but should also know openEHR. That will leave all the scope for RBAC outside openEHR. Even if the checks and filters are done in the middleware. Then we can put some of the checks at the API level, there the API needs to know more about the user/role/group and might be able to keep the openEHR functionality and the RBAC checks quite separated, but we need to add those checks to the spec somewhere. Lastly if the RBAC is at the CDR / backend, we might need a full RM for that, how that is constrained by ADL, and how permission are checked, so those are also fully modeled and managed by openEHR. My personal preference would be to avoid the backend level and try to not depend on access permissions at the CDR / backend. Another personal preference would be to make the RBAC totally independent from the RM, and have a strict spec we can replicate in different implementations, even if we actually use different systems for implementing those checks. Though I'm not sure how much of openEHR the permission checking will need to implement the rules I mentioned above. Anyway, I would like to analyze requirements and start documenting something concrete that can serve for further discussion and definition. Maybe the SMART thing is enough and I might be over complicating things, or maybe not, hehe --- ## Post #26 by @joostholslag some nice first steps to define an access policy to openEHR data in OPA/Rego: https://github.com/jorritspee/openEHRxNuts/issues/11#issuecomment-2228703660 --- ## Post #27 by @joshua.grisham Hi folks! I stumbled on this thread and thought to at least share some of the things that we at Karolinska University Hospital have found in case it helps to further any part of this for anyone. First if you are using Kubernetes I would STRONGLY recommend using [OPA kube-mgmt](https://github.com/open-policy-agent/kube-mgmt) instead of OPAL as it is lightyears more simple -- you just create ConfigMaps with your rego scripts and the right label, then your policies will be picked up by the service. Deploy these ConfigMap resources however you normally do (e.g. with ArgoCD, Flux, etc). Keep in mind that the name needs to be completely unique, so we usually prefix the name with our namespace's name. With OPAL you need a database and lots of other stuff that (IMO) just make it way more complex and non-Kubernetes-feeling ;) Then when it comes to actual policies... I am not exactly sure where we will go in the future (as we are moving things around a bit...), but what we have done in the past is create some policies for the REST APIs themselves. You can think of it more like an implementation of [SMART on FHIR](https://build.fhir.org/ig/HL7/smart-app-launch/app-launch.html) / [SMART on openEHR](https://specifications.openehr.org/releases/ITS-REST/development/smart_app_launch.html) where it is limiting access to certain HTTP methods based on the scopes that exist in the requestor's token. Here is an example of a FHIR API policy for the `Patient` resource just to give a taste: ``` default allow = false allow { create_patient } allow { read_patient } allow { search_patient } read_patient { regex.match("/Patient/[A-Za-z0-9-]{36}$", input.request.http.path) input.request.http.method == "GET" scope[_] in ["user/Patient.rs", "system/Patient.rs"] } create_patient { regex.match("/Patient$", input.request.http.path) input.request.http.method == "POST" # Here we can also put a check on the body and/or existence and format of headers like "if-none-exist" etc scope[_] in ["user/Patient.c", "system/Patient.c"] } search_patient { regex.match("/Patient/_search$", input.request.http.path) input.request.http.method == "POST" # Here we can also put a check on the body and/or existence and format of headers like "content-type" etc regex.match("^application/x-www-form-urlencoded$", input.request.http.headers["content-type"]) scope[_] in ["user/Patient.rs", "system/Patient.rs"] } ... ``` Note that this isn't 100% our "real" policy but it hopefully gives a rough idea. Basically this policy has a semi-opinionated view of how we wanted to allow usage of the Patient resources in our FHIR API. We have done similar with the openEHR API.. here is a small example of part of it: ``` default allow = false allow { read_ehr } allow { write_ehr } read_ehr { # match on /ehr, /ehr?*, /ehr#*, /ehr/{uuid}, /ehr/{uuid}?*, /ehr/{uuid}#* # especially that we do not want to match /ehr/{uuid}/* as that pattern will be for other rules (compositions, contributions, etc) regex.match("/ehr(($|[?#])|(\/[A-z\\-0-9]{0,}($|[?#])))", input.request.http.path) input.request.http.method == "GET" scope[_] in ["user/openehr-ehr.rs", "system/openehr-ehr.rs"] } write_ehr { # match on /ehr, /ehr?*, /ehr#*, /ehr/{uuid}, /ehr/{uuid}?*, /ehr/{uuid}#* # especially that we do not want to match /ehr/{uuid}/* as that pattern will be for other rules (compositions, contributions, etc) regex.match("/ehr(($|[?#])|(\/[A-z\\-0-9]{0,}($|[?#])))", input.request.http.path) input.request.http.method in ["POST", "PUT"] scope[_] in ["user/openehr-ehr.cu", "system/openehr-ehr.cu"] } ... ``` Note that the above scope names were before the SMART on openEHR proposal was created but hopefully this gives at an idea of what we have done so far (at least with the REST API). It should be possible to in theory evaluate any input data; for example if you needed some kind of "gateway" service that needed to evaluate the response body from the openEHR API against something within the users' context (their token or data from an external source) -- the more complicated part there is how to package both incoming response and other external data together and how you want to treat/attempt to standardize all of the various external systems or data sources that could be used there, I guess? --- ## Post #28 by @joostholslag This is really interesting @joshua.grisham. I’m presenting our work at the annual conference in the UK tomorrow. Are you there by any chance, so we can discuss? https://conference.openehr.org/?page_id=865 --- **Canonical:** https://discourse.openehr.org/t/access-control-for-openehr-resources/5144 **Original content:** https://discourse.openehr.org/t/access-control-for-openehr-resources/5144