Fix composite identity support for dependency proxy access
What does this MR do and why?
Duo bot service accounts (composite_identity_enforced: true) were denied access to the dependency proxy for two reasons:
-
GroupPolicy#dependency_proxy_access_allowedchecked the service account's own group membership. Service accounts are only added as project-level members, somax_member_access_for_userreturnedNO_ACCESSfor the group, causing the primary ability check to fail before the composite identity dual-check could run. -
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/authand then used for the actual image pull. The JWT only carrieduser_id, so the scoped user context established during OAuth authentication was lost. WhenJwtAuthenticatablereconstructedcurrent_userfrom the JWT, no composite identity was linked in the request store, causingIdentity#valid?to returnfalse.
Fix
- Embed
scoped_user_idin the dependency proxy JWT when composite identity is linked at token-issuance time (ContainerProxyAuthenticationService). - Re-link the composite identity in
JwtAuthenticatablewhen the JWT is consumed, using the embeddedscoped_user_id. - Define
User#ai_service_account?asservice_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_identitycondition toGroupPolicyso AI service accounts with an active composite identity link pass the primary policy check. The scoped user's group membership is left entirely toAbility#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)