# Master Data Management User Interface (MDMUI) [Premium](/content/docs/hdp_product/premium_solutions.html)

⚠️ 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](/content/docs/guide_tutorials/mdm_web_ui_admin_setup.html).

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 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](/content/docs/configuration_categories/fhir_configuration.html#property-seed-base-validation-resources/index.html)**. 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 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, we recommend setting:
  - 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](/content/docs/configuration_categories/security_out_smart_callback.html#property-post-authorization-script-text/index.html)** the following script: **[Post Authorization Text Script JS Script](/content/docs/guide_tutorials/tutorial_mdm_ui.html#post-authorization-text-script-js-script/index.html)**
- **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`
- 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.

## 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`
- 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)
- 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

## Configure 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.

## 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 `/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

## Add 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`

## 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](/content/docs/security/roles_and_permissions.html).

```javascript

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

```javascript
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 Tutorial](/content/docs/guide_tutorials/tutorial_mdm.html)

MDM UI Tutorial

Tutorials

[Introduction](/content/docs/guide_tutorials/tutorial_intro.html)
[Integration Testing Tutorial](/content/docs/guide_tutorials/tutorial_integration_test.html)
[Custom Operations Tutorial](/content/docs/guide_tutorials/tutorial_custom_operations.html)
[HL7 v2.x Ingestion Tutorial](/content/docs/guide_tutorials/tutorial_processing_hl7_v2x_feeds.html)
[Federated OAuth2/OIDC Tutorial](/content/docs/guide_tutorials/tutorial_federated_oauth2.html)
[MDM Tutorial](/content/docs/guide_tutorials/tutorial_mdm.html)
[MDM UI Tutorial](/content/docs/guide_tutorials/tutorial_mdm_ui.html)
[MDM First Golden Resource Tutorial](/content/docs/guide_tutorials/first_golden_resource.html)
[MDM Advanced Rule Authoring Tutorial](/content/docs/guide_tutorials/advanced_rule_authoring.html)
[MDM Web UI Admin Setup Tutorial](/content/docs/guide_tutorials/mdm_web_ui_admin_setup.html)
[CDA Import Tutorial](/content/docs/guide_tutorials/tutorial_cda_import.html)
[CDA Export Tutorial](/content/docs/guide_tutorials/tutorial_cda_export.html)
[CDA Export - Custom Narrative Generation Tutorial](/content/docs/guide_tutorials/tutorial_cda_export_narrative_generation.html)
[CDA Import JSON example](/content/docs/guide_tutorials/tutorial_cda_import_json.html)
[CDA Export Template JSON example](/content/docs/guide_tutorials/tutorial_cda_export_json.html)
[Consent Tutorial](/content/docs/guide_tutorials/tutorial_consent.html)
[CMS-0057-F IG Setup Tutorial](/content/docs/guide_tutorials/tutorial_terminology_cms57f.html)

[MDM First Golden Resource Tutorial](/content/docs/guide_tutorials/first_golden_resource.html)
