22.6.1Master Data Management User Interface (MDMUI)
Premium

 
MDM v2.0 will be licensed, 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.

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).

22.6.1.1MDM 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:

  • Any action performed by a user in the UI will create an Audit Event, this can support any privacy and security concerns
  • A Task resource will be created to track each POSSIBLE_MATCH and POSSIBLE_DUPLICATE link created by MDM that requires workflow management

22.6.2Setup

 

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.

22.6.2.1Add 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

22.6.2.2Add 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
  • NOTE: Before v2023.11 - Set interceptor bean type to -- ca.cdr.endpoint.mdm.ui.interceptor.MDMInterceptorContext --.
  • Start module

22.6.2.3Configure FHIR Storage R4 RDBMS (persistence) module:

  • Click on FHIR MDM Server in the On this page sidebar on the right of the page to navigate to MDM mode setting. Enable MDM mode (this will allow MDM module to work).
  • Click on FHIR Subscription Persistence in the On this page sidebar on the right side of the page to navigate to Subscription setting. Enable Message 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.

22.6.2.4Configure SMART outbound module (smart_auth):

  • Click on SMART Authorization in the On this page sidebar. Disable Enforce approved scopes to restrict permissions.
  • Click on Cross-Origin Resource Sharing in the On this page sidebar. Enable CORS to future MDMUI app port (cannot use * here due to logout URL constraints) (ie. http://localhost:8080).
  • For ease of use and to differentiate paths, recommended to change:
    • HTTP Listener > Context Path -- change to /smartauth
    • OpenID Connect (OIDC) > Issuer URL -- add /smartauth to the end
  • In the SMART Callback Script section, 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.

22.6.2.5Configure JSON Admin API (admin_json) endpoint

  • For ease of use and to differentiate paths, recommended to change:
    • HTTP Listener > Context Path -- change to /json-admin
  • Go to the Cross-Origin Resource Sharing (CORS) section. Enable CORS to * (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.

22.6.2.6Configure FHIR REST endpoint

  • Add module FHIR REST Endpoint (All FHIR Versions) - NOTE: On a fresh install, this is already available and configured - this step can be skipped
  • For ease of use and to differentiate paths, recommended to change:
    • HTTP Listener > Context Path -- change to /fhir-request
  • Set value for Fixed Value for Endpoint Base URL to the externally visible URL for this endpoint ( e.g. http://localhost:8000/fhir-request or https://<base URL>/fhir-request/)
  • Under HTTP Listener configure a Listener Port (any port not in use e.g. 8000) - NOTE: On a fresh install, this is already available and configured - this step can be skipped
  • Enable OpenID Connect Security
  • Enable CORS to * 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

22.6.2.7Configure OIDC client

  • Under Users & Authorization OpenID Connect Clients, use the Add Client button 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, and JWT 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 Client to create the OIDC client.

22.6.2.8Add 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 /smartauth if 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

22.6.2.9Add External IdP (Optional - if using SMART outbaund without federated mode, skip to next step)

  • Toggle Federated OAuth2/OIDC Login in smart_auth module;
  • Add client for external IdP under 'Users & Authorization' > 'OpenID Connect clients'...
    • Add field for Client ID and Client Name
    • Toggle Enabled to Yes
    • Add for 'Authorized Grant Types' the three options Authorization Code, Refresh Token, and JWT Bearer Token
    • Add Authorized Redirect URLs (ie. http://localhost:8080/)
    • Toggle Client Secrets to Yes
    • 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 Scopes to Yes
  • 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

22.6.2.10Use 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"

22.6.2.11Post 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. The script is based off of checking the username - the two common uses are ADMIN and DATA STEWARD. It is recommended to customize based on how the user role may be checked.

function onTokenGenerating(theUserSession, theAuthorizationRequestDetails) {
    var userName = theUserSession.username;
    Log.info('User Name : ' + userName);
    theUserSession.addAuthority('FHIR_WRITE_ALL_OF_TYPE', 'AuditEvent');
    theUserSession.addAuthority('FHIR_WRITE_ALL_OF_TYPE', 'Task');
    theUserSession.addAuthority('ACCESS_ADMIN_JSON');
     theUserSession.addAuthority('UPDATE_USER');
     theUserSession.addAuthority('VIEW_USERS');
    if (userName === 'DATA_STEWARD') {
        theUserSession.addAuthority('ROLE_MDMUI_DATASTEWARD_FHIR');
        theUserSession.addAuthority('VIEW_MODULE_CONFIG_FOR_MODULE', 'Master/mdm');
        theAuthorizationRequestDetails.addAccessTokenClaim('user_role', 'data_steward');
    } else if (userName === 'ADMIN') {
        theUserSession.addAuthority('FHIR_EXTENDED_OPERATION_ON_SERVER', '$mdm-evaluate');
        theUserSession.addAuthority('ROLE_MDMUI_ADMIN_FHIR');
        theUserSession.addAuthority('MODULE_ADMIN_FOR_MODULE', 'Master/mdm');
        theUserSession.addAuthority('VIEW_BATCH_JOBS');
        theAuthorizationRequestDetails.addAccessTokenClaim('user_role', 'admin');
    }
}

  • NOTE: [Updated - for any release after November 2024 Pre-24, please use the script above this bullet point] for onAuthenticateSuccess function, 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);
    });
}