Skip to content

Allow adjuncts to be access controlled - #1310

Open
JackLewis-digirati wants to merge 2 commits into
developfrom
feature/1302/adjunct_access_control
Open

JackLewis-digirati wants to merge 2 commits into
developfrom
feature/1302/adjunct_access_control

Conversation

@JackLewis-digirati

@JackLewis-digirati JackLewis-digirati commented Sep 21, 2026 •

Copy link
Copy Markdown
Collaborator

What does this change?

Resolves #1302

This PR allows adjuncts to be access controlled, based on the role of the parent asset

@JackLewis-digirati
JackLewis-digirati requested a review from a team as a code owner September 21, 2026 16:08
@JackLewis-digirati

Copy link
Copy Markdown
Collaborator Author

How this works

Adjuncts don't have their own roles — they inherit access control from their parent Asset. This PR wires that inheritance through to request handling.

Data flow:

  • DapperAdjunctRepository joins Adjuncts → Images to pull the parent Asset's Roles column, stashing them on Adjunct.Asset.Roles (reusing the existing Adjunct.Asset nav property rather than adding a new mapped column).
  • MemoryAssetTracker.ConvertAdjunctToTrackedAdjunct copies those roles onto OrchestrationAdjunct.Roles, mirroring how OrchestrationAsset already tracks roles. OrchestrationAdjunct.RequiresAuth => Roles.Count > 0.
  • Adjunct.Asset is an EF nav property (= null!) only actually populated along this one path — anything else constructing an Adjunct (including several existing tests) leaves it null, so the roles copy uses adjunct.Asset?.Roles defensively.

Request handling (AdjunctRequestHandler):

  • If RequiresAuth is false (no roles on the Asset), the request proceeds exactly as before — no auth check, no cache-control changes.
  • If RequiresAuth is true, it calls the shared AssetRequestProcessor.IsAuthenticated(assetId, roles, request) — the same auth check FileRequestHandler and TimeBasedRequestHandler already used (previously each had its own near-duplicate private method; now consolidated).
  • Unauthenticated + roles present → 401.
  • Authenticated → served with Cache-Control: private, max-age=600 instead of the default, so authorised content isn't cached in any shared/CDN layer.

Also:

  • Comma-delimited column splitting (Roles/Tags) was pulled into a shared DapperColumnMapping.SplitDelimited, used by both DapperAssetRepository and the new DapperAdjunctRepository call, rather than duplicating that parsing logic.
  • IIIF Auth2's /verifyaccess/{assetId} endpoint needed no changes — it's already generic over asset id + roles, so it works unmodified for adjuncts.

@donaldgray donaldgray left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this ticket is incomplete - it's missing call to adjunct specific verifyAccess endpoint, which will require a change to iiif-auth-v2.

IIIF-Auth-V2 /verifyAccess/* can handle request for Adjuncts.

MediaType = firstAdjunct.MediaType,
Type = firstAdjunct.Type
Type = firstAdjunct.Type,
// Adjuncts don't have their own roles, they inherit those of the parent Asset

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I wonder if it'd be possible to add Roles directly to the Adjunct here. .ApplicableRoles property maybe?

My thinking that this current implementation ties more to the "Adjunct inherits roles from Asset" (this is MVP only, we will be revisiting). If we just had a Roles/ApplicableRoles property on Adjuncts we could set that however we need to close to the source - downstream callers then only need to worry about consuming that property and not that it comes from the parent. Then it'd be easy to update the query to make allow Adjunct to have it's own Roles (e.g. to make it COALESCE("Adjuncts"."Roles", "Images"."Roles") as "Roles" rather than "Images"."Roles").

If the boundary is the creation of the OrchestrationAdjunct then it might not be worth it at this point.

// TBD - AUTH
if (orchestrationAdjunct.RequiresAuth)
{
if (!await assetRequestProcessor.IsAuthenticated(adjunctRequest.GetAssetId(), orchestrationAdjunct.Roles,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we want to pass the adjunct id here, rather than just the asset id. Following this call chain ends up making a GET to /verifyAccess/1/2/foo but we want to call /verifyAccess/1/2/foo/mets.xml. This could be something like .GetDeliverableId() to make it generic.

This is covered by the following from the ticket:

IIIF-Auth-V2 /verifyAccess/* can handle request for Adjuncts.

/// <param name="assetId">AssetId the roles belong to</param>
/// <param name="roles">Roles associated with the asset/adjunct being requested</param>
/// <param name="httpRequest">Current <see cref="HttpRequest"/>, used to determine auth mechanism + for logging</param>
public async Task<bool> IsAuthenticated(AssetId assetId, IReadOnlyList<string> roles, HttpRequest httpRequest)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

See comment in AdjunctRequestHandler - can this be DeliverableId

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Adjunct access control is enforced

2 participants