MDM UI Tutorial - Smile CDR Documentation
Master Data Management User Interface (MDMUI) Premium
⚠️ This guide assumes that MDM is already installed and configured. It focuses on enabling a user-facing interface to review and manage potential matches and duplicates.
MDM v2.0 will require a license, effective May 2024 Release onwards. MDM UI requires MDM as a dependency.
MDMUI is a user-facing application designed to make it easy for administrators to accept or refute links created by MDM (Master Data Management). It includes the ability to search for a particular resource, view a list of all potential matches and potential duplicates, and assign tasks to users for workload management.
For a more detailed step-by-step walkthrough of the same setup, including creating data steward user accounts, see Setting Up the MDM Web UI for Data Stewards.
MDMUI currently supports FHIR R4 endpoints only.
NOTE: if you are using a reverse / web proxy, please see step 10 at the very bottom (last step).
MDM UI Resources
MDM UI will create FHIR resources for its own workflow management, these will be tagged with the tag https://smilecdr.com/fhir/ns/mdm/ui/NamingSystem/tags|MDM_UI.
It will create new resources in the following scenarios:
- Since MDM UI actions can involve inspecting patient data to distinguish matches, these actions are all audited. All action performed by a user in the UI will create an Audit Event to support privacy and security management activities.
- A Task resource will be created to track each POSSIBLE_MATCH and POSSIBLE_DUPLICATE link created by MDM that requires workflow management
Setup
MDM UI depends on having certain components already configured in Smile. Please follow these steps to configure a fresh Smile install to work with MDMUI.
Add Subscription Matcher module
- NOTE: On a fresh install, this is already available and configured - this step can be skipped
- Add module of type Subscription Matcher (All FHIR Versions)
- Select dependency FHIR Storage module as FHIR Storage (R4 RDBMS)
- Create and start module
Add MDM Module
- Add module of type mdm
- Set scripts for matching and survivorship -- NOTE: this is not required for setting up MDM, but is required for creating Golden records and match rules
- Select dependency Subscription Matcher from step 1 above
- Start module
Configure FHIR Storage R4 RDBMS (persistence) module:
- Click on
FHIR MDM Serverin theOn this pagesidebar on the right of the page to navigate to MDM mode setting. EnableMDM mode(this will allow MDM module to work). - Click on
FHIR Subscription Persistencein theOn this pagesidebar on the right side of the page to navigate to Subscription setting. EnableMessage Subscription. - NOTE: Starting Smile CDR v2024.02, enable Seed Base Validation resources. This will seed resources like
StructureDefinition, which will be used by the front application. - Save and restart persistence module.
Configure SMART outbound module (smart_auth):
- Click on
SMART Authorizationin theOn this pagesidebar. DisableEnforce approved scopesto restrict permissions. - Click on
Cross-Origin Resource Sharingin theOn this pagesidebar. EnableCORSto future MDMUI app port (cannot use * here due to logout URL constraints) (ie. http://localhost:8080). - For ease of use and to differentiate paths, we recommend setting:
- HTTP Listener > Context Path -- change to
/smartauth - OpenID Connect (OIDC) > Issuer URL -- add
/smartauthto the end
- HTTP Listener > Context Path -- change to
- In the
SMART Callback Scriptsection, add to the Post Authorization Script Text the following script: Post Authorization Text Script JS Script - NOTE: If adding an external IdP, also see step 9 below.
- NOTE: We currently only support SMART Inbound in federated mode.
- Save and restart module.
Configure JSON Admin API (admin_json) endpoint
- For ease of use and to differentiate paths, we recommend setting:
- HTTP Listener > Context Path -- change to
/json-admin
- HTTP Listener > Context Path -- change to
- Go to the
Cross-Origin Resource Sharing (CORS)section. EnableCORSto*(for testing purposes) or MDMUI app url (ie. http://localhost:8080). - In dependencies, enable
OpenID Connect Security. - Add dependency for OpenID Connect Authentication to smart_auth
- Save and restart module.
Configure FHIR REST endpoint
- Add module FHIR REST Endpoint (All FHIR Versions)
- For ease of use and to differentiate paths, we recommend setting:
- HTTP Listener > Context Path -- change to
/fhir-request
- HTTP Listener > Context Path -- change to
- Set value for Fixed Value for Endpoint Base URL to the externally visible URL for this endpoint (
e.g.
http://localhost:8000/fhir-requestorhttps://<base URL>/fhir-request/) - Under HTTP Listener configure a Listener Port (any port not in use e.g. 8000)
- Enable
OpenID Connect Security - Enable
CORSto * or MDM UI port (ie. http://localhost:8080) - Add dependency for OpenID Connect Authentication to smart_auth
- Add dependency for FHIR Storage Module (any FHIR version) to persistence (FHIR Storage (R4 RDBMS))
- Start module
Configure OIDC client
- Under Users & Authorization OpenID Connect Clients, use the
Add Clientbutton to add a new OIDC client - Enter a Client ID and Client Name of your choice (e.g. "MDM_UI")
- Select Authorized Grant Types:
Authorization Code,Refresh Token, andJWT Bearer Token - Set Authorized Redirect URLs to the base URL for your new MDM UI instance, e.g.
http://localhost:8080/or other port/context path of your choice (NOTE: this requires the context path as well, if one has been set --http://localhost:8080/mdm-ui/-- this context path can be set in step 8 when setting up MDM UI Module) - Set Scopes to:
cdr_all_user_authorities``launch/patient``launch/practitioner``offline_access``openid``profile - Click on
Create Clientto create the OIDC client.
Add MDM UI Module
- Add module of type MDM UI
- Set Organization Identifier - a new Organization resource will be created if one does not exist with the given
system|identifier - Set JSON admin URL (JSON admin endpoint, ie. if using localhost then
http://localhost:9000/json-admin) - Set OIDC client ID (should match Client ID of OIDC client created in step 7, ie.
MDM_UI) - Set issuer url (this is your issuer url where the application will retrieve the token, ie. if smart auth configured on port 9200 with context path /smartauth, then use
http://localhost:9200/smartauth) - Set redirect URL of MDM UI application (ie. if you set port 8080 and context path /mdm-ui/ then use
http://localhost:8080/mdm-ui/- this is also set in OIDC Client) - Set logout url (this is your smartauth URL, remember to set
/smartauthif this was the context path set) for smart auth with the correct logout url (ie.http://localhost:9200/smartauth/logout?cb=none&revoke=token&revoke=token_refresh) - Set HTTP Listener > Listener Port (e.g.
8080) - should match the redirect URL of the OIDC client and any CORS settings configured above - NOTE: Recommended to also set context path (ie.
/mdm-ui/) -- this should match the redirect URL above, and the OIDC Client Authorized Redirect URLS saved - Select all dependencies
- Start module
Add External IdP (Optional - if using SMART outbaund without federated mode, skip to next step)
- Toggle
Federated OAuth2/OIDC Loginin smart_auth module; - Add client for external IdP under 'Users & Authorization' > 'OpenID Connect clients'...
- Add field for Client ID and Client Name
- Toggle
EnabledtoYes - Add for 'Authorized Grant Types' the three options
Authorization Code,Refresh Token, andJWT Bearer Token - Add Authorized Redirect URLs (ie.
http://localhost:8080/) - Toggle
Client SecretstoYes - Generate a secret and copy value - this will go to the server in the next step so keep it available
- In 'SMART Scopes' field 'Scopes', add the following
cdr_all_user_authorities``launch/patient``launch/practitioner``offline_access``openid profile - Toggle
Remember User Approved ScopestoYes
- Add server for external IdP under 'Users & Authorization' > 'OpenID Connect Servers'...
- Add a Name
- Add Issuer url (ie. Keycloak would use
<base Url>/keycloak/auth/realms/master) - Add the 'Client ID' from the Client just created
- Add the 'Client Secret' from the Secret you just generated in the Client above
- Add the relevant fields to the 'Federated OAuth2/OIDC Login' portion, see keycloak example below:
- Registration ID - alphanumeric number
- Authorization URL -
<base Url>/keycloak/auth/realms/master/protocol/openid-connect/auth - Token URL -
<base Url>/keycloak/auth/realms/master/protocol/openid-connect/token - UserInfo URL -
<base Url>/keycloak/auth/realms/master/protocol/openid-connect/userinfo - JWKSet URL -
<base Url>/keycloak/auth/realms/master/protocol/openid-connect/certs - Request Scopes -
openid profile
Use the MDM UI app
- You should now be ready to view MDM UI at the port and context path specified in step 7 above! ( ie. if using local host, then use http://localhost:8080/mdm-ui/).
- If you are using a web proxy to expose ports externally you will need to configure the following endpoints for
external access:
- MDM UI port and context path
- FHIR endpoint
- Smart outbound security module
- JSON Admin API
- You will also need to set in Smart_auth module
- HTTP Listener
- Enable "Respect Forward Headers"
- Enable "HTTPS Forwarding Assume"
Post Authorization Text Script JS Script
- NOTE: Updated for November 2024 Pre-25 release - for any releases prior to that, please see the next bullet point. This sample script uses a simple check for hard-coded username - the two common uses are ADMIN and DATA STEWARD. We recommend you use directory services (LDAP, OIDC, etc.) to implement these mappings in a production environment.
The following permissions are required for proper MDM UI operation. Please see the full list at Smile CDR Roles & Permissions.
function onTokenGenerating(theUserSession, theAuthorizationRequestDetails) {
var userName = theUserSession.username;
Log.info('User Name : ' + userName);
// Allows writing AuditEvent resources, which are used to log user actions in MDM UI (e.g., match decisions)
theUserSession.addAuthority('FHIR_WRITE_ALL_OF_TYPE', 'AuditEvent');
// Allows writing Task resources, used by MDM UI to track review actions (e.g., POSSIBLE_MATCH or DUPLICATE review tasks)
theUserSession.addAuthority('FHIR_WRITE_ALL_OF_TYPE', 'Task');
// Grants access to the Admin JSON API, which MDM UI uses to fetch module configuration and metadata
theUserSession.addAuthority('ACCESS_ADMIN_JSON');
// Allows updating user records via internal Admin UI (if applicable)
theUserSession.addAuthority('UPDATE_USER');
// Allows viewing the list of users via internal Admin UI
theUserSession.addAuthority('VIEW_USERS');
if (userName === 'DATA_STEWARD') {
// Enables Data Steward mode in the MDM UI, allowing users to resolve MDM match links
theUserSession.addAuthority('ROLE_MDMUI_DATASTEWARD_FHIR');
// Allows viewing the MDM module configuration (but not editing it)
theUserSession.addAuthority('VIEW_MODULE_CONFIG_FOR_MODULE', 'Master/mdm');
// Adds a claim to the access token indicating the role of the user, used by the frontend UI
theAuthorizationRequestDetails.addAccessTokenClaim('user_role', 'data_steward');
} else if (userName === 'ADMIN') {
// Allows executing the $mdm-evaluate operation to re-run MDM matching logic on demand
theUserSession.addAuthority('FHIR_EXTENDED_OPERATION_ON_SERVER', '$mdm-evaluate');
// Enables Admin mode in the MDM UI, which includes access to config pages and job visibility
theUserSession.addAuthority('ROLE_MDMUI_ADMIN_FHIR');
// Grants full administrative privileges for the MDM module, including modifying match rules and scripts
theUserSession.addAuthority('MODULE_ADMIN_FOR_MODULE', 'Master/mdm');
// Allows viewing all batch jobs in the system, such as match evaluation runs
theUserSession.addAuthority('VIEW_BATCH_JOBS');
// Adds a claim to the access token indicating the role of the user, used by the frontend UI
theAuthorizationRequestDetails.addAccessTokenClaim('user_role', 'admin');
}
}
Copy
- NOTE: [Updated - for any release after November 2024 Pre-24, please use the script above this bullet point] for
onAuthenticateSuccessfunction, update username for admin to any username for user added that you want to be an admin; you can also add an else if condition for data stewards.
function onTokenGenerating(theUserSession, theAuthorizationRequestDetails, theClientDetails) {
const userRole = theUserSession.getUserData('user_role');
Log.info('User Role : ' + userRole);
theAuthorizationRequestDetails.addAccessTokenClaim('user_role', userRole);
}
function onAuthenticateSuccess(theOutcome, theOutcomeFactory, theContext) {
const userRole = theContext.getClaim('user_role');
if (userRole === 'data_steward') {
addDataStewardAuthorities(theOutcome);
} else if (userRole === 'admin') {
addAdminAuthorities(theOutcome);
}
theOutcome.setUserData('user_role', userRole);
return theOutcome;
}
function addDataStewardAuthorities(outcome) {
addCommonAuthorities(outcome);
const readResourceTypes = ['CodeSystem'];
const readAndWriteResourceTypes = ['AuditEvent', 'Task'];
addReadAuthorities(outcome, readResourceTypes);
addReadAndWriteAuthorities(outcome, readAndWriteResourceTypes);
}
function addAdminAuthorities(outcome) {
addCommonAuthorities(outcome);
const readAndWriteResourceTypes = ['AuditEvent', 'Task'];
const readResourceTypes = ['SearchParameter', 'CodeSystem'];
addReadAndWriteAuthorities(outcome, readAndWriteResourceTypes);
addReadAuthorities(outcome, readResourceTypes);
addSmileSpecificAuthorities(outcome);
}
function addCommonAuthorities(outcome) {
const commonResourceTypes = ['Patient', 'Practitioner', 'StructureDefinition', 'Organization', 'PractitionerRole'];
addReadAndWriteAuthorities(outcome, commonResourceTypes);
addSmileSpecificAuthorities(outcome);
}
function addReadAndWriteAuthorities(outcome, resourceTypes) {
resourceTypes.forEach(resourceType => {
outcome.addAuthority('FHIR_READ_ALL_OF_TYPE', resourceType);
outcome.addAuthority('FHIR_WRITE_ALL_OF_TYPE', resourceType);
});
}
function addReadAuthorities(outcome, resourceTypes) {
resourceTypes.forEach(resourceType => {
outcome.addAuthority('FHIR_READ_ALL_OF_TYPE', resourceType);
});
}
function addSmileSpecificAuthorities(outcome) {
const smileSpecificAuthorities = [\
'FHIR_OP_MDM_DUPLICATE_GOLDEN_RESOURCES',\
'FHIR_OP_MDM_LINK_HISTORY',\
'FHIR_OP_MDM_MERGE_GOLDEN_RESOURCES',\
'FHIR_OP_MDM_NOT_DUPLICATE',\
'FHIR_OP_MDM_QUERY_LINKS',\
'FHIR_OP_MDM_UPDATE_LINK',\
'ACCESS_ADMIN_JSON',\
'CHANGE_OWN_DEFAULT_LAUNCH_CONTEXTS',\
'VIEW_BATCH_JOBS',\
'VIEW_MODULE_CONFIG',\
'VIEW_USERS',\
];
smileSpecificAuthorities.forEach(authority => {
outcome.addAuthority(authority);
});
}
Copy
MDM UI Tutorial
Tutorials
Introduction Integration Testing Tutorial Custom Operations Tutorial HL7 v2.x Ingestion Tutorial Federated OAuth2/OIDC Tutorial MDM Tutorial MDM UI Tutorial MDM First Golden Resource Tutorial MDM Advanced Rule Authoring Tutorial MDM Web UI Admin Setup Tutorial CDA Import Tutorial CDA Export Tutorial CDA Export - Custom Narrative Generation Tutorial CDA Import JSON example CDA Export Template JSON example Consent Tutorial CMS-0057-F IG Setup Tutorial