chore: implement api for creating custom roles (#13298)

api endpoint (gated by experiment) to create custom_roles
This commit is contained in:
Steven Masley
2024-05-16 13:47:47 -05:00
committed by GitHub
parent 85de0e966d
commit ad8c314130
33 changed files with 1009 additions and 132 deletions

View File

@ -54,6 +54,7 @@ const (
FeatureWorkspaceBatchActions FeatureName = "workspace_batch_actions"
FeatureAccessControl FeatureName = "access_control"
FeatureControlSharedPorts FeatureName = "control_shared_ports"
FeatureCustomRoles FeatureName = "custom_roles"
)
// FeatureNames must be kept in-sync with the Feature enum above.
@ -74,6 +75,7 @@ var FeatureNames = []FeatureName{
FeatureWorkspaceBatchActions,
FeatureAccessControl,
FeatureControlSharedPorts,
FeatureCustomRoles,
}
// Humanize returns the feature name in a human-readable format.
@ -98,6 +100,7 @@ func (n FeatureName) AlwaysEnable() bool {
FeatureAppearance: true,
FeatureWorkspaceBatchActions: true,
FeatureHighAvailability: true,
FeatureCustomRoles: true,
}[n]
}
@ -2218,6 +2221,7 @@ const (
ExperimentExample Experiment = "example" // This isn't used for anything.
ExperimentAutoFillParameters Experiment = "auto-fill-parameters" // This should not be taken out of experiments until we have redesigned the feature.
ExperimentMultiOrganization Experiment = "multi-organization" // Requires organization context for interactions, default org is assumed.
ExperimentCustomRoles Experiment = "custom-roles" // Allows creating runtime custom roles
)
// ExperimentsAll should include all experiments that are safe for

View File

@ -48,11 +48,11 @@ type Organization struct {
}
type OrganizationMember struct {
UserID uuid.UUID `db:"user_id" json:"user_id" format:"uuid"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id" format:"uuid"`
CreatedAt time.Time `db:"created_at" json:"created_at" format:"date-time"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at" format:"date-time"`
Roles []Role `db:"roles" json:"roles"`
UserID uuid.UUID `db:"user_id" json:"user_id" format:"uuid"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id" format:"uuid"`
CreatedAt time.Time `db:"created_at" json:"created_at" format:"date-time"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at" format:"date-time"`
Roles []SlimRole `db:"roles" json:"roles"`
}
// CreateTemplateVersionRequest enables callers to create a new Template Version.

View File

@ -9,16 +9,52 @@ import (
"github.com/google/uuid"
)
type Role struct {
// SlimRole omits permission information from a role.
// At present, this is because our apis do not return permission information,
// and it would require extra db calls to fetch this information. The UI does
// not need it, so most api calls will use this structure that omits information.
type SlimRole struct {
Name string `json:"name"`
DisplayName string `json:"display_name"`
}
type AssignableRoles struct {
Role
SlimRole
Assignable bool `json:"assignable"`
}
// Permission is the format passed into the rego.
type Permission struct {
// Negate makes this a negative permission
Negate bool `json:"negate"`
ResourceType RBACResource `json:"resource_type"`
Action RBACAction `json:"action"`
}
// Role is a longer form of SlimRole used to edit custom roles.
type Role struct {
Name string `json:"name"`
DisplayName string `json:"display_name"`
SitePermissions []Permission `json:"site_permissions"`
// map[<org_id>] -> Permissions
OrganizationPermissions map[string][]Permission `json:"organization_permissions"`
UserPermissions []Permission `json:"user_permissions"`
}
// PatchRole will upsert a custom site wide role
func (c *Client) PatchRole(ctx context.Context, req Role) (Role, error) {
res, err := c.Request(ctx, http.MethodPatch, "/api/v2/users/roles", req)
if err != nil {
return Role{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return Role{}, ReadBodyAsError(res)
}
var role Role
return role, json.NewDecoder(res.Body).Decode(&role)
}
// ListSiteRoles lists all assignable site wide roles.
func (c *Client) ListSiteRoles(ctx context.Context) ([]AssignableRoles, error) {
res, err := c.Request(ctx, http.MethodGet, "/api/v2/users/roles", nil)
@ -46,3 +82,17 @@ func (c *Client) ListOrganizationRoles(ctx context.Context, org uuid.UUID) ([]As
var roles []AssignableRoles
return roles, json.NewDecoder(res.Body).Decode(&roles)
}
// CreatePermissions is a helper function to quickly build permissions.
func CreatePermissions(mapping map[RBACResource][]RBACAction) []Permission {
perms := make([]Permission, 0)
for t, actions := range mapping {
for _, action := range actions {
perms = append(perms, Permission{
ResourceType: t,
Action: action,
})
}
}
return perms
}

View File

@ -63,7 +63,7 @@ type User struct {
ReducedUser `table:"r,recursive_inline"`
OrganizationIDs []uuid.UUID `json:"organization_ids" format:"uuid"`
Roles []Role `json:"roles"`
Roles []SlimRole `json:"roles"`
}
type GetUsersResponse struct {