[18.9] Fix composite identity support for dependency proxy access

What does this MR do and why?

This backports three merge requests to 18-9-stable-ee:

!224207 (merged) was released in 18.10 and refactors the dependency proxy authentication in a way that makes it difficult to pick !228662 (merged) cleanly without it.

Duo bot service accounts (composite_identity_enforced: true) were denied access to the dependency proxy for two reasons:

  1. GroupPolicy#dependency_proxy_access_allowed checked the service account's own group membership. Service accounts are only added as project-level members, so max_member_access_for_user returned NO_ACCESS for the group, causing the primary ability check to fail before the composite identity dual-check could run.

  2. Composite identity was not re-linked when the JWT was consumed. The dependency proxy uses a two-step flow: a short-lived JWT is issued at /jwt/auth and then used for the actual image pull. The JWT only carried user_id, so the scoped user context established during OAuth authentication was lost. When JwtAuthenticatable reconstructed current_user from the JWT, no composite identity was linked in the request store, causing Identity#valid? to return false.

Fix

  • Embed scoped_user_id in the dependency proxy JWT when composite identity is linked at token-issuance time (ContainerProxyAuthenticationService).
  • Re-link the composite identity in JwtAuthenticatable when the JWT is consumed, using the embedded scoped_user_id.
  • Define User#ai_service_account? as service_account? && composite_identity_enforced?. All composite-identity-enforced service accounts are AI bots, so this is both accurate and free of fragile username-prefix heuristics.
  • Add an ai_service_account_with_composite_identity condition to GroupPolicy so AI service accounts with an active composite identity link pass the primary policy check. The scoped user's group membership is left entirely to Ability#with_composite_identity_check, keeping the policy free of redundant access-level lookups.

How the dual-check works

When Ability.allowed? is called for a user with composite_identity_enforced?, it runs the ability check twice: once for the current user (the AI service account) and once for the scoped user. Both must pass. The ai_service_account_with_composite_identity policy condition handles the service account side; Ability#with_composite_identity_check independently verifies Ability.allowed?(scoped_user, :read_dependency_proxy, group) for the scoped user side. Cross-group protection follows automatically — a linked identity from a different group will fail the scoped user's membership check.

References

Relates to #594535 (closed)

MR acceptance checklist

This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.

  • This MR is backporting a bug fix, documentation update, or spec fix, previously merged in the default branch.
  • The MR that fixed the bug on the default branch has been deployed to GitLab.com (not applicable for documentation or spec changes).
  • The MR title is descriptive (e.g. "Backport of 'title of default branch MR'"). This is important, since the title will be copied to the patch blog post.
  • Required labels have been applied to this merge request
  • This MR has been approved by a maintainer (only one approval is required).
  • Ensure the e2e:test-on-omnibus-ee job has succeeded, or if it has failed, investigate the failures. If you determine the failures are unrelated, you may proceed. If you need assistance investigating, reach out to a Software Engineer in Test in #s_developer_experience.

Note to the merge request author and maintainer

If you have questions about the patch release process, please:

Edited by Stan Hu

Merge request reports

Loading