diff --git a/coderd/database/check_constraint.go b/coderd/database/check_constraint.go index 8d0b7189e7..80dfd2bce4 100644 --- a/coderd/database/check_constraint.go +++ b/coderd/database/check_constraint.go @@ -17,9 +17,9 @@ const ( CheckMaxLogsLength CheckConstraint = "max_logs_length" // workspace_agents CheckSubsystemsNotNone CheckConstraint = "subsystems_not_none" // workspace_agents CheckWorkspaceBuildsDeadlineBelowMaxDeadline CheckConstraint = "workspace_builds_deadline_below_max_deadline" // workspace_builds + CheckGroupAclIsObject CheckConstraint = "group_acl_is_object" // workspaces + CheckUserAclIsObject CheckConstraint = "user_acl_is_object" // workspaces CheckTelemetryLockEventTypeConstraint CheckConstraint = "telemetry_lock_event_type_constraint" // telemetry_locks CheckValidationMonotonicOrder CheckConstraint = "validation_monotonic_order" // template_version_parameters CheckUsageEventTypeCheck CheckConstraint = "usage_event_type_check" // usage_events - CheckGroupAclIsObject CheckConstraint = "group_acl_is_object" // workspaces - CheckUserAclIsObject CheckConstraint = "user_acl_is_object" // workspaces ) diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index 1b7d927eaa..839c439cd6 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -3409,12 +3409,7 @@ func (q *querier) GetTaskSnapshot(ctx context.Context, taskID uuid.UUID) (databa return database.TaskSnapshot{}, err } - obj := rbac.ResourceTask. - WithID(task.ID). - WithOwner(task.OwnerID.String()). - InOrg(task.OrganizationID) - - if err := q.authorizeContext(ctx, policy.ActionRead, obj); err != nil { + if err := q.authorizeContext(ctx, policy.ActionRead, task.RBACObject()); err != nil { return database.TaskSnapshot{}, err } @@ -6635,12 +6630,7 @@ func (q *querier) UpsertTaskSnapshot(ctx context.Context, arg database.UpsertTas return err } - obj := rbac.ResourceTask. - WithID(task.ID). - WithOwner(task.OwnerID.String()). - InOrg(task.OrganizationID) - - if err := q.authorizeContext(ctx, policy.ActionUpdate, obj); err != nil { + if err := q.authorizeContext(ctx, policy.ActionUpdate, task.RBACObject()); err != nil { return err } diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index 878eba3409..bc0c444012 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -2094,6 +2094,31 @@ CREATE TABLE workspace_builds ( CONSTRAINT workspace_builds_deadline_below_max_deadline CHECK ((((deadline <> '0001-01-01 00:00:00+00'::timestamp with time zone) AND (deadline <= max_deadline)) OR (max_deadline = '0001-01-01 00:00:00+00'::timestamp with time zone))) ); +CREATE TABLE workspaces ( + id uuid NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + owner_id uuid NOT NULL, + organization_id uuid NOT NULL, + template_id uuid NOT NULL, + deleted boolean DEFAULT false NOT NULL, + name character varying(64) NOT NULL, + autostart_schedule text, + ttl bigint, + last_used_at timestamp with time zone DEFAULT '0001-01-01 00:00:00+00'::timestamp with time zone NOT NULL, + dormant_at timestamp with time zone, + deleting_at timestamp with time zone, + automatic_updates automatic_updates DEFAULT 'never'::automatic_updates NOT NULL, + favorite boolean DEFAULT false NOT NULL, + next_start_at timestamp with time zone, + group_acl jsonb DEFAULT '{}'::jsonb NOT NULL, + user_acl jsonb DEFAULT '{}'::jsonb NOT NULL, + CONSTRAINT group_acl_is_object CHECK ((jsonb_typeof(group_acl) = 'object'::text)), + CONSTRAINT user_acl_is_object CHECK ((jsonb_typeof(user_acl) = 'object'::text)) +); + +COMMENT ON COLUMN workspaces.favorite IS 'Favorite is true if the workspace owner has favorited the workspace.'; + CREATE VIEW tasks_with_status AS SELECT tasks.id, tasks.organization_id, @@ -2106,6 +2131,8 @@ CREATE VIEW tasks_with_status AS tasks.created_at, tasks.deleted_at, tasks.display_name, + COALESCE(workspaces.group_acl, '{}'::jsonb) AS workspace_group_acl, + COALESCE(workspaces.user_acl, '{}'::jsonb) AS workspace_user_acl, CASE WHEN (tasks.workspace_id IS NULL) THEN 'pending'::task_status WHEN (build_status.status <> 'active'::task_status) THEN build_status.status @@ -2121,7 +2148,8 @@ CREATE VIEW tasks_with_status AS task_owner.owner_username, task_owner.owner_name, task_owner.owner_avatar_url - FROM ((((((((tasks + FROM (((((((((tasks + LEFT JOIN workspaces ON ((workspaces.id = tasks.workspace_id))) CROSS JOIN LATERAL ( SELECT vu.username AS owner_username, vu.name AS owner_name, vu.avatar_url AS owner_avatar_url @@ -2864,31 +2892,6 @@ CREATE VIEW workspace_build_with_user AS COMMENT ON VIEW workspace_build_with_user IS 'Joins in the username + avatar url of the initiated by user.'; -CREATE TABLE workspaces ( - id uuid NOT NULL, - created_at timestamp with time zone NOT NULL, - updated_at timestamp with time zone NOT NULL, - owner_id uuid NOT NULL, - organization_id uuid NOT NULL, - template_id uuid NOT NULL, - deleted boolean DEFAULT false NOT NULL, - name character varying(64) NOT NULL, - autostart_schedule text, - ttl bigint, - last_used_at timestamp with time zone DEFAULT '0001-01-01 00:00:00+00'::timestamp with time zone NOT NULL, - dormant_at timestamp with time zone, - deleting_at timestamp with time zone, - automatic_updates automatic_updates DEFAULT 'never'::automatic_updates NOT NULL, - favorite boolean DEFAULT false NOT NULL, - next_start_at timestamp with time zone, - group_acl jsonb DEFAULT '{}'::jsonb NOT NULL, - user_acl jsonb DEFAULT '{}'::jsonb NOT NULL, - CONSTRAINT group_acl_is_object CHECK ((jsonb_typeof(group_acl) = 'object'::text)), - CONSTRAINT user_acl_is_object CHECK ((jsonb_typeof(user_acl) = 'object'::text)) -); - -COMMENT ON COLUMN workspaces.favorite IS 'Favorite is true if the workspace owner has favorited the workspace.'; - CREATE VIEW workspace_latest_builds AS SELECT latest_build.id, latest_build.workspace_id, diff --git a/coderd/database/migrations/000427_add_workspace_acl_to_tasks_view.down.sql b/coderd/database/migrations/000427_add_workspace_acl_to_tasks_view.down.sql new file mode 100644 index 0000000000..9e0fe06ab0 --- /dev/null +++ b/coderd/database/migrations/000427_add_workspace_acl_to_tasks_view.down.sql @@ -0,0 +1,145 @@ +-- Fix task status logic: pending provisioner job should give pending task status, not initializing. +-- A task is pending when the provisioner hasn't picked up the job yet. +-- A task is initializing when the provisioner is actively running the job. +DROP VIEW IF EXISTS tasks_with_status; + +CREATE VIEW + tasks_with_status +AS + SELECT + tasks.*, + -- Combine component statuses with precedence: build -> agent -> app. + CASE + WHEN tasks.workspace_id IS NULL THEN 'pending'::task_status + WHEN build_status.status != 'active' THEN build_status.status::task_status + WHEN agent_status.status != 'active' THEN agent_status.status::task_status + ELSE app_status.status::task_status + END AS status, + -- Attach debug information for troubleshooting status. + jsonb_build_object( + 'build', jsonb_build_object( + 'transition', latest_build_raw.transition, + 'job_status', latest_build_raw.job_status, + 'computed', build_status.status + ), + 'agent', jsonb_build_object( + 'lifecycle_state', agent_raw.lifecycle_state, + 'computed', agent_status.status + ), + 'app', jsonb_build_object( + 'health', app_raw.health, + 'computed', app_status.status + ) + ) AS status_debug, + task_app.*, + agent_raw.lifecycle_state AS workspace_agent_lifecycle_state, + app_raw.health AS workspace_app_health, + task_owner.* + FROM + tasks + CROSS JOIN LATERAL ( + SELECT + vu.username AS owner_username, + vu.name AS owner_name, + vu.avatar_url AS owner_avatar_url + FROM + visible_users vu + WHERE + vu.id = tasks.owner_id + ) task_owner + LEFT JOIN LATERAL ( + SELECT + task_app.workspace_build_number, + task_app.workspace_agent_id, + task_app.workspace_app_id + FROM + task_workspace_apps task_app + WHERE + task_id = tasks.id + ORDER BY + task_app.workspace_build_number DESC + LIMIT 1 + ) task_app ON TRUE + + -- Join the raw data for computing task status. + LEFT JOIN LATERAL ( + SELECT + workspace_build.transition, + provisioner_job.job_status, + workspace_build.job_id + FROM + workspace_builds workspace_build + JOIN + provisioner_jobs provisioner_job + ON provisioner_job.id = workspace_build.job_id + WHERE + workspace_build.workspace_id = tasks.workspace_id + AND workspace_build.build_number = task_app.workspace_build_number + ) latest_build_raw ON TRUE + LEFT JOIN LATERAL ( + SELECT + workspace_agent.lifecycle_state + FROM + workspace_agents workspace_agent + WHERE + workspace_agent.id = task_app.workspace_agent_id + ) agent_raw ON TRUE + LEFT JOIN LATERAL ( + SELECT + workspace_app.health + FROM + workspace_apps workspace_app + WHERE + workspace_app.id = task_app.workspace_app_id + ) app_raw ON TRUE + + -- Compute the status for each component. + CROSS JOIN LATERAL ( + SELECT + CASE + WHEN latest_build_raw.job_status IS NULL THEN 'pending'::task_status + WHEN latest_build_raw.job_status IN ('failed', 'canceling', 'canceled') THEN 'error'::task_status + WHEN + latest_build_raw.transition IN ('stop', 'delete') + AND latest_build_raw.job_status = 'succeeded' THEN 'paused'::task_status + -- Job is pending (not picked up by provisioner yet). + WHEN + latest_build_raw.transition = 'start' + AND latest_build_raw.job_status = 'pending' THEN 'pending'::task_status + -- Job is running or done, defer to agent/app status. + WHEN + latest_build_raw.transition = 'start' + AND latest_build_raw.job_status IN ('running', 'succeeded') THEN 'active'::task_status + ELSE 'unknown'::task_status + END AS status + ) build_status + CROSS JOIN LATERAL ( + SELECT + CASE + -- No agent or connecting. + WHEN + agent_raw.lifecycle_state IS NULL + OR agent_raw.lifecycle_state IN ('created', 'starting') THEN 'initializing'::task_status + -- Agent is running, defer to app status. + -- NOTE(mafredri): The start_error/start_timeout states means connected, but some startup script failed. + -- This may or may not affect the task status but this has to be caught by app health check. + WHEN agent_raw.lifecycle_state IN ('ready', 'start_timeout', 'start_error') THEN 'active'::task_status + -- If the agent is shutting down or turned off, this is an unknown state because we would expect a stop + -- build to be running. + -- This is essentially equal to: `IN ('shutting_down', 'shutdown_timeout', 'shutdown_error', 'off')`, + -- but we cannot use them because the values were added in a migration. + WHEN agent_raw.lifecycle_state NOT IN ('created', 'starting', 'ready', 'start_timeout', 'start_error') THEN 'unknown'::task_status + ELSE 'unknown'::task_status + END AS status + ) agent_status + CROSS JOIN LATERAL ( + SELECT + CASE + WHEN app_raw.health = 'initializing' THEN 'initializing'::task_status + WHEN app_raw.health = 'unhealthy' THEN 'error'::task_status + WHEN app_raw.health IN ('healthy', 'disabled') THEN 'active'::task_status + ELSE 'unknown'::task_status + END AS status + ) app_status + WHERE + tasks.deleted_at IS NULL; diff --git a/coderd/database/migrations/000427_add_workspace_acl_to_tasks_view.up.sql b/coderd/database/migrations/000427_add_workspace_acl_to_tasks_view.up.sql new file mode 100644 index 0000000000..1b62aad2f7 --- /dev/null +++ b/coderd/database/migrations/000427_add_workspace_acl_to_tasks_view.up.sql @@ -0,0 +1,151 @@ +-- Fix task status logic: pending provisioner job should give pending task status, not initializing. +-- A task is pending when the provisioner hasn't picked up the job yet. +-- A task is initializing when the provisioner is actively running the job. +DROP VIEW IF EXISTS tasks_with_status; + +CREATE VIEW + tasks_with_status +AS + SELECT + tasks.*, + coalesce(workspaces.group_acl, '{}'::jsonb) as workspace_group_acl, + coalesce(workspaces.user_acl, '{}'::jsonb) as workspace_user_acl, + -- Combine component statuses with precedence: build -> agent -> app. + CASE + WHEN tasks.workspace_id IS NULL THEN 'pending'::task_status + WHEN build_status.status != 'active' THEN build_status.status::task_status + WHEN agent_status.status != 'active' THEN agent_status.status::task_status + ELSE app_status.status::task_status + END AS status, + -- Attach debug information for troubleshooting status. + jsonb_build_object( + 'build', jsonb_build_object( + 'transition', latest_build_raw.transition, + 'job_status', latest_build_raw.job_status, + 'computed', build_status.status + ), + 'agent', jsonb_build_object( + 'lifecycle_state', agent_raw.lifecycle_state, + 'computed', agent_status.status + ), + 'app', jsonb_build_object( + 'health', app_raw.health, + 'computed', app_status.status + ) + ) AS status_debug, + task_app.*, + agent_raw.lifecycle_state AS workspace_agent_lifecycle_state, + app_raw.health AS workspace_app_health, + task_owner.* + FROM + tasks + + LEFT JOIN + workspaces ON workspaces.id = tasks.workspace_id + + CROSS JOIN LATERAL ( + SELECT + vu.username AS owner_username, + vu.name AS owner_name, + vu.avatar_url AS owner_avatar_url + FROM + visible_users vu + WHERE + vu.id = tasks.owner_id + ) task_owner + LEFT JOIN LATERAL ( + SELECT + task_app.workspace_build_number, + task_app.workspace_agent_id, + task_app.workspace_app_id + FROM + task_workspace_apps task_app + WHERE + task_id = tasks.id + ORDER BY + task_app.workspace_build_number DESC + LIMIT 1 + ) task_app ON TRUE + + -- Join the raw data for computing task status. + LEFT JOIN LATERAL ( + SELECT + workspace_build.transition, + provisioner_job.job_status, + workspace_build.job_id + FROM + workspace_builds workspace_build + JOIN + provisioner_jobs provisioner_job + ON provisioner_job.id = workspace_build.job_id + WHERE + workspace_build.workspace_id = tasks.workspace_id + AND workspace_build.build_number = task_app.workspace_build_number + ) latest_build_raw ON TRUE + LEFT JOIN LATERAL ( + SELECT + workspace_agent.lifecycle_state + FROM + workspace_agents workspace_agent + WHERE + workspace_agent.id = task_app.workspace_agent_id + ) agent_raw ON TRUE + LEFT JOIN LATERAL ( + SELECT + workspace_app.health + FROM + workspace_apps workspace_app + WHERE + workspace_app.id = task_app.workspace_app_id + ) app_raw ON TRUE + + -- Compute the status for each component. + CROSS JOIN LATERAL ( + SELECT + CASE + WHEN latest_build_raw.job_status IS NULL THEN 'pending'::task_status + WHEN latest_build_raw.job_status IN ('failed', 'canceling', 'canceled') THEN 'error'::task_status + WHEN + latest_build_raw.transition IN ('stop', 'delete') + AND latest_build_raw.job_status = 'succeeded' THEN 'paused'::task_status + -- Job is pending (not picked up by provisioner yet). + WHEN + latest_build_raw.transition = 'start' + AND latest_build_raw.job_status = 'pending' THEN 'pending'::task_status + -- Job is running or done, defer to agent/app status. + WHEN + latest_build_raw.transition = 'start' + AND latest_build_raw.job_status IN ('running', 'succeeded') THEN 'active'::task_status + ELSE 'unknown'::task_status + END AS status + ) build_status + CROSS JOIN LATERAL ( + SELECT + CASE + -- No agent or connecting. + WHEN + agent_raw.lifecycle_state IS NULL + OR agent_raw.lifecycle_state IN ('created', 'starting') THEN 'initializing'::task_status + -- Agent is running, defer to app status. + -- NOTE(mafredri): The start_error/start_timeout states means connected, but some startup script failed. + -- This may or may not affect the task status but this has to be caught by app health check. + WHEN agent_raw.lifecycle_state IN ('ready', 'start_timeout', 'start_error') THEN 'active'::task_status + -- If the agent is shutting down or turned off, this is an unknown state because we would expect a stop + -- build to be running. + -- This is essentially equal to: `IN ('shutting_down', 'shutdown_timeout', 'shutdown_error', 'off')`, + -- but we cannot use them because the values were added in a migration. + WHEN agent_raw.lifecycle_state NOT IN ('created', 'starting', 'ready', 'start_timeout', 'start_error') THEN 'unknown'::task_status + ELSE 'unknown'::task_status + END AS status + ) agent_status + CROSS JOIN LATERAL ( + SELECT + CASE + WHEN app_raw.health = 'initializing' THEN 'initializing'::task_status + WHEN app_raw.health = 'unhealthy' THEN 'error'::task_status + WHEN app_raw.health IN ('healthy', 'disabled') THEN 'active'::task_status + ELSE 'unknown'::task_status + END AS status + ) app_status + WHERE + tasks.deleted_at IS NULL; diff --git a/coderd/database/modelmethods.go b/coderd/database/modelmethods.go index 8c6d7e0bd1..3408ab20d5 100644 --- a/coderd/database/modelmethods.go +++ b/coderd/database/modelmethods.go @@ -155,14 +155,23 @@ func (t Task) TaskTable() TaskTable { } func (t Task) RBACObject() rbac.Object { - return t.TaskTable().RBACObject() -} - -func (t TaskTable) RBACObject() rbac.Object { - return rbac.ResourceTask. + obj := rbac.ResourceTask. WithID(t.ID). WithOwner(t.OwnerID.String()). InOrg(t.OrganizationID) + + if rbac.WorkspaceACLDisabled() { + return obj + } + + if t.WorkspaceGroupACL != nil { + obj = obj.WithGroupACL(t.WorkspaceGroupACL.RBACACL()) + } + if t.WorkspaceUserACL != nil { + obj = obj.WithACLUserList(t.WorkspaceUserACL.RBACACL()) + } + + return obj } func (c Chat) RBACObject() rbac.Object { diff --git a/coderd/database/models.go b/coderd/database/models.go index 20c1d09700..b37ff85d3d 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -4494,6 +4494,8 @@ type Task struct { CreatedAt time.Time `db:"created_at" json:"created_at"` DeletedAt sql.NullTime `db:"deleted_at" json:"deleted_at"` DisplayName string `db:"display_name" json:"display_name"` + WorkspaceGroupACL WorkspaceACL `db:"workspace_group_acl" json:"workspace_group_acl"` + WorkspaceUserACL WorkspaceACL `db:"workspace_user_acl" json:"workspace_user_acl"` Status TaskStatus `db:"status" json:"status"` StatusDebug json.RawMessage `db:"status_debug" json:"status_debug"` WorkspaceBuildNumber sql.NullInt32 `db:"workspace_build_number" json:"workspace_build_number"` diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 088c1cdf12..6805478525 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -15196,7 +15196,7 @@ func (q *sqlQuerier) DeleteTask(ctx context.Context, arg DeleteTaskParams) (uuid } const getTaskByID = `-- name: GetTaskByID :one -SELECT id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at, display_name, status, status_debug, workspace_build_number, workspace_agent_id, workspace_app_id, workspace_agent_lifecycle_state, workspace_app_health, owner_username, owner_name, owner_avatar_url FROM tasks_with_status WHERE id = $1::uuid +SELECT id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at, display_name, workspace_group_acl, workspace_user_acl, status, status_debug, workspace_build_number, workspace_agent_id, workspace_app_id, workspace_agent_lifecycle_state, workspace_app_health, owner_username, owner_name, owner_avatar_url FROM tasks_with_status WHERE id = $1::uuid ` func (q *sqlQuerier) GetTaskByID(ctx context.Context, id uuid.UUID) (Task, error) { @@ -15214,6 +15214,8 @@ func (q *sqlQuerier) GetTaskByID(ctx context.Context, id uuid.UUID) (Task, error &i.CreatedAt, &i.DeletedAt, &i.DisplayName, + &i.WorkspaceGroupACL, + &i.WorkspaceUserACL, &i.Status, &i.StatusDebug, &i.WorkspaceBuildNumber, @@ -15229,7 +15231,7 @@ func (q *sqlQuerier) GetTaskByID(ctx context.Context, id uuid.UUID) (Task, error } const getTaskByOwnerIDAndName = `-- name: GetTaskByOwnerIDAndName :one -SELECT id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at, display_name, status, status_debug, workspace_build_number, workspace_agent_id, workspace_app_id, workspace_agent_lifecycle_state, workspace_app_health, owner_username, owner_name, owner_avatar_url FROM tasks_with_status +SELECT id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at, display_name, workspace_group_acl, workspace_user_acl, status, status_debug, workspace_build_number, workspace_agent_id, workspace_app_id, workspace_agent_lifecycle_state, workspace_app_health, owner_username, owner_name, owner_avatar_url FROM tasks_with_status WHERE owner_id = $1::uuid AND deleted_at IS NULL @@ -15256,6 +15258,8 @@ func (q *sqlQuerier) GetTaskByOwnerIDAndName(ctx context.Context, arg GetTaskByO &i.CreatedAt, &i.DeletedAt, &i.DisplayName, + &i.WorkspaceGroupACL, + &i.WorkspaceUserACL, &i.Status, &i.StatusDebug, &i.WorkspaceBuildNumber, @@ -15271,7 +15275,7 @@ func (q *sqlQuerier) GetTaskByOwnerIDAndName(ctx context.Context, arg GetTaskByO } const getTaskByWorkspaceID = `-- name: GetTaskByWorkspaceID :one -SELECT id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at, display_name, status, status_debug, workspace_build_number, workspace_agent_id, workspace_app_id, workspace_agent_lifecycle_state, workspace_app_health, owner_username, owner_name, owner_avatar_url FROM tasks_with_status WHERE workspace_id = $1::uuid +SELECT id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at, display_name, workspace_group_acl, workspace_user_acl, status, status_debug, workspace_build_number, workspace_agent_id, workspace_app_id, workspace_agent_lifecycle_state, workspace_app_health, owner_username, owner_name, owner_avatar_url FROM tasks_with_status WHERE workspace_id = $1::uuid ` func (q *sqlQuerier) GetTaskByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) (Task, error) { @@ -15289,6 +15293,8 @@ func (q *sqlQuerier) GetTaskByWorkspaceID(ctx context.Context, workspaceID uuid. &i.CreatedAt, &i.DeletedAt, &i.DisplayName, + &i.WorkspaceGroupACL, + &i.WorkspaceUserACL, &i.Status, &i.StatusDebug, &i.WorkspaceBuildNumber, @@ -15568,7 +15574,7 @@ func (q *sqlQuerier) InsertTask(ctx context.Context, arg InsertTaskParams) (Task } const listTasks = `-- name: ListTasks :many -SELECT id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at, display_name, status, status_debug, workspace_build_number, workspace_agent_id, workspace_app_id, workspace_agent_lifecycle_state, workspace_app_health, owner_username, owner_name, owner_avatar_url FROM tasks_with_status tws +SELECT id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at, display_name, workspace_group_acl, workspace_user_acl, status, status_debug, workspace_build_number, workspace_agent_id, workspace_app_id, workspace_agent_lifecycle_state, workspace_app_health, owner_username, owner_name, owner_avatar_url FROM tasks_with_status tws WHERE tws.deleted_at IS NULL AND CASE WHEN $1::UUID != '00000000-0000-0000-0000-000000000000' THEN tws.owner_id = $1::UUID ELSE TRUE END AND CASE WHEN $2::UUID != '00000000-0000-0000-0000-000000000000' THEN tws.organization_id = $2::UUID ELSE TRUE END @@ -15603,6 +15609,8 @@ func (q *sqlQuerier) ListTasks(ctx context.Context, arg ListTasksParams) ([]Task &i.CreatedAt, &i.DeletedAt, &i.DisplayName, + &i.WorkspaceGroupACL, + &i.WorkspaceUserACL, &i.Status, &i.StatusDebug, &i.WorkspaceBuildNumber, diff --git a/coderd/database/sqlc.yaml b/coderd/database/sqlc.yaml index 78d20574c6..bc5e216726 100644 --- a/coderd/database/sqlc.yaml +++ b/coderd/database/sqlc.yaml @@ -82,6 +82,12 @@ sql: - column: "template_usage_stats.app_usage_mins" go_type: type: "StringMapOfInt" + - column: "tasks_with_status.workspace_user_acl" + go_type: + type: "WorkspaceACL" + - column: "tasks_with_status.workspace_group_acl" + go_type: + type: "WorkspaceACL" - column: "workspaces.user_acl" go_type: type: "WorkspaceACL" @@ -186,6 +192,8 @@ sql: jwt: JWT user_acl: UserACL group_acl: GroupACL + workspace_user_acl: WorkspaceUserACL + workspace_group_acl: WorkspaceGroupACL user_acl_display_info: UserACLDisplayInfo group_acl_display_info: GroupACLDisplayInfo troubleshooting_url: TroubleshootingURL