Support atomic organization role sets and owner-managed invitations
verify / verify (push) Successful in 4m17s

Signed-off-by: Cole Speelman <crspeelman@gmail.com>
This commit is contained in:
2026-09-05 00:10:08 -04:00
parent ed0cc8ceff
commit c0986168bc
19 changed files with 1052 additions and 69 deletions
+38
View File
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: MPL-2.0
package organizations
import (
"context"
"errors"
"slices"
"time"
)
var ErrRoleInvitationUnsupported = errors.New("organizations: atomic role-set invitations are unsupported")
// RoleInvitationRepository must preserve the entire role set and its required
// owner authority, then commit acceptance, membership, grants and audit together.
type RoleInvitationRepository interface {
CreateInvitationWithRoles(context.Context, Invitation, string, AuditEvent) error
AcceptInvitationWithRoles(context.Context, [32]byte, string, string, time.Time, AuditEvent) error
}
// RoleNames returns a validated copy of the invitation's direct roles. The older
// DirectRole remains supported; supplying both forms is an error, not a union.
func (invitation Invitation) RoleNames() ([]string, error) {
if invitation.DirectRole != "" && len(invitation.DirectRoles) > 0 || len(invitation.DirectRoles) > 16 {
return nil, errors.New("organizations: invalid invitation roles")
}
roles := append([]string(nil), invitation.DirectRoles...)
if invitation.DirectRole != "" {
roles = append(roles, invitation.DirectRole)
}
slices.Sort(roles)
for i, role := range roles {
if !safeNamePattern.MatchString(role) || i > 0 && role == roles[i-1] {
return nil, errors.New("organizations: invalid invitation role")
}
}
return roles, nil
}