Skip to main content
Glama
access.ts7.12 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import type { AccessPolicy, AccessPolicyResource, Resource, ResourceType } from '@medplum/fhirtypes'; import { matchesSearchRequest } from './search/match'; import { parseSearchRequest } from './search/search'; const universalAccessPolicy: AccessPolicyResource = { resourceType: '*', }; /** * Protected resource types are in the "medplum" project. * Reading and writing is limited to the system account. */ export const protectedResourceTypes = ['DomainConfiguration', 'JsonWebKey', 'Login']; /** * Project admin resource types are special resources that are only * accessible to project administrators. */ export const projectAdminResourceTypes = ['UserSecurityRequest', 'Project', 'ProjectMembership', 'User']; /** * Interactions with a resource that can be controlled via an access policy. * * Codes taken from http://hl7.org/fhir/codesystem-restful-interaction.html */ export const AccessPolicyInteraction = { READ: 'read', VREAD: 'vread', UPDATE: 'update', DELETE: 'delete', HISTORY: 'history', CREATE: 'create', SEARCH: 'search', } as const; export type AccessPolicyInteraction = (typeof AccessPolicyInteraction)[keyof typeof AccessPolicyInteraction]; export const readInteractions: AccessPolicyInteraction[] = [ AccessPolicyInteraction.READ, AccessPolicyInteraction.VREAD, AccessPolicyInteraction.HISTORY, AccessPolicyInteraction.SEARCH, ]; /** * Determines if the current user can read the specified resource type. * @param accessPolicy - The access policy. * @param resourceType - The resource type. * @returns True if the current user can read the specified resource type. * @deprecated Use accessPolicySupportsInteraction() instead. */ export function canReadResourceType(accessPolicy: AccessPolicy, resourceType: ResourceType): boolean { return accessPolicySupportsInteraction(accessPolicy, AccessPolicyInteraction.SEARCH, resourceType); } /** * Determines if the current user can write the specified resource type. * This is a preliminary check before evaluating a write operation in depth. * If a user cannot write a resource type at all, then don't bother looking up previous versions. * @param accessPolicy - The access policy. * @param resourceType - The resource type. * @returns True if the current user can write the specified resource type. * @deprecated Use accessPolicySupportsInteraction() instead. */ export function canWriteResourceType(accessPolicy: AccessPolicy, resourceType: ResourceType): boolean { if (protectedResourceTypes.includes(resourceType)) { return false; } return accessPolicySupportsInteraction(accessPolicy, AccessPolicyInteraction.UPDATE, resourceType); } /** * Shallow check that an interaction is permitted by the AccessPolicy on a given resource type, * at least for some resources. A more in-depth check for the specific resource(s) being accessed * is required in addition to this one. * @param accessPolicy - The AccessPolicy to check against. * @param interaction - The FHIR interaction being performed. * @param resourceType - The type of resource being interacted with. * @returns True when the interaction is provisionally permitted by the AccessPolicy. */ export function accessPolicySupportsInteraction( accessPolicy: AccessPolicy, interaction: AccessPolicyInteraction, resourceType: ResourceType ): boolean { return Boolean( accessPolicy.resource?.some((policy) => shallowMatchesResourcePolicy(policy, resourceType, interaction)) ); } /** * Determines if the current user can write the specified resource. * This is a more in-depth check after building the candidate result of a write operation. * @param accessPolicy - The access policy. * @param resource - The resource. * @returns True if the current user can write the specified resource type. * @deprecated Use satisfiedAccessPolicy() instead. */ export function canWriteResource(accessPolicy: AccessPolicy, resource: Resource): boolean { return Boolean(satisfiedAccessPolicy(resource, AccessPolicyInteraction.UPDATE, accessPolicy)); } /** * Checks that there is an access policy permitting the given resource interaction, returning the matching policy object. * @param resource - The resource being acted upon. * @param interaction - The interaction being performed on the resource. * @param accessPolicy - The relevant access policy for the current user. * @returns The satisfied access policy, or undefined if the access policy does not permit the given interaction. */ export function satisfiedAccessPolicy( resource: Resource, interaction: AccessPolicyInteraction, accessPolicy: AccessPolicy | undefined ): AccessPolicyResource | undefined { if (!accessPolicy) { return universalAccessPolicy; } return accessPolicy.resource?.find((policy) => matchesAccessPolicyResourcePolicy(resource, interaction, policy)); } /** * Returns true if the resource satisfies the specified access policy resource policy. * @param resource - The resource. * @param interaction - The interaction being performed on the resource. * @param resourcePolicy - One per-resource policy section from the access policy. * @returns True if the resource matches the access policy. */ function matchesAccessPolicyResourcePolicy( resource: Resource, interaction: AccessPolicyInteraction, resourcePolicy: AccessPolicyResource ): boolean { const resourceType = resource.resourceType; if (!shallowMatchesResourcePolicy(resourcePolicy, resourceType, interaction)) { return false; } // Binary has no search parameters, so it cannot be restricted by compartment or criteria if (resourceType === 'Binary') { return true; } if ( resourcePolicy.compartment && !resource.meta?.compartment?.some((c) => c.reference === resourcePolicy.compartment?.reference) ) { // Deprecated - to be removed in v5 return false; } if (resourcePolicy.criteria && !matchesSearchRequest(resource, parseSearchRequest(resourcePolicy.criteria))) { return false; } return true; } /** * Shallow check if the given interaction on a resource type matches the resource access policy. * @param policy - The AccessPolicy resource policy. * @param resourceType - The candidate resource type. * @param interaction - Interaction type to check against the policy. * @returns True when the resource type matches the resource policy. */ function shallowMatchesResourcePolicy( policy: AccessPolicyResource, resourceType: ResourceType, interaction: AccessPolicyInteraction ): boolean { if ( policy.resourceType !== resourceType && // Project admin resource types are not allowed to be wildcarded; they must be explicitly included (policy.resourceType !== '*' || projectAdminResourceTypes.includes(resourceType)) ) { return false; } // Only use `readonly` if `interaction` is not specified if (!policy.interaction) { return !policy.readonly || readInteractions.includes(interaction); } return policy.interaction.includes(interaction); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/medplum/medplum'

If you have feedback or need assistance with the MCP directory API, please join our Discord server