-
Notifications
You must be signed in to change notification settings - Fork 44
Open
Description
Problem
Group membership currently uses direct group#member@user relations, while organization and project membership use policies (role bindings). This inconsistency means groups require special handling.
Current State
| Resource | How membership works |
|---|---|
| Organization | Policies (role bindings) |
| Project | Policies (role bindings) |
| Group | Direct group#member@user relation |
When a user is added to a group, we create both a policy AND a direct relation.
Source: core/group/service.go:168-191
// AddMember adds a subject(user) to group as member
func (s Service) AddMember(ctx context.Context, groupID string, principal authenticate.Principal) error {
// first create a policy for the user as member of the group
if err := s.addMemberPolicy(ctx, groupID, principal); err != nil {
return err
}
// then create a relation between group and user as member
rel := relation.Relation{
Object: relation.Object{
ID: groupID,
Namespace: schema.GroupNamespace,
},
Subject: relation.Subject{
ID: principal.ID,
Namespace: principal.Type,
},
RelationName: schema.MemberRelationName,
}
if _, err := s.relationService.Create(ctx, rel); err != nil {
return err
}
return nil
}Same pattern exists for addOwner at lines 193-221.
Proposed State
| Resource | How membership works |
|---|---|
| Organization | Policies (role bindings) |
| Project | Policies (role bindings) |
| Group | Policies (role bindings) |
Group membership becomes computed from policies, just like org and project.
How it works
Schema change:
// Before
definition group {
relation member: app/user // direct relation
permission get = ... + member
}
// After
definition group {
relation granted: app/rolebinding
permission members = granted->app_group_member // computed from policies
permission get = ...
}
SpiceDB resolves group members by:
- Find all role bindings granted to the group
- Filter to those with "Group Member" role
- Return their bearers
Tested in AuthZed Playground - this approach works.
Benefits
| Benefit | Description |
|---|---|
| Consistency | All resources (org, project, group) use the same pattern |
| Single source of truth | Membership determined by policies only, no sync issues |
| Simpler code | Remove direct relation management for groups |
| SDK simplicity | Same role-based API for all resources |
Potential Downsides
| Concern | Impact | Mitigation |
|---|---|---|
| Query complexity | Membership is computed, not direct lookup | SpiceDB optimizes and caches these |
| Schema migration | Need to update SpiceDB schema | Can be done incrementally |
| Breaking change | External systems using group#member relation |
Document in release notes |
Code Changes
- Update
base_schema.zed- changegroup#memberfrom relation to computed permission core/group/service.go-AddMember: removerelationService.Createcall, keep only policycore/group/service.go-addOwner: removerelationService.Createcall, keep only policy- Update references from
group#membertogroup#members(the computed permission)
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels