Init Aidbox configuration project
It is important to syncronize directory and file name to the :ns
parameter of the configuration
Copy cd acl && touch system.edn
Populate configuration file
In the following configuration project user and client credentials are written as plain text to simplify the topic. In real life scenarios it is important to define credentials with ENVs
Copy {:ns acl.system
:import #{aidbox
aidbox.config
aidbox.rest
aidbox.rest.acl}
search-config
{:zen/tags #{aidbox.config/search}
:zen-fhir :disable
:resource-compat false
:engine :knife
:fhir-comparisons true
:default-params {:timeout 30
:total "none"
:count 100}
:chain {:subselect true}}
compatibility-config
{:zen/tags #{aidbox.config/compatibility}
:validation {:json-schema {:regex #{:fhir-datetime}}}
:auth {:pkce {:code-challenge {:s256 {:conformant true}}}}}
zen-config
{:zen/tags #{aidbox.config/config}
:search search-config
:compatibility compatibility-config
:fhir-version "4.0.1"
:compliant-mode-enabled? true
:override-createdat-url "http://fhir.aidbox.app/extension/createdat"
:correct-aidbox-format true
:disable-legacy-seed true}
seed-data
{:zen/tags #{aidbox/service}
:engine aidbox/seed-v2
:resources
{:Client {:root {:secret "secret"
:first_party true
:grant_types ["client_credentials" "basic"]}
:postman {:secret "secret"
:grant_types ["password" "basic"]}}
:Organization {:org-1 {:resourceType "Organization" :id "org-1"}
:org-2 {:resourceType "Organization" :id "org-2"}}
:User {:admin {:password "password"}
:admin-org-1 {:password "password" :organization {:resourceType "Organization" :id "org-1"}}
:user-org-1 {:organization {:resourceType "Organization" :id "org-1"}}
:user-org-2 {:organization {:resourceType "Organization" :id "org-2"}}}
:AccessPolicy {:admin-seed-policy
{:engine "allow"
:link [{:resourceType "User" :id "admin"}
{:resourceType "Client" :id "root"}]}
:org-admin-can-read-its-users
{:engine "matcho"
:roleName "OrganizationAdmin"
:matcho {:uri "#/User.+" :request-method "get"}}}
:Role {:OrganizationAdmin {:name "OrganizationAdmin"
:user {:resourceType "User" :id "admin-org-1"}}}}}
;; define additional filtering rules based
;; org-id-param value is taken from the requester user resource accroding to the :path
org-id-param
{:zen/tags #{aidbox.rest.acl/request-param}
:source-schema {:type zen/string}
:path [:user :organization :id]}
;; defined SQL statement injecting the org-id-param value
org-condition
{:zen/tags #{aidbox.rest.acl/sql-template}
:params {:org-id org-id-param}
:template "{{target-resource}}#>>'{organization,id}'={{params.org-id}}"}
;; attach injected SQL to the filter
user-filter
{:zen/tags #{aidbox.rest.acl/filter}
:expression org-condition}
;; create user-read operation & apply user-filer
user-read
{:zen/tags #{aidbox.rest/op}
:engine aidbox.rest.acl/read
:resource "User"
:format "aidbox"
:filter user-filter}
;; rebing standard User read operation
user-api
{:zen/tags #{aidbox.rest/api}
"User" {[:id] {:GET user-read}}}
;; attach api to the server
server
{:zen/tags #{aidbox/service}
:engine aidbox/http
:apis #{user-api}}
box
{:zen/tags #{aidbox/system}
:config zen-config
:services {:seed seed-data
:http server}}}
Check configuration works
Get access token for admin-org-1
user
request access_token for user admin-org-1 response status: 200
Copy POST [base-url]/auth/token
Content-Type: text/yaml
{
"client_id": "postman",
"client_secret": "secret",
"username": "admin-org-1",
"password": "password",
"grant_type": "password"
}
Copy token_type: Bearer
userinfo:
organization: { id: org-1, resourceType: Organization }
id: admin-org-1
resourceType: User
sub: admin-org-1
need_patient_banner: true
access_token: MW...Ex
Read user belonging to the requester organization
fetch allowed user data response status: 200
Copy GET [base-url]/User/user-org-1
Content-Type: text/yaml
authorization: "Bearer MW...Ex"
Copy organization: { id: org-1, resourceType: Organization }
id: user-org-1
resourceType: User
Read attempt user from the different organization
fetch allowed user data response status: 404
Copy GET [base-url]/User/user-org-2
Content-Type: text/yaml
authorization: "Bearer MW...Ex"
Copy resourceType: OperationOutcome
id: deleted
text:
status: generated
div: "Resource User/user-org-2 not found"
issue:
- severity: fatal
code: deleted
diagnostics: "Resource User/user-org-2 not found"