mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: Add template-admin + user-admin role for managing templates + users (#3490)
Co-authored-by: Mathias Fredriksson <mafredri@gmail.com>
This commit is contained in:
co-authored by
Mathias Fredriksson
parent
c41261cf6e
commit
40e68cb80b
+45
-8
@@ -9,9 +9,11 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
admin string = "admin"
|
||||
member string = "member"
|
||||
auditor string = "auditor"
|
||||
admin string = "admin"
|
||||
member string = "member"
|
||||
templateAdmin string = "template-admin"
|
||||
userAdmin string = "user-admin"
|
||||
auditor string = "auditor"
|
||||
|
||||
orgAdmin string = "organization-admin"
|
||||
orgMember string = "organization-member"
|
||||
@@ -26,6 +28,14 @@ func RoleAdmin() string {
|
||||
return roleName(admin, "")
|
||||
}
|
||||
|
||||
func RoleTemplateAdmin() string {
|
||||
return roleName(templateAdmin, "")
|
||||
}
|
||||
|
||||
func RoleUserAdmin() string {
|
||||
return roleName(userAdmin, "")
|
||||
}
|
||||
|
||||
func RoleMember() string {
|
||||
return roleName(member, "")
|
||||
}
|
||||
@@ -93,6 +103,31 @@ var (
|
||||
}
|
||||
},
|
||||
|
||||
templateAdmin: func(_ string) Role {
|
||||
return Role{
|
||||
Name: templateAdmin,
|
||||
DisplayName: "Template Admin",
|
||||
Site: permissions(map[Object][]Action{
|
||||
ResourceTemplate: {ActionCreate, ActionRead, ActionUpdate, ActionDelete},
|
||||
// CRUD all files, even those they did not upload.
|
||||
ResourceFile: {ActionCreate, ActionRead, ActionUpdate, ActionDelete},
|
||||
ResourceWorkspace: {ActionCreate, ActionRead, ActionUpdate, ActionDelete},
|
||||
// CRUD to provisioner daemons for now.
|
||||
ResourceProvisionerDaemon: {ActionCreate, ActionRead, ActionUpdate, ActionDelete},
|
||||
}),
|
||||
}
|
||||
},
|
||||
|
||||
userAdmin: func(_ string) Role {
|
||||
return Role{
|
||||
Name: userAdmin,
|
||||
DisplayName: "User Admin",
|
||||
Site: permissions(map[Object][]Action{
|
||||
ResourceUser: {ActionCreate, ActionRead, ActionUpdate, ActionDelete},
|
||||
}),
|
||||
}
|
||||
},
|
||||
|
||||
// orgAdmin returns a role with all actions allows in a given
|
||||
// organization scope.
|
||||
orgAdmin: func(organizationID string) Role {
|
||||
@@ -153,11 +188,13 @@ var (
|
||||
// map[actor_role][assign_role]<can_assign>
|
||||
assignRoles = map[string]map[string]bool{
|
||||
admin: {
|
||||
admin: true,
|
||||
auditor: true,
|
||||
member: true,
|
||||
orgAdmin: true,
|
||||
orgMember: true,
|
||||
admin: true,
|
||||
auditor: true,
|
||||
member: true,
|
||||
orgAdmin: true,
|
||||
orgMember: true,
|
||||
templateAdmin: true,
|
||||
userAdmin: true,
|
||||
},
|
||||
orgAdmin: {
|
||||
orgAdmin: true,
|
||||
|
||||
@@ -18,6 +18,8 @@ func TestRoleByName(t *testing.T) {
|
||||
}{
|
||||
{Role: builtInRoles[admin]("")},
|
||||
{Role: builtInRoles[member]("")},
|
||||
{Role: builtInRoles[templateAdmin]("")},
|
||||
{Role: builtInRoles[userAdmin]("")},
|
||||
{Role: builtInRoles[auditor]("")},
|
||||
|
||||
{Role: builtInRoles[orgAdmin](uuid.New().String())},
|
||||
|
||||
+43
-25
@@ -111,6 +111,7 @@ func TestRolePermissions(t *testing.T) {
|
||||
// currentUser is anything that references "me", "mine", or "my".
|
||||
currentUser := uuid.New()
|
||||
adminID := uuid.New()
|
||||
templateAdminID := uuid.New()
|
||||
orgID := uuid.New()
|
||||
otherOrg := uuid.New()
|
||||
|
||||
@@ -124,9 +125,12 @@ func TestRolePermissions(t *testing.T) {
|
||||
otherOrgMember := authSubject{Name: "org_member_other", UserID: uuid.NewString(), Roles: []string{rbac.RoleMember(), rbac.RoleOrgMember(otherOrg)}}
|
||||
otherOrgAdmin := authSubject{Name: "org_admin_other", UserID: uuid.NewString(), Roles: []string{rbac.RoleMember(), rbac.RoleOrgMember(otherOrg), rbac.RoleOrgAdmin(otherOrg)}}
|
||||
|
||||
templateAdmin := authSubject{Name: "template-admin", UserID: templateAdminID.String(), Roles: []string{rbac.RoleMember(), rbac.RoleTemplateAdmin()}}
|
||||
userAdmin := authSubject{Name: "user-admin", UserID: templateAdminID.String(), Roles: []string{rbac.RoleMember(), rbac.RoleUserAdmin()}}
|
||||
|
||||
// requiredSubjects are required to be asserted in each test case. This is
|
||||
// to make sure one is not forgotten.
|
||||
requiredSubjects := []authSubject{memberMe, admin, orgMemberMe, orgAdmin, otherOrgAdmin, otherOrgMember}
|
||||
requiredSubjects := []authSubject{memberMe, admin, orgMemberMe, orgAdmin, otherOrgAdmin, otherOrgMember, templateAdmin, userAdmin}
|
||||
|
||||
testCases := []struct {
|
||||
// Name the test case to better locate the failing test case.
|
||||
@@ -146,7 +150,7 @@ func TestRolePermissions(t *testing.T) {
|
||||
Actions: []rbac.Action{rbac.ActionRead},
|
||||
Resource: rbac.ResourceUser,
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin, memberMe, orgMemberMe, orgAdmin, otherOrgMember, otherOrgAdmin},
|
||||
true: {admin, memberMe, orgMemberMe, orgAdmin, otherOrgMember, otherOrgAdmin, templateAdmin, userAdmin},
|
||||
false: {},
|
||||
},
|
||||
},
|
||||
@@ -155,8 +159,8 @@ func TestRolePermissions(t *testing.T) {
|
||||
Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionUpdate, rbac.ActionDelete},
|
||||
Resource: rbac.ResourceUser,
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin},
|
||||
false: {memberMe, orgMemberMe, orgAdmin, otherOrgMember, otherOrgAdmin},
|
||||
true: {admin, userAdmin},
|
||||
false: {memberMe, orgMemberMe, orgAdmin, otherOrgMember, otherOrgAdmin, templateAdmin},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -165,8 +169,18 @@ func TestRolePermissions(t *testing.T) {
|
||||
Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionRead, rbac.ActionUpdate, rbac.ActionDelete},
|
||||
Resource: rbac.ResourceWorkspace.InOrg(orgID).WithOwner(currentUser.String()),
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin, orgMemberMe, orgAdmin},
|
||||
false: {memberMe, otherOrgAdmin, otherOrgMember},
|
||||
true: {admin, orgMemberMe, orgAdmin, templateAdmin},
|
||||
false: {memberMe, otherOrgAdmin, otherOrgMember, userAdmin},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "MyWorkspaceInOrgExecution",
|
||||
// When creating the WithID won't be set, but it does not change the result.
|
||||
Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionRead, rbac.ActionUpdate, rbac.ActionDelete},
|
||||
Resource: rbac.ResourceWorkspaceExecution.InOrg(orgID).WithOwner(currentUser.String()),
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin, orgAdmin, orgMemberMe},
|
||||
false: {memberMe, otherOrgAdmin, otherOrgMember, templateAdmin, userAdmin},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -174,8 +188,8 @@ func TestRolePermissions(t *testing.T) {
|
||||
Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionUpdate, rbac.ActionDelete},
|
||||
Resource: rbac.ResourceTemplate.InOrg(orgID),
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin, orgAdmin},
|
||||
false: {memberMe, orgMemberMe, otherOrgAdmin, otherOrgMember},
|
||||
true: {admin, orgAdmin, templateAdmin},
|
||||
false: {memberMe, orgMemberMe, otherOrgAdmin, otherOrgMember, userAdmin},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -183,8 +197,8 @@ func TestRolePermissions(t *testing.T) {
|
||||
Actions: []rbac.Action{rbac.ActionRead},
|
||||
Resource: rbac.ResourceTemplate.InOrg(orgID),
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin, orgMemberMe, orgAdmin},
|
||||
false: {memberMe, otherOrgAdmin, otherOrgMember},
|
||||
true: {admin, orgMemberMe, orgAdmin, templateAdmin},
|
||||
false: {memberMe, otherOrgAdmin, otherOrgMember, userAdmin},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -192,8 +206,8 @@ func TestRolePermissions(t *testing.T) {
|
||||
Actions: []rbac.Action{rbac.ActionCreate},
|
||||
Resource: rbac.ResourceFile,
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin},
|
||||
false: {orgMemberMe, orgAdmin, memberMe, otherOrgAdmin, otherOrgMember},
|
||||
true: {admin, templateAdmin},
|
||||
false: {orgMemberMe, orgAdmin, memberMe, otherOrgAdmin, otherOrgMember, userAdmin},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -201,8 +215,8 @@ func TestRolePermissions(t *testing.T) {
|
||||
Actions: []rbac.Action{rbac.ActionRead, rbac.ActionUpdate, rbac.ActionDelete},
|
||||
Resource: rbac.ResourceFile.WithOwner(currentUser.String()),
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin, memberMe, orgMemberMe},
|
||||
false: {orgAdmin, otherOrgAdmin, otherOrgMember},
|
||||
true: {admin, memberMe, orgMemberMe, templateAdmin},
|
||||
false: {orgAdmin, otherOrgAdmin, otherOrgMember, userAdmin},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -211,7 +225,7 @@ func TestRolePermissions(t *testing.T) {
|
||||
Resource: rbac.ResourceOrganization,
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin},
|
||||
false: {orgAdmin, otherOrgAdmin, otherOrgMember, memberMe, orgMemberMe},
|
||||
false: {orgAdmin, otherOrgAdmin, otherOrgMember, memberMe, orgMemberMe, templateAdmin, userAdmin},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -220,7 +234,7 @@ func TestRolePermissions(t *testing.T) {
|
||||
Resource: rbac.ResourceOrganization.InOrg(orgID),
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin, orgAdmin},
|
||||
false: {otherOrgAdmin, otherOrgMember, memberMe, orgMemberMe},
|
||||
false: {otherOrgAdmin, otherOrgMember, memberMe, orgMemberMe, templateAdmin, userAdmin},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -229,7 +243,7 @@ func TestRolePermissions(t *testing.T) {
|
||||
Resource: rbac.ResourceOrganization.InOrg(orgID),
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin, orgAdmin, orgMemberMe},
|
||||
false: {otherOrgAdmin, otherOrgMember, memberMe},
|
||||
false: {otherOrgAdmin, otherOrgMember, memberMe, templateAdmin, userAdmin},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -238,7 +252,7 @@ func TestRolePermissions(t *testing.T) {
|
||||
Resource: rbac.ResourceRoleAssignment,
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin},
|
||||
false: {orgAdmin, orgMemberMe, otherOrgAdmin, otherOrgMember, memberMe},
|
||||
false: {orgAdmin, orgMemberMe, otherOrgAdmin, otherOrgMember, memberMe, templateAdmin, userAdmin},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -246,7 +260,7 @@ func TestRolePermissions(t *testing.T) {
|
||||
Actions: []rbac.Action{rbac.ActionRead},
|
||||
Resource: rbac.ResourceRoleAssignment,
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin, orgAdmin, orgMemberMe, otherOrgAdmin, otherOrgMember, memberMe},
|
||||
true: {admin, orgAdmin, orgMemberMe, otherOrgAdmin, otherOrgMember, memberMe, templateAdmin, userAdmin},
|
||||
false: {},
|
||||
},
|
||||
},
|
||||
@@ -256,7 +270,7 @@ func TestRolePermissions(t *testing.T) {
|
||||
Resource: rbac.ResourceOrgRoleAssignment.InOrg(orgID),
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin, orgAdmin},
|
||||
false: {orgMemberMe, otherOrgAdmin, otherOrgMember, memberMe},
|
||||
false: {orgMemberMe, otherOrgAdmin, otherOrgMember, memberMe, templateAdmin, userAdmin},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -265,7 +279,7 @@ func TestRolePermissions(t *testing.T) {
|
||||
Resource: rbac.ResourceOrgRoleAssignment.InOrg(orgID),
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin, orgAdmin, orgMemberMe},
|
||||
false: {otherOrgAdmin, otherOrgMember, memberMe},
|
||||
false: {otherOrgAdmin, otherOrgMember, memberMe, templateAdmin, userAdmin},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -274,7 +288,7 @@ func TestRolePermissions(t *testing.T) {
|
||||
Resource: rbac.ResourceAPIKey.WithOwner(currentUser.String()),
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin, orgMemberMe, memberMe},
|
||||
false: {orgAdmin, otherOrgAdmin, otherOrgMember},
|
||||
false: {orgAdmin, otherOrgAdmin, otherOrgMember, templateAdmin, userAdmin},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -283,7 +297,7 @@ func TestRolePermissions(t *testing.T) {
|
||||
Resource: rbac.ResourceUserData.WithOwner(currentUser.String()),
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin, orgMemberMe, memberMe},
|
||||
false: {orgAdmin, otherOrgAdmin, otherOrgMember},
|
||||
false: {orgAdmin, otherOrgAdmin, otherOrgMember, templateAdmin, userAdmin},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -292,7 +306,7 @@ func TestRolePermissions(t *testing.T) {
|
||||
Resource: rbac.ResourceOrganizationMember.InOrg(orgID),
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin, orgAdmin},
|
||||
false: {orgMemberMe, memberMe, otherOrgAdmin, otherOrgMember},
|
||||
false: {orgMemberMe, memberMe, otherOrgAdmin, otherOrgMember, templateAdmin, userAdmin},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -301,7 +315,7 @@ func TestRolePermissions(t *testing.T) {
|
||||
Resource: rbac.ResourceOrganizationMember.InOrg(orgID),
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin, orgAdmin, orgMemberMe},
|
||||
false: {memberMe, otherOrgAdmin, otherOrgMember},
|
||||
false: {memberMe, otherOrgAdmin, otherOrgMember, templateAdmin, userAdmin},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -396,10 +410,14 @@ func TestListRoles(t *testing.T) {
|
||||
|
||||
// If this test is ever failing, just update the list to the roles
|
||||
// expected from the builtin set.
|
||||
// Always use constant strings, as if the names change, we need to write
|
||||
// a SQL migration to change the name on the backend.
|
||||
require.ElementsMatch(t, []string{
|
||||
"admin",
|
||||
"member",
|
||||
"auditor",
|
||||
"template-admin",
|
||||
"user-admin",
|
||||
},
|
||||
siteRoleNames)
|
||||
|
||||
|
||||
@@ -22,6 +22,15 @@ var (
|
||||
Type: "workspace",
|
||||
}
|
||||
|
||||
// ResourceWorkspaceExecution CRUD. Org + User owner
|
||||
// create = workspace remote execution
|
||||
// read = ?
|
||||
// update = ?
|
||||
// delete = ?
|
||||
ResourceWorkspaceExecution = Object{
|
||||
Type: "workspace_execution",
|
||||
}
|
||||
|
||||
// ResourceAuditLog
|
||||
// read = access audit log
|
||||
ResourceAuditLog = Object{
|
||||
|
||||
Reference in New Issue
Block a user