feat: add sharing info to /workspaces endpoint (#21049)

closes: https://github.com/coder/internal/issues/858

Similar to https://github.com/coder/coder/pull/19375, this one uses
system permissions for fetching actual user and group data.

Modifies the `workspaces_expanded` view to fetch the required data; this way it's made available to all code paths that make use of it.  

Also fixes a bug in a test helper function that can result in `null` being saved to the DB for `user_acl` or `group_acl` and break tests; a defensive check constraint that prevents this is worth a PR, e.g:

`ALTER TABLE workspaces
   ADD CONSTRAINT group_acl_is_object CHECK (jsonb_typeof(group_acl) = 'object');`

Also adds missing  `OwnerName` in `ConvertWorkspaceRows`.
This commit is contained in:
George K
2025-12-15 08:42:08 -08:00
committed by GitHub
parent 7ecfd1aa07
commit 103967ed02
22 changed files with 838 additions and 92 deletions
+29 -6
View File
@@ -67,9 +67,10 @@ func (t *TemplateACL) Scan(src interface{}) error {
switch v := src.(type) {
case string:
return json.Unmarshal([]byte(v), &t)
case []byte, json.RawMessage:
//nolint
return json.Unmarshal(v.([]byte), &t)
case []byte:
return json.Unmarshal(v, &t)
case json.RawMessage:
return json.Unmarshal(v, &t)
}
return xerrors.Errorf("unexpected type %T", src)
@@ -85,9 +86,10 @@ func (t *WorkspaceACL) Scan(src interface{}) error {
switch v := src.(type) {
case string:
return json.Unmarshal([]byte(v), &t)
case []byte, json.RawMessage:
//nolint
return json.Unmarshal(v.([]byte), &t)
case []byte:
return json.Unmarshal(v, &t)
case json.RawMessage:
return json.Unmarshal(v, &t)
}
return xerrors.Errorf("unexpected type %T", src)
@@ -112,6 +114,27 @@ type WorkspaceACLEntry struct {
Permissions []policy.Action `json:"permissions"`
}
// WorkspaceACLDisplayInfo supplements workspace ACLs with the actors'
// display info. Key is string rather than uuid.UUID as this aligns
// with how RBAC represents actor IDs.
type WorkspaceACLDisplayInfo map[string]struct {
Name string `json:"name"`
AvatarURL string `json:"avatar_url"`
}
// WorkspaceACLDisplayInfo is only used to read from the DB.
func (w *WorkspaceACLDisplayInfo) Scan(src interface{}) error {
switch v := src.(type) {
case string:
return json.Unmarshal([]byte(v), w)
case []byte:
return json.Unmarshal(v, w)
case json.RawMessage:
return json.Unmarshal(v, w)
}
return xerrors.Errorf("unexpected type %T", src)
}
type ExternalAuthProvider struct {
ID string `json:"id"`
Optional bool `json:"optional,omitempty"`