-
Notifications
You must be signed in to change notification settings - Fork 957
refactor: consolidate template and workspace acl validation #19192
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e30250a
e1d3e3c
c978adf
0e108f1
3a7c8e6
7ed357c
468cd79
5ffa8f9
1e10aa0
e38dceb
03125e3
bfe31b8
e368606
5e3f858
4a162df
48dd824
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
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 | ||
} | ||
Comment on lines
+14
to
+26
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❤️ |
||
|
||
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 | ||
} |
Uh oh!
There was an error while loading. Please reload this page.