Use computed permissions for group membership instead of direct relations
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 344
- Forks
- 47
- Avg merge
- 4d 4h
- Merged PRs (30d)
- 26
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)
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with base_schema.zed and core/group/service.go:168-221, then find references to group#member. Verify how organization and project policies expose membership before changing the group schema and removing direct relation creation from AddMember and addOwner. Done means group membership uses policies consistently and all group#member references use the computed permission.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- authorization, backend
- Issue type
- Refactor
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100