Skip to content

Fix SMART system search scope filtering - #5788

Open
Jared Erwin (feordin) wants to merge 1 commit into
mainfrom
users/jaerwin/smart-resource-type-fix
Open

Fix SMART system search scope filtering#5788
Jared Erwin (feordin) wants to merge 1 commit into
mainfrom
users/jaerwin/smart-resource-type-fix

Conversation

@feordin

@feordin Jared Erwin (feordin) commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Description

Preserves the resource/action pairing for SMART clinical scopes when search requests do not carry a concrete route resource type.

  • Normal searches use only legacy .read or SMART v2 .s restrictions.
  • Direct reads by known ID continue to use legacy .read or SMART v2 .r restrictions.
  • The filtered restrictions are applied to _type, all-resource searches, history and compartment searches, include/revinclude validation, and Cosmos DB include queries.
  • SMART write-operation enforcement is unchanged.

Related issues

N/A - addresses an externally reported SMART scope authorization issue.

Testing

  • Added SQL/Cosmos integration coverage

FHIR Team Checklist

  • Update the title of the PR to be succinct and less than 65 characters
  • Add a milestone to the PR for the sprint that it is merged (i.e. add S47)
  • Tag the PR with the type of update: Bug, Build, Dependencies, Enhancement, New-Feature or Documentation
  • Tag the PR with Open source, Azure API for FHIR (CosmosDB or common code) or Azure Healthcare APIs (SQL or common code) to specify where this change is intended to be released.
  • Tag the PR with Schema Version backward compatible or Schema Version backward incompatible or Schema Version unchanged if this adds or updates Sql script which is/is not backward compatible with the code.
  • When changing or adding behavior, if your code modifies the system design or changes design assumptions, please create and include an ADR.
  • CI is green before merge Build Status
  • Review squash-merge requirements

Semver Change (docs)

Patch

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 43c8b4b3-3e41-45af-b117-06bce9ca5517
@feordin
Jared Erwin (feordin) requested a review from a team as a code owner September 2, 2026 17:45
@feordin Jared Erwin (feordin) added Bug-Security Security related bugs. Azure API for FHIR Label denotes that the issue or PR is relevant to the Azure API for FHIR Azure Healthcare APIs Label denotes that the issue or PR is relevant to the FHIR service in the Azure Healthcare APIs Schema Version unchanged No-PaaS-breaking-change No-ADR ADR not needed labels Sep 2, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

One access-control related behavioral regression was identified (include parsing can be silently skipped under FGAC in cases that previously threw) and should be corrected before approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR fixes SMART (v2) system-level search authorization by preserving the resource/action pairing for scope restrictions when requests don’t have a concrete route resource type, ensuring search vs read-by-id semantics are enforced consistently across _type filtering, include/revinclude validation, and Cosmos DB include queries.

Changes:

  • Introduces action-aware scope filtering (ScopeDataActions) so searches apply only .read/.s-equivalent restrictions, while direct reads by ID apply .read/.r-equivalent restrictions.
  • Propagates the action context through search option creation, expression access checks, and Cosmos include query scope filtering.
  • Adds/extends unit + integration test coverage for mixed wildcard read-by-id + resource-scoped search scenarios.
