On this page:

7.19Consent Service

 

A common concern in building healthcare applications is ensuring that local rules are respected around who can see and change different data. These rules may take many forms. A few examples are presented below:

  • In a healthcare app, administrative users might not be allowed to look at specific details of some resources (e.g. perhaps they can access Observations but not see the values)

  • In a regional data exchange, you might want to allow individual patients to specify that their data should be restricted.

  • In a research database, you might want to prevent the release of data about a specific patient until that patient has provided consent.

  • In a patient portal, a specific user should only ever be allowed to access their own data.

The list above shows a few examples, but the overall point here is that there is a bottomless list of possible consent rules that might need to be applied.

Architecture

As described in Authorization and Consent, the Authorization Service and the Consent Service play related but distinct roles (and in fact these roles may overlap).

The following diagram shows the interplay between these two systems.

Authorization Svc

7.19.1Enabling the Consent Service

 

The Smile CDR consent service is driven by a user-supplied script that implements the specific consent rules of your application. This script can be simple or very powerful, and has the ability to make decisions based on various kinds of input.

The following methods may be implemented in a consent service script:

7.19.2Method: consentStartOperation

 

This method is called once at the start of each request. The purpose is to determine whether a request should be permitted to proceed, and whether to apply the rest of the consent service logic to the request. It may also pre-fetch any relevant data or resources that will be used to provide consent rules in subsequent method calls.

Inputs:

  • theRequestDetails – This object contains details about the FHIR request being made. The object will be of type RequestDetailsJson.
  • theUserSession – This object contains details about the authenticated user and their session. This includes demographics, permissions, approved scopes, etc. The object will be of type UserSessionDetailsJson.
  • theContextServices – This object has utility methods for interacting with the environment, and indicating a consent outcome to the rest of the framework. The object will be of type ScriptConsentContextServices.

Outputs:

  • This method does not return an output, but must indicate a response mode by calling an appropriate method on theContextServices.

Example:

function consentStartOperation(theRequestDetails, theUserSession, theContextServices) {

	// For superusers, we will skip the rest of the consent service entirely
	if (theUserSession != null && theUserSession.hasAuthority('ROLE_SUPERUSER')) {
		theContextServices.authorized();
		return;
	}

	// By default, this request should continue but still be evaluated by the service
	theContextServices.proceed();

}

7.19.3Method: consentCanSeeResource

 

This method is called once for each resource that may be returned to the user. It has the ability to authorize the release of data, or to block it, or to request that it be passed to consentWillSeeResource for masking.

Note that this method will not be called if theContextServices.authorized() was invoked by the consent script in the consentStartOperation(...) method. This method can carry a significant performance impact, so it should only be used if necessary.

Inputs:

  • theRequestDetails – This object contains details about the FHIR request being made. The object will be of type RequestDetailsJson.
  • theUserSession – This object contains details about the authenticated user and their session. This includes demographics, permissions, approved scopes, etc. The object will be of type UserSessionDetailsJson.
  • theContextServices – This object has utility methods for interacting with the environment, and indicating a consent outcome to the rest of the framework. The object will be of type ScriptConsentContextServices.
  • theResource – The resource that will be returned. The object will be a FHIR resource and can be examined using the techniques described in FHIR Model API.

Outputs:

  • This method does not return an output, but must indicate a response mode by calling an appropriate method on theContextServices.

Example:

/**
 * If present, this function is called before every resource that may be returned
 * to the user.
 *
 * @param theRequestDetails  Contains details about the request (e.g. the
 *                           FHIR operation being performed, the HTTP method,
 *                           the URL, etc.
 * @param theUserSession     Contains details about the logged in user and
 *                           their session.
 * @param theContextServices Contains various utiltity methods for acccessing
 *                           relevant information about the request, as well
 *                           as providing a response.
 * @package theResource      The resource that will be accessed
 */
function consentCanSeeResource(theRequestDetails, theUserSession, theContextServices, theResource) {
	if ( ...some condition... ) {
		theContextServices.reject();
	} else {
		theContextServices.proceed();
	}
}

7.19.4Method: consentWillSeeResource

 

This method is called once for each resource that is being returned to the user. It has the ability to mask specific elements (or even the whole resource) from being returned to the user.

Inputs:

  • theRequestDetails – This object contains details about the FHIR request being made. The object will be of type RequestDetailsJson.
  • theUserSession – This object contains details about the authenticated user and their session. This includes demographics, permissions, approved scopes, etc. The object will be of type UserSessionDetailsJson.
  • theContextServices – This object has utility methods for interacting with the environment, and indicating a consent outcome to the rest of the framework. The object will be of type ScriptConsentContextServices.
  • theResource – The resource that will be returned. The object will be a FHIR resource and can be examined using the techniques described in FHIR Model API.

Outputs:

  • This method does not return an output, but must indicate a response mode by calling an appropriate method on theContextServices.

Example:

/**
 * If present, this function is called before every resource that may be returned
 * to the user.
 *
 * @param theRequestDetails  Contains details about the request (e.g. the
 *                           FHIR operation being performed, the HTTP method,
 *                           the URL, etc.
 * @param theUserSession     Contains details about the logged in user and
 *                           their session.
 * @param theContextServices Contains various utiltity methods for acccessing
 *                           relevant information about the request, as well
 *                           as providing a response.
 * @package theResource      The resource that will be accessed
 */
function consentWillSeeResource(theRequestDetails, theUserSession, theContextServices, theResource) {
	if ( ...some condition... ) { 
	   theResource.clear('value');
	}
}

7.19.5Method: completeOperationSuccess

 

This function will be called exactly once for each request that completes successfully (i.e. returns an HTTP 2xx response). Note that in the case of failed requests (HTTP 4xx or 5xx), completeOperationFailure will be called instead.

Inputs:

  • theRequestDetails – This object contains details about the FHIR request being made. The object will be of type RequestDetailsJson.
  • theUserSession – This object contains details about the authenticated user and their session. This includes demographics, permissions, approved scopes, etc. The object will be of type UserSessionDetailsJson.
  • theContextServices – This object has utility methods for interacting with the environment, and indicating a consent outcome to the rest of the framework. The object will be of type ScriptConsentContextServices.

Outputs:

  • This method does not return an output.

Example:

/**
 * If present, this function is called when a request has been handled
 * successfully (i.e. an HTTP 2xx response)
 *
 * @param theRequestDetails  Contains details about the request (e.g. the
 *                           FHIR operation being performed, the HTTP method,
 *                           the URL, etc.
 * @param theUserSession     Contains details about the logged in user and
 *                           their session.
 * @param theContextServices Contains various utiltity methods for acccessing
 *                           relevant information about the request, as well
 *                           as providing a response.
 */
function completeOperationSuccess(theRequestDetails, theUserSession, theContextServices) {
	// some functions...
}

7.19.6Method: completeOperationFailure

 

This function will be called exactly once for each request that completes unsuccessfully (i.e. returns an HTTP 4xx or 5xx response).

Inputs:

  • theRequestDetails – This object contains details about the FHIR request being made. The object will be of type RequestDetailsJson.
  • theUserSession – This object contains details about the authenticated user and their session. This includes demographics, permissions, approved scopes, etc. The object will be of type UserSessionDetailsJson.
  • theContextServices – This object has utility methods for interacting with the environment, and indicating a consent outcome to the rest of the framework. The object will be of type ScriptConsentContextServices.

Outputs:

  • This method does not return an output.

Example:

/**
 * If present, this function is called when a request has been handled
 * unsuccessfully (i.e. an HTTP 4xx/5xx response)
 *
 * @param theRequestDetails  Contains details about the request (e.g. the
 *                           FHIR operation being performed, the HTTP method,
 *                           the URL, etc.
 * @param theUserSession     Contains details about the logged in user and
 *                           their session.
 * @param theContextServices Contains various utiltity methods for acccessing
 *                           relevant information about the request, as well
 *                           as providing a response.
 */
