Skip to content

ZCU-PUB/fix: shibboleth special groups lost in short-lived and refreshed tokens - 403 on download (backport #1347) - #1374

Merged
milanmajchrak merged 4 commits into
customer/zcu-pubfrom
zcu-pub/backport-1347-shib-special-groups
Jul 20, 2026
Merged

milanmajchrak merged 4 commits into
customer/zcu-pubfrom
zcu-pub/backport-1347-shib-special-groups

Conversation

@milanmajchrak

@milanmajchrak milanmajchrak commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Problem

Fixes #900.

After a Shibboleth login the user is implicitly a member of the special group Authenticated (authentication-shibboleth.default.auth.group, clarin-dspace.cfg). An item restricted to this group is visible, but the bitstream download returns 403.

Cause: ClarinShibAuthentication.getSpecialGroups() reads the special groups from the servlet session attribute shib.authenticated, which is set only during the Shibboleth login request. Every newly minted token recomputes the sg JWT claim (SpecialGroupClaimProvider.getValue()), so on a stateless request:

  • the short-lived token used by the UI for downloads (POST /api/authn/shortlivedtokens) is issued with an empty sg claimGET /api/core/bitstreams/{uuid}/content?authentication-token=… → 403,
  • the refreshed login token (POST /api/authn/login with Bearer) loses the special groups too, so the user loses the group-based access after the first token refresh.

Fix

Backport of #1347 (74f58627, port of ufal#1378, issue ufal#1373) from dtq-dev:

Behavior note: special groups are now echoed from the user context until re-login, so an affiliation revocation at the IdP takes effect at the next login (same semantics as upstream).

TDD

Commit 1 adds ClarinShibbolethSpecialGroupsIT (restricted download via short-lived token + token refresh). Run locally on this branch without the fix — both tests fail with 403:

IT failing without the fix - 403 instead of 200

Commit 2 is the backport — both tests pass:

IT passing with the backported fix

(The images are rendered from the real local mvn verify logs. Note: the first commit intentionally fails CI when checked out alone - TDD ordering. The evidence images live on the deletable branch assets/zcu-shibboleth-pr-evidence.)

How to replicate on current zcu-pub

  1. Restrict a bitstream's READ policy to the group Authenticated.
  2. Sign in via Shibboleth → the item and the file are visible.
  3. Download the bitstream → 403.

API check: POST /api/authn/shortlivedtokens with the login Bearer token and decode the returned JWT — the sg claim is empty.

Related

milanmajchrak and others added 3 commits July 16, 2026 15:28
…refreshed tokens

Replicates #900: a bitstream restricted to the default
shibboleth group (Authenticated) is readable with the login token, but the
download via a short-lived token returns 403, because the special groups
are recomputed from the (missing) servlet session instead of the user
context when a new token is minted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ted (on token refresh) (ufal#1378) (#1347)

* Issue 1373: obtain special groups from user context when new token is generated (on token refresh)

* resolve Copilot comments

* resolve Copilot Comments: compute special groups only when when user is authenticated

* Remove HttpSession dependency from ClarinShibAuthentication

Use request-scoped attributes for shib.authenticated instead of
HttpSession/JSESSIONID, aligning with upstream ShibAuthentication.
Follow-up to ufal#1373/ufal#1378.

* Guard against null special groups in Context.getSpecialGroups

A special-group UUID may reference a Group that has since been deleted;
GroupService.find returns null in that case. The list was built with an
unconditional add, so it could contain null elements, which caused an NPE
downstream (e.g. SpecialGroupClaimProvider.getValue maps group.getID()
while generating the JWT sg claim on token refresh). Filter nulls once
here so every caller is covered. Follow-up to ufal#1373/ufal#1378.

---------

(cherry picked from commit 4c294b2)

Co-authored-by: Milan Kuchtiak <kuchtiak@ufal.mff.cuni.cz>
(cherry picked from commit 74f5862)
… raw-type read

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0b90dcf2-5d07-4fae-b3b1-6fae56ce8e65

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Fixes loss of Shibboleth “special groups” (e.g., default Authenticated) across stateless REST requests so group-based authorization remains consistent for short-lived download tokens and refreshed login tokens (addresses #900).

Changes:

  • Update ClarinShibAuthentication.getSpecialGroups() to prefer context.getSpecialGroups() (restored from the JWT sg claim) and move shib.authenticated tracking from session to request scope.
  • Harden Context.getSpecialGroups() to skip deleted (null) groups to avoid downstream NPEs.
  • Add an integration test covering restricted bitstream download via short-lived token and access persistence after login token refresh.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
dspace-server-webapp/src/test/java/org/dspace/app/rest/security/ClarinShibbolethSpecialGroupsIT.java Adds IT coverage for short-lived token downloads and token refresh preserving Shibboleth special groups.
dspace-api/src/main/java/org/dspace/core/Context.java Filters null (deleted) groups from getSpecialGroups() results.
dspace-api/src/main/java/org/dspace/authenticate/clarin/ClarinShibAuthentication.java Restores special groups from Context/JWT, and switches shib.authenticated to request scope to support stateless flows.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- reuse AUTHORIZATION_HEADER/AUTHORIZATION_TYPE from AbstractControllerIntegrationTest
- assert the Authorization header and the token field are present before using them

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@milanmajchrak
milanmajchrak merged commit 7920d0f into customer/zcu-pub Jul 20, 2026
11 checks passed
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.

3 participants