File summaries
File Description
test/Microsoft.Health.Fhir.Shared.Tests.Integration/Features/Smart/SmartSearchTests.cs Adds an integration test covering mixed wildcard read-by-id + Patient search scopes in system searches.
src/Microsoft.Health.Fhir.Shared.Core/Features/Search/SearchOptionsFactory.cs Adds ScopeDataActions, filters applicable SMART scope restrictions by action, and threads filtered scopes through include parsing and access checks.
src/Microsoft.Health.Fhir.Shared.Core/Features/Resources/Get/GetResourceHandler.cs Ensures GET-by-id uses `Read
src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Search/SearchOptionsFactoryTests.cs Adds tests validating action-aware scope filtering and include parsing behavior under mixed scopes.
src/Microsoft.Health.Fhir.CosmosDb/Features/Search/FhirCosmosSearchService.cs Applies action-aware SMART scope filtering to Cosmos include/revinclude query filtering.
src/Microsoft.Health.Fhir.Core/Features/Search/SearchService.cs Adds an overload to pass scopeDataActions into SearchOptionsFactory.
src/Microsoft.Health.Fhir.Core/Features/Search/SearchOptions.cs Stores ScopeDataActions on SearchOptions and copies it in the copy ctor.
src/Microsoft.Health.Fhir.Core/Features/Search/ISearchService.cs Adds a new overload for action-aware SMART scope filtering.
src/Microsoft.Health.Fhir.Core/Features/Search/ISearchOptionsFactory.cs Adds a new overload for action-aware SMART scope filtering at options creation time.
src/Microsoft.Health.Fhir.Core/Features/Search/Access/ExpressionAccessControl.cs Adds an overload to validate include/chained expressions against the action-filtered scope set.
src/Microsoft.Health.Fhir.Core/Features/Context/ScopeRestriction.cs Adds AllowsAny(DataActions) helper used to filter restrictions by requested actions.
Review details
  • Files reviewed: 11/11 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +737 to 753
if (allowedResourceTypesByScope != null &&
!allowedResourceTypesByScope.Contains(KnownResourceTypes.All))
{
string includeSourceResourceType = p.query?.Split(':')[0];
if (!string.Equals(includeSourceResourceType, "*", StringComparison.Ordinal) &&
!string.Equals(includeSourceResourceType, KnownResourceTypes.All, StringComparison.Ordinal) &&
!allowedResourceTypesByScope.Contains(includeSourceResourceType))
{
return null;
}

includeResourceTypeList = includeResourceTypeList.Intersect(allowedResourceTypesByScope).ToArray();
if (includeResourceTypeList.Length == 0)
{
return null;
}
}
Comment on lines +22 to +32
SearchOptions Create(
string resourceType,
IReadOnlyList<Tuple<string, string>> queryParameters,
DataActions scopeDataActions,
bool isAsyncOperation = false,
ResourceVersionType resourceVersionTypes = ResourceVersionType.Latest,
bool onlyIds = false,
bool isIncludesOperation = false)
{
return Create(resourceType, queryParameters, isAsyncOperation, resourceVersionTypes, onlyIds, isIncludesOperation);
}
Comment on lines +42 to +54
/// <summary>
/// Searches resources using only SMART scope restrictions that permit one of the supplied actions.
/// </summary>
/// <param name="resourceType">The resource type that should be searched.</param>
/// <param name="queryParameters">The search queries.</param>
/// <param name="scopeDataActions">The data actions that may authorize the search.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="isAsyncOperation">Whether the search is part of an async operation.</param>
/// <param name="resourceVersionTypes">Which version types to include in search.</param>
/// <param name="onlyIds">Whether to return only resource IDs.</param>
/// <param name="isIncludesOperation">Whether the search is querying remaining include resources.</param>
/// <returns>A <see cref="SearchResult"/> representing the result.</returns>
Task<SearchResult> SearchAsync(
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 78.91%. Comparing base (20d379f) to head (01d9b7a).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #5788      +/-   ##
==========================================
+ Coverage   78.28%   78.91%   +0.63%     
==========================================
  Files        1016     1018       +2     
  Lines       36940    37025      +85     
  Branches     5619     5625       +6     
==========================================
+ Hits        28920    29220     +300     
+ Misses       6644     6418     -226     
- Partials     1376     1387      +11     

see 20 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Azure API for FHIR Label denotes that the issue or PR is relevant to the Azure API for FHIR Azure Healthcare APIs Label denotes that the issue or PR is relevant to the FHIR service in the Azure Healthcare APIs Bug-Security Security related bugs. No-ADR ADR not needed No-PaaS-breaking-change Schema Version unchanged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants