mirror of
https://github.com/coder/coder.git
synced 2025-07-03 16:13:58 +00:00
chore: implement api for creating custom roles (#13298)
api endpoint (gated by experiment) to create custom_roles
This commit is contained in:
@ -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),
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
Reference in New Issue
Block a user