mirror of
https://github.com/coder/coder.git
synced 2026-09-21 20:51:01 +08:00
feat: add endpoint for retrieving workspace acl (#19375)
Implements `/acl [get]` for workspaces, with tests. Blocked by experiment enablement
This commit is contained in:
Generated
+141
-3
@@ -9988,6 +9988,39 @@ const docTemplate = `{
|
||||
}
|
||||
},
|
||||
"/workspaces/{workspace}/acl": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Workspaces"
|
||||
],
|
||||
"summary": "Get workspace ACLs",
|
||||
"operationId": "get-workspace-acls",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Workspace ID",
|
||||
"name": "workspace",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceACL"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"patch": {
|
||||
"security": [
|
||||
{
|
||||
@@ -17293,7 +17326,7 @@ const docTemplate = `{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"group_perms": {
|
||||
"description": "GroupPerms should be a mapping of group id to role.",
|
||||
"description": "GroupPerms is a mapping from valid group UUIDs to the template role they\nshould be granted. To remove a group from the template, use \"\" as the role\n(available as a constant named codersdk.TemplateRoleDeleted)",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/codersdk.TemplateRole"
|
||||
@@ -17304,7 +17337,7 @@ const docTemplate = `{
|
||||
}
|
||||
},
|
||||
"user_perms": {
|
||||
"description": "UserPerms should be a mapping of user id to role. The user id must be the\nuuid of the user, not a username or email address.",
|
||||
"description": "UserPerms is a mapping from valid user UUIDs to the template role they\nshould be granted. To remove a user from the template, use \"\" as the role\n(available as a constant named codersdk.TemplateRoleDeleted)",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/codersdk.TemplateRole"
|
||||
@@ -17469,13 +17502,14 @@ const docTemplate = `{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"group_roles": {
|
||||
"description": "GroupRoles is a mapping from valid group UUIDs to the workspace role they\nshould be granted. To remove a group from the workspace, use \"\" as the role\n(available as a constant named codersdk.WorkspaceRoleDeleted)",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceRole"
|
||||
}
|
||||
},
|
||||
"user_roles": {
|
||||
"description": "Keys must be valid UUIDs. To remove a user/group from the ACL use \"\" as the\nrole name (available as a constant named ` + "`" + `codersdk.WorkspaceRoleDeleted` + "`" + `)",
|
||||
"description": "UserRoles is a mapping from valid user UUIDs to the workspace role they\nshould be granted. To remove a user from the workspace, use \"\" as the role\n(available as a constant named codersdk.WorkspaceRoleDeleted)",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceRole"
|
||||
@@ -18088,6 +18122,23 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspaceACL": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"group": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceGroup"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceUser"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspaceAgent": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -19042,6 +19093,62 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspaceGroup": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"avatar_url": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"display_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"members": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.ReducedUser"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"organization_display_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"organization_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"organization_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"quota_allowance": {
|
||||
"type": "integer"
|
||||
},
|
||||
"role": {
|
||||
"enum": [
|
||||
"admin",
|
||||
"use"
|
||||
],
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/codersdk.WorkspaceRole"
|
||||
}
|
||||
]
|
||||
},
|
||||
"source": {
|
||||
"$ref": "#/definitions/codersdk.GroupSource"
|
||||
},
|
||||
"total_member_count": {
|
||||
"description": "How many members are in this group. Shows the total count,\neven if the user is not authorized to read group member details.\nMay be greater than ` + "`" + `len(Group.Members)` + "`" + `.",
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspaceHealth": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -19271,6 +19378,37 @@ const docTemplate = `{
|
||||
"WorkspaceTransitionDelete"
|
||||
]
|
||||
},
|
||||
"codersdk.WorkspaceUser": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"username"
|
||||
],
|
||||
"properties": {
|
||||
"avatar_url": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"role": {
|
||||
"enum": [
|
||||
"admin",
|
||||
"use"
|
||||
],
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/codersdk.WorkspaceRole"
|
||||
}
|
||||
]
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspacesResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
Generated
+128
-3
@@ -8832,6 +8832,35 @@
|
||||
}
|
||||
},
|
||||
"/workspaces/{workspace}/acl": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"produces": ["application/json"],
|
||||
"tags": ["Workspaces"],
|
||||
"summary": "Get workspace ACLs",
|
||||
"operationId": "get-workspace-acls",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Workspace ID",
|
||||
"name": "workspace",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceACL"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"patch": {
|
||||
"security": [
|
||||
{
|
||||
@@ -15784,7 +15813,7 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"group_perms": {
|
||||
"description": "GroupPerms should be a mapping of group id to role.",
|
||||
"description": "GroupPerms is a mapping from valid group UUIDs to the template role they\nshould be granted. To remove a group from the template, use \"\" as the role\n(available as a constant named codersdk.TemplateRoleDeleted)",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/codersdk.TemplateRole"
|
||||
@@ -15795,7 +15824,7 @@
|
||||
}
|
||||
},
|
||||
"user_perms": {
|
||||
"description": "UserPerms should be a mapping of user id to role. The user id must be the\nuuid of the user, not a username or email address.",
|
||||
"description": "UserPerms is a mapping from valid user UUIDs to the template role they\nshould be granted. To remove a user from the template, use \"\" as the role\n(available as a constant named codersdk.TemplateRoleDeleted)",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/codersdk.TemplateRole"
|
||||
@@ -15951,13 +15980,14 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"group_roles": {
|
||||
"description": "GroupRoles is a mapping from valid group UUIDs to the workspace role they\nshould be granted. To remove a group from the workspace, use \"\" as the role\n(available as a constant named codersdk.WorkspaceRoleDeleted)",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceRole"
|
||||
}
|
||||
},
|
||||
"user_roles": {
|
||||
"description": "Keys must be valid UUIDs. To remove a user/group from the ACL use \"\" as the\nrole name (available as a constant named `codersdk.WorkspaceRoleDeleted`)",
|
||||
"description": "UserRoles is a mapping from valid user UUIDs to the workspace role they\nshould be granted. To remove a user from the workspace, use \"\" as the role\n(available as a constant named codersdk.WorkspaceRoleDeleted)",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceRole"
|
||||
@@ -16534,6 +16564,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspaceACL": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"group": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceGroup"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceUser"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspaceAgent": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -17428,6 +17475,59 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspaceGroup": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"avatar_url": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"display_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"members": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.ReducedUser"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"organization_display_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"organization_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"organization_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"quota_allowance": {
|
||||
"type": "integer"
|
||||
},
|
||||
"role": {
|
||||
"enum": ["admin", "use"],
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/codersdk.WorkspaceRole"
|
||||
}
|
||||
]
|
||||
},
|
||||
"source": {
|
||||
"$ref": "#/definitions/codersdk.GroupSource"
|
||||
},
|
||||
"total_member_count": {
|
||||
"description": "How many members are in this group. Shows the total count,\neven if the user is not authorized to read group member details.\nMay be greater than `len(Group.Members)`.",
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspaceHealth": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -17645,6 +17745,31 @@
|
||||
"WorkspaceTransitionDelete"
|
||||
]
|
||||
},
|
||||
"codersdk.WorkspaceUser": {
|
||||
"type": "object",
|
||||
"required": ["id", "username"],
|
||||
"properties": {
|
||||
"avatar_url": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"role": {
|
||||
"enum": ["admin", "use"],
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/codersdk.WorkspaceRole"
|
||||
}
|
||||
]
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspacesResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -1448,6 +1448,7 @@ func New(options *Options) *API {
|
||||
httpmw.RequireExperiment(api.Experiments, codersdk.ExperimentWorkspaceSharing),
|
||||
)
|
||||
|
||||
r.Get("/", api.workspaceACL)
|
||||
r.Patch("/", api.patchWorkspaceACL)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -184,20 +184,24 @@ func TemplateVersionParameter(param database.TemplateVersionParameter) (codersdk
|
||||
}, nil
|
||||
}
|
||||
|
||||
func MinimalUser(user database.User) codersdk.MinimalUser {
|
||||
return codersdk.MinimalUser{
|
||||
ID: user.ID,
|
||||
Username: user.Username,
|
||||
AvatarURL: user.AvatarURL,
|
||||
}
|
||||
}
|
||||
|
||||
func ReducedUser(user database.User) codersdk.ReducedUser {
|
||||
return codersdk.ReducedUser{
|
||||
MinimalUser: codersdk.MinimalUser{
|
||||
ID: user.ID,
|
||||
Username: user.Username,
|
||||
AvatarURL: user.AvatarURL,
|
||||
},
|
||||
Email: user.Email,
|
||||
Name: user.Name,
|
||||
CreatedAt: user.CreatedAt,
|
||||
UpdatedAt: user.UpdatedAt,
|
||||
LastSeenAt: user.LastSeenAt,
|
||||
Status: codersdk.UserStatus(user.Status),
|
||||
LoginType: codersdk.LoginType(user.LoginType),
|
||||
MinimalUser: MinimalUser(user),
|
||||
Email: user.Email,
|
||||
Name: user.Name,
|
||||
CreatedAt: user.CreatedAt,
|
||||
UpdatedAt: user.UpdatedAt,
|
||||
LastSeenAt: user.LastSeenAt,
|
||||
Status: codersdk.UserStatus(user.Status),
|
||||
LoginType: codersdk.LoginType(user.LoginType),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3236,6 +3236,17 @@ func (q *querier) GetWebpushVAPIDKeys(ctx context.Context) (database.GetWebpushV
|
||||
return q.db.GetWebpushVAPIDKeys(ctx)
|
||||
}
|
||||
|
||||
func (q *querier) GetWorkspaceACLByID(ctx context.Context, id uuid.UUID) (database.GetWorkspaceACLByIDRow, error) {
|
||||
workspace, err := q.db.GetWorkspaceByID(ctx, id)
|
||||
if err != nil {
|
||||
return database.GetWorkspaceACLByIDRow{}, err
|
||||
}
|
||||
if err := q.authorizeContext(ctx, policy.ActionCreate, workspace); err != nil {
|
||||
return database.GetWorkspaceACLByIDRow{}, err
|
||||
}
|
||||
return q.db.GetWorkspaceACLByID(ctx, id)
|
||||
}
|
||||
|
||||
func (q *querier) GetWorkspaceAgentAndLatestBuildByAuthToken(ctx context.Context, authToken uuid.UUID) (database.GetWorkspaceAgentAndLatestBuildByAuthTokenRow, error) {
|
||||
// This is a system function
|
||||
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceSystem); err != nil {
|
||||
|
||||
@@ -1887,21 +1887,18 @@ func (s *MethodTestSuite) TestWorkspace() {
|
||||
// no asserts here because SQLFilter
|
||||
check.Args([]uuid.UUID{}, emptyPreparedAuthorized{}).Asserts()
|
||||
}))
|
||||
s.Run("UpdateWorkspaceACLByID", s.Subtest(func(db database.Store, check *expects) {
|
||||
u := dbgen.User(s.T(), db, database.User{})
|
||||
o := dbgen.Organization(s.T(), db, database.Organization{})
|
||||
tpl := dbgen.Template(s.T(), db, database.Template{
|
||||
OrganizationID: o.ID,
|
||||
CreatedBy: u.ID,
|
||||
})
|
||||
ws := dbgen.Workspace(s.T(), db, database.WorkspaceTable{
|
||||
OwnerID: u.ID,
|
||||
OrganizationID: o.ID,
|
||||
TemplateID: tpl.ID,
|
||||
})
|
||||
check.Args(database.UpdateWorkspaceACLByIDParams{
|
||||
ID: ws.ID,
|
||||
}).Asserts(ws, policy.ActionCreate)
|
||||
s.Run("GetWorkspaceACLByID", s.Mocked(func(dbM *dbmock.MockStore, faker *gofakeit.Faker, check *expects) {
|
||||
ws := testutil.Fake(s.T(), faker, database.Workspace{})
|
||||
dbM.EXPECT().GetWorkspaceByID(gomock.Any(), ws.ID).Return(ws, nil).AnyTimes()
|
||||
dbM.EXPECT().GetWorkspaceACLByID(gomock.Any(), ws.ID).Return(database.GetWorkspaceACLByIDRow{}, nil).AnyTimes()
|
||||
check.Args(ws.ID).Asserts(ws, policy.ActionCreate)
|
||||
}))
|
||||
s.Run("UpdateWorkspaceACLByID", s.Mocked(func(dbM *dbmock.MockStore, faker *gofakeit.Faker, check *expects) {
|
||||
ws := testutil.Fake(s.T(), faker, database.Workspace{})
|
||||
params := database.UpdateWorkspaceACLByIDParams{ID: ws.ID}
|
||||
dbM.EXPECT().GetWorkspaceByID(gomock.Any(), ws.ID).Return(ws, nil).AnyTimes()
|
||||
dbM.EXPECT().UpdateWorkspaceACLByID(gomock.Any(), params).Return(nil).AnyTimes()
|
||||
check.Args(params).Asserts(ws, policy.ActionCreate)
|
||||
}))
|
||||
s.Run("GetLatestWorkspaceBuildByWorkspaceID", s.Subtest(func(db database.Store, check *expects) {
|
||||
u := dbgen.User(s.T(), db, database.User{})
|
||||
|
||||
@@ -1748,6 +1748,13 @@ func (m queryMetricsStore) GetWebpushVAPIDKeys(ctx context.Context) (database.Ge
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) GetWorkspaceACLByID(ctx context.Context, id uuid.UUID) (database.GetWorkspaceACLByIDRow, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetWorkspaceACLByID(ctx, id)
|
||||
m.queryLatencies.WithLabelValues("GetWorkspaceACLByID").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) GetWorkspaceAgentAndLatestBuildByAuthToken(ctx context.Context, authToken uuid.UUID) (database.GetWorkspaceAgentAndLatestBuildByAuthTokenRow, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetWorkspaceAgentAndLatestBuildByAuthToken(ctx, authToken)
|
||||
|
||||
@@ -3721,6 +3721,21 @@ func (mr *MockStoreMockRecorder) GetWebpushVAPIDKeys(ctx any) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWebpushVAPIDKeys", reflect.TypeOf((*MockStore)(nil).GetWebpushVAPIDKeys), ctx)
|
||||
}
|
||||
|
||||
// GetWorkspaceACLByID mocks base method.
|
||||
func (m *MockStore) GetWorkspaceACLByID(ctx context.Context, id uuid.UUID) (database.GetWorkspaceACLByIDRow, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetWorkspaceACLByID", ctx, id)
|
||||
ret0, _ := ret[0].(database.GetWorkspaceACLByIDRow)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetWorkspaceACLByID indicates an expected call of GetWorkspaceACLByID.
|
||||
func (mr *MockStoreMockRecorder) GetWorkspaceACLByID(ctx, id any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkspaceACLByID", reflect.TypeOf((*MockStore)(nil).GetWorkspaceACLByID), ctx, id)
|
||||
}
|
||||
|
||||
// GetWorkspaceAgentAndLatestBuildByAuthToken mocks base method.
|
||||
func (m *MockStore) GetWorkspaceAgentAndLatestBuildByAuthToken(ctx context.Context, authToken uuid.UUID) (database.GetWorkspaceAgentAndLatestBuildByAuthTokenRow, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
||||
@@ -416,6 +416,7 @@ type sqlcQuerier interface {
|
||||
GetUsersByIDs(ctx context.Context, ids []uuid.UUID) ([]User, error)
|
||||
GetWebpushSubscriptionsByUserID(ctx context.Context, userID uuid.UUID) ([]WebpushSubscription, error)
|
||||
GetWebpushVAPIDKeys(ctx context.Context) (GetWebpushVAPIDKeysRow, error)
|
||||
GetWorkspaceACLByID(ctx context.Context, id uuid.UUID) (GetWorkspaceACLByIDRow, error)
|
||||
GetWorkspaceAgentAndLatestBuildByAuthToken(ctx context.Context, authToken uuid.UUID) (GetWorkspaceAgentAndLatestBuildByAuthTokenRow, error)
|
||||
GetWorkspaceAgentByID(ctx context.Context, id uuid.UUID) (WorkspaceAgent, error)
|
||||
GetWorkspaceAgentByInstanceID(ctx context.Context, authInstanceID string) (WorkspaceAgent, error)
|
||||
|
||||
@@ -20128,6 +20128,28 @@ func (q *sqlQuerier) GetDeploymentWorkspaceStats(ctx context.Context) (GetDeploy
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getWorkspaceACLByID = `-- name: GetWorkspaceACLByID :one
|
||||
SELECT
|
||||
group_acl as groups,
|
||||
user_acl as users
|
||||
FROM
|
||||
workspaces
|
||||
WHERE
|
||||
id = $1
|
||||
`
|
||||
|
||||
type GetWorkspaceACLByIDRow struct {
|
||||
Groups WorkspaceACL `db:"groups" json:"groups"`
|
||||
Users WorkspaceACL `db:"users" json:"users"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) GetWorkspaceACLByID(ctx context.Context, id uuid.UUID) (GetWorkspaceACLByIDRow, error) {
|
||||
row := q.db.QueryRowContext(ctx, getWorkspaceACLByID, id)
|
||||
var i GetWorkspaceACLByIDRow
|
||||
err := row.Scan(&i.Groups, &i.Users)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getWorkspaceByAgentID = `-- name: GetWorkspaceByAgentID :one
|
||||
SELECT
|
||||
id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at, automatic_updates, favorite, next_start_at, group_acl, user_acl, owner_avatar_url, owner_username, owner_name, organization_name, organization_display_name, organization_icon, organization_description, template_name, template_display_name, template_icon, template_description
|
||||
|
||||
@@ -906,6 +906,15 @@ GROUP BY workspaces.id, workspaces.name, latest_build.job_status, latest_build.j
|
||||
-- name: GetWorkspacesByTemplateID :many
|
||||
SELECT * FROM workspaces WHERE template_id = $1 AND deleted = false;
|
||||
|
||||
-- name: GetWorkspaceACLByID :one
|
||||
SELECT
|
||||
group_acl as groups,
|
||||
user_acl as users
|
||||
FROM
|
||||
workspaces
|
||||
WHERE
|
||||
id = @id;
|
||||
|
||||
-- name: UpdateWorkspaceACLByID :exec
|
||||
UPDATE
|
||||
workspaces
|
||||
|
||||
+114
-10
@@ -39,6 +39,7 @@ import (
|
||||
"github.com/coder/coder/v2/coderd/searchquery"
|
||||
"github.com/coder/coder/v2/coderd/telemetry"
|
||||
"github.com/coder/coder/v2/coderd/util/ptr"
|
||||
"github.com/coder/coder/v2/coderd/util/slice"
|
||||
"github.com/coder/coder/v2/coderd/wsbuilder"
|
||||
"github.com/coder/coder/v2/coderd/wspubsub"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
@@ -2155,6 +2156,110 @@ func (api *API) workspaceTimings(rw http.ResponseWriter, r *http.Request) {
|
||||
httpapi.Write(ctx, rw, http.StatusOK, timings)
|
||||
}
|
||||
|
||||
// @Summary Get workspace ACLs
|
||||
// @ID get-workspace-acls
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Workspaces
|
||||
// @Param workspace path string true "Workspace ID" format(uuid)
|
||||
// @Success 200 {object} codersdk.WorkspaceACL
|
||||
// @Router /workspaces/{workspace}/acl [get]
|
||||
func (api *API) workspaceACL(rw http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
ctx = r.Context()
|
||||
workspace = httpmw.WorkspaceParam(r)
|
||||
)
|
||||
|
||||
// Fetch the ACL data.
|
||||
workspaceACL, err := api.Database.GetWorkspaceACLByID(ctx, workspace.ID)
|
||||
if err != nil {
|
||||
httpapi.InternalServerError(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
// This is largely based on the template ACL implementation, and is far from
|
||||
// ideal. Usually, when we use the System context it's because we need to
|
||||
// run some query that won't actually be exposed to the user. That is not
|
||||
// the case here. This data goes directly to an unauthorized user. We are
|
||||
// just straight up breaking security promises.
|
||||
//
|
||||
// Fine for now while behind the shared-workspaces experiment, but needs to
|
||||
// be fixed before GA.
|
||||
|
||||
// Fetch all of the users and their organization memberships
|
||||
userIDs := make([]uuid.UUID, 0, len(workspaceACL.Users))
|
||||
for userID := range workspaceACL.Users {
|
||||
id, err := uuid.Parse(userID)
|
||||
if err != nil {
|
||||
api.Logger.Warn(ctx, "found invalid user uuid in workspace acl", slog.Error(err), slog.F("workspace_id", workspace.ID))
|
||||
continue
|
||||
}
|
||||
userIDs = append(userIDs, id)
|
||||
}
|
||||
// For context see https://github.com/coder/coder/pull/19375
|
||||
// nolint:gocritic
|
||||
dbUsers, err := api.Database.GetUsersByIDs(dbauthz.AsSystemRestricted(ctx), userIDs)
|
||||
if err != nil && !xerrors.Is(err, sql.ErrNoRows) {
|
||||
httpapi.InternalServerError(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Convert the db types to the codersdk.WorkspaceUser type
|
||||
users := make([]codersdk.WorkspaceUser, 0, len(dbUsers))
|
||||
for _, it := range dbUsers {
|
||||
users = append(users, codersdk.WorkspaceUser{
|
||||
MinimalUser: db2sdk.MinimalUser(it),
|
||||
Role: convertToWorkspaceRole(workspaceACL.Users[it.ID.String()].Permissions),
|
||||
})
|
||||
}
|
||||
|
||||
// Fetch all of the groups
|
||||
groupIDs := make([]uuid.UUID, 0, len(workspaceACL.Groups))
|
||||
for groupID := range workspaceACL.Groups {
|
||||
id, err := uuid.Parse(groupID)
|
||||
if err != nil {
|
||||
api.Logger.Warn(ctx, "found invalid group uuid in workspace acl", slog.Error(err), slog.F("workspace_id", workspace.ID))
|
||||
continue
|
||||
}
|
||||
groupIDs = append(groupIDs, id)
|
||||
}
|
||||
// For context see https://github.com/coder/coder/pull/19375
|
||||
// nolint:gocritic
|
||||
dbGroups, err := api.Database.GetGroups(dbauthz.AsSystemRestricted(ctx), database.GetGroupsParams{GroupIds: groupIDs})
|
||||
if err != nil && !xerrors.Is(err, sql.ErrNoRows) {
|
||||
httpapi.InternalServerError(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
groups := make([]codersdk.WorkspaceGroup, 0, len(dbGroups))
|
||||
for _, it := range dbGroups {
|
||||
var members []database.GroupMember
|
||||
// For context see https://github.com/coder/coder/pull/19375
|
||||
// nolint:gocritic
|
||||
members, err = api.Database.GetGroupMembersByGroupID(dbauthz.AsSystemRestricted(ctx), database.GetGroupMembersByGroupIDParams{
|
||||
GroupID: it.Group.ID,
|
||||
IncludeSystem: false,
|
||||
})
|
||||
if err != nil {
|
||||
httpapi.InternalServerError(rw, err)
|
||||
return
|
||||
}
|
||||
groups = append(groups, codersdk.WorkspaceGroup{
|
||||
Group: db2sdk.Group(database.GetGroupsRow{
|
||||
Group: it.Group,
|
||||
OrganizationName: it.OrganizationName,
|
||||
OrganizationDisplayName: it.OrganizationDisplayName,
|
||||
}, members, len(members)),
|
||||
Role: convertToWorkspaceRole(workspaceACL.Groups[it.Group.ID.String()].Permissions),
|
||||
})
|
||||
}
|
||||
|
||||
httpapi.Write(ctx, rw, http.StatusOK, codersdk.WorkspaceACL{
|
||||
Users: users,
|
||||
Groups: groups,
|
||||
})
|
||||
}
|
||||
|
||||
// @Summary Update workspace ACL
|
||||
// @ID update-workspace-acl
|
||||
// @Security CoderSessionToken
|
||||
@@ -2612,14 +2717,13 @@ func (WorkspaceACLUpdateValidator) ValidateRole(role codersdk.WorkspaceRole) err
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: This will go here
|
||||
// func convertToWorkspaceRole(actions []policy.Action) codersdk.TemplateRole {
|
||||
// switch {
|
||||
// case len(actions) == 2 && slice.SameElements(actions, []policy.Action{policy.ActionUse, policy.ActionRead}):
|
||||
// return codersdk.TemplateRoleUse
|
||||
// case len(actions) == 1 && actions[0] == policy.WildcardSymbol:
|
||||
// return codersdk.TemplateRoleAdmin
|
||||
// }
|
||||
func convertToWorkspaceRole(actions []policy.Action) codersdk.WorkspaceRole {
|
||||
switch {
|
||||
case slice.SameElements(actions, db2sdk.WorkspaceRoleActions(codersdk.WorkspaceRoleAdmin)):
|
||||
return codersdk.WorkspaceRoleAdmin
|
||||
case slice.SameElements(actions, db2sdk.WorkspaceRoleActions(codersdk.WorkspaceRoleUse)):
|
||||
return codersdk.WorkspaceRoleUse
|
||||
}
|
||||
|
||||
// return ""
|
||||
// }
|
||||
return codersdk.WorkspaceRoleDeleted
|
||||
}
|
||||
|
||||
@@ -4836,6 +4836,12 @@ func TestUpdateWorkspaceACL(t *testing.T) {
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
workspaceACL, err := client.WorkspaceACL(ctx, ws.ID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, workspaceACL.Users, 1)
|
||||
require.Equal(t, workspaceACL.Users[0].ID, friend.ID)
|
||||
require.Equal(t, workspaceACL.Users[0].Role, codersdk.WorkspaceRoleAdmin)
|
||||
})
|
||||
|
||||
t.Run("UnknownUserID", func(t *testing.T) {
|
||||
|
||||
@@ -193,10 +193,13 @@ type TemplateUser struct {
|
||||
}
|
||||
|
||||
type UpdateTemplateACL struct {
|
||||
// UserPerms should be a mapping of user id to role. The user id must be the
|
||||
// uuid of the user, not a username or email address.
|
||||
// UserPerms is a mapping from valid user UUIDs to the template role they
|
||||
// should be granted. To remove a user from the template, use "" as the role
|
||||
// (available as a constant named codersdk.TemplateRoleDeleted)
|
||||
UserPerms map[string]TemplateRole `json:"user_perms,omitempty" example:"<user_id>:admin,4df59e74-c027-470b-ab4d-cbba8963a5e9:use"`
|
||||
// GroupPerms should be a mapping of group id to role.
|
||||
// GroupPerms is a mapping from valid group UUIDs to the template role they
|
||||
// should be granted. To remove a group from the template, use "" as the role
|
||||
// (available as a constant named codersdk.TemplateRoleDeleted)
|
||||
GroupPerms map[string]TemplateRole `json:"group_perms,omitempty" example:"<group_id>:admin,8bd26b20-f3e8-48be-a903-46bb920cf671:use"`
|
||||
}
|
||||
|
||||
|
||||
+37
-5
@@ -663,11 +663,19 @@ func (c *Client) WorkspaceTimings(ctx context.Context, id uuid.UUID) (WorkspaceB
|
||||
return timings, json.NewDecoder(res.Body).Decode(&timings)
|
||||
}
|
||||
|
||||
type UpdateWorkspaceACL struct {
|
||||
// Keys must be valid UUIDs. To remove a user/group from the ACL use "" as the
|
||||
// role name (available as a constant named `codersdk.WorkspaceRoleDeleted`)
|
||||
UserRoles map[string]WorkspaceRole `json:"user_roles,omitempty"`
|
||||
GroupRoles map[string]WorkspaceRole `json:"group_roles,omitempty"`
|
||||
type WorkspaceACL struct {
|
||||
Users []WorkspaceUser `json:"users"`
|
||||
Groups []WorkspaceGroup `json:"group"`
|
||||
}
|
||||
|
||||
type WorkspaceGroup struct {
|
||||
Group
|
||||
Role WorkspaceRole `json:"role" enums:"admin,use"`
|
||||
}
|
||||
|
||||
type WorkspaceUser struct {
|
||||
MinimalUser
|
||||
Role WorkspaceRole `json:"role" enums:"admin,use"`
|
||||
}
|
||||
|
||||
type WorkspaceRole string
|
||||
@@ -678,6 +686,30 @@ const (
|
||||
WorkspaceRoleDeleted WorkspaceRole = ""
|
||||
)
|
||||
|
||||
func (c *Client) WorkspaceACL(ctx context.Context, workspaceID uuid.UUID) (WorkspaceACL, error) {
|
||||
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/workspaces/%s/acl", workspaceID), nil)
|
||||
if err != nil {
|
||||
return WorkspaceACL{}, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return WorkspaceACL{}, ReadBodyAsError(res)
|
||||
}
|
||||
var acl WorkspaceACL
|
||||
return acl, json.NewDecoder(res.Body).Decode(&acl)
|
||||
}
|
||||
|
||||
type UpdateWorkspaceACL struct {
|
||||
// UserRoles is a mapping from valid user UUIDs to the workspace role they
|
||||
// should be granted. To remove a user from the workspace, use "" as the role
|
||||
// (available as a constant named codersdk.WorkspaceRoleDeleted)
|
||||
UserRoles map[string]WorkspaceRole `json:"user_roles,omitempty"`
|
||||
// GroupRoles is a mapping from valid group UUIDs to the workspace role they
|
||||
// should be granted. To remove a group from the workspace, use "" as the role
|
||||
// (available as a constant named codersdk.WorkspaceRoleDeleted)
|
||||
GroupRoles map[string]WorkspaceRole `json:"group_roles,omitempty"`
|
||||
}
|
||||
|
||||
func (c *Client) UpdateWorkspaceACL(ctx context.Context, workspaceID uuid.UUID, req UpdateWorkspaceACL) error {
|
||||
res, err := c.Request(ctx, http.MethodPatch, fmt.Sprintf("/api/v2/workspaces/%s/acl", workspaceID), req)
|
||||
if err != nil {
|
||||
|
||||
Generated
+148
-12
@@ -8080,12 +8080,12 @@ Restarts will only happen on weekdays in this list on weeks which line up with W
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|--------------------|------------------------------------------------|----------|--------------|-------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `group_perms` | object | false | | Group perms should be a mapping of group ID to role. |
|
||||
| » `[any property]` | [codersdk.TemplateRole](#codersdktemplaterole) | false | | |
|
||||
| `user_perms` | object | false | | User perms should be a mapping of user ID to role. The user ID must be the uuid of the user, not a username or email address. |
|
||||
| » `[any property]` | [codersdk.TemplateRole](#codersdktemplaterole) | false | | |
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|--------------------|------------------------------------------------|----------|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `group_perms` | object | false | | Group perms is a mapping from valid group UUIDs to the template role they should be granted. To remove a group from the template, use "" as the role (available as a constant named codersdk.TemplateRoleDeleted) |
|
||||
| » `[any property]` | [codersdk.TemplateRole](#codersdktemplaterole) | false | | |
|
||||
| `user_perms` | object | false | | User perms is a mapping from valid user UUIDs to the template role they should be granted. To remove a user from the template, use "" as the role (available as a constant named codersdk.TemplateRoleDeleted) |
|
||||
| » `[any property]` | [codersdk.TemplateRole](#codersdktemplaterole) | false | | |
|
||||
|
||||
## codersdk.UpdateTemplateMeta
|
||||
|
||||
@@ -8251,12 +8251,12 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|--------------------|--------------------------------------------------|----------|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `group_roles` | object | false | | |
|
||||
| » `[any property]` | [codersdk.WorkspaceRole](#codersdkworkspacerole) | false | | |
|
||||
| `user_roles` | object | false | | Keys must be valid UUIDs. To remove a user/group from the ACL use "" as the role name (available as a constant named `codersdk.WorkspaceRoleDeleted`) |
|
||||
| » `[any property]` | [codersdk.WorkspaceRole](#codersdkworkspacerole) | false | | |
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|--------------------|--------------------------------------------------|----------|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `group_roles` | object | false | | Group roles is a mapping from valid group UUIDs to the workspace role they should be granted. To remove a group from the workspace, use "" as the role (available as a constant named codersdk.WorkspaceRoleDeleted) |
|
||||
| » `[any property]` | [codersdk.WorkspaceRole](#codersdkworkspacerole) | false | | |
|
||||
| `user_roles` | object | false | | User roles is a mapping from valid user UUIDs to the workspace role they should be granted. To remove a user from the workspace, use "" as the role (available as a constant named codersdk.WorkspaceRoleDeleted) |
|
||||
| » `[any property]` | [codersdk.WorkspaceRole](#codersdkworkspacerole) | false | | |
|
||||
|
||||
## codersdk.UpdateWorkspaceAutomaticUpdatesRequest
|
||||
|
||||
@@ -9158,6 +9158,58 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
||||
| `automatic_updates` | `always` |
|
||||
| `automatic_updates` | `never` |
|
||||
|
||||
## codersdk.WorkspaceACL
|
||||
|
||||
```json
|
||||
{
|
||||
"group": [
|
||||
{
|
||||
"avatar_url": "http://example.com",
|
||||
"display_name": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"members": [
|
||||
{
|
||||
"avatar_url": "http://example.com",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"email": "user@example.com",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"last_seen_at": "2019-08-24T14:15:22Z",
|
||||
"login_type": "",
|
||||
"name": "string",
|
||||
"status": "active",
|
||||
"theme_preference": "string",
|
||||
"updated_at": "2019-08-24T14:15:22Z",
|
||||
"username": "string"
|
||||
}
|
||||
],
|
||||
"name": "string",
|
||||
"organization_display_name": "string",
|
||||
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
|
||||
"organization_name": "string",
|
||||
"quota_allowance": 0,
|
||||
"role": "admin",
|
||||
"source": "user",
|
||||
"total_member_count": 0
|
||||
}
|
||||
],
|
||||
"users": [
|
||||
{
|
||||
"avatar_url": "http://example.com",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"role": "admin",
|
||||
"username": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|---------|-------------------------------------------------------------|----------|--------------|-------------|
|
||||
| `group` | array of [codersdk.WorkspaceGroup](#codersdkworkspacegroup) | false | | |
|
||||
| `users` | array of [codersdk.WorkspaceUser](#codersdkworkspaceuser) | false | | |
|
||||
|
||||
## codersdk.WorkspaceAgent
|
||||
|
||||
```json
|
||||
@@ -10369,6 +10421,63 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
||||
| `stopped` | integer | false | | |
|
||||
| `tx_bytes` | integer | false | | |
|
||||
|
||||
## codersdk.WorkspaceGroup
|
||||
|
||||
```json
|
||||
{
|
||||
"avatar_url": "http://example.com",
|
||||
"display_name": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"members": [
|
||||
{
|
||||
"avatar_url": "http://example.com",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"email": "user@example.com",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"last_seen_at": "2019-08-24T14:15:22Z",
|
||||
"login_type": "",
|
||||
"name": "string",
|
||||
"status": "active",
|
||||
"theme_preference": "string",
|
||||
"updated_at": "2019-08-24T14:15:22Z",
|
||||
"username": "string"
|
||||
}
|
||||
],
|
||||
"name": "string",
|
||||
"organization_display_name": "string",
|
||||
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
|
||||
"organization_name": "string",
|
||||
"quota_allowance": 0,
|
||||
"role": "admin",
|
||||
"source": "user",
|
||||
"total_member_count": 0
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|-----------------------------|-------------------------------------------------------|----------|--------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `avatar_url` | string | false | | |
|
||||
| `display_name` | string | false | | |
|
||||
| `id` | string | false | | |
|
||||
| `members` | array of [codersdk.ReducedUser](#codersdkreduceduser) | false | | |
|
||||
| `name` | string | false | | |
|
||||
| `organization_display_name` | string | false | | |
|
||||
| `organization_id` | string | false | | |
|
||||
| `organization_name` | string | false | | |
|
||||
| `quota_allowance` | integer | false | | |
|
||||
| `role` | [codersdk.WorkspaceRole](#codersdkworkspacerole) | false | | |
|
||||
| `source` | [codersdk.GroupSource](#codersdkgroupsource) | false | | |
|
||||
| `total_member_count` | integer | false | | How many members are in this group. Shows the total count, even if the user is not authorized to read group member details. May be greater than `len(Group.Members)`. |
|
||||
|
||||
#### Enumerated Values
|
||||
|
||||
| Property | Value |
|
||||
|----------|---------|
|
||||
| `role` | `admin` |
|
||||
| `role` | `use` |
|
||||
|
||||
## codersdk.WorkspaceHealth
|
||||
|
||||
```json
|
||||
@@ -10715,6 +10824,33 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
||||
| `stop` |
|
||||
| `delete` |
|
||||
|
||||
## codersdk.WorkspaceUser
|
||||
|
||||
```json
|
||||
{
|
||||
"avatar_url": "http://example.com",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"role": "admin",
|
||||
"username": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|--------------|--------------------------------------------------|----------|--------------|-------------|
|
||||
| `avatar_url` | string | false | | |
|
||||
| `id` | string | true | | |
|
||||
| `role` | [codersdk.WorkspaceRole](#codersdkworkspacerole) | false | | |
|
||||
| `username` | string | true | | |
|
||||
|
||||
#### Enumerated Values
|
||||
|
||||
| Property | Value |
|
||||
|----------|---------|
|
||||
| `role` | `admin` |
|
||||
| `role` | `use` |
|
||||
|
||||
## codersdk.WorkspacesResponse
|
||||
|
||||
```json
|
||||
|
||||
Generated
+74
@@ -1519,6 +1519,80 @@ curl -X PATCH http://coder-server:8080/api/v2/workspaces/{workspace} \
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Get workspace ACLs
|
||||
|
||||
### Code samples
|
||||
|
||||
```shell
|
||||
# Example request using curl
|
||||
curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace}/acl \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'Coder-Session-Token: API_KEY'
|
||||
```
|
||||
|
||||
`GET /workspaces/{workspace}/acl`
|
||||
|
||||
### Parameters
|
||||
|
||||
| Name | In | Type | Required | Description |
|
||||
|-------------|------|--------------|----------|--------------|
|
||||
| `workspace` | path | string(uuid) | true | Workspace ID |
|
||||
|
||||
### Example responses
|
||||
|
||||
> 200 Response
|
||||
|
||||
```json
|
||||
{
|
||||
"group": [
|
||||
{
|
||||
"avatar_url": "http://example.com",
|
||||
"display_name": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"members": [
|
||||
{
|
||||
"avatar_url": "http://example.com",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"email": "user@example.com",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"last_seen_at": "2019-08-24T14:15:22Z",
|
||||
"login_type": "",
|
||||
"name": "string",
|
||||
"status": "active",
|
||||
"theme_preference": "string",
|
||||
"updated_at": "2019-08-24T14:15:22Z",
|
||||
"username": "string"
|
||||
}
|
||||
],
|
||||
"name": "string",
|
||||
"organization_display_name": "string",
|
||||
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
|
||||
"organization_name": "string",
|
||||
"quota_allowance": 0,
|
||||
"role": "admin",
|
||||
"source": "user",
|
||||
"total_member_count": 0
|
||||
}
|
||||
],
|
||||
"users": [
|
||||
{
|
||||
"avatar_url": "http://example.com",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"role": "admin",
|
||||
"username": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Responses
|
||||
|
||||
| Status | Meaning | Description | Schema |
|
||||
|--------|---------------------------------------------------------|-------------|----------------------------------------------------------|
|
||||
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspaceACL](schemas.md#codersdkworkspaceacl) |
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Update workspace ACL
|
||||
|
||||
### Code samples
|
||||
|
||||
@@ -308,13 +308,13 @@ func convertTemplateUsers(tus []database.TemplateUser, orgIDsByUserIDs map[uuid.
|
||||
|
||||
func convertToTemplateRole(actions []policy.Action) codersdk.TemplateRole {
|
||||
switch {
|
||||
case len(actions) == 2 && slice.SameElements(actions, []policy.Action{policy.ActionUse, policy.ActionRead}):
|
||||
return codersdk.TemplateRoleUse
|
||||
case len(actions) == 1 && actions[0] == policy.WildcardSymbol:
|
||||
case slice.SameElements(actions, db2sdk.TemplateRoleActions(codersdk.TemplateRoleAdmin)):
|
||||
return codersdk.TemplateRoleAdmin
|
||||
case slice.SameElements(actions, db2sdk.TemplateRoleActions(codersdk.TemplateRoleUse)):
|
||||
return codersdk.TemplateRoleUse
|
||||
}
|
||||
|
||||
return ""
|
||||
return codersdk.TemplateRoleDeleted
|
||||
}
|
||||
|
||||
// TODO move to api.RequireFeatureMW when we are OK with changing the behavior.
|
||||
|
||||
@@ -3909,13 +3909,22 @@ func TestUpdateWorkspaceACL(t *testing.T) {
|
||||
ctx := testutil.Context(t, testutil.WaitMedium)
|
||||
err := client.UpdateWorkspaceACL(ctx, ws.ID, codersdk.UpdateWorkspaceACL{
|
||||
UserRoles: map[string]codersdk.WorkspaceRole{
|
||||
friend.ID.String(): codersdk.WorkspaceRoleAdmin,
|
||||
friend.ID.String(): codersdk.WorkspaceRoleUse,
|
||||
},
|
||||
GroupRoles: map[string]codersdk.WorkspaceRole{
|
||||
group.ID.String(): codersdk.WorkspaceRoleAdmin,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
workspaceACL, err := client.WorkspaceACL(ctx, ws.ID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, workspaceACL.Users, 1)
|
||||
require.Equal(t, workspaceACL.Users[0].ID, friend.ID)
|
||||
require.Equal(t, workspaceACL.Users[0].Role, codersdk.WorkspaceRoleUse)
|
||||
require.Len(t, workspaceACL.Groups, 1)
|
||||
require.Equal(t, workspaceACL.Groups[0].ID, group.ID)
|
||||
require.Equal(t, workspaceACL.Groups[0].Role, codersdk.WorkspaceRoleAdmin)
|
||||
})
|
||||
|
||||
t.Run("UnknownIDs", func(t *testing.T) {
|
||||
|
||||
Generated
+16
@@ -3571,6 +3571,12 @@ export interface Workspace {
|
||||
readonly is_prebuild: boolean;
|
||||
}
|
||||
|
||||
// From codersdk/workspaces.go
|
||||
export interface WorkspaceACL {
|
||||
readonly users: readonly WorkspaceUser[];
|
||||
readonly group: readonly WorkspaceGroup[];
|
||||
}
|
||||
|
||||
// From codersdk/workspaceagents.go
|
||||
export interface WorkspaceAgent {
|
||||
readonly id: string;
|
||||
@@ -3969,6 +3975,11 @@ export interface WorkspaceFilter {
|
||||
readonly q?: string;
|
||||
}
|
||||
|
||||
// From codersdk/workspaces.go
|
||||
export interface WorkspaceGroup extends Group {
|
||||
readonly role: WorkspaceRole;
|
||||
}
|
||||
|
||||
// From codersdk/workspaces.go
|
||||
export interface WorkspaceHealth {
|
||||
readonly healthy: boolean;
|
||||
@@ -4078,6 +4089,11 @@ export const WorkspaceTransitions: WorkspaceTransition[] = [
|
||||
"stop",
|
||||
];
|
||||
|
||||
// From codersdk/workspaces.go
|
||||
export interface WorkspaceUser extends MinimalUser {
|
||||
readonly role: WorkspaceRole;
|
||||
}
|
||||
|
||||
// From codersdk/workspaces.go
|
||||
export interface WorkspacesRequest extends Pagination {
|
||||
readonly q?: string;
|
||||
|
||||
Reference in New Issue
Block a user