generated from coulomb/repo-seed
198 lines
4.9 KiB
Go
198 lines
4.9 KiB
Go
package relationship
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/netkingdom/flex-auth/internal/registry"
|
|
"github.com/netkingdom/flex-auth/pkg/api"
|
|
)
|
|
|
|
// SnapshotToTuples maps flex-auth registry data to relationship tuples.
|
|
func SnapshotToTuples(snapshot registry.Snapshot) []Tuple {
|
|
index := newSnapshotIndex(snapshot)
|
|
tuples := map[string]Tuple{}
|
|
addTuple := func(tuple Tuple) {
|
|
if tuple.ObjectType == "" || tuple.ObjectID == "" || tuple.Relation == "" ||
|
|
tuple.SubjectType == "" || tuple.SubjectID == "" {
|
|
return
|
|
}
|
|
tuples[tupleKey(tuple)] = tuple
|
|
}
|
|
|
|
for _, group := range snapshot.Groups {
|
|
for _, member := range group.Members {
|
|
addTuple(Tuple{
|
|
ObjectType: "group",
|
|
ObjectID: group.ID,
|
|
Relation: "member",
|
|
SubjectType: index.subjectType(member),
|
|
SubjectID: index.subjectID(member),
|
|
})
|
|
}
|
|
}
|
|
|
|
for _, team := range snapshot.Teams {
|
|
for _, member := range team.Members {
|
|
addTuple(Tuple{
|
|
ObjectType: "group",
|
|
ObjectID: teamObjectID(team.ID),
|
|
Relation: "member",
|
|
SubjectType: index.subjectType(member),
|
|
SubjectID: index.subjectID(member),
|
|
})
|
|
}
|
|
}
|
|
|
|
for _, manifest := range snapshot.ResourceManifests {
|
|
for _, resource := range manifest.Resources {
|
|
if resource.Parent != "" {
|
|
addTuple(Tuple{
|
|
ObjectType: resource.Type,
|
|
ObjectID: resource.ID,
|
|
Relation: "parent",
|
|
SubjectType: index.resourceType(resource.Parent),
|
|
SubjectID: resource.Parent,
|
|
Metadata: map[string]any{
|
|
"system": manifest.System,
|
|
},
|
|
})
|
|
}
|
|
if resource.Owner != "" {
|
|
addTuple(Tuple{
|
|
ObjectType: resource.Type,
|
|
ObjectID: resource.ID,
|
|
Relation: "owner_team",
|
|
SubjectType: "group",
|
|
SubjectID: teamOrGroupObjectID(resource.Owner),
|
|
Metadata: map[string]any{
|
|
"system": manifest.System,
|
|
},
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, relationship := range snapshot.Relationships {
|
|
subjectType := index.subjectType(relationship.Subject)
|
|
subjectRelation := ""
|
|
if subjectType == "group" && relationship.Relation != "member" && relationship.Relation != "owner_team" {
|
|
subjectRelation = "member"
|
|
}
|
|
addTuple(Tuple{
|
|
ObjectType: index.resourceType(relationship.Object),
|
|
ObjectID: relationship.Object,
|
|
Relation: relationship.Relation,
|
|
SubjectType: subjectType,
|
|
SubjectID: index.subjectID(relationship.Subject),
|
|
SubjectRelation: subjectRelation,
|
|
Conditions: append([]api.Condition(nil), relationship.Conditions...),
|
|
Caring: relationship.Caring,
|
|
Provenance: copyMap(relationship.Provenance),
|
|
Metadata: copyMap(relationship.Metadata),
|
|
})
|
|
}
|
|
|
|
return sortedTuples(tuples)
|
|
}
|
|
|
|
type snapshotIndex struct {
|
|
groups map[string]api.Group
|
|
teams map[string]api.Team
|
|
resourceTypes map[string]string
|
|
}
|
|
|
|
func newSnapshotIndex(snapshot registry.Snapshot) snapshotIndex {
|
|
index := snapshotIndex{
|
|
groups: make(map[string]api.Group),
|
|
teams: make(map[string]api.Team),
|
|
resourceTypes: make(map[string]string),
|
|
}
|
|
for _, group := range snapshot.Groups {
|
|
index.groups[group.ID] = group
|
|
}
|
|
for _, team := range snapshot.Teams {
|
|
index.teams[team.ID] = team
|
|
index.teams[teamObjectID(team.ID)] = team
|
|
}
|
|
for _, manifest := range snapshot.ResourceManifests {
|
|
for _, resource := range manifest.Resources {
|
|
index.resourceTypes[resource.ID] = resource.Type
|
|
}
|
|
}
|
|
return index
|
|
}
|
|
|
|
func (i snapshotIndex) subjectType(id string) string {
|
|
if _, ok := i.groups[id]; ok {
|
|
return "group"
|
|
}
|
|
if _, ok := i.teams[id]; ok {
|
|
return "group"
|
|
}
|
|
if strings.HasPrefix(id, "group:") || strings.HasPrefix(id, "team:") || strings.HasPrefix(id, "reader:") {
|
|
return "group"
|
|
}
|
|
if resourceType := i.resourceType(id); resourceType != "" {
|
|
return resourceType
|
|
}
|
|
return "user"
|
|
}
|
|
|
|
func (i snapshotIndex) subjectID(id string) string {
|
|
if _, ok := i.teams[id]; ok {
|
|
return teamObjectID(id)
|
|
}
|
|
return id
|
|
}
|
|
|
|
func (i snapshotIndex) resourceType(id string) string {
|
|
if resourceType := i.resourceTypes[id]; resourceType != "" {
|
|
return resourceType
|
|
}
|
|
if inferred := inferTypeFromID(id); inferred != "" {
|
|
return inferred
|
|
}
|
|
return "resource"
|
|
}
|
|
|
|
func tupleKey(tuple Tuple) string {
|
|
return fmt.Sprintf(
|
|
"%s\x00%s\x00%s\x00%s\x00%s\x00%s",
|
|
tuple.ObjectType,
|
|
tuple.ObjectID,
|
|
tuple.Relation,
|
|
tuple.SubjectType,
|
|
tuple.SubjectID,
|
|
tuple.SubjectRelation,
|
|
)
|
|
}
|
|
|
|
func sortedTuples(tuples map[string]Tuple) []Tuple {
|
|
keys := make([]string, 0, len(tuples))
|
|
for key := range tuples {
|
|
keys = append(keys, key)
|
|
}
|
|
sort.Strings(keys)
|
|
out := make([]Tuple, 0, len(keys))
|
|
for _, key := range keys {
|
|
out = append(out, tuples[key])
|
|
}
|
|
return out
|
|
}
|
|
|
|
func teamObjectID(id string) string {
|
|
if strings.HasPrefix(id, "team:") {
|
|
return id
|
|
}
|
|
return "team:" + id
|
|
}
|
|
|
|
func teamOrGroupObjectID(id string) string {
|
|
if strings.HasPrefix(id, "team:") || strings.HasPrefix(id, "group:") || strings.HasPrefix(id, "reader:") {
|
|
return id
|
|
}
|
|
return teamObjectID(id)
|
|
}
|