add variable and method comments
This commit is contained in:
@@ -17,22 +17,27 @@ type Client struct {
|
|||||||
provider providers.Provider
|
provider providers.Provider
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetOrganizationDetails fetches the organization id provided with its memberships.
|
||||||
func (c *Client) GetOrganizationDetails(ctx context.Context, id gidx.PrefixedID) (*models.OrganizationDetails, error) {
|
func (c *Client) GetOrganizationDetails(ctx context.Context, id gidx.PrefixedID) (*models.OrganizationDetails, error) {
|
||||||
return c.provider.GetOrganizationDetails(ctx, id)
|
return c.provider.GetOrganizationDetails(ctx, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetProjectDetails fetchs the provided project id with membership information.
|
||||||
func (c *Client) GetProjectDetails(ctx context.Context, id gidx.PrefixedID) (*models.ProjectDetails, error) {
|
func (c *Client) GetProjectDetails(ctx context.Context, id gidx.PrefixedID) (*models.ProjectDetails, error) {
|
||||||
return c.provider.GetProjectDetails(ctx, id)
|
return c.provider.GetProjectDetails(ctx, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUserDetails fetches the provided user id.
|
||||||
func (c *Client) GetUserDetails(ctx context.Context, id gidx.PrefixedID) (*models.UserDetails, error) {
|
func (c *Client) GetUserDetails(ctx context.Context, id gidx.PrefixedID) (*models.UserDetails, error) {
|
||||||
return c.provider.GetUserDetails(ctx, id)
|
return c.provider.GetUserDetails(ctx, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUserOrganizationRole returns the role for the user in the organization.
|
||||||
func (c *Client) GetUserOrganizationRole(ctx context.Context, userID, orgID gidx.PrefixedID) (string, error) {
|
func (c *Client) GetUserOrganizationRole(ctx context.Context, userID, orgID gidx.PrefixedID) (string, error) {
|
||||||
return c.provider.GetUserOrganizationRole(ctx, userID, orgID)
|
return c.provider.GetUserOrganizationRole(ctx, userID, orgID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUserProjectRole returns the role for the user in the project.
|
||||||
func (c *Client) GetUserProjectRole(ctx context.Context, userID, projID gidx.PrefixedID) (string, error) {
|
func (c *Client) GetUserProjectRole(ctx context.Context, userID, projID gidx.PrefixedID) (string, error) {
|
||||||
return c.provider.GetUserProjectRole(ctx, userID, projID)
|
return c.provider.GetUserProjectRole(ctx, userID, projID)
|
||||||
}
|
}
|
||||||
|
|||||||
2
internal/metal/models/doc.go
Normal file
2
internal/metal/models/doc.go
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// Package models defines generic models each provider must be able to return.
|
||||||
|
package models
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
|
// Membership contains metal membership details.
|
||||||
type Membership[T any] struct {
|
type Membership[T any] struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
User *UserDetails `json:"user"`
|
User *UserDetails `json:"user"`
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package models
|
|||||||
|
|
||||||
import "go.infratographer.com/x/gidx"
|
import "go.infratographer.com/x/gidx"
|
||||||
|
|
||||||
|
// ProjectDetails contains project and membership information.
|
||||||
type ProjectDetails struct {
|
type ProjectDetails struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@@ -9,6 +10,7 @@ type ProjectDetails struct {
|
|||||||
Organization *OrganizationDetails `json:"organization"`
|
Organization *OrganizationDetails `json:"organization"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PrefixedID returns the prefixed id for the project.
|
||||||
func (d *ProjectDetails) PrefixedID() gidx.PrefixedID {
|
func (d *ProjectDetails) PrefixedID() gidx.PrefixedID {
|
||||||
if d.ID == "" {
|
if d.ID == "" {
|
||||||
return gidx.NullPrefixedID
|
return gidx.NullPrefixedID
|
||||||
|
|||||||
@@ -8,10 +8,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MetalUserIssuer = "https://auth.equinix.com/"
|
// MetalUserIssuer is the issuer that is used for metal api token users.
|
||||||
|
MetalUserIssuer = "https://auth.equinix.com/"
|
||||||
|
|
||||||
|
// MetaluserIssuerIDPrefix is the issuer id prefix added by the issuer.
|
||||||
MetalUserIssuerIDPrefix = "auth|"
|
MetalUserIssuerIDPrefix = "auth|"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// UserDetails contains the user information.
|
||||||
type UserDetails struct {
|
type UserDetails struct {
|
||||||
id *gidx.PrefixedID
|
id *gidx.PrefixedID
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
@@ -21,6 +25,7 @@ type UserDetails struct {
|
|||||||
Roles []string `json:"roles"`
|
Roles []string `json:"roles"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PrefixedID returns the identity prefixed id for the user.
|
||||||
func (d *UserDetails) PrefixedID() gidx.PrefixedID {
|
func (d *UserDetails) PrefixedID() gidx.PrefixedID {
|
||||||
if d.id != nil {
|
if d.id != nil {
|
||||||
return *d.id
|
return *d.id
|
||||||
@@ -44,6 +49,7 @@ func (d *UserDetails) PrefixedID() gidx.PrefixedID {
|
|||||||
return *d.id
|
return *d.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenerateSubjectID builds a identity prefixed id with the provided prefix for the issuer and subject.
|
||||||
func GenerateSubjectID(prefix, iss, sub string) (gidx.PrefixedID, error) {
|
func GenerateSubjectID(prefix, iss, sub string) (gidx.PrefixedID, error) {
|
||||||
// Concatenate the iss and sub values, then hash them
|
// Concatenate the iss and sub values, then hash them
|
||||||
issSub := iss + sub
|
issSub := iss + sub
|
||||||
|
|||||||
@@ -25,12 +25,14 @@ const (
|
|||||||
staffHeaderValue = "true"
|
staffHeaderValue = "true"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DefaultHTTPClient is the default http client used if no client is provided.
|
||||||
var DefaultHTTPClient = &http.Client{
|
var DefaultHTTPClient = &http.Client{
|
||||||
Timeout: defaultHTTPTimeout,
|
Timeout: defaultHTTPTimeout,
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ providers.Provider = &Client{}
|
var _ providers.Provider = &Client{}
|
||||||
|
|
||||||
|
// Client is the client to interact with the equinix metal api.
|
||||||
type Client struct {
|
type Client struct {
|
||||||
logger *zap.SugaredLogger
|
logger *zap.SugaredLogger
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
@@ -39,6 +41,8 @@ type Client struct {
|
|||||||
consumerToken string
|
consumerToken string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do executes the provided request.
|
||||||
|
// If the out value is provided, the response will attempt to be json decoded.
|
||||||
func (c *Client) Do(req *http.Request, out any) (*http.Response, error) {
|
func (c *Client) Do(req *http.Request, out any) (*http.Response, error) {
|
||||||
if c.authToken != "" {
|
if c.authToken != "" {
|
||||||
req.Header.Set(authHeader, c.authToken)
|
req.Header.Set(authHeader, c.authToken)
|
||||||
@@ -68,6 +72,7 @@ func (c *Client) Do(req *http.Request, out any) (*http.Response, error) {
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DoRequest creates a new request from the provided parameters and executes the request.
|
||||||
func (c *Client) DoRequest(ctx context.Context, method, path string, body io.Reader, out any) (*http.Response, error) {
|
func (c *Client) DoRequest(ctx context.Context, method, path string, body io.Reader, out any) (*http.Response, error) {
|
||||||
path = strings.TrimPrefix(path, c.baseURL.Path)
|
path = strings.TrimPrefix(path, c.baseURL.Path)
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ type Config struct {
|
|||||||
ConsumerToken string
|
ConsumerToken string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Populated checks if any field has been populated.
|
||||||
func (c Config) Populated() bool {
|
func (c Config) Populated() bool {
|
||||||
return c.AuthToken != "" || c.ConsumerToken != "" || c.BaseURL != ""
|
return c.AuthToken != "" || c.ConsumerToken != "" || c.BaseURL != ""
|
||||||
}
|
}
|
||||||
|
|||||||
2
internal/metal/providers/emapi/doc.go
Normal file
2
internal/metal/providers/emapi/doc.go
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// Package emapi implement a metal provider which fetches details from the Equinix Metal API.
|
||||||
|
package emapi
|
||||||
@@ -2,4 +2,5 @@ package emapi
|
|||||||
|
|
||||||
import "errors"
|
import "errors"
|
||||||
|
|
||||||
|
// ErrBaseURLRequired is returned if no base url is provided.
|
||||||
var ErrBaseURLRequired = errors.New("emapi base url required")
|
var ErrBaseURLRequired = errors.New("emapi base url required")
|
||||||
|
|||||||
@@ -4,10 +4,13 @@ import (
|
|||||||
"go.equinixmetal.net/infra9-metal-bridge/internal/metal/models"
|
"go.equinixmetal.net/infra9-metal-bridge/internal/metal/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Roles contains a list of roles.
|
||||||
type Roles []string
|
type Roles []string
|
||||||
|
|
||||||
|
// Memberships contains a list of memberships
|
||||||
type Memberships []*Membership
|
type Memberships []*Membership
|
||||||
|
|
||||||
|
// ToDetailsWithOrganizationDetails convers the memberships to generic membership models with organization details.
|
||||||
func (m Memberships) ToDetailsWithOrganizationDetails(orgDetails *models.OrganizationDetails) []*models.Membership[models.OrganizationDetails] {
|
func (m Memberships) ToDetailsWithOrganizationDetails(orgDetails *models.OrganizationDetails) []*models.Membership[models.OrganizationDetails] {
|
||||||
memberships := make([]*models.Membership[models.OrganizationDetails], len(m))
|
memberships := make([]*models.Membership[models.OrganizationDetails], len(m))
|
||||||
|
|
||||||
@@ -28,6 +31,7 @@ func (m Memberships) ToDetailsWithOrganizationDetails(orgDetails *models.Organiz
|
|||||||
return memberships
|
return memberships
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToDetailsWithProjectDetails convers the memberships to generic membership models with project details.
|
||||||
func (m Memberships) ToDetailsWithProjectDetails(projDetails *models.ProjectDetails) []*models.Membership[models.ProjectDetails] {
|
func (m Memberships) ToDetailsWithProjectDetails(projDetails *models.ProjectDetails) []*models.Membership[models.ProjectDetails] {
|
||||||
memberships := make([]*models.Membership[models.ProjectDetails], len(m))
|
memberships := make([]*models.Membership[models.ProjectDetails], len(m))
|
||||||
|
|
||||||
@@ -48,6 +52,7 @@ func (m Memberships) ToDetailsWithProjectDetails(projDetails *models.ProjectDeta
|
|||||||
return memberships
|
return memberships
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Membership contains membership information.
|
||||||
type Membership struct {
|
type Membership struct {
|
||||||
client *Client
|
client *Client
|
||||||
|
|
||||||
@@ -57,6 +62,7 @@ type Membership struct {
|
|||||||
User *User `json:"user"`
|
User *User `json:"user"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToDetailsWithOrganizationDetails convers the membership to generic membership model with organization details.
|
||||||
func (m *Membership) ToDetailsWithOrganizationDetails(orgDetails *models.OrganizationDetails) *models.Membership[models.OrganizationDetails] {
|
func (m *Membership) ToDetailsWithOrganizationDetails(orgDetails *models.OrganizationDetails) *models.Membership[models.OrganizationDetails] {
|
||||||
if m.ID == "" {
|
if m.ID == "" {
|
||||||
return nil
|
return nil
|
||||||
@@ -70,6 +76,7 @@ func (m *Membership) ToDetailsWithOrganizationDetails(orgDetails *models.Organiz
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToDetailsWithOrganizationDetails convers the membership to generic membership model with organization details.
|
||||||
func (m *Membership) ToDetailsWithProjectDetails(projDetails *models.ProjectDetails) *models.Membership[models.ProjectDetails] {
|
func (m *Membership) ToDetailsWithProjectDetails(projDetails *models.ProjectDetails) *models.Membership[models.ProjectDetails] {
|
||||||
if m.ID == "" {
|
if m.ID == "" {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -14,8 +14,10 @@ const (
|
|||||||
organizationsPath = "/organizations"
|
organizationsPath = "/organizations"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Organizations contains a list of organizations.
|
||||||
type Organizations []*Organization
|
type Organizations []*Organization
|
||||||
|
|
||||||
|
// ToDetails converts to a generic model organization details.
|
||||||
func (o Organizations) ToDetails() []*models.OrganizationDetails {
|
func (o Organizations) ToDetails() []*models.OrganizationDetails {
|
||||||
orgs := make([]*models.OrganizationDetails, len(o))
|
orgs := make([]*models.OrganizationDetails, len(o))
|
||||||
|
|
||||||
@@ -36,6 +38,7 @@ func (o Organizations) ToDetails() []*models.OrganizationDetails {
|
|||||||
return orgs
|
return orgs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Organization contains organization information.
|
||||||
type Organization struct {
|
type Organization struct {
|
||||||
client *Client
|
client *Client
|
||||||
|
|
||||||
@@ -47,6 +50,7 @@ type Organization struct {
|
|||||||
Projects Projects `json:"projects"`
|
Projects Projects `json:"projects"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToDetails converts the object to a generic orgnization details.
|
||||||
func (o *Organization) ToDetails() *models.OrganizationDetails {
|
func (o *Organization) ToDetails() *models.OrganizationDetails {
|
||||||
var id string
|
var id string
|
||||||
|
|
||||||
@@ -69,6 +73,7 @@ func (o *Organization) ToDetails() *models.OrganizationDetails {
|
|||||||
return details
|
return details
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getOrganizationWithMemberships fetches an organization from the equinix metal api with membership user information.
|
||||||
func (c *Client) getOrganizationWithMemberships(ctx context.Context, id string) (*Organization, error) {
|
func (c *Client) getOrganizationWithMemberships(ctx context.Context, id string) (*Organization, error) {
|
||||||
var org Organization
|
var org Organization
|
||||||
|
|
||||||
@@ -80,6 +85,7 @@ func (c *Client) getOrganizationWithMemberships(ctx context.Context, id string)
|
|||||||
return &org, nil
|
return &org, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetOrganizationDetails fetches the organization id provided with its memberships.
|
||||||
func (c *Client) GetOrganizationDetails(ctx context.Context, id gidx.PrefixedID) (*models.OrganizationDetails, error) {
|
func (c *Client) GetOrganizationDetails(ctx context.Context, id gidx.PrefixedID) (*models.OrganizationDetails, error) {
|
||||||
org, err := c.getOrganizationWithMemberships(ctx, id.String()[gidx.PrefixPartLength+1:])
|
org, err := c.getOrganizationWithMemberships(ctx, id.String()[gidx.PrefixPartLength+1:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -14,8 +14,10 @@ const (
|
|||||||
projectsPath = "/projects"
|
projectsPath = "/projects"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Projects contains a list of projects.
|
||||||
type Projects []*Project
|
type Projects []*Project
|
||||||
|
|
||||||
|
// ToDetails converts the objects to generic project details.
|
||||||
func (p Projects) ToDetails() []*models.ProjectDetails {
|
func (p Projects) ToDetails() []*models.ProjectDetails {
|
||||||
projects := make([]*models.ProjectDetails, len(p))
|
projects := make([]*models.ProjectDetails, len(p))
|
||||||
|
|
||||||
@@ -36,6 +38,7 @@ func (p Projects) ToDetails() []*models.ProjectDetails {
|
|||||||
return projects
|
return projects
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Project contains project information.
|
||||||
type Project struct {
|
type Project struct {
|
||||||
client *Client
|
client *Client
|
||||||
|
|
||||||
@@ -47,6 +50,7 @@ type Project struct {
|
|||||||
Organization *Organization `json:"organization"`
|
Organization *Organization `json:"organization"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToDetails converts the project to generic project details.
|
||||||
func (p *Project) ToDetails() *models.ProjectDetails {
|
func (p *Project) ToDetails() *models.ProjectDetails {
|
||||||
var id string
|
var id string
|
||||||
|
|
||||||
@@ -69,6 +73,7 @@ func (p *Project) ToDetails() *models.ProjectDetails {
|
|||||||
return details
|
return details
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getProjectWithMemberships fetches the provided project with membership information.
|
||||||
func (c *Client) getProjectWithMemberships(ctx context.Context, id string) (*Project, error) {
|
func (c *Client) getProjectWithMemberships(ctx context.Context, id string) (*Project, error) {
|
||||||
var project Project
|
var project Project
|
||||||
|
|
||||||
@@ -80,6 +85,7 @@ func (c *Client) getProjectWithMemberships(ctx context.Context, id string) (*Pro
|
|||||||
return &project, nil
|
return &project, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetProjectDetails fetchs the provided project id with membership information.
|
||||||
func (c *Client) GetProjectDetails(ctx context.Context, id gidx.PrefixedID) (*models.ProjectDetails, error) {
|
func (c *Client) GetProjectDetails(ctx context.Context, id gidx.PrefixedID) (*models.ProjectDetails, error) {
|
||||||
project, err := c.getProjectWithMemberships(ctx, id.String()[gidx.PrefixPartLength+1:])
|
project, err := c.getProjectWithMemberships(ctx, id.String()[gidx.PrefixPartLength+1:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -14,8 +14,10 @@ const (
|
|||||||
usersPath = "/users"
|
usersPath = "/users"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Users contains a list of users.
|
||||||
type Users []*User
|
type Users []*User
|
||||||
|
|
||||||
|
// ToDetails converts the objects to generic user details.
|
||||||
func (u Users) ToDetails() []*models.UserDetails {
|
func (u Users) ToDetails() []*models.UserDetails {
|
||||||
users := make([]*models.UserDetails, len(u))
|
users := make([]*models.UserDetails, len(u))
|
||||||
|
|
||||||
@@ -36,6 +38,7 @@ func (u Users) ToDetails() []*models.UserDetails {
|
|||||||
return users
|
return users
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// User contains user information.
|
||||||
type User struct {
|
type User struct {
|
||||||
client *Client
|
client *Client
|
||||||
|
|
||||||
@@ -46,6 +49,7 @@ type User struct {
|
|||||||
Projects Projects `json:"projects"`
|
Projects Projects `json:"projects"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToDetails converts the user to generic user details.
|
||||||
func (u *User) ToDetails() *models.UserDetails {
|
func (u *User) ToDetails() *models.UserDetails {
|
||||||
var id string
|
var id string
|
||||||
|
|
||||||
@@ -65,6 +69,7 @@ func (u *User) ToDetails() *models.UserDetails {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getUser fetches the provided user.
|
||||||
func (c *Client) getUser(ctx context.Context, id string) (*User, error) {
|
func (c *Client) getUser(ctx context.Context, id string) (*User, error) {
|
||||||
var user User
|
var user User
|
||||||
|
|
||||||
@@ -76,6 +81,7 @@ func (c *Client) getUser(ctx context.Context, id string) (*User, error) {
|
|||||||
return &user, nil
|
return &user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUserDetails fetches the provided user id.
|
||||||
func (c *Client) GetUserDetails(ctx context.Context, id gidx.PrefixedID) (*models.UserDetails, error) {
|
func (c *Client) GetUserDetails(ctx context.Context, id gidx.PrefixedID) (*models.UserDetails, error) {
|
||||||
user, err := c.getUser(ctx, id.String()[gidx.PrefixPartLength+1:])
|
user, err := c.getUser(ctx, id.String()[gidx.PrefixPartLength+1:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -85,10 +91,12 @@ func (c *Client) GetUserDetails(ctx context.Context, id gidx.PrefixedID) (*model
|
|||||||
return user.ToDetails(), nil
|
return user.ToDetails(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUserOrganizationRole returns collaborator for all organizations.
|
||||||
func (c *Client) GetUserOrganizationRole(ctx context.Context, userID, orgID gidx.PrefixedID) (string, error) {
|
func (c *Client) GetUserOrganizationRole(ctx context.Context, userID, orgID gidx.PrefixedID) (string, error) {
|
||||||
return "collaborator", nil
|
return "collaborator", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUserProjectRole returns collaborator for all projects.
|
||||||
func (c *Client) GetUserProjectRole(ctx context.Context, userID, projectID gidx.PrefixedID) (string, error) {
|
func (c *Client) GetUserProjectRole(ctx context.Context, userID, projectID gidx.PrefixedID) (string, error) {
|
||||||
return "collaborator", nil
|
return "collaborator", nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,12 +14,14 @@ const (
|
|||||||
defaultHTTPTimeout = 5 * time.Second
|
defaultHTTPTimeout = 5 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DefaultHTTPClient is the default http client used if no client is provided.
|
||||||
var DefaultHTTPClient = &http.Client{
|
var DefaultHTTPClient = &http.Client{
|
||||||
Timeout: defaultHTTPTimeout,
|
Timeout: defaultHTTPTimeout,
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ providers.Provider = &Client{}
|
var _ providers.Provider = &Client{}
|
||||||
|
|
||||||
|
// Client is the client to interact with the equinix metal graphql service.
|
||||||
type Client struct {
|
type Client struct {
|
||||||
logger *zap.SugaredLogger
|
logger *zap.SugaredLogger
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ package emgql
|
|||||||
|
|
||||||
// Config provides configuration for connecting to the Equinix Metal API provider.
|
// Config provides configuration for connecting to the Equinix Metal API provider.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
|
||||||
// BaseURL is the baseurl to use when connecting to the Equinix Metal API Provider.
|
// BaseURL is the baseurl to use when connecting to the Equinix Metal API Provider.
|
||||||
BaseURL string
|
BaseURL string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Populated checks if any field has been populated.
|
||||||
func (c Config) Populated() bool {
|
func (c Config) Populated() bool {
|
||||||
return c.BaseURL != ""
|
return c.BaseURL != ""
|
||||||
}
|
}
|
||||||
|
|||||||
2
internal/metal/providers/emgql/doc.go
Normal file
2
internal/metal/providers/emgql/doc.go
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// Package emgql implements a metal provider which fetches details from the Equinix Metal GraphQL.
|
||||||
|
package emgql
|
||||||
@@ -3,10 +3,12 @@ package emgql
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"go.equinixmetal.net/infra9-metal-bridge/internal/metal/models"
|
|
||||||
"go.infratographer.com/x/gidx"
|
"go.infratographer.com/x/gidx"
|
||||||
|
|
||||||
|
"go.equinixmetal.net/infra9-metal-bridge/internal/metal/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// GetOrganizationDetails fetches the organization id provided with its memberships.
|
||||||
func (c *Client) GetOrganizationDetails(ctx context.Context, id gidx.PrefixedID) (*models.OrganizationDetails, error) {
|
func (c *Client) GetOrganizationDetails(ctx context.Context, id gidx.PrefixedID) (*models.OrganizationDetails, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ package emgql
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"go.equinixmetal.net/infra9-metal-bridge/internal/metal/models"
|
|
||||||
"go.infratographer.com/x/gidx"
|
"go.infratographer.com/x/gidx"
|
||||||
|
|
||||||
|
"go.equinixmetal.net/infra9-metal-bridge/internal/metal/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// GetProjectDetails fetchs the provided project id with membership information.
|
||||||
func (c *Client) GetProjectDetails(ctx context.Context, id gidx.PrefixedID) (*models.ProjectDetails, error) {
|
func (c *Client) GetProjectDetails(ctx context.Context, id gidx.PrefixedID) (*models.ProjectDetails, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,14 +8,17 @@ import (
|
|||||||
"go.equinixmetal.net/infra9-metal-bridge/internal/metal/models"
|
"go.equinixmetal.net/infra9-metal-bridge/internal/metal/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// GetUserDetails fetches the provided user id.
|
||||||
func (c *Client) GetUserDetails(ctx context.Context, id gidx.PrefixedID) (*models.UserDetails, error) {
|
func (c *Client) GetUserDetails(ctx context.Context, id gidx.PrefixedID) (*models.UserDetails, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUserOrganizationRole returns collaborator for all organizations.
|
||||||
func (c *Client) GetUserOrganizationRole(ctx context.Context, userID, orgID gidx.PrefixedID) (string, error) {
|
func (c *Client) GetUserOrganizationRole(ctx context.Context, userID, orgID gidx.PrefixedID) (string, error) {
|
||||||
return "collaborator", nil
|
return "collaborator", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUserProjectRole returns collaborator for all projects.
|
||||||
func (c *Client) GetUserProjectRole(ctx context.Context, userID, projID gidx.PrefixedID) (string, error) {
|
func (c *Client) GetUserProjectRole(ctx context.Context, userID, projID gidx.PrefixedID) (string, error) {
|
||||||
return "collaborator", nil
|
return "collaborator", nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// Package providers defines the provider interface for fetching metal resources.
|
||||||
package providers
|
package providers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -8,6 +9,7 @@ import (
|
|||||||
"go.equinixmetal.net/infra9-metal-bridge/internal/metal/models"
|
"go.equinixmetal.net/infra9-metal-bridge/internal/metal/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Provider defines the provider implementation.
|
||||||
type Provider interface {
|
type Provider interface {
|
||||||
GetOrganizationDetails(ctx context.Context, id gidx.PrefixedID) (*models.OrganizationDetails, error)
|
GetOrganizationDetails(ctx context.Context, id gidx.PrefixedID) (*models.OrganizationDetails, error)
|
||||||
GetProjectDetails(ctx context.Context, id gidx.PrefixedID) (*models.ProjectDetails, error)
|
GetProjectDetails(ctx context.Context, id gidx.PrefixedID) (*models.ProjectDetails, error)
|
||||||
|
|||||||
@@ -8,20 +8,24 @@ import (
|
|||||||
"go.infratographer.com/x/gidx"
|
"go.infratographer.com/x/gidx"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// RoleAssign is the role assignment request body.
|
||||||
type RoleAssign struct {
|
type RoleAssign struct {
|
||||||
SubjectID string `json:"subject_id"`
|
SubjectID string `json:"subject_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RoleAssignResponse is the response from a role assignment.
|
||||||
type RoleAssignResponse struct {
|
type RoleAssignResponse struct {
|
||||||
Success bool `json:"success"`
|
Success bool `json:"success"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// roleAssignmentData is the response from listing a role assignment
|
||||||
type roleAssignmentData struct {
|
type roleAssignmentData struct {
|
||||||
Data []struct {
|
Data []struct {
|
||||||
SubjectID string `json:"subject_id"`
|
SubjectID string `json:"subject_id"`
|
||||||
} `json:"data"`
|
} `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AssignRole assigns the provided member ID to the given role ID.
|
||||||
func (c *Client) AssignRole(ctx context.Context, roleID gidx.PrefixedID, memberID gidx.PrefixedID) error {
|
func (c *Client) AssignRole(ctx context.Context, roleID gidx.PrefixedID, memberID gidx.PrefixedID) error {
|
||||||
path := fmt.Sprintf("/api/v1/roles/%s/assignments", roleID.String())
|
path := fmt.Sprintf("/api/v1/roles/%s/assignments", roleID.String())
|
||||||
|
|
||||||
@@ -45,6 +49,7 @@ func (c *Client) AssignRole(ctx context.Context, roleID gidx.PrefixedID, memberI
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnassignRole removes the provided member ID from the given role ID.
|
||||||
func (c *Client) UnassignRole(ctx context.Context, roleID gidx.PrefixedID, memberID gidx.PrefixedID) error {
|
func (c *Client) UnassignRole(ctx context.Context, roleID gidx.PrefixedID, memberID gidx.PrefixedID) error {
|
||||||
path := fmt.Sprintf("/api/v1/roles/%s/assignments", roleID.String())
|
path := fmt.Sprintf("/api/v1/roles/%s/assignments", roleID.String())
|
||||||
|
|
||||||
@@ -68,6 +73,7 @@ func (c *Client) UnassignRole(ctx context.Context, roleID gidx.PrefixedID, membe
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListRoleAssignments lists all assignments for the given role.
|
||||||
func (c *Client) ListRoleAssignments(ctx context.Context, roleID gidx.PrefixedID) ([]gidx.PrefixedID, error) {
|
func (c *Client) ListRoleAssignments(ctx context.Context, roleID gidx.PrefixedID) ([]gidx.PrefixedID, error) {
|
||||||
path := fmt.Sprintf("/api/v1/roles/%s/assignments", roleID.String())
|
path := fmt.Sprintf("/api/v1/roles/%s/assignments", roleID.String())
|
||||||
|
|
||||||
@@ -91,6 +97,7 @@ func (c *Client) ListRoleAssignments(ctx context.Context, roleID gidx.PrefixedID
|
|||||||
return assignments, nil
|
return assignments, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RoleHasAssignment gets the assignments for the given role and check for the provided member id.
|
||||||
func (c *Client) RoleHasAssignment(ctx context.Context, roleID gidx.PrefixedID, memberID gidx.PrefixedID) (bool, error) {
|
func (c *Client) RoleHasAssignment(ctx context.Context, roleID gidx.PrefixedID, memberID gidx.PrefixedID) (bool, error) {
|
||||||
assignments, err := c.ListRoleAssignments(ctx, roleID)
|
assignments, err := c.ListRoleAssignments(ctx, roleID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ var defaultHTTPClient = &http.Client{
|
|||||||
Timeout: 5 * time.Second,
|
Timeout: 5 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Client is the permissions client.
|
||||||
type Client struct {
|
type Client struct {
|
||||||
logger *zap.SugaredLogger
|
logger *zap.SugaredLogger
|
||||||
|
|
||||||
@@ -32,6 +33,8 @@ type Client struct {
|
|||||||
allowURL *url.URL
|
allowURL *url.URL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do executes the provided request.
|
||||||
|
// If the out value is provided, the response will attempt to be json decoded.
|
||||||
func (c *Client) Do(req *http.Request, out any) (*http.Response, error) {
|
func (c *Client) Do(req *http.Request, out any) (*http.Response, error) {
|
||||||
if c.token != "" {
|
if c.token != "" {
|
||||||
req.Header.Set(echo.HeaderAuthorization, "Bearer "+c.token)
|
req.Header.Set(echo.HeaderAuthorization, "Bearer "+c.token)
|
||||||
@@ -55,6 +58,7 @@ func (c *Client) Do(req *http.Request, out any) (*http.Response, error) {
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DoRequest creates a new request from the provided parameters and executes the request.
|
||||||
func (c *Client) DoRequest(ctx context.Context, method, path string, body io.Reader, out any) (*http.Response, error) {
|
func (c *Client) DoRequest(ctx context.Context, method, path string, body io.Reader, out any) (*http.Response, error) {
|
||||||
path = strings.TrimPrefix(path, c.baseURL.Path)
|
path = strings.TrimPrefix(path, c.baseURL.Path)
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ type Config struct {
|
|||||||
BearerToken string
|
BearerToken string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MustViperFlags registers command flags along with the viper bindings.
|
||||||
func MustViperFlags(v *viper.Viper, flags *pflag.FlagSet) {
|
func MustViperFlags(v *viper.Viper, flags *pflag.FlagSet) {
|
||||||
flags.String("permissions-baseurl", "", "permissions base url")
|
flags.String("permissions-baseurl", "", "permissions base url")
|
||||||
viperx.MustBindFlag(v, "permissions.baseurl", flags.Lookup("permissions-baseurl"))
|
viperx.MustBindFlag(v, "permissions.baseurl", flags.Lookup("permissions-baseurl"))
|
||||||
|
|||||||
2
internal/permissions/doc.go
Normal file
2
internal/permissions/doc.go
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// Package permissions implements a Permissions API client for fetching and manipulating relationships and role assignments.
|
||||||
|
package permissions
|
||||||
@@ -3,9 +3,18 @@ package permissions
|
|||||||
import "errors"
|
import "errors"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrRoleNotFound = errors.New("role not found")
|
// ErrRoleNotFound is returned when no role is found for a given list of actions.
|
||||||
ErrAssignmentFailed = errors.New("assignment failed")
|
ErrRoleNotFound = errors.New("role not found")
|
||||||
ErrUnassignmentFailed = errors.New("unassignment failed")
|
|
||||||
ErrUnexpectedRoleDeleteFailed = errors.New("unknown role delete error")
|
// ErrAssignmentFailed is returned when a user assignment to a role fails.
|
||||||
|
ErrAssignmentFailed = errors.New("assignment failed")
|
||||||
|
|
||||||
|
// ErrUnassignmentFailed is returned when a user assignment is removed from a role fails.
|
||||||
|
ErrUnassignmentFailed = errors.New("unassignment failed")
|
||||||
|
|
||||||
|
// ErrUnexpectedRoleDeleteFailed is returned when an unknown error is returned when deleting a role.
|
||||||
|
ErrUnexpectedRoleDeleteFailed = errors.New("unknown role delete error")
|
||||||
|
|
||||||
|
// ErrUnexpectedRelationshipDeleteFailed is returned when an unknown error is returned when deleting a relationship.
|
||||||
ErrUnexpectedRelationshipDeleteFailed = errors.New("unknown relationship delete error")
|
ErrUnexpectedRelationshipDeleteFailed = errors.New("unknown relationship delete error")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ import (
|
|||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Option is a client configuration option definition.
|
||||||
type Option func(*Client) error
|
type Option func(*Client) error
|
||||||
|
|
||||||
|
// WithLogger sets the logger for the client.
|
||||||
func WithLogger(logger *zap.SugaredLogger) Option {
|
func WithLogger(logger *zap.SugaredLogger) Option {
|
||||||
return func(c *Client) error {
|
return func(c *Client) error {
|
||||||
c.logger = logger
|
c.logger = logger
|
||||||
|
|||||||
@@ -15,21 +15,25 @@ type resourceRelationship struct {
|
|||||||
SubjectID string `json:"subject_id"`
|
SubjectID string `json:"subject_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResourceRelationship defines the resource to subject relationship.
|
||||||
type ResourceRelationship struct {
|
type ResourceRelationship struct {
|
||||||
ResourceID gidx.PrefixedID
|
ResourceID gidx.PrefixedID
|
||||||
Relation string
|
Relation string
|
||||||
SubjectID gidx.PrefixedID
|
SubjectID gidx.PrefixedID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResourceRelationshipRequest defines the request to relate to a subject.
|
||||||
type ResourceRelationshipRequest struct {
|
type ResourceRelationshipRequest struct {
|
||||||
Relation string `json:"relation"`
|
Relation string `json:"relation"`
|
||||||
SubjectID string `json:"subject_id"`
|
SubjectID string `json:"subject_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResourceRelationshipDeleteResponse defines the response for a delete of a relationship.
|
||||||
type ResourceRelationshipDeleteResponse struct {
|
type ResourceRelationshipDeleteResponse struct {
|
||||||
Success bool `json:"success"`
|
Success bool `json:"success"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteResourceRelationship deletes the provided resources relationship to the given subject id.
|
||||||
func (c *Client) DeleteResourceRelationship(ctx context.Context, resourceID gidx.PrefixedID, relation string, relatedResourceID gidx.PrefixedID) error {
|
func (c *Client) DeleteResourceRelationship(ctx context.Context, resourceID gidx.PrefixedID, relation string, relatedResourceID gidx.PrefixedID) error {
|
||||||
path := fmt.Sprintf("/api/v1/resources/%s/relationships", resourceID.String())
|
path := fmt.Sprintf("/api/v1/resources/%s/relationships", resourceID.String())
|
||||||
|
|
||||||
@@ -54,6 +58,9 @@ func (c *Client) DeleteResourceRelationship(ctx context.Context, resourceID gidx
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListResourceRelationships returns resources related to the given id.
|
||||||
|
// If relatedResourceType is not provied, relations to subjects are returned.
|
||||||
|
// If relatedResourceType is provided, relations to the given resource are returned which match the given type.
|
||||||
func (c *Client) ListResourceRelationships(ctx context.Context, resourceID gidx.PrefixedID, relatedResourceType string) ([]ResourceRelationship, error) {
|
func (c *Client) ListResourceRelationships(ctx context.Context, resourceID gidx.PrefixedID, relatedResourceType string) ([]ResourceRelationship, error) {
|
||||||
query := url.Values{
|
query := url.Values{
|
||||||
"resourceType": []string{relatedResourceType},
|
"resourceType": []string{relatedResourceType},
|
||||||
|
|||||||
@@ -9,25 +9,31 @@ import (
|
|||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ResourceRoleCreate is the role create request.
|
||||||
type ResourceRoleCreate struct {
|
type ResourceRoleCreate struct {
|
||||||
Actions []string `json:"actions"`
|
Actions []string `json:"actions"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResourceRoleCreateResponse is the role creation response.
|
||||||
type ResourceRoleCreateResponse struct {
|
type ResourceRoleCreateResponse struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResourceRoleDeleteResponse is the role deletion response.
|
||||||
type ResourceRoleDeleteResponse struct {
|
type ResourceRoleDeleteResponse struct {
|
||||||
Success bool `json:"success"`
|
Success bool `json:"success"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResourceRoles is a listg of resource roles.
|
||||||
type ResourceRoles []ResourceRole
|
type ResourceRoles []ResourceRole
|
||||||
|
|
||||||
|
// ResourceRole contains the role id and its actions.
|
||||||
type ResourceRole struct {
|
type ResourceRole struct {
|
||||||
ID gidx.PrefixedID `json:"id"`
|
ID gidx.PrefixedID `json:"id"`
|
||||||
Actions []string `json:"actions"`
|
Actions []string `json:"actions"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateRole creates a role on the given resource id with the provided actions.
|
||||||
func (c *Client) CreateRole(ctx context.Context, resourceID gidx.PrefixedID, actions []string) (gidx.PrefixedID, error) {
|
func (c *Client) CreateRole(ctx context.Context, resourceID gidx.PrefixedID, actions []string) (gidx.PrefixedID, error) {
|
||||||
path := fmt.Sprintf("/api/v1/resources/%s/roles", resourceID.String())
|
path := fmt.Sprintf("/api/v1/resources/%s/roles", resourceID.String())
|
||||||
|
|
||||||
@@ -52,6 +58,7 @@ func (c *Client) CreateRole(ctx context.Context, resourceID gidx.PrefixedID, act
|
|||||||
return roleID, nil
|
return roleID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteRole deletes the provided role.
|
||||||
func (c *Client) DeleteRole(ctx context.Context, roleID gidx.PrefixedID) error {
|
func (c *Client) DeleteRole(ctx context.Context, roleID gidx.PrefixedID) error {
|
||||||
path := fmt.Sprintf("/api/v1/roles/%s", roleID.String())
|
path := fmt.Sprintf("/api/v1/roles/%s", roleID.String())
|
||||||
|
|
||||||
@@ -68,6 +75,7 @@ func (c *Client) DeleteRole(ctx context.Context, roleID gidx.PrefixedID) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListResourceRoles fetches all roles assigned to the provided resource.
|
||||||
func (c *Client) ListResourceRoles(ctx context.Context, resourceID gidx.PrefixedID) (ResourceRoles, error) {
|
func (c *Client) ListResourceRoles(ctx context.Context, resourceID gidx.PrefixedID) (ResourceRoles, error) {
|
||||||
path := fmt.Sprintf("/api/v1/resources/%s/roles", resourceID.String())
|
path := fmt.Sprintf("/api/v1/resources/%s/roles", resourceID.String())
|
||||||
|
|
||||||
@@ -82,6 +90,7 @@ func (c *Client) ListResourceRoles(ctx context.Context, resourceID gidx.Prefixed
|
|||||||
return response.Data, nil
|
return response.Data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FindResourceRoleByActions fetches roles assigned to the provided resource and finds the first role where the actions match the provided actions.
|
||||||
func (c *Client) FindResourceRoleByActions(ctx context.Context, resourceID gidx.PrefixedID, actions []string) (ResourceRole, error) {
|
func (c *Client) FindResourceRoleByActions(ctx context.Context, resourceID gidx.PrefixedID, actions []string) (ResourceRole, error) {
|
||||||
roles, err := c.ListResourceRoles(ctx, resourceID)
|
roles, err := c.ListResourceRoles(ctx, resourceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
const organizationEvent = "metalorganization"
|
const organizationEvent = "metalorganization"
|
||||||
|
|
||||||
|
// buildOrganizationRelationships compiles all relations into a relationships object to be processed by the processors.
|
||||||
func (s *service) buildOrganizationRelationships(org *models.OrganizationDetails) (Relationships, error) {
|
func (s *service) buildOrganizationRelationships(org *models.OrganizationDetails) (Relationships, error) {
|
||||||
relations := Relationships{
|
relations := Relationships{
|
||||||
Resource: org,
|
Resource: org,
|
||||||
@@ -46,6 +47,7 @@ func (s *service) buildOrganizationRelationships(org *models.OrganizationDetails
|
|||||||
return relations, nil
|
return relations, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsOrganizationID checks if the provided id has the metal organization prefix.
|
||||||
func (s *service) IsOrganizationID(id gidx.PrefixedID) bool {
|
func (s *service) IsOrganizationID(id gidx.PrefixedID) bool {
|
||||||
if idType, ok := s.idPrefixMap[id.Prefix()]; ok {
|
if idType, ok := s.idPrefixMap[id.Prefix()]; ok {
|
||||||
return idType == TypeOrganization
|
return idType == TypeOrganization
|
||||||
@@ -54,6 +56,7 @@ func (s *service) IsOrganizationID(id gidx.PrefixedID) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TouchOrganization initializes a sync for the provided organization id for relationships and memberships.
|
||||||
func (s *service) TouchOrganization(ctx context.Context, id gidx.PrefixedID) error {
|
func (s *service) TouchOrganization(ctx context.Context, id gidx.PrefixedID) error {
|
||||||
logger := s.logger.With("organization.id", id.String())
|
logger := s.logger.With("organization.id", id.String())
|
||||||
|
|
||||||
@@ -84,6 +87,7 @@ func (s *service) TouchOrganization(ctx context.Context, id gidx.PrefixedID) err
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteOrganization deletes the provided organization id.
|
||||||
func (s *service) DeleteOrganization(ctx context.Context, id gidx.PrefixedID) error {
|
func (s *service) DeleteOrganization(ctx context.Context, id gidx.PrefixedID) error {
|
||||||
err := s.publisher.PublishChange(ctx, organizationEvent, events.ChangeMessage{
|
err := s.publisher.PublishChange(ctx, organizationEvent, events.ChangeMessage{
|
||||||
SubjectID: id,
|
SubjectID: id,
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import (
|
|||||||
"go.equinixmetal.net/infra9-metal-bridge/internal/permissions"
|
"go.equinixmetal.net/infra9-metal-bridge/internal/permissions"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// syncMemberships determines the changes between what is wanted and what is live and executes on the differences.
|
||||||
|
// If skipDeletions is true, no deletes will be executed.
|
||||||
func (s *service) syncMemberships(ctx context.Context, relationships Relationships, skipDeletions bool) (int, int) {
|
func (s *service) syncMemberships(ctx context.Context, relationships Relationships, skipDeletions bool) (int, int) {
|
||||||
if len(relationships.Memberships) == 0 {
|
if len(relationships.Memberships) == 0 {
|
||||||
return 0, 0
|
return 0, 0
|
||||||
@@ -170,6 +172,10 @@ func (s *service) syncMemberships(ctx context.Context, relationships Relationshi
|
|||||||
return rolesCreated + rolesDeleted, roleAssignments + roleUnassignments
|
return rolesCreated + rolesDeleted, roleAssignments + roleUnassignments
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mapResourceWants processes the provided memberships and returns two maps.
|
||||||
|
// A Role Key is computed based on a sorted slice of actions for each role.
|
||||||
|
// The first map is of Role Key -> list of actions
|
||||||
|
// The second map is of Role Key -> Member ID -> true
|
||||||
func (s *service) mapResourceWants(memberships []ResourceMemberships) (map[string][]string, map[string]map[gidx.PrefixedID]bool) {
|
func (s *service) mapResourceWants(memberships []ResourceMemberships) (map[string][]string, map[string]map[gidx.PrefixedID]bool) {
|
||||||
roleActionsKey := make(map[string]string)
|
roleActionsKey := make(map[string]string)
|
||||||
|
|
||||||
@@ -196,6 +202,10 @@ func (s *service) mapResourceWants(memberships []ResourceMemberships) (map[strin
|
|||||||
return wantRoles, wantAssignments
|
return wantRoles, wantAssignments
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mapResourceDetails fetches the provided ResourceID's live state and returns two maps and an error.
|
||||||
|
// A Role Key is computed based on a sorted slice of actions for each role.
|
||||||
|
// The first map is of Role Key -> Permissions Resource Role
|
||||||
|
// The second map is of Role Key -> Member ID -> true
|
||||||
func (s *service) mapResourceDetails(ctx context.Context, resourceID gidx.PrefixedID) (map[string]permissions.ResourceRole, map[string]map[gidx.PrefixedID]bool, error) {
|
func (s *service) mapResourceDetails(ctx context.Context, resourceID gidx.PrefixedID) (map[string]permissions.ResourceRole, map[string]map[gidx.PrefixedID]bool, error) {
|
||||||
roles := make(map[string]permissions.ResourceRole)
|
roles := make(map[string]permissions.ResourceRole)
|
||||||
assignments := make(map[string]map[gidx.PrefixedID]bool)
|
assignments := make(map[string]map[gidx.PrefixedID]bool)
|
||||||
|
|||||||
@@ -9,13 +9,9 @@ import (
|
|||||||
"go.equinixmetal.net/infra9-metal-bridge/internal/permissions"
|
"go.equinixmetal.net/infra9-metal-bridge/internal/permissions"
|
||||||
)
|
)
|
||||||
|
|
||||||
type relationshipStats struct {
|
// processRelationships determines the changes between what is wanted and what is live and executes on the differences.
|
||||||
parentCreated bool
|
// Relationship creations use events.
|
||||||
parentsDeleted int
|
// Relationship deletions use the api, as delete events delete all related resources and not just the provided ones.
|
||||||
subjectRelationshipsCreated int
|
|
||||||
subjectRelationshipsDeleted int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) processRelationships(ctx context.Context, eventType string, relationships Relationships) int {
|
func (s *service) processRelationships(ctx context.Context, eventType string, relationships Relationships) int {
|
||||||
rlogger := s.logger.With("resource.id", relationships.Resource.PrefixedID())
|
rlogger := s.logger.With("resource.id", relationships.Resource.PrefixedID())
|
||||||
|
|
||||||
@@ -161,6 +157,7 @@ func (s *service) processRelationships(ctx context.Context, eventType string, re
|
|||||||
return changes
|
return changes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mapRelationWants returns the parent relation if provided and a map of Subjects -> relation.
|
||||||
func (s *service) mapRelationWants(relationships Relationships) (*Relation, map[gidx.PrefixedID]RelationshipType) {
|
func (s *service) mapRelationWants(relationships Relationships) (*Relation, map[gidx.PrefixedID]RelationshipType) {
|
||||||
var wantParent *Relation
|
var wantParent *Relation
|
||||||
|
|
||||||
@@ -177,6 +174,9 @@ func (s *service) mapRelationWants(relationships Relationships) (*Relation, map[
|
|||||||
return wantParent, wantSubject
|
return wantParent, wantSubject
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getRelationshipMap fetches the provided resources relationships, as the source resource and the destination subject.
|
||||||
|
// Returned are two maps, the first maps Subject IDs -> Relationship
|
||||||
|
// The second map, maps Resource IDs -> relationship
|
||||||
func (s *service) getRelationshipMap(ctx context.Context, resource IDPrefixableResource, relatedObjectType ObjectType) (map[gidx.PrefixedID]RelationshipType, map[gidx.PrefixedID]RelationshipType, error) {
|
func (s *service) getRelationshipMap(ctx context.Context, resource IDPrefixableResource, relatedObjectType ObjectType) (map[gidx.PrefixedID]RelationshipType, map[gidx.PrefixedID]RelationshipType, error) {
|
||||||
liveResource, err := s.perms.ListResourceRelationships(ctx, resource.PrefixedID(), "")
|
liveResource, err := s.perms.ListResourceRelationships(ctx, resource.PrefixedID(), "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
const projectEvent = "metalproject"
|
const projectEvent = "metalproject"
|
||||||
|
|
||||||
|
// buildProjectRelationships compiles all relations into a relationships object to be processed by the processors.
|
||||||
func (s *service) buildProjectRelationships(project *models.ProjectDetails) (Relationships, error) {
|
func (s *service) buildProjectRelationships(project *models.ProjectDetails) (Relationships, error) {
|
||||||
relations := Relationships{
|
relations := Relationships{
|
||||||
Resource: project,
|
Resource: project,
|
||||||
@@ -39,6 +40,7 @@ func (s *service) buildProjectRelationships(project *models.ProjectDetails) (Rel
|
|||||||
return relations, nil
|
return relations, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsProjectID checks if the provided id has the metal project prefix.
|
||||||
func (s *service) IsProjectID(id gidx.PrefixedID) bool {
|
func (s *service) IsProjectID(id gidx.PrefixedID) bool {
|
||||||
if idType, ok := s.idPrefixMap[id.Prefix()]; ok {
|
if idType, ok := s.idPrefixMap[id.Prefix()]; ok {
|
||||||
return idType == TypeProject
|
return idType == TypeProject
|
||||||
@@ -47,6 +49,7 @@ func (s *service) IsProjectID(id gidx.PrefixedID) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TouchProject initializes a sync for the provided project id for relationships and memberships.
|
||||||
func (s *service) TouchProject(ctx context.Context, id gidx.PrefixedID) error {
|
func (s *service) TouchProject(ctx context.Context, id gidx.PrefixedID) error {
|
||||||
logger := s.logger.With("project.id", id.String())
|
logger := s.logger.With("project.id", id.String())
|
||||||
|
|
||||||
@@ -77,6 +80,7 @@ func (s *service) TouchProject(ctx context.Context, id gidx.PrefixedID) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteProject deletes the provided project id.
|
||||||
func (s *service) DeleteProject(ctx context.Context, id gidx.PrefixedID) error {
|
func (s *service) DeleteProject(ctx context.Context, id gidx.PrefixedID) error {
|
||||||
err := s.publisher.PublishChange(ctx, projectEvent, events.ChangeMessage{
|
err := s.publisher.PublishChange(ctx, projectEvent, events.ChangeMessage{
|
||||||
SubjectID: id,
|
SubjectID: id,
|
||||||
|
|||||||
@@ -5,16 +5,22 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
RelateOwner RelationshipType = "owner"
|
// RelateOwner is the owner relationship type.
|
||||||
|
RelateOwner RelationshipType = "owner"
|
||||||
|
|
||||||
|
// RelateParent is the parent relationship type.
|
||||||
RelateParent RelationshipType = "parent"
|
RelateParent RelationshipType = "parent"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// RelationshipType are relationship types.
|
||||||
type RelationshipType string
|
type RelationshipType string
|
||||||
|
|
||||||
|
// IDPrefixableResource ensures the the interface passed provides prefixed ids.
|
||||||
type IDPrefixableResource interface {
|
type IDPrefixableResource interface {
|
||||||
PrefixedID() gidx.PrefixedID
|
PrefixedID() gidx.PrefixedID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Relationships defines a resource and all possible relationships and memberships.
|
||||||
type Relationships struct {
|
type Relationships struct {
|
||||||
Resource IDPrefixableResource
|
Resource IDPrefixableResource
|
||||||
Parent Relation
|
Parent Relation
|
||||||
@@ -23,11 +29,13 @@ type Relationships struct {
|
|||||||
Memberships []ResourceMemberships
|
Memberships []ResourceMemberships
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Relation defines a relation to a resource.
|
||||||
type Relation struct {
|
type Relation struct {
|
||||||
Relation RelationshipType
|
Relation RelationshipType
|
||||||
Resource IDPrefixableResource
|
Resource IDPrefixableResource
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResourceMemberships defines a member and role.
|
||||||
type ResourceMemberships struct {
|
type ResourceMemberships struct {
|
||||||
Role string
|
Role string
|
||||||
Member IDPrefixableResource
|
Member IDPrefixableResource
|
||||||
|
|||||||
@@ -29,8 +29,10 @@ var DefaultPrefixMap = map[string]ObjectType{
|
|||||||
TypeUser.Prefix(): TypeUser,
|
TypeUser.Prefix(): TypeUser,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ObjectType defines a type of object.
|
||||||
type ObjectType string
|
type ObjectType string
|
||||||
|
|
||||||
|
// Prefix returns the objects id prefix.
|
||||||
func (t ObjectType) Prefix() string {
|
func (t ObjectType) Prefix() string {
|
||||||
switch t {
|
switch t {
|
||||||
case TypeOrganization:
|
case TypeOrganization:
|
||||||
@@ -44,6 +46,7 @@ func (t ObjectType) Prefix() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// String returns a string fo the object type.
|
||||||
func (t ObjectType) String() string {
|
func (t ObjectType) String() string {
|
||||||
return string(t)
|
return string(t)
|
||||||
}
|
}
|
||||||
@@ -96,6 +99,7 @@ func (r prefixedID) PrefixedID() gidx.PrefixedID {
|
|||||||
return r.id
|
return r.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New creates a new service.
|
||||||
func New(publisher *events.Publisher, metal *metal.Client, perms *permissions.Client, options ...Option) (Service, error) {
|
func New(publisher *events.Publisher, metal *metal.Client, perms *permissions.Client, options ...Option) (Service, error) {
|
||||||
svc := &service{
|
svc := &service{
|
||||||
publisher: publisher,
|
publisher: publisher,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"go.infratographer.com/x/gidx"
|
"go.infratographer.com/x/gidx"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// IsUser checks the provided id has the metal user prefix.
|
||||||
func (s *service) IsUser(id gidx.PrefixedID) bool {
|
func (s *service) IsUser(id gidx.PrefixedID) bool {
|
||||||
if idType, ok := s.idPrefixMap[id.Prefix()]; ok {
|
if idType, ok := s.idPrefixMap[id.Prefix()]; ok {
|
||||||
return idType == TypeUser
|
return idType == TypeUser
|
||||||
@@ -14,6 +15,7 @@ func (s *service) IsUser(id gidx.PrefixedID) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsAssignableResource checks that the provided id is an id which can have memberships assignments.
|
||||||
func (s *service) IsAssignableResource(id gidx.PrefixedID) bool {
|
func (s *service) IsAssignableResource(id gidx.PrefixedID) bool {
|
||||||
if idType, ok := s.idPrefixMap[id.Prefix()]; ok {
|
if idType, ok := s.idPrefixMap[id.Prefix()]; ok {
|
||||||
switch idType {
|
switch idType {
|
||||||
@@ -27,6 +29,7 @@ func (s *service) IsAssignableResource(id gidx.PrefixedID) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Assignuser assigns the provided users to the given resource ids.
|
||||||
func (s *service) AssignUser(ctx context.Context, userID gidx.PrefixedID, resourceIDs ...gidx.PrefixedID) error {
|
func (s *service) AssignUser(ctx context.Context, userID gidx.PrefixedID, resourceIDs ...gidx.PrefixedID) error {
|
||||||
var totalResources, rolesChanged, assignmentsChanged int
|
var totalResources, rolesChanged, assignmentsChanged int
|
||||||
|
|
||||||
@@ -68,6 +71,7 @@ func (s *service) AssignUser(ctx context.Context, userID gidx.PrefixedID, resour
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnassignUser removes the assignment for the provided user id to the given resources.
|
||||||
func (s *service) UnassignUser(ctx context.Context, userID gidx.PrefixedID, resourceIDs ...gidx.PrefixedID) error {
|
func (s *service) UnassignUser(ctx context.Context, userID gidx.PrefixedID, resourceIDs ...gidx.PrefixedID) error {
|
||||||
for _, resourceID := range resourceIDs {
|
for _, resourceID := range resourceIDs {
|
||||||
rlogger := s.logger.With("user.id", userID, "resource.id", resourceID)
|
rlogger := s.logger.With("user.id", userID, "resource.id", resourceID)
|
||||||
@@ -119,6 +123,7 @@ func (s *service) UnassignUser(ctx context.Context, userID gidx.PrefixedID, reso
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getuserResourceRole fetches the appropriate object types user role for the given resource.
|
||||||
func (s *service) getUserResourceRole(ctx context.Context, userID, resourceID gidx.PrefixedID) (string, error) {
|
func (s *service) getUserResourceRole(ctx context.Context, userID, resourceID gidx.PrefixedID) (string, error) {
|
||||||
var (
|
var (
|
||||||
role string
|
role string
|
||||||
|
|||||||
Reference in New Issue
Block a user