function completeOperationFailure(theRequestDetails, theUserSession, theContextServices) {
	// some functions...
}

7.19.7Example: Tag-Based Consent Script

 

The following example shows a simple consent script, implementing consent rules based on user permissions and resource security labels.

In this example:

  • Users with the RULE_SUPERUSER permission do not have any consent rules applied
  • Observation resources with the R (restricted) security label will have their Observation.value and Observation.note fields cleared before they are returned to the user.
  • Any resources with the V (very restricted) security label will not be shown to the user at all.
/**
 * If present, this function is called before every request. It serves two
 * primary purposes:
 *
 * 1. It can be used to proactively determine that a request does not need
 *    to have the consent service applied to it. This is good for
 *    performance, since applying the consent service has performance
 *    implications.
 * 2. It can be used to proactively load consent directives, user
 *    information, etc. that will be used in future methods.
 *
 * @param theRequestDetails  Contains details about the request (e.g. the
 *                           FHIR operation being performed, the HTTP method,
 *                           the URL, etc.
 * @param theUserSession     Contains details about the logged in user and
 *                           their session.
 * @param theContextServices Contains various utiltity methods for acccessing
 *                           relevant information about the request, as well
 *                           as providing a response.
 */
function consentStartOperation(theRequestDetails, theUserSession, theContextServices) {

	// For superusers, we will skip the rest of the consent service entirely
	if (theUserSession != null && theUserSession.hasAuthority('ROLE_SUPERUSER')) {
		theContextServices.authorized();
		return;
	}

	// By default, this request should continue but still be evaluated by the service
	theContextServices.proceed();

}

/**
 * If present, this function is called before every resource that may be returned
 * to the user.
 *
 * @param theRequestDetails  Contains details about the request (e.g. the
 *                           FHIR operation being performed, the HTTP method,
 *                           the URL, etc.
 * @param theUserSession     Contains details about the logged in user and
 *                           their session.
 * @param theContextServices Contains various utiltity methods for acccessing
 *                           relevant information about the request, as well
 *                           as providing a response.
 * @package theResource      The resource that will be accessed
 */
function consentCanSeeResource(theRequestDetails, theUserSession, theContextServices, theResource) {

	if (theResource.meta.hasSecurity('https://www.hl7.org/fhir/v3/Confidentiality/cs.html', 'V')) {

		// Any resources with a security label of "V" (very restricted) is rejected,
		// meaning that the client doesn't see them and doesn't even know that they
		// exist
		theContextServices.reject();

	} else if (theResource.meta.hasSecurity('https://www.hl7.org/fhir/v3/Confidentiality/cs.html', 'R')) {

		// Any resources with a security label of "R" (restricted) is allowed to
		// proceed, but will still pass through consentWillSeeResource(...) for
		// filtering
		theContextServices.proceed();

	} else {

		// Any other resources are authorized, meaning that the client will get
		// to see the resources, and consentWillSeeResource(...) is not called. This
		// is good for performance but means that no filtering can take place.
		theContextServices.authorized();
	}

}

/**
 * If present, this function is called before every resource that will be
 * shown to the user.
 *
 * @param theRequestDetails  Contains details about the request (e.g. the
 *                           FHIR operation being performed, the HTTP method,
 *                           the URL, etc.
 * @param theUserSession     Contains details about the logged in user and
 *                           their session.
 * @param theContextServices Contains various utiltity methods for acccessing
 *                           relevant information about the request, as well
 *                           as providing a response.
 * @package theResource      The resource that will be accessed
 */
function consentWillSeeResource(theRequestDetails, theUserSession, theContextServices, theResource) {

	// For Observation resources with a confidentiality code of "R" (restricted),
	// we will strip the value and notes from what the user will see
	if (theResource.meta.hasSecurity('https://www.hl7.org/fhir/v3/Confidentiality/cs.html', 'R')) {
		if (theResource.resourceType === 'Observation') {
			theResource.clear('value');
			theResource.clear('note');
		}
	}

	// Let the user see the resource
	theContextServices.proceed();
}