feat: add an organization member permission level (#19953)

This commit is contained in:
ケイラ
2025-10-27 17:14:16 -06:00
committed by GitHub
parent c3cbd977f1
commit 4f7b279fd8
23 changed files with 612 additions and 305 deletions
+8 -7
View File
@@ -714,12 +714,13 @@ func RBACRole(role rbac.Role) codersdk.Role {
orgPerms := role.ByOrgID[slim.OrganizationID]
return codersdk.Role{
Name: slim.Name,
OrganizationID: slim.OrganizationID,
DisplayName: slim.DisplayName,
SitePermissions: List(role.Site, RBACPermission),
OrganizationPermissions: List(orgPerms.Org, RBACPermission),
UserPermissions: List(role.User, RBACPermission),
Name: slim.Name,
OrganizationID: slim.OrganizationID,
DisplayName: slim.DisplayName,
SitePermissions: List(role.Site, RBACPermission),
UserPermissions: List(role.User, RBACPermission),
OrganizationPermissions: List(orgPerms.Org, RBACPermission),
OrganizationMemberPermissions: List(orgPerms.Member, RBACPermission),
}
}
@@ -734,8 +735,8 @@ func Role(role database.CustomRole) codersdk.Role {
OrganizationID: orgID,
DisplayName: role.DisplayName,
SitePermissions: List(role.SitePermissions, Permission),
OrganizationPermissions: List(role.OrgPermissions, Permission),
UserPermissions: List(role.UserPermissions, Permission),
OrganizationPermissions: List(role.OrgPermissions, Permission),
}
}
+32 -16
View File
@@ -395,11 +395,13 @@ var (
Identifier: rbac.RoleIdentifier{Name: "subagentapi"},
DisplayName: "Sub Agent API",
Site: []rbac.Permission{},
User: rbac.Permissions(map[string][]policy.Action{
rbac.ResourceWorkspace.Type: {policy.ActionRead, policy.ActionUpdate, policy.ActionCreateAgent, policy.ActionDeleteAgent},
}),
User: []rbac.Permission{},
ByOrgID: map[string]rbac.OrgPermissions{
orgID.String(): {},
orgID.String(): {
Member: rbac.Permissions(map[string][]policy.Action{
rbac.ResourceWorkspace.Type: {policy.ActionRead, policy.ActionUpdate, policy.ActionCreateAgent, policy.ActionDeleteAgent},
}),
},
},
},
}),
@@ -1290,14 +1292,17 @@ func (q *querier) customRoleCheck(ctx context.Context, role database.CustomRole)
return xerrors.Errorf("invalid role: %w", err)
}
if len(rbacRole.ByOrgID) > 0 && len(rbacRole.Site) > 0 {
// This is a choice to keep roles simple. If we allow mixing site and org scoped perms, then knowing who can
// do what gets more complicated.
return xerrors.Errorf("invalid custom role, cannot assign both org and site permissions at the same time")
if len(rbacRole.ByOrgID) > 0 && (len(rbacRole.Site) > 0 || len(rbacRole.User) > 0) {
// This is a choice to keep roles simple. If we allow mixing site and org
// scoped perms, then knowing who can do what gets more complicated. Roles
// should either be entirely org-scoped or entirely unrelated to
// organizations.
return xerrors.Errorf("invalid custom role, cannot assign both org-scoped and site/user permissions at the same time")
}
if len(rbacRole.ByOrgID) > 1 {
// Again to avoid more complexity in our roles
// Again to avoid more complexity in our roles. Roles are limited to one
// organization.
return xerrors.Errorf("invalid custom role, cannot assign permissions to more than 1 org at a time")
}
@@ -1313,7 +1318,18 @@ func (q *querier) customRoleCheck(ctx context.Context, role database.CustomRole)
for _, orgPerm := range perms.Org {
err := q.customRoleEscalationCheck(ctx, act, orgPerm, rbac.Object{OrgID: orgID, Type: orgPerm.ResourceType})
if err != nil {
return xerrors.Errorf("org=%q: %w", orgID, err)
return xerrors.Errorf("org=%q: org: %w", orgID, err)
}
}
for _, memberPerm := range perms.Member {
// The person giving the permission should still be required to have
// the permissions throughout the org in order to give individuals the
// same permission among their own resources, since the role can be given
// to anyone. The `Owner` is intentionally omitted from the `Object` to
// enforce this.
err := q.customRoleEscalationCheck(ctx, act, memberPerm, rbac.Object{OrgID: orgID, Type: memberPerm.ResourceType})
if err != nil {
return xerrors.Errorf("org=%q: member: %w", orgID, err)
}
}
}
@@ -1331,8 +1347,8 @@ func (q *querier) customRoleCheck(ctx context.Context, role database.CustomRole)
func (q *querier) authorizeProvisionerJob(ctx context.Context, job database.ProvisionerJob) error {
switch job.Type {
case database.ProvisionerJobTypeWorkspaceBuild:
// Authorized call to get workspace build. If we can read the build, we
// can read the job.
// Authorized call to get workspace build. If we can read the build, we can
// read the job.
_, err := q.GetWorkspaceBuildByJobID(ctx, job.ID)
if err != nil {
return xerrors.Errorf("fetch related workspace build: %w", err)
@@ -1375,8 +1391,8 @@ func (q *querier) ActivityBumpWorkspace(ctx context.Context, arg database.Activi
}
func (q *querier) AllUserIDs(ctx context.Context, includeSystem bool) ([]uuid.UUID, error) {
// Although this technically only reads users, only system-related functions should be
// allowed to call this.
// Although this technically only reads users, only system-related functions
// should be allowed to call this.
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceSystem); err != nil {
return nil, err
}
@@ -1395,8 +1411,8 @@ func (q *querier) ArchiveUnusedTemplateVersions(ctx context.Context, arg databas
}
func (q *querier) BatchUpdateWorkspaceLastUsedAt(ctx context.Context, arg database.BatchUpdateWorkspaceLastUsedAtParams) error {
// Could be any workspace and checking auth to each workspace is overkill for the purpose
// of this function.
// Could be any workspace and checking auth to each workspace is overkill for
// the purpose of this function.
if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceWorkspace.All()); err != nil {
return err
}
+2
View File
@@ -208,6 +208,7 @@ func (s APIKeyScopes) expandRBACScope() (rbac.Scope, error) {
for orgID, perms := range expanded.ByOrgID {
orgPerms := merged.ByOrgID[orgID]
orgPerms.Org = append(orgPerms.Org, perms.Org...)
orgPerms.Member = append(orgPerms.Member, perms.Member...)
merged.ByOrgID[orgID] = orgPerms
}
merged.User = append(merged.User, expanded.User...)
@@ -220,6 +221,7 @@ func (s APIKeyScopes) expandRBACScope() (rbac.Scope, error) {
merged.User = rbac.DeduplicatePermissions(merged.User)
for orgID, perms := range merged.ByOrgID {
perms.Org = rbac.DeduplicatePermissions(perms.Org)
perms.Member = rbac.DeduplicatePermissions(perms.Member)
merged.ByOrgID[orgID] = perms
}