mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-07-20 06:52:24 +00:00
iam: surface OIDC groups and roles into the STS session request context for resource-policy ABAC (#10263)
* iam: surface OIDC groups into STS session request context for resource-policy ABAC The groups claim was added to ExternalIdentity.Groups but excluded from Attributes (processedClaims), so it never reached the session RequestContext. As a result group membership was usable only in role trust policies (assume-time), not in resource/permission policies (request-time) - so a single role could not scope access by the caller's groups (aggregate ABAC). Surface Groups as a []string in the request context; the string-condition evaluator already handles multi-valued context keys, and the S3 middleware exposes it as jwt:groups. * iam: also surface OIDC roles into the STS request context (companion to groups) The 'roles' claim is excluded from the OIDC attributes by the same processedClaims set that excluded 'groups', so it never reached the session request context and was usable only via provider-configured role mapping - not a raw token roles claim. Add ExternalIdentity.Roles, populate it from the token's roles claim, and surface it as a []string in the request context so resource policies can gate on jwt:roles, exactly as the previous commit did for jwt:groups.
This commit is contained in:
@@ -481,6 +481,10 @@ func (p *OIDCProvider) Authenticate(ctx context.Context, token string) (*provide
|
||||
email, _ := claims.GetClaimString("email")
|
||||
displayName, _ := claims.GetClaimString("name")
|
||||
groups, _ := claims.GetClaimStringSlice("groups")
|
||||
// The raw `roles` claim (e.g. from a Keycloak realm-role mapper). Surfaced into
|
||||
// the STS request context below (see ExternalIdentity.Roles) so resource policies
|
||||
// can gate on jwt:roles. Distinct from the provider-configured role mapping below.
|
||||
rawRoles, _ := claims.GetClaimStringSlice("roles")
|
||||
|
||||
// Debug: Log available claims
|
||||
glog.V(3).Infof("Available claims: %+v", claims.Claims)
|
||||
@@ -535,6 +539,7 @@ func (p *OIDCProvider) Authenticate(ctx context.Context, token string) (*provide
|
||||
Email: email,
|
||||
DisplayName: displayName,
|
||||
Groups: groups,
|
||||
Roles: rawRoles,
|
||||
Attributes: attributes,
|
||||
Provider: p.name,
|
||||
Issuer: claims.Issuer,
|
||||
|
||||
@@ -43,6 +43,13 @@ type ExternalIdentity struct {
|
||||
// Groups are the groups the user belongs to
|
||||
Groups []string `json:"groups,omitempty"`
|
||||
|
||||
// Roles are the roles the user holds (from the token's `roles` claim).
|
||||
// Surfaced into the STS request context so resource policies can gate on
|
||||
// jwt:roles - same rationale as Groups (the claim is otherwise excluded from
|
||||
// attributes by the provider's processedClaims set, so it never reached the
|
||||
// request context and was usable only via provider-configured role mapping).
|
||||
Roles []string `json:"roles,omitempty"`
|
||||
|
||||
// Attributes are additional user attributes
|
||||
Attributes map[string]string `json:"attributes,omitempty"`
|
||||
|
||||
|
||||
@@ -640,6 +640,26 @@ func (s *STSService) AssumeRoleWithWebIdentity(ctx context.Context, request *Ass
|
||||
// Add sub as well since it's commonly used
|
||||
requestContext["sub"] = externalIdentity.UserID
|
||||
|
||||
// Surface federated group memberships so resource (permission) policies can do
|
||||
// group-based ABAC - not just role trust policies. Stored as a []string; the
|
||||
// string-condition evaluator already handles multi-valued context keys, and the
|
||||
// S3 middleware additionally exposes it as jwt:groups. Without this, "groups"
|
||||
// was excluded from the OIDC attributes (see oidc_provider processedClaims) and
|
||||
// so only usable at assume-time (trust policy), never at request-time - meaning
|
||||
// a single role's permission policy could not scope access by the caller's
|
||||
// groups (aggregate/ABAC).
|
||||
if len(externalIdentity.Groups) > 0 {
|
||||
requestContext["groups"] = externalIdentity.Groups
|
||||
}
|
||||
|
||||
// Same for the caller's roles (the `roles` claim). Surfaced as a []string so a
|
||||
// resource policy can gate on jwt:roles - the S3 middleware exposes it that way.
|
||||
// Without this, "roles" was excluded from the OIDC attributes (processedClaims)
|
||||
// and so unusable in a request-time permission policy.
|
||||
if len(externalIdentity.Roles) > 0 {
|
||||
requestContext["roles"] = externalIdentity.Roles
|
||||
}
|
||||
|
||||
// Compute a stable parent-user hash from (sub, iss). Only this tuple is
|
||||
// guaranteed stable across token refresh per OIDC Core 1.0, so this is the
|
||||
// right key for any per-identity state (audit trail, future quotas).
|
||||
|
||||
Reference in New Issue
Block a user