add remaining org/proj/user initial sync

This commit is contained in:
Mike Mason
2023-07-11 21:32:30 +00:00
parent 80fb879ef6
commit 11fe8f8f2a
15 changed files with 215 additions and 34 deletions

View File

@@ -55,7 +55,7 @@ func WithRootTenant(sid string) Option {
return err
}
s.rootResource = rootResource{id}
s.rootResource = prefixedID{id}
return nil
}

View File

@@ -50,6 +50,7 @@ func (s *service) processMemberships(ctx context.Context, memberships []Resource
roleActions[role] = s.roles[role]
}
resourceRoleID[resourceID][role] = gidx.NullPrefixedID
resourceRoleMembers[resourceID][role][memberID] = true
}

View File

@@ -4,8 +4,41 @@ import (
"context"
"go.infratographer.com/x/gidx"
"go.equinixmetal.net/infra9-metal-bridge/internal/metal/models"
)
func (s *service) buildProjectRelationships(project *models.ProjectDetails) (Relationships, error) {
relations := Relationships{
Relationships: []Relationship{
// Relate project to organization.
{
Resource: project,
Relation: RelateParent,
RelatedResource: project.Organization,
},
},
}
for _, member := range project.Memberships {
for _, role := range member.Roles {
if _, ok := s.roles[role]; !ok {
s.logger.Warnf("unrecognized project role '%s' for %s on %s", role, member.User.PrefixedID(), project.PrefixedID())
continue
}
relations.Memberships = append(relations.Memberships, ResourceMemberships{
Resource: project,
Role: role,
Member: member.User,
})
}
}
return relations, nil
}
func (s *service) IsProjectID(id gidx.PrefixedID) bool {
if idType, ok := s.idPrefixMap[id.Prefix()]; ok {
return idType == TypeProject
@@ -15,6 +48,27 @@ func (s *service) IsProjectID(id gidx.PrefixedID) bool {
}
func (s *service) TouchProject(ctx context.Context, id gidx.PrefixedID) error {
logger := s.logger.With("project.id", id.String())
project, err := s.metal.GetProjectDetails(ctx, id)
if err != nil {
logger.Errorw("failed to get project", "error", err)
return err
}
relationships, err := s.buildProjectRelationships(project)
if err != nil {
logger.Errorw("failed to build project relationships", "error", err)
return err
}
s.processRelationships(ctx, "metal-relation", relationships.Relationships)
s.processMemberships(ctx, relationships.Memberships)
s.logger.Infow("project sync complete", "relationships", len(relationships.Relationships), "memberships", len(relationships.Memberships))
return nil
}

View File

@@ -2,8 +2,6 @@ package service
import (
"go.infratographer.com/x/gidx"
"go.equinixmetal.net/infra9-metal-bridge/internal/metal/models"
)
const (
@@ -60,5 +58,5 @@ type Relationship struct {
type ResourceMemberships struct {
Resource IDPrefixableResource
Role string
Member *models.UserDetails
Member IDPrefixableResource
}

View File

@@ -47,10 +47,13 @@ type Service interface {
// IsUser checks if the provided id has an id prefix which is a user.
IsUser(id gidx.PrefixedID) bool
// TouchUser triggers a sync of a user and their permissions.
TouchUser(ctx context.Context, id gidx.PrefixedID) error
// DeleteUser deletes the user and their permissions.
DeleteUser(ctx context.Context, id gidx.PrefixedID) error
// AssignUser assigns a user to the given resource.
AssignUser(ctx context.Context, userID gidx.PrefixedID, resourceIDs ...gidx.PrefixedID) error
// RemoveUser removes the users from the given resource.
RemoveUser(ctx context.Context, userID gidx.PrefixedID, resourceIDs ...gidx.PrefixedID) error
// IsAssignableResource checks if the provided resource ID may have assigned users.
IsAssignableResource(id gidx.PrefixedID) bool
}
var _ Service = &service{}
@@ -62,15 +65,15 @@ type service struct {
perms *permissions.Client
idPrefixMap map[string]string
rootResource rootResource
rootResource prefixedID
roles map[string][]string
}
type rootResource struct {
type prefixedID struct {
id gidx.PrefixedID
}
func (r rootResource) PrefixedID() gidx.PrefixedID {
func (r prefixedID) PrefixedID() gidx.PrefixedID {
return r.id
}

View File

@@ -14,10 +14,59 @@ func (s *service) IsUser(id gidx.PrefixedID) bool {
return false
}
func (s *service) TouchUser(ctx context.Context, id gidx.PrefixedID) error {
func (s *service) IsAssignableResource(id gidx.PrefixedID) bool {
if idType, ok := s.idPrefixMap[id.Prefix()]; ok {
switch idType {
case TypeOrganization, TypeProject:
return true
default:
return false
}
}
return false
}
func (s *service) AssignUser(ctx context.Context, userID gidx.PrefixedID, resourceIDs ...gidx.PrefixedID) error {
var memberships []ResourceMemberships
for _, resourceID := range resourceIDs {
var (
role string
err error
)
if idType, ok := s.idPrefixMap[resourceID.Prefix()]; ok {
switch idType {
case TypeOrganization:
role, err = s.metal.GetUserOrganizationRole(ctx, userID, resourceID)
case TypeProject:
role, err = s.metal.GetUserProjectRole(ctx, userID, resourceID)
}
}
if err != nil {
return err
}
if role == "" {
continue
}
memberships = append(memberships, ResourceMemberships{
Resource: prefixedID{resourceID},
Role: role,
Member: prefixedID{userID},
})
}
s.processMemberships(ctx, memberships)
s.logger.Infow("assignment sync complete", "memberships", len(memberships))
return nil
}
func (s *service) DeleteUser(ctx context.Context, id gidx.PrefixedID) error {
func (s *service) RemoveUser(ctx context.Context, userID gidx.PrefixedID, resourceIDs ...gidx.PrefixedID) error {
return nil
}