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

@ -18,6 +18,7 @@ import (
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/parameter"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/coderd/rbac/policy"
"github.com/coder/coder/v2/coderd/workspaceapps/appurl"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/provisionersdk/proto"
@ -28,9 +29,25 @@ import (
// database types to slices of codersdk types.
// Only works if the function takes a single argument.
func List[F any, T any](list []F, convert func(F) T) []T {
into := make([]T, 0, len(list))
for _, item := range list {
into = append(into, convert(item))
return ListLazy(convert)(list)
}
// ListLazy returns the converter function for a list, but does not eval
// the input. Helpful for combining the Map and the List functions.
func ListLazy[F any, T any](convert func(F) T) func(list []F) []T {
return func(list []F) []T {
into := make([]T, 0, len(list))
for _, item := range list {
into = append(into, convert(item))
}
return into
}
}
func Map[K comparable, F any, T any](params map[K]F, convert func(F) T) map[K]T {
into := make(map[K]T)
for k, item := range params {
into[k] = convert(item)
}
return into
}
@ -150,12 +167,20 @@ func User(user database.User, organizationIDs []uuid.UUID) codersdk.User {
convertedUser := codersdk.User{
ReducedUser: ReducedUser(user),
OrganizationIDs: organizationIDs,
Roles: make([]codersdk.Role, 0, len(user.RBACRoles)),
Roles: make([]codersdk.SlimRole, 0, len(user.RBACRoles)),
}
for _, roleName := range user.RBACRoles {
rbacRole, _ := rbac.RoleByName(roleName)
convertedUser.Roles = append(convertedUser.Roles, Role(rbacRole))
rbacRole, err := rbac.RoleByName(roleName)
if err == nil {
convertedUser.Roles = append(convertedUser.Roles, SlimRole(rbacRole))
} else {
// TODO: Fix this for custom roles to display the actual display_name
// Requires plumbing either a cached role value, or the db.
convertedUser.Roles = append(convertedUser.Roles, codersdk.SlimRole{
Name: roleName,
})
}
}
return convertedUser
@ -180,8 +205,8 @@ func Group(group database.Group, members []database.User) codersdk.Group {
}
}
func Role(role rbac.Role) codersdk.Role {
return codersdk.Role{
func SlimRole(role rbac.Role) codersdk.SlimRole {
return codersdk.SlimRole{
DisplayName: role.DisplayName,
Name: role.Name,
}
@ -500,3 +525,39 @@ func ProvisionerDaemon(dbDaemon database.ProvisionerDaemon) codersdk.Provisioner
}
return result
}
func Role(role rbac.Role) codersdk.Role {
return codersdk.Role{
Name: role.Name,
DisplayName: role.DisplayName,
SitePermissions: List(role.Site, Permission),
OrganizationPermissions: Map(role.Org, ListLazy(Permission)),
UserPermissions: List(role.Site, Permission),
}
}
func Permission(permission rbac.Permission) codersdk.Permission {
return codersdk.Permission{
Negate: permission.Negate,
ResourceType: codersdk.RBACResource(permission.ResourceType),
Action: codersdk.RBACAction(permission.Action),
}
}
func RoleToRBAC(role codersdk.Role) rbac.Role {
return rbac.Role{
Name: role.Name,
DisplayName: role.DisplayName,
Site: List(role.SitePermissions, PermissionToRBAC),
Org: Map(role.OrganizationPermissions, ListLazy(PermissionToRBAC)),
User: List(role.UserPermissions, PermissionToRBAC),
}
}
func PermissionToRBAC(permission codersdk.Permission) rbac.Permission {
return rbac.Permission{
Negate: permission.Negate,
ResourceType: string(permission.ResourceType),
Action: policy.Action(permission.Action),
}
}

View File

@ -620,7 +620,8 @@ func (q *querier) canAssignRoles(ctx context.Context, orgID *uuid.UUID, added, r
}
if len(customRoles) > 0 {
expandedCustomRoles, err := q.CustomRolesByName(ctx, customRoles)
// Leverage any custom role cache that might exist.
expandedCustomRoles, err := rolestore.Expand(ctx, q.db, customRoles)
if err != nil {
return xerrors.Errorf("fetching custom roles: %w", err)
}
@ -632,7 +633,7 @@ func (q *querier) canAssignRoles(ctx context.Context, orgID *uuid.UUID, added, r
// Stop at the first one found. We could make a better error that
// returns them all, but then someone could pass in a large list to make us do
// a lot of loop iterations.
if !slices.ContainsFunc(expandedCustomRoles, func(customRole database.CustomRole) bool {
if !slices.ContainsFunc(expandedCustomRoles, func(customRole rbac.Role) bool {
return strings.EqualFold(customRole.Name, role)
}) {
return xerrors.Errorf("%q is not a supported role", role)