mirror of
https://github.com/coder/coder.git
synced 2026-06-03 13:08:25 +00:00
131 lines
4.1 KiB
Go
131 lines
4.1 KiB
Go
package acl
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"github.com/coder/coder/v2/coderd/database"
|
|
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
|
"github.com/coder/coder/v2/codersdk"
|
|
)
|
|
|
|
type UpdateValidator[Role codersdk.WorkspaceRole | codersdk.TemplateRole] interface {
|
|
// Users should return a map from user UUIDs (as strings) to the role they
|
|
// are being assigned. Additionally, it should return a string that will be
|
|
// used as the field name for the ValidationErrors returned from Validate.
|
|
Users() (map[string]Role, string)
|
|
// Groups should return a map from group UUIDs (as strings) to the role they
|
|
// are being assigned. Additionally, it should return a string that will be
|
|
// used as the field name for the ValidationErrors returned from Validate.
|
|
Groups() (map[string]Role, string)
|
|
// ValidateRole should return an error that will be used in the
|
|
// ValidationError if the role is invalid for the corresponding resource type.
|
|
ValidateRole(role Role) error
|
|
}
|
|
|
|
func Validate[Role codersdk.WorkspaceRole | codersdk.TemplateRole](
|
|
ctx context.Context,
|
|
db database.Store,
|
|
v UpdateValidator[Role],
|
|
) []codersdk.ValidationError {
|
|
// nolint:gocritic // Validate requires full read access to users and groups
|
|
ctx = dbauthz.AsSystemRestricted(ctx)
|
|
var validErrs []codersdk.ValidationError
|
|
|
|
groupRoles, groupsField := v.Groups()
|
|
groupIDs := make([]uuid.UUID, 0, len(groupRoles))
|
|
for idStr, role := range groupRoles {
|
|
// Validate the provided role names
|
|
if err := v.ValidateRole(role); err != nil {
|
|
validErrs = append(validErrs, codersdk.ValidationError{
|
|
Field: groupsField,
|
|
Detail: err.Error(),
|
|
})
|
|
}
|
|
// Validate that the IDs are UUIDs
|
|
id, err := uuid.Parse(idStr)
|
|
if err != nil {
|
|
validErrs = append(validErrs, codersdk.ValidationError{
|
|
Field: groupsField,
|
|
Detail: fmt.Sprintf("%v is not a valid UUID.", idStr),
|
|
})
|
|
continue
|
|
}
|
|
// Don't check if the ID exists when setting the role to
|
|
// WorkspaceRoleDeleted or TemplateRoleDeleted. They might've existing at
|
|
// some point and got deleted. If we report that as an error here then they
|
|
// can't be removed.
|
|
if string(role) == "" {
|
|
continue
|
|
}
|
|
groupIDs = append(groupIDs, id)
|
|
}
|
|
|
|
// Validate that the groups exist
|
|
groupValidation, err := db.ValidateGroupIDs(ctx, groupIDs)
|
|
if err != nil {
|
|
validErrs = append(validErrs, codersdk.ValidationError{
|
|
Field: groupsField,
|
|
Detail: fmt.Sprintf("failed to validate group IDs: %v", err.Error()),
|
|
})
|
|
}
|
|
if !groupValidation.Ok {
|
|
for _, id := range groupValidation.InvalidGroupIds {
|
|
validErrs = append(validErrs, codersdk.ValidationError{
|
|
Field: groupsField,
|
|
Detail: fmt.Sprintf("group with ID %v does not exist", id),
|
|
})
|
|
}
|
|
}
|
|
|
|
userRoles, usersField := v.Users()
|
|
userIDs := make([]uuid.UUID, 0, len(userRoles))
|
|
for idStr, role := range userRoles {
|
|
// Validate the provided role names
|
|
if err := v.ValidateRole(role); err != nil {
|
|
validErrs = append(validErrs, codersdk.ValidationError{
|
|
Field: usersField,
|
|
Detail: err.Error(),
|
|
})
|
|
}
|
|
// Validate that the IDs are UUIDs
|
|
id, err := uuid.Parse(idStr)
|
|
if err != nil {
|
|
validErrs = append(validErrs, codersdk.ValidationError{
|
|
Field: usersField,
|
|
Detail: fmt.Sprintf("%v is not a valid UUID.", idStr),
|
|
})
|
|
continue
|
|
}
|
|
// Don't check if the ID exists when setting the role to
|
|
// WorkspaceRoleDeleted or TemplateRoleDeleted. They might've existing at
|
|
// some point and got deleted. If we report that as an error here then they
|
|
// can't be removed.
|
|
if string(role) == "" {
|
|
continue
|
|
}
|
|
userIDs = append(userIDs, id)
|
|
}
|
|
|
|
// Validate that the groups exist
|
|
userValidation, err := db.ValidateUserIDs(ctx, userIDs)
|
|
if err != nil {
|
|
validErrs = append(validErrs, codersdk.ValidationError{
|
|
Field: usersField,
|
|
Detail: fmt.Sprintf("failed to validate user IDs: %v", err.Error()),
|
|
})
|
|
}
|
|
if !userValidation.Ok {
|
|
for _, id := range userValidation.InvalidUserIds {
|
|
validErrs = append(validErrs, codersdk.ValidationError{
|
|
Field: usersField,
|
|
Detail: fmt.Sprintf("user with ID %v does not exist", id),
|
|
})
|
|
}
|
|
}
|
|
|
|
return validErrs
|
|
}
|