mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
feat: add an organization member permission level (#19953)
This commit is contained in:
Generated
+21
@@ -12500,6 +12500,13 @@ const docTemplate = `{
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"organization_member_permissions": {
|
||||
"description": "OrganizationMemberPermissions are specific for the organization in the field 'OrganizationID' above.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.Permission"
|
||||
}
|
||||
},
|
||||
"organization_permissions": {
|
||||
"description": "OrganizationPermissions are specific for the organization in the field 'OrganizationID' above.",
|
||||
"type": "array",
|
||||
@@ -13723,6 +13730,13 @@ const docTemplate = `{
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"organization_member_permissions": {
|
||||
"description": "OrganizationMemberPermissions are specific to the organization the role belongs to.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.Permission"
|
||||
}
|
||||
},
|
||||
"organization_permissions": {
|
||||
"description": "OrganizationPermissions are specific to the organization the role belongs to.",
|
||||
"type": "array",
|
||||
@@ -17490,6 +17504,13 @@ const docTemplate = `{
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"organization_member_permissions": {
|
||||
"description": "OrganizationMemberPermissions are specific for the organization in the field 'OrganizationID' above.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.Permission"
|
||||
}
|
||||
},
|
||||
"organization_permissions": {
|
||||
"description": "OrganizationPermissions are specific for the organization in the field 'OrganizationID' above.",
|
||||
"type": "array",
|
||||
|
||||
Generated
+21
@@ -11182,6 +11182,13 @@
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"organization_member_permissions": {
|
||||
"description": "OrganizationMemberPermissions are specific for the organization in the field 'OrganizationID' above.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.Permission"
|
||||
}
|
||||
},
|
||||
"organization_permissions": {
|
||||
"description": "OrganizationPermissions are specific for the organization in the field 'OrganizationID' above.",
|
||||
"type": "array",
|
||||
@@ -12337,6 +12344,13 @@
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"organization_member_permissions": {
|
||||
"description": "OrganizationMemberPermissions are specific to the organization the role belongs to.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.Permission"
|
||||
}
|
||||
},
|
||||
"organization_permissions": {
|
||||
"description": "OrganizationPermissions are specific to the organization the role belongs to.",
|
||||
"type": "array",
|
||||
@@ -15982,6 +15996,13 @@
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"organization_member_permissions": {
|
||||
"description": "OrganizationMemberPermissions are specific for the organization in the field 'OrganizationID' above.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.Permission"
|
||||
}
|
||||
},
|
||||
"organization_permissions": {
|
||||
"description": "OrganizationPermissions are specific for the organization in the field 'OrganizationID' above.",
|
||||
"type": "array",
|
||||
|
||||
+11
-10
@@ -50,6 +50,13 @@ func TestCheckPermissions(t *testing.T) {
|
||||
},
|
||||
Action: "read",
|
||||
},
|
||||
readOrgWorkspaces: {
|
||||
Object: codersdk.AuthorizationObject{
|
||||
ResourceType: codersdk.ResourceWorkspace,
|
||||
OrganizationID: adminUser.OrganizationID.String(),
|
||||
},
|
||||
Action: "read",
|
||||
},
|
||||
readMyself: {
|
||||
Object: codersdk.AuthorizationObject{
|
||||
ResourceType: codersdk.ResourceUser,
|
||||
@@ -58,16 +65,10 @@ func TestCheckPermissions(t *testing.T) {
|
||||
Action: "read",
|
||||
},
|
||||
readOwnWorkspaces: {
|
||||
Object: codersdk.AuthorizationObject{
|
||||
ResourceType: codersdk.ResourceWorkspace,
|
||||
OwnerID: "me",
|
||||
},
|
||||
Action: "read",
|
||||
},
|
||||
readOrgWorkspaces: {
|
||||
Object: codersdk.AuthorizationObject{
|
||||
ResourceType: codersdk.ResourceWorkspace,
|
||||
OrganizationID: adminUser.OrganizationID.String(),
|
||||
OwnerID: "me",
|
||||
},
|
||||
Action: "read",
|
||||
},
|
||||
@@ -92,9 +93,9 @@ func TestCheckPermissions(t *testing.T) {
|
||||
UserID: adminUser.UserID,
|
||||
Check: map[string]bool{
|
||||
readAllUsers: true,
|
||||
readOrgWorkspaces: true,
|
||||
readMyself: true,
|
||||
readOwnWorkspaces: true,
|
||||
readOrgWorkspaces: true,
|
||||
updateSpecificTemplate: true,
|
||||
},
|
||||
},
|
||||
@@ -104,9 +105,9 @@ func TestCheckPermissions(t *testing.T) {
|
||||
UserID: orgAdminUser.ID,
|
||||
Check: map[string]bool{
|
||||
readAllUsers: true,
|
||||
readOrgWorkspaces: true,
|
||||
readMyself: true,
|
||||
readOwnWorkspaces: true,
|
||||
readOrgWorkspaces: true,
|
||||
updateSpecificTemplate: true,
|
||||
},
|
||||
},
|
||||
@@ -116,9 +117,9 @@ func TestCheckPermissions(t *testing.T) {
|
||||
UserID: memberUser.ID,
|
||||
Check: map[string]bool{
|
||||
readAllUsers: false,
|
||||
readOrgWorkspaces: false,
|
||||
readMyself: true,
|
||||
readOwnWorkspaces: true,
|
||||
readOrgWorkspaces: false,
|
||||
updateSpecificTemplate: false,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+72
-26
@@ -58,22 +58,68 @@ This can be represented by the following truth table, where Y represents _positi
|
||||
- `+site.app.*.read`: allowed to perform the `read` action against all objects of type `app` in a given Coder deployment.
|
||||
- `-user.workspace.*.create`: user is not allowed to create workspaces.
|
||||
|
||||
## Levels
|
||||
|
||||
A user can be given (or deprived) a permission at several levels. Currently,
|
||||
those levels are:
|
||||
|
||||
- Site-wide level
|
||||
- Organization level
|
||||
- User level
|
||||
- Organization member level
|
||||
|
||||
The site-wide level is the most authoritative. Any permission granted or denied at the side-wide level is absolute. After checking the site-wide level, depending of if the resource is owned by an organization or not, it will check the other levels.
|
||||
|
||||
- If the resource is owned by an organization, the next most authoritative level is the organization level. It acts like the site-wide level, but only for resources within the corresponding organization. The user can use that permission on any resource within that organization.
|
||||
- After the organization level is the member level. This level only applies to resources that are owned by both the organization _and_ the user.
|
||||
|
||||
- If the resource is not owned by an organization, the next level to check is the user level. This level only applies to resources owned by the user and that are not owned by any organization.
|
||||
|
||||
```
|
||||
┌──────────┐
|
||||
│ Site │
|
||||
└─────┬────┘
|
||||
┌──────────┴───────────┐
|
||||
┌──┤ Owned by an org? ├──┐
|
||||
│ └──────────────────────┘ │
|
||||
┌──┴──┐ ┌──┴─┐
|
||||
│ Yes │ │ No │
|
||||
└──┬──┘ └──┬─┘
|
||||
┌────────┴─────────┐ ┌─────┴────┐
|
||||
│ Organization │ │ User │
|
||||
└────────┬─────────┘ └──────────┘
|
||||
┌─────┴──────┐
|
||||
│ Member │
|
||||
└────────────┘
|
||||
```
|
||||
|
||||
## Roles
|
||||
|
||||
A _role_ is a set of permissions. When evaluating a role's permission to form an action, all the relevant permissions for the role are combined at each level. Permissions at a higher level override permissions at a lower level.
|
||||
|
||||
The following table shows the per-level role evaluation.
|
||||
Y indicates that the role provides positive permissions, N indicates the role provides negative permissions, and _indicates the role does not provide positive or negative permissions. YN_ indicates that the value in the cell does not matter for the access result.
|
||||
The following tables show the per-level role evaluation. Y indicates that the role provides positive permissions, N indicates the role provides negative permissions, and _indicates the role does not provide positive or negative permissions. YN_ indicates that the value in the cell does not matter for the access result. The table varies depending on if the resource belongs to an organization or not.
|
||||
|
||||
| Role (example) | Site | Org | User | Result |
|
||||
|-----------------|------|------|------|--------|
|
||||
| site-admin | Y | YN\_ | YN\_ | Y |
|
||||
| no-permission | N | YN\_ | YN\_ | N |
|
||||
| org-admin | \_ | Y | YN\_ | Y |
|
||||
| non-org-member | \_ | N | YN\_ | N |
|
||||
| user | \_ | \_ | Y | Y |
|
||||
| | \_ | \_ | N | N |
|
||||
| unauthenticated | \_ | \_ | \_ | N |
|
||||
If the resource is owned by an organization, such as a template or a workspace:
|
||||
|
||||
| Role (example) | Site | Org | OrgMember | Result |
|
||||
|--------------------------|------|------|-----------|--------|
|
||||
| site-admin | Y | YN\_ | YN\_ | Y |
|
||||
| negative-site-permission | N | YN\_ | YN\_ | N |
|
||||
| org-admin | \_ | Y | YN\_ | Y |
|
||||
| non-org-member | \_ | N | YN\_ | N |
|
||||
| member-owned | \_ | \_ | Y | Y |
|
||||
| not-member-owned | \_ | \_ | N | N |
|
||||
| unauthenticated | \_ | \_ | \_ | N |
|
||||
|
||||
If the resource is not owned by an organization:
|
||||
|
||||
| Role (example) | Site | User | Result |
|
||||
|--------------------------|------|------|--------|
|
||||
| site-admin | Y | YN\_ | Y |
|
||||
| negative-site-permission | N | YN\_ | N |
|
||||
| user-owned | \_ | Y | Y |
|
||||
| not-user-owned | \_ | N | N |
|
||||
| unauthenticated | \_ | \_ | N |
|
||||
|
||||
## Scopes
|
||||
|
||||
@@ -126,31 +172,31 @@ To learn more about OPA and Rego, see https://www.openpolicyagent.org/docs.
|
||||
There are two types of evaluation in OPA:
|
||||
|
||||
- **Full evaluation**: Produces a decision that can be enforced.
|
||||
This is the default evaluation mode, where OPA evaluates the policy using `input` data that contains all known values and returns output data with the `allow` variable.
|
||||
This is the default evaluation mode, where OPA evaluates the policy using `input` data that contains all known values and returns output data with the `allow` variable.
|
||||
- **Partial evaluation**: Produces a new policy that can be evaluated later when the _unknowns_ become _known_.
|
||||
This is an optimization in OPA where it evaluates as much of the policy as possible without resolving expressions that depend on _unknown_ values from the `input`.
|
||||
To learn more about partial evaluation, see this [OPA blog post](https://blog.openpolicyagent.org/partial-evaluation-162750eaf422).
|
||||
This is an optimization in OPA where it evaluates as much of the policy as possible without resolving expressions that depend on _unknown_ values from the `input`.
|
||||
To learn more about partial evaluation, see this [OPA blog post](https://blog.openpolicyagent.org/partial-evaluation-162750eaf422).
|
||||
|
||||
Application of Full and Partial evaluation in `rbac` package:
|
||||
|
||||
- **Full Evaluation** is handled by the `RegoAuthorizer.Authorize()` method in [`authz.go`](authz.go).
|
||||
This method determines whether a subject (user) can perform a specific action on an object.
|
||||
It performs a full evaluation of the Rego policy, which returns the `allow` variable to decide whether access is granted (`true`) or denied (`false` or undefined).
|
||||
This method determines whether a subject (user) can perform a specific action on an object.
|
||||
It performs a full evaluation of the Rego policy, which returns the `allow` variable to decide whether access is granted (`true`) or denied (`false` or undefined).
|
||||
- **Partial Evaluation** is handled by the `RegoAuthorizer.Prepare()` method in [`authz.go`](authz.go).
|
||||
This method compiles OPA’s partial evaluation queries into `SQL WHERE` clauses.
|
||||
These clauses are then used to enforce authorization directly in database queries, rather than in application code.
|
||||
This method compiles OPA’s partial evaluation queries into `SQL WHERE` clauses.
|
||||
These clauses are then used to enforce authorization directly in database queries, rather than in application code.
|
||||
|
||||
Authorization Patterns:
|
||||
|
||||
- Fetch-then-authorize: an object is first retrieved from the database, and a single authorization check is performed using full evaluation via `Authorize()`.
|
||||
- Authorize-while-fetching: Partial evaluation via `Prepare()` is used to inject SQL filters directly into queries, allowing efficient authorization of many objects of the same type.
|
||||
`dbauthz` methods that enforce authorization directly in the SQL query are prefixed with `Authorized`, for example, `GetAuthorizedWorkspaces`.
|
||||
`dbauthz` methods that enforce authorization directly in the SQL query are prefixed with `Authorized`, for example, `GetAuthorizedWorkspaces`.
|
||||
|
||||
## Testing
|
||||
|
||||
- OPA Playground: https://play.openpolicyagent.org/
|
||||
- OPA CLI (`opa eval`): useful for experimenting with different inputs and understanding how the policy behaves under various conditions.
|
||||
`opa eval` returns the constraints that must be satisfied for a rule to evaluate to `true`.
|
||||
`opa eval` returns the constraints that must be satisfied for a rule to evaluate to `true`.
|
||||
- `opa eval` requires an `input.json` file containing the input data to run the policy against.
|
||||
You can generate this file using the [gen_input.go](../../scripts/rbac-authz/gen_input.go) script.
|
||||
Note: the script currently produces a fixed input. You may need to tweak it for your specific use case.
|
||||
@@ -198,12 +244,12 @@ The script [`benchmark_authz.sh`](../../scripts/rbac-authz/benchmark_authz.sh) r
|
||||
|
||||
- To run benchmark on the current branch:
|
||||
|
||||
```bash
|
||||
benchmark_authz.sh --single
|
||||
```
|
||||
```bash
|
||||
benchmark_authz.sh --single
|
||||
```
|
||||
|
||||
- To compare benchmarks between 2 branches:
|
||||
|
||||
```bash
|
||||
benchmark_authz.sh --compare main prebuild_policy
|
||||
```
|
||||
```bash
|
||||
benchmark_authz.sh --compare main prebuild_policy
|
||||
```
|
||||
|
||||
@@ -165,6 +165,10 @@ func (role Role) regoValue() ast.Value {
|
||||
ast.StringTerm("org"),
|
||||
ast.NewTerm(regoSlice(p.Org)),
|
||||
},
|
||||
[2]*ast.Term{
|
||||
ast.StringTerm("member"),
|
||||
ast.NewTerm(regoSlice(p.Member)),
|
||||
},
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -287,7 +287,7 @@ func TestFilter(t *testing.T) {
|
||||
func TestAuthorizeDomain(t *testing.T) {
|
||||
t.Parallel()
|
||||
defOrg := uuid.New()
|
||||
unuseID := uuid.New()
|
||||
unusedID := uuid.New()
|
||||
allUsersGroup := "Everyone"
|
||||
|
||||
// orphanedUser has no organization
|
||||
@@ -318,21 +318,21 @@ func TestAuthorizeDomain(t *testing.T) {
|
||||
|
||||
testAuthorize(t, "UserACLList", user, []authTestCase{
|
||||
{
|
||||
resource: ResourceWorkspace.WithOwner(unuseID.String()).InOrg(unuseID).WithACLUserList(map[string][]policy.Action{
|
||||
resource: ResourceWorkspace.WithOwner(unusedID.String()).InOrg(unusedID).WithACLUserList(map[string][]policy.Action{
|
||||
user.ID: ResourceWorkspace.AvailableActions(),
|
||||
}),
|
||||
actions: ResourceWorkspace.AvailableActions(),
|
||||
allow: true,
|
||||
},
|
||||
{
|
||||
resource: ResourceWorkspace.WithOwner(unuseID.String()).InOrg(unuseID).WithACLUserList(map[string][]policy.Action{
|
||||
resource: ResourceWorkspace.WithOwner(unusedID.String()).InOrg(unusedID).WithACLUserList(map[string][]policy.Action{
|
||||
user.ID: {policy.WildcardSymbol},
|
||||
}),
|
||||
actions: ResourceWorkspace.AvailableActions(),
|
||||
allow: true,
|
||||
},
|
||||
{
|
||||
resource: ResourceWorkspace.WithOwner(unuseID.String()).InOrg(unuseID).WithACLUserList(map[string][]policy.Action{
|
||||
resource: ResourceWorkspace.WithOwner(unusedID.String()).InOrg(unusedID).WithACLUserList(map[string][]policy.Action{
|
||||
user.ID: {policy.ActionRead, policy.ActionUpdate},
|
||||
}),
|
||||
actions: []policy.Action{policy.ActionCreate, policy.ActionDelete},
|
||||
@@ -350,21 +350,21 @@ func TestAuthorizeDomain(t *testing.T) {
|
||||
|
||||
testAuthorize(t, "GroupACLList", user, []authTestCase{
|
||||
{
|
||||
resource: ResourceWorkspace.WithOwner(unuseID.String()).InOrg(defOrg).WithGroupACL(map[string][]policy.Action{
|
||||
resource: ResourceWorkspace.WithOwner(unusedID.String()).InOrg(defOrg).WithGroupACL(map[string][]policy.Action{
|
||||
allUsersGroup: ResourceWorkspace.AvailableActions(),
|
||||
}),
|
||||
actions: ResourceWorkspace.AvailableActions(),
|
||||
allow: true,
|
||||
},
|
||||
{
|
||||
resource: ResourceWorkspace.WithOwner(unuseID.String()).InOrg(defOrg).WithGroupACL(map[string][]policy.Action{
|
||||
resource: ResourceWorkspace.WithOwner(unusedID.String()).InOrg(defOrg).WithGroupACL(map[string][]policy.Action{
|
||||
allUsersGroup: {policy.WildcardSymbol},
|
||||
}),
|
||||
actions: ResourceWorkspace.AvailableActions(),
|
||||
allow: true,
|
||||
},
|
||||
{
|
||||
resource: ResourceWorkspace.WithOwner(unuseID.String()).InOrg(defOrg).WithGroupACL(map[string][]policy.Action{
|
||||
resource: ResourceWorkspace.WithOwner(unusedID.String()).InOrg(defOrg).WithGroupACL(map[string][]policy.Action{
|
||||
allUsersGroup: {policy.ActionRead, policy.ActionUpdate},
|
||||
}),
|
||||
actions: []policy.Action{policy.ActionCreate, policy.ActionDelete},
|
||||
@@ -389,13 +389,14 @@ func TestAuthorizeDomain(t *testing.T) {
|
||||
{resource: ResourceWorkspace.AnyOrganization().WithOwner(user.ID), actions: ResourceWorkspace.AvailableActions(), allow: true},
|
||||
{resource: ResourceTemplate.AnyOrganization(), actions: []policy.Action{policy.ActionCreate}, allow: false},
|
||||
|
||||
{resource: ResourceWorkspace.WithOwner(user.ID), actions: ResourceWorkspace.AvailableActions(), allow: true},
|
||||
// No org + me
|
||||
{resource: ResourceWorkspace.WithOwner(user.ID), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
|
||||
{resource: ResourceWorkspace.All(), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
|
||||
// Other org + me
|
||||
{resource: ResourceWorkspace.InOrg(unuseID).WithOwner(user.ID), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unuseID), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID).WithOwner(user.ID), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
|
||||
// Other org + other user
|
||||
{resource: ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
@@ -403,8 +404,8 @@ func TestAuthorizeDomain(t *testing.T) {
|
||||
{resource: ResourceWorkspace.WithOwner("not-me"), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
|
||||
// Other org + other us
|
||||
{resource: ResourceWorkspace.InOrg(unuseID).WithOwner("not-me"), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unuseID), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID).WithOwner("not-me"), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
|
||||
{resource: ResourceWorkspace.WithOwner("not-me"), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
})
|
||||
@@ -435,8 +436,8 @@ func TestAuthorizeDomain(t *testing.T) {
|
||||
{resource: ResourceWorkspace.All(), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
|
||||
// Other org + me
|
||||
{resource: ResourceWorkspace.InOrg(unuseID).WithOwner(user.ID), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unuseID), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID).WithOwner(user.ID), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
|
||||
// Other org + other user
|
||||
{resource: ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
@@ -444,8 +445,8 @@ func TestAuthorizeDomain(t *testing.T) {
|
||||
{resource: ResourceWorkspace.WithOwner("not-me"), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
|
||||
// Other org + other use
|
||||
{resource: ResourceWorkspace.InOrg(unuseID).WithOwner("not-me"), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unuseID), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID).WithOwner("not-me"), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
|
||||
{resource: ResourceWorkspace.WithOwner("not-me"), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
})
|
||||
@@ -455,6 +456,7 @@ func TestAuthorizeDomain(t *testing.T) {
|
||||
Scope: must(ExpandScope(ScopeAll)),
|
||||
Roles: Roles{
|
||||
must(RoleByName(ScopedRoleOrgAdmin(defOrg))),
|
||||
must(RoleByName(ScopedRoleOrgMember(defOrg))),
|
||||
must(RoleByName(RoleMember())),
|
||||
},
|
||||
}
|
||||
@@ -469,13 +471,14 @@ func TestAuthorizeDomain(t *testing.T) {
|
||||
{resource: ResourceWorkspace.InOrg(defOrg), actions: workspaceExceptConnect, allow: true},
|
||||
{resource: ResourceWorkspace.InOrg(defOrg), actions: workspaceConnect, allow: false},
|
||||
|
||||
{resource: ResourceWorkspace.WithOwner(user.ID), actions: ResourceWorkspace.AvailableActions(), allow: true},
|
||||
// No org + me
|
||||
{resource: ResourceWorkspace.WithOwner(user.ID), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
|
||||
{resource: ResourceWorkspace.All(), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
|
||||
// Other org + me
|
||||
{resource: ResourceWorkspace.InOrg(unuseID).WithOwner(user.ID), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unuseID), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID).WithOwner(user.ID), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
|
||||
// Other org + other user
|
||||
{resource: ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), actions: workspaceExceptConnect, allow: true},
|
||||
@@ -483,9 +486,9 @@ func TestAuthorizeDomain(t *testing.T) {
|
||||
|
||||
{resource: ResourceWorkspace.WithOwner("not-me"), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
|
||||
// Other org + other use
|
||||
{resource: ResourceWorkspace.InOrg(unuseID).WithOwner("not-me"), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unuseID), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
// Other org + other user
|
||||
{resource: ResourceWorkspace.InOrg(unusedID).WithOwner("not-me"), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
|
||||
{resource: ResourceWorkspace.WithOwner("not-me"), actions: ResourceWorkspace.AvailableActions(), allow: false},
|
||||
})
|
||||
@@ -512,8 +515,8 @@ func TestAuthorizeDomain(t *testing.T) {
|
||||
{resource: ResourceWorkspace.All(), actions: ResourceWorkspace.AvailableActions(), allow: true},
|
||||
|
||||
// Other org + me
|
||||
{resource: ResourceWorkspace.InOrg(unuseID).WithOwner(user.ID), actions: ResourceWorkspace.AvailableActions(), allow: true},
|
||||
{resource: ResourceWorkspace.InOrg(unuseID), actions: ResourceWorkspace.AvailableActions(), allow: true},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID).WithOwner(user.ID), actions: ResourceWorkspace.AvailableActions(), allow: true},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID), actions: ResourceWorkspace.AvailableActions(), allow: true},
|
||||
|
||||
// Other org + other user
|
||||
{resource: ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), actions: ResourceWorkspace.AvailableActions(), allow: true},
|
||||
@@ -521,8 +524,8 @@ func TestAuthorizeDomain(t *testing.T) {
|
||||
{resource: ResourceWorkspace.WithOwner("not-me"), actions: ResourceWorkspace.AvailableActions(), allow: true},
|
||||
|
||||
// Other org + other use
|
||||
{resource: ResourceWorkspace.InOrg(unuseID).WithOwner("not-me"), actions: ResourceWorkspace.AvailableActions(), allow: true},
|
||||
{resource: ResourceWorkspace.InOrg(unuseID), actions: ResourceWorkspace.AvailableActions(), allow: true},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID).WithOwner("not-me"), actions: ResourceWorkspace.AvailableActions(), allow: true},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID), actions: ResourceWorkspace.AvailableActions(), allow: true},
|
||||
|
||||
{resource: ResourceWorkspace.WithOwner("not-me"), actions: ResourceWorkspace.AvailableActions(), allow: true},
|
||||
})
|
||||
@@ -546,13 +549,14 @@ func TestAuthorizeDomain(t *testing.T) {
|
||||
{resource: ResourceWorkspace.InOrg(defOrg).WithOwner(user.ID), allow: true},
|
||||
{resource: ResourceWorkspace.InOrg(defOrg), allow: false},
|
||||
|
||||
{resource: ResourceWorkspace.WithOwner(user.ID), allow: true},
|
||||
// No org + me
|
||||
{resource: ResourceWorkspace.WithOwner(user.ID), allow: false},
|
||||
|
||||
{resource: ResourceWorkspace.All(), allow: false},
|
||||
|
||||
// Other org + me
|
||||
{resource: ResourceWorkspace.InOrg(unuseID).WithOwner(user.ID), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unuseID), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID).WithOwner(user.ID), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID), allow: false},
|
||||
|
||||
// Other org + other user
|
||||
{resource: ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), allow: false},
|
||||
@@ -560,8 +564,8 @@ func TestAuthorizeDomain(t *testing.T) {
|
||||
{resource: ResourceWorkspace.WithOwner("not-me"), allow: false},
|
||||
|
||||
// Other org + other use
|
||||
{resource: ResourceWorkspace.InOrg(unuseID).WithOwner("not-me"), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unuseID), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID).WithOwner("not-me"), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID), allow: false},
|
||||
|
||||
{resource: ResourceWorkspace.WithOwner("not-me"), allow: false},
|
||||
}),
|
||||
@@ -580,8 +584,8 @@ func TestAuthorizeDomain(t *testing.T) {
|
||||
{resource: ResourceWorkspace.All()},
|
||||
|
||||
// Other org + me
|
||||
{resource: ResourceWorkspace.InOrg(unuseID).WithOwner(user.ID)},
|
||||
{resource: ResourceWorkspace.InOrg(unuseID)},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID).WithOwner(user.ID)},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID)},
|
||||
|
||||
// Other org + other user
|
||||
{resource: ResourceWorkspace.InOrg(defOrg).WithOwner("not-me")},
|
||||
@@ -589,8 +593,8 @@ func TestAuthorizeDomain(t *testing.T) {
|
||||
{resource: ResourceWorkspace.WithOwner("not-me")},
|
||||
|
||||
// Other org + other use
|
||||
{resource: ResourceWorkspace.InOrg(unuseID).WithOwner("not-me")},
|
||||
{resource: ResourceWorkspace.InOrg(unuseID)},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID).WithOwner("not-me")},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID)},
|
||||
|
||||
{resource: ResourceWorkspace.WithOwner("not-me")},
|
||||
}),
|
||||
@@ -609,8 +613,8 @@ func TestAuthorizeDomain(t *testing.T) {
|
||||
{resource: ResourceTemplate.All()},
|
||||
|
||||
// Other org + me
|
||||
{resource: ResourceTemplate.InOrg(unuseID).WithOwner(user.ID)},
|
||||
{resource: ResourceTemplate.InOrg(unuseID)},
|
||||
{resource: ResourceTemplate.InOrg(unusedID).WithOwner(user.ID)},
|
||||
{resource: ResourceTemplate.InOrg(unusedID)},
|
||||
|
||||
// Other org + other user
|
||||
{resource: ResourceTemplate.InOrg(defOrg).WithOwner("not-me")},
|
||||
@@ -618,8 +622,8 @@ func TestAuthorizeDomain(t *testing.T) {
|
||||
{resource: ResourceTemplate.WithOwner("not-me")},
|
||||
|
||||
// Other org + other use
|
||||
{resource: ResourceTemplate.InOrg(unuseID).WithOwner("not-me")},
|
||||
{resource: ResourceTemplate.InOrg(unuseID)},
|
||||
{resource: ResourceTemplate.InOrg(unusedID).WithOwner("not-me")},
|
||||
{resource: ResourceTemplate.InOrg(unusedID)},
|
||||
|
||||
{resource: ResourceTemplate.WithOwner("not-me")},
|
||||
}),
|
||||
@@ -647,6 +651,7 @@ func TestAuthorizeDomain(t *testing.T) {
|
||||
ResourceType: "*",
|
||||
Action: policy.ActionRead,
|
||||
}},
|
||||
Member: []Permission{},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -668,8 +673,8 @@ func TestAuthorizeDomain(t *testing.T) {
|
||||
{resource: ResourceWorkspace.All(), allow: false},
|
||||
|
||||
// Other org + me
|
||||
{resource: ResourceWorkspace.InOrg(unuseID).WithOwner(user.ID), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unuseID), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID).WithOwner(user.ID), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID), allow: false},
|
||||
|
||||
// Other org + other user
|
||||
{resource: ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), allow: true},
|
||||
@@ -677,8 +682,8 @@ func TestAuthorizeDomain(t *testing.T) {
|
||||
{resource: ResourceWorkspace.WithOwner("not-me"), allow: false},
|
||||
|
||||
// Other org + other use
|
||||
{resource: ResourceWorkspace.InOrg(unuseID).WithOwner("not-me"), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unuseID), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID).WithOwner("not-me"), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID), allow: false},
|
||||
|
||||
{resource: ResourceWorkspace.WithOwner("not-me"), allow: false},
|
||||
}),
|
||||
@@ -699,8 +704,8 @@ func TestAuthorizeDomain(t *testing.T) {
|
||||
{resource: ResourceWorkspace.All()},
|
||||
|
||||
// Other org + me
|
||||
{resource: ResourceWorkspace.InOrg(unuseID).WithOwner(user.ID)},
|
||||
{resource: ResourceWorkspace.InOrg(unuseID)},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID).WithOwner(user.ID)},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID)},
|
||||
|
||||
// Other org + other user
|
||||
{resource: ResourceWorkspace.InOrg(defOrg).WithOwner("not-me")},
|
||||
@@ -708,8 +713,8 @@ func TestAuthorizeDomain(t *testing.T) {
|
||||
{resource: ResourceWorkspace.WithOwner("not-me")},
|
||||
|
||||
// Other org + other use
|
||||
{resource: ResourceWorkspace.InOrg(unuseID).WithOwner("not-me")},
|
||||
{resource: ResourceWorkspace.InOrg(unuseID)},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID).WithOwner("not-me")},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID)},
|
||||
|
||||
{resource: ResourceWorkspace.WithOwner("not-me")},
|
||||
}))
|
||||
@@ -737,6 +742,7 @@ func TestAuthorizeLevels(t *testing.T) {
|
||||
Action: "*",
|
||||
},
|
||||
},
|
||||
Member: []Permission{},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1150,6 +1156,7 @@ func TestAuthorizeScope(t *testing.T) {
|
||||
Org: Permissions(map[string][]policy.Action{
|
||||
ResourceWorkspace.Type: {policy.ActionRead},
|
||||
}),
|
||||
Member: []Permission{},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1316,9 +1323,9 @@ type authTestCase struct {
|
||||
func testAuthorize(t *testing.T, name string, subject Subject, sets ...[]authTestCase) {
|
||||
t.Helper()
|
||||
authorizer := NewAuthorizer(prometheus.NewRegistry())
|
||||
for _, cases := range sets {
|
||||
for i, c := range cases {
|
||||
caseName := fmt.Sprintf("%s/%d", name, i)
|
||||
for i, cases := range sets {
|
||||
for j, c := range cases {
|
||||
caseName := fmt.Sprintf("%s/Set%d/Case%d", name, i, j)
|
||||
t.Run(caseName, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
for _, a := range c.actions {
|
||||
|
||||
+15
-4
@@ -23,8 +23,13 @@
|
||||
"action": "*"
|
||||
}
|
||||
],
|
||||
"org": {},
|
||||
"user": []
|
||||
"user": [],
|
||||
"by_org_id": {
|
||||
"bf7b72bd-a2b1-4ef2-962c-1d698e0483f6": {
|
||||
"org": [],
|
||||
"member": []
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"groups": ["b617a647-b5d0-4cbe-9e40-26f89710bf18"],
|
||||
@@ -38,13 +43,19 @@
|
||||
"action": "*"
|
||||
}
|
||||
],
|
||||
"org": {},
|
||||
"user": [],
|
||||
"by_org_id": {
|
||||
"bf7b72bd-a2b1-4ef2-962c-1d698e0483f6": {
|
||||
"org": [],
|
||||
"member": []
|
||||
}
|
||||
},
|
||||
"allow_list": [
|
||||
{
|
||||
"type": "workspace",
|
||||
"id": "*"
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+81
-50
@@ -38,6 +38,44 @@ check_site_permissions(roles) := vote if {
|
||||
vote := to_vote(allow)
|
||||
}
|
||||
|
||||
#==============================================================================#
|
||||
# User level rules #
|
||||
#==============================================================================#
|
||||
|
||||
# User level rules apply to all objects owned by the subject which are not also
|
||||
# owned by an org. Permissions for objects which are "jointly" owned by an org
|
||||
# instead defer to the org member level rules.
|
||||
|
||||
default user := 0
|
||||
|
||||
user := check_user_permissions(input.subject.roles)
|
||||
|
||||
default scope_user := 0
|
||||
|
||||
scope_user := check_user_permissions([input.subject.scope])
|
||||
|
||||
check_user_permissions(roles) := vote if {
|
||||
# The object must be owned by the subject.
|
||||
input.subject.id = input.object.owner
|
||||
|
||||
# If there is an org, use org_member permissions instead
|
||||
input.object.org_owner == ""
|
||||
not input.object.any_org
|
||||
|
||||
allow := {is_allowed |
|
||||
# Iterate over all user permissions in all roles, and check which ones match
|
||||
# the action and object type.
|
||||
perm := roles[_].user[_]
|
||||
perm.action in [input.action, "*"]
|
||||
perm.resource_type in [input.object.type, "*"]
|
||||
|
||||
# If a negative matching permission was found, then we vote to disallow it.
|
||||
# If the permission is not negative, then we vote to allow it.
|
||||
is_allowed := bool_flip(perm.negate)
|
||||
}
|
||||
vote := to_vote(allow)
|
||||
}
|
||||
|
||||
#==============================================================================#
|
||||
# Org level rules #
|
||||
#==============================================================================#
|
||||
@@ -144,49 +182,33 @@ is_org_member if {
|
||||
count(org_memberships) > 0
|
||||
}
|
||||
|
||||
org_ok if {
|
||||
is_org_member
|
||||
}
|
||||
|
||||
# If the object has no organization, then the user is also considered part of
|
||||
# the non-existent org.
|
||||
org_ok if {
|
||||
input.object.org_owner == ""
|
||||
not input.object.any_org
|
||||
}
|
||||
|
||||
#==============================================================================#
|
||||
# User level rules #
|
||||
# Org member level rules #
|
||||
#==============================================================================#
|
||||
|
||||
# User level rules apply to all objects owned by the subject which are not also
|
||||
# owned by an org. Permissions for objects which are "jointly" owned by an org
|
||||
# instead defer to the org member level rules.
|
||||
# Org member level permissions apply to all objects owned by the subject _and_
|
||||
# the corresponding org. Permissions for objects which are not owned by an
|
||||
# organization instead defer to the user level rules.
|
||||
#
|
||||
# The rules for this level are very similar to the rules for the organization
|
||||
# level, and so we reuse the `check_org_permissions` function from those rules.
|
||||
|
||||
default user := 0
|
||||
default org_member := 0
|
||||
|
||||
user := check_user_permissions(input.subject.roles)
|
||||
org_member := vote if {
|
||||
# Object must be jointly owned by the user
|
||||
input.object.owner != ""
|
||||
input.subject.id = input.object.owner
|
||||
vote := check_org_permissions(input.subject.roles, "member")
|
||||
}
|
||||
|
||||
default scope_user := 0
|
||||
default scope_org_member := 0
|
||||
|
||||
scope_user := check_user_permissions([input.subject.scope])
|
||||
|
||||
check_user_permissions(roles) := vote if {
|
||||
# The object must be owned by the subject.
|
||||
input.subject.id == input.object.owner
|
||||
|
||||
allow := {is_allowed |
|
||||
# Iterate over all user permissions in all roles, and check which ones match
|
||||
# the action and object type.
|
||||
perm := roles[_].user[_]
|
||||
perm.action in [input.action, "*"]
|
||||
perm.resource_type in [input.object.type, "*"]
|
||||
|
||||
# If a negative matching permission was found, then we vote to disallow it.
|
||||
# If the permission is not negative, then we vote to allow it.
|
||||
is_allowed := bool_flip(perm.negate)
|
||||
}
|
||||
vote := to_vote(allow)
|
||||
scope_org_member := vote if {
|
||||
# Object must be jointly owned by the user
|
||||
input.object.owner != ""
|
||||
input.subject.id = input.object.owner
|
||||
vote := check_org_permissions([input.subject.scope], "member")
|
||||
}
|
||||
|
||||
#==============================================================================#
|
||||
@@ -204,6 +226,13 @@ role_allow if {
|
||||
site = 1
|
||||
}
|
||||
|
||||
# User level authorization
|
||||
role_allow if {
|
||||
not site = -1
|
||||
|
||||
user = 1
|
||||
}
|
||||
|
||||
# Org level authorization
|
||||
role_allow if {
|
||||
not site = -1
|
||||
@@ -211,16 +240,12 @@ role_allow if {
|
||||
org = 1
|
||||
}
|
||||
|
||||
# User level authorization
|
||||
# Org member authorization
|
||||
role_allow if {
|
||||
not site = -1
|
||||
not org = -1
|
||||
|
||||
# If we are not a member of an org, and the object has an org, then we are
|
||||
# not authorized. This is an "implied -1" for not being in the org.
|
||||
org_ok
|
||||
|
||||
user = 1
|
||||
org_member = 1
|
||||
}
|
||||
|
||||
#==============================================================================#
|
||||
@@ -239,6 +264,16 @@ scope_allow if {
|
||||
scope_site = 1
|
||||
}
|
||||
|
||||
# User level scope enforcement
|
||||
scope_allow if {
|
||||
# User scope permissions must be allowed by the scope, and not denied
|
||||
# by the site. The object *must not* be owned by an organization.
|
||||
object_is_included_in_scope_allow_list
|
||||
not scope_site = -1
|
||||
|
||||
scope_user = 1
|
||||
}
|
||||
|
||||
# Org level scope enforcement
|
||||
scope_allow if {
|
||||
# Org member scope permissions must be allowed by the scope, and not denied
|
||||
@@ -249,19 +284,15 @@ scope_allow if {
|
||||
scope_org = 1
|
||||
}
|
||||
|
||||
# User level scope enforcement
|
||||
# Org member level scope enforcement
|
||||
scope_allow if {
|
||||
# User scope permissions must be allowed by the scope, and not denied
|
||||
# by the site. The object *must not* be owned by an organization.
|
||||
# Org member scope permissions must be allowed by the scope, and not denied
|
||||
# by the site or org. The object *must* be owned by an organization.
|
||||
object_is_included_in_scope_allow_list
|
||||
not scope_site = -1
|
||||
not scope_org = -1
|
||||
|
||||
# If we are not a member of an org, and the object has an org, then we are
|
||||
# not authorized. This is an "implied -1" for not being in the org.
|
||||
org_ok
|
||||
|
||||
scope_user = 1
|
||||
scope_org_member = 1
|
||||
}
|
||||
|
||||
# If *.* is allowed, then all objects are in scope.
|
||||
|
||||
+28
-10
@@ -295,15 +295,11 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
|
||||
ResourceOauth2App.Type: {policy.ActionRead},
|
||||
ResourceWorkspaceProxy.Type: {policy.ActionRead},
|
||||
}),
|
||||
User: append(allPermsExcept(ResourceWorkspaceDormant, ResourcePrebuiltWorkspace, ResourceUser, ResourceOrganizationMember),
|
||||
User: append(allPermsExcept(ResourceWorkspaceDormant, ResourcePrebuiltWorkspace, ResourceWorkspace, ResourceUser, ResourceOrganizationMember, ResourceOrganizationMember),
|
||||
Permissions(map[string][]policy.Action{
|
||||
// Reduced permission set on dormant workspaces. No build, ssh, or exec
|
||||
ResourceWorkspaceDormant.Type: {policy.ActionRead, policy.ActionDelete, policy.ActionCreate, policy.ActionUpdate, policy.ActionWorkspaceStop, policy.ActionCreateAgent, policy.ActionDeleteAgent},
|
||||
// Users cannot do create/update/delete on themselves, but they
|
||||
// can read their own details.
|
||||
ResourceUser.Type: {policy.ActionRead, policy.ActionReadPersonal, policy.ActionUpdatePersonal},
|
||||
// Can read their own organization member record
|
||||
ResourceOrganizationMember.Type: {policy.ActionRead},
|
||||
// Users can create provisioner daemons scoped to themselves.
|
||||
ResourceProvisionerDaemon.Type: {policy.ActionRead, policy.ActionCreate, policy.ActionRead, policy.ActionUpdate},
|
||||
})...,
|
||||
@@ -431,6 +427,7 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
|
||||
// Note: even without PrebuiltWorkspace permissions, access is still granted via Workspace permissions.
|
||||
ResourcePrebuiltWorkspace.Type: {policy.ActionUpdate, policy.ActionDelete},
|
||||
})...),
|
||||
Member: []Permission{},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -454,6 +451,16 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
|
||||
// Can read available roles.
|
||||
ResourceAssignOrgRole.Type: {policy.ActionRead},
|
||||
}),
|
||||
Member: append(allPermsExcept(ResourceWorkspaceDormant, ResourcePrebuiltWorkspace, ResourceUser, ResourceOrganizationMember),
|
||||
Permissions(map[string][]policy.Action{
|
||||
// Reduced permission set on dormant workspaces. No build, ssh, or exec
|
||||
ResourceWorkspaceDormant.Type: {policy.ActionRead, policy.ActionDelete, policy.ActionCreate, policy.ActionUpdate, policy.ActionWorkspaceStop, policy.ActionCreateAgent, policy.ActionDeleteAgent},
|
||||
// Can read their own organization member record
|
||||
ResourceOrganizationMember.Type: {policy.ActionRead},
|
||||
// Users can create provisioner daemons scoped to themselves.
|
||||
ResourceProvisionerDaemon.Type: {policy.ActionRead, policy.ActionCreate, policy.ActionRead, policy.ActionUpdate},
|
||||
})...,
|
||||
),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -476,6 +483,7 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
|
||||
ResourceOrganization.Type: {policy.ActionRead},
|
||||
ResourceOrganizationMember.Type: {policy.ActionRead},
|
||||
}),
|
||||
Member: []Permission{},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -502,6 +510,7 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
|
||||
ResourceGroupMember.Type: ResourceGroupMember.AvailableActions(),
|
||||
ResourceIdpsyncSettings.Type: {policy.ActionRead, policy.ActionUpdate},
|
||||
}),
|
||||
Member: []Permission{},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -531,6 +540,7 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
|
||||
ResourceProvisionerDaemon.Type: {policy.ActionCreate, policy.ActionRead, policy.ActionUpdate, policy.ActionDelete},
|
||||
ResourceProvisionerJobs.Type: {policy.ActionRead, policy.ActionUpdate, policy.ActionCreate},
|
||||
}),
|
||||
Member: []Permission{},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -568,6 +578,7 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
|
||||
Action: policy.ActionDeleteAgent,
|
||||
},
|
||||
},
|
||||
Member: []Permission{},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -680,9 +691,10 @@ func (perm Permission) Valid() error {
|
||||
}
|
||||
|
||||
// Role is a set of permissions at multiple levels:
|
||||
// - Site level permissions apply EVERYWHERE
|
||||
// - Org level permissions apply to EVERYTHING in a given ORG
|
||||
// - User level permissions are the lowest
|
||||
// - Site permissions apply EVERYWHERE
|
||||
// - Org permissions apply to EVERYTHING in a given ORG
|
||||
// - User permissions apply to all resources the user owns
|
||||
// - OrgMember permissions apply to resources in the given org that the user owns
|
||||
// This is the type passed into the rego as a json payload.
|
||||
// Users of this package should instead **only** use the role names, and
|
||||
// this package will expand the role names into their json payloads.
|
||||
@@ -703,7 +715,8 @@ type Role struct {
|
||||
}
|
||||
|
||||
type OrgPermissions struct {
|
||||
Org []Permission `json:"org"`
|
||||
Org []Permission `json:"org"`
|
||||
Member []Permission `json:"member"`
|
||||
}
|
||||
|
||||
// Valid will check all it's permissions and ensure they are all correct
|
||||
@@ -720,7 +733,12 @@ func (role Role) Valid() error {
|
||||
for orgID, orgPermissions := range role.ByOrgID {
|
||||
for _, perm := range orgPermissions.Org {
|
||||
if err := perm.Valid(); err != nil {
|
||||
errs = append(errs, xerrors.Errorf("org=%q: %w", orgID, err))
|
||||
errs = append(errs, xerrors.Errorf("org=%q: org %w", orgID, err))
|
||||
}
|
||||
}
|
||||
for _, perm := range orgPermissions.Member {
|
||||
if err := perm.Valid(); err != nil {
|
||||
errs = append(errs, xerrors.Errorf("org=%q: member: %w", orgID, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,10 +33,11 @@ func BenchmarkRBACValueAllocation(b *testing.B) {
|
||||
uuid.NewString(): {policy.ActionRead, policy.ActionCreate},
|
||||
uuid.NewString(): {policy.ActionRead, policy.ActionCreate},
|
||||
uuid.NewString(): {policy.ActionRead, policy.ActionCreate},
|
||||
}).WithACLUserList(map[string][]policy.Action{
|
||||
uuid.NewString(): {policy.ActionRead, policy.ActionCreate},
|
||||
uuid.NewString(): {policy.ActionRead, policy.ActionCreate},
|
||||
})
|
||||
}).
|
||||
WithACLUserList(map[string][]policy.Action{
|
||||
uuid.NewString(): {policy.ActionRead, policy.ActionCreate},
|
||||
uuid.NewString(): {policy.ActionRead, policy.ActionCreate},
|
||||
})
|
||||
|
||||
jsonSubject := authSubject{
|
||||
ID: actor.ID,
|
||||
@@ -107,7 +108,7 @@ func TestRegoInputValue(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// This is the input that would be passed to the rego policy.
|
||||
jsonInput := map[string]interface{}{
|
||||
jsonInput := map[string]any{
|
||||
"subject": authSubject{
|
||||
ID: actor.ID,
|
||||
Roles: must(actor.Roles.Expand()),
|
||||
@@ -138,7 +139,7 @@ func TestRegoInputValue(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// This is the input that would be passed to the rego policy.
|
||||
jsonInput := map[string]interface{}{
|
||||
jsonInput := map[string]any{
|
||||
"subject": authSubject{
|
||||
ID: actor.ID,
|
||||
Roles: must(actor.Roles.Expand()),
|
||||
@@ -146,7 +147,7 @@ func TestRegoInputValue(t *testing.T) {
|
||||
Scope: must(actor.Scope.Expand()),
|
||||
},
|
||||
"action": action,
|
||||
"object": map[string]interface{}{
|
||||
"object": map[string]any{
|
||||
"type": obj.Type,
|
||||
},
|
||||
}
|
||||
@@ -282,5 +283,6 @@ func equalRoles(t *testing.T, a, b Role) {
|
||||
bv, ok := b.ByOrgID[ak]
|
||||
require.True(t, ok, "org permissions missing: %s", ak)
|
||||
require.ElementsMatchf(t, av.Org, bv.Org, "org %s permissions", ak)
|
||||
require.ElementsMatchf(t, av.Member, bv.Member, "member %s permissions", ak)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,11 +324,19 @@ func (p *DBTokenProvider) authorizeRequest(ctx context.Context, roles *rbac.Subj
|
||||
// rbacResourceOwned is for the level "authenticated". We still need to
|
||||
// make sure the API key has permissions to connect to the actor's own
|
||||
// workspace. Scopes would prevent this.
|
||||
rbacResourceOwned rbac.Object = rbac.ResourceWorkspace.WithOwner(roles.ID)
|
||||
// TODO: This is an odd repercussion of the org_member permission level.
|
||||
// This Object used to not specify an org restriction, and `InOrg` would
|
||||
// actually have a significantly different meaning (only sharing with
|
||||
// other authenticated users in the same org, whereas the existing behavior
|
||||
// is to share with any authenticated user). Because workspaces are always
|
||||
// jointly owned by an organization, there _must_ be an org restriction on
|
||||
// the object to check the proper permissions. AnyOrg is almost the same,
|
||||
// but technically excludes users who are not in any organization. This is
|
||||
// the closest we can get though without more significant refactoring.
|
||||
rbacResourceOwned rbac.Object = rbac.ResourceWorkspace.WithOwner(roles.ID).AnyOrganization()
|
||||
)
|
||||
if dbReq.AccessMethod == AccessMethodTerminal {
|
||||
rbacAction = policy.ActionSSH
|
||||
rbacResourceOwned = rbac.ResourceWorkspace.WithOwner(roles.ID)
|
||||
}
|
||||
|
||||
// Do a standard RBAC check. This accounts for share level "owner" and any
|
||||
|
||||
+18
-12
@@ -56,9 +56,11 @@ type Role struct {
|
||||
OrganizationID string `json:"organization_id,omitempty" table:"organization id" format:"uuid"`
|
||||
DisplayName string `json:"display_name" table:"display name"`
|
||||
SitePermissions []Permission `json:"site_permissions" table:"site permissions"`
|
||||
UserPermissions []Permission `json:"user_permissions" table:"user permissions"`
|
||||
// OrganizationPermissions are specific for the organization in the field 'OrganizationID' above.
|
||||
OrganizationPermissions []Permission `json:"organization_permissions" table:"organization permissions"`
|
||||
UserPermissions []Permission `json:"user_permissions" table:"user permissions"`
|
||||
// OrganizationMemberPermissions are specific for the organization in the field 'OrganizationID' above.
|
||||
OrganizationMemberPermissions []Permission `json:"organization_member_permissions" table:"organization member permissions"`
|
||||
}
|
||||
|
||||
// CustomRoleRequest is used to edit custom roles.
|
||||
@@ -66,9 +68,11 @@ type CustomRoleRequest struct {
|
||||
Name string `json:"name" table:"name,default_sort" validate:"username"`
|
||||
DisplayName string `json:"display_name" table:"display name"`
|
||||
SitePermissions []Permission `json:"site_permissions" table:"site permissions"`
|
||||
UserPermissions []Permission `json:"user_permissions" table:"user permissions"`
|
||||
// OrganizationPermissions are specific to the organization the role belongs to.
|
||||
OrganizationPermissions []Permission `json:"organization_permissions" table:"organization permissions"`
|
||||
UserPermissions []Permission `json:"user_permissions" table:"user permissions"`
|
||||
// OrganizationMemberPermissions are specific to the organization the role belongs to.
|
||||
OrganizationMemberPermissions []Permission `json:"organization_member_permissions" table:"organization member permissions"`
|
||||
}
|
||||
|
||||
// FullName returns the role name scoped to the organization ID. This is useful if
|
||||
@@ -85,11 +89,12 @@ func (r Role) FullName() string {
|
||||
// CreateOrganizationRole will create a custom organization role
|
||||
func (c *Client) CreateOrganizationRole(ctx context.Context, role Role) (Role, error) {
|
||||
req := CustomRoleRequest{
|
||||
Name: role.Name,
|
||||
DisplayName: role.DisplayName,
|
||||
SitePermissions: role.SitePermissions,
|
||||
OrganizationPermissions: role.OrganizationPermissions,
|
||||
UserPermissions: role.UserPermissions,
|
||||
Name: role.Name,
|
||||
DisplayName: role.DisplayName,
|
||||
SitePermissions: role.SitePermissions,
|
||||
UserPermissions: role.UserPermissions,
|
||||
OrganizationPermissions: role.OrganizationPermissions,
|
||||
OrganizationMemberPermissions: role.OrganizationMemberPermissions,
|
||||
}
|
||||
|
||||
res, err := c.Request(ctx, http.MethodPost,
|
||||
@@ -108,11 +113,12 @@ func (c *Client) CreateOrganizationRole(ctx context.Context, role Role) (Role, e
|
||||
// UpdateOrganizationRole will update an existing custom organization role
|
||||
func (c *Client) UpdateOrganizationRole(ctx context.Context, role Role) (Role, error) {
|
||||
req := CustomRoleRequest{
|
||||
Name: role.Name,
|
||||
DisplayName: role.DisplayName,
|
||||
SitePermissions: role.SitePermissions,
|
||||
OrganizationPermissions: role.OrganizationPermissions,
|
||||
UserPermissions: role.UserPermissions,
|
||||
Name: role.Name,
|
||||
DisplayName: role.DisplayName,
|
||||
SitePermissions: role.SitePermissions,
|
||||
UserPermissions: role.UserPermissions,
|
||||
OrganizationPermissions: role.OrganizationPermissions,
|
||||
OrganizationMemberPermissions: role.OrganizationMemberPermissions,
|
||||
}
|
||||
|
||||
res, err := c.Request(ctx, http.MethodPut,
|
||||
|
||||
Generated
+118
-64
@@ -112,6 +112,13 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/members
|
||||
"display_name": "string",
|
||||
"name": "string",
|
||||
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
|
||||
"organization_member_permissions": [
|
||||
{
|
||||
"action": "application_connect",
|
||||
"negate": true,
|
||||
"resource_type": "*"
|
||||
}
|
||||
],
|
||||
"organization_permissions": [
|
||||
{
|
||||
"action": "application_connect",
|
||||
@@ -147,20 +154,21 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/members
|
||||
|
||||
Status Code **200**
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|------------------------------|----------------------------------------------------------|----------|--------------|-------------------------------------------------------------------------------------------------|
|
||||
| `[array item]` | array | false | | |
|
||||
| `» assignable` | boolean | false | | |
|
||||
| `» built_in` | boolean | false | | Built in roles are immutable |
|
||||
| `» display_name` | string | false | | |
|
||||
| `» name` | string | false | | |
|
||||
| `» organization_id` | string(uuid) | false | | |
|
||||
| `» organization_permissions` | array | false | | Organization permissions are specific for the organization in the field 'OrganizationID' above. |
|
||||
| `»» action` | [codersdk.RBACAction](schemas.md#codersdkrbacaction) | false | | |
|
||||
| `»» negate` | boolean | false | | Negate makes this a negative permission |
|
||||
| `»» resource_type` | [codersdk.RBACResource](schemas.md#codersdkrbacresource) | false | | |
|
||||
| `» site_permissions` | array | false | | |
|
||||
| `» user_permissions` | array | false | | |
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|-------------------------------------|----------------------------------------------------------|----------|--------------|--------------------------------------------------------------------------------------------------------|
|
||||
| `[array item]` | array | false | | |
|
||||
| `» assignable` | boolean | false | | |
|
||||
| `» built_in` | boolean | false | | Built in roles are immutable |
|
||||
| `» display_name` | string | false | | |
|
||||
| `» name` | string | false | | |
|
||||
| `» organization_id` | string(uuid) | false | | |
|
||||
| `» organization_member_permissions` | array | false | | Organization member permissions are specific for the organization in the field 'OrganizationID' above. |
|
||||
| `»» action` | [codersdk.RBACAction](schemas.md#codersdkrbacaction) | false | | |
|
||||
| `»» negate` | boolean | false | | Negate makes this a negative permission |
|
||||
| `»» resource_type` | [codersdk.RBACResource](schemas.md#codersdkrbacresource) | false | | |
|
||||
| `» organization_permissions` | array | false | | Organization permissions are specific for the organization in the field 'OrganizationID' above. |
|
||||
| `» site_permissions` | array | false | | |
|
||||
| `» user_permissions` | array | false | | |
|
||||
|
||||
#### Enumerated Values
|
||||
|
||||
@@ -248,6 +256,13 @@ curl -X PUT http://coder-server:8080/api/v2/organizations/{organization}/members
|
||||
{
|
||||
"display_name": "string",
|
||||
"name": "string",
|
||||
"organization_member_permissions": [
|
||||
{
|
||||
"action": "application_connect",
|
||||
"negate": true,
|
||||
"resource_type": "*"
|
||||
}
|
||||
],
|
||||
"organization_permissions": [
|
||||
{
|
||||
"action": "application_connect",
|
||||
@@ -289,6 +304,13 @@ curl -X PUT http://coder-server:8080/api/v2/organizations/{organization}/members
|
||||
"display_name": "string",
|
||||
"name": "string",
|
||||
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
|
||||
"organization_member_permissions": [
|
||||
{
|
||||
"action": "application_connect",
|
||||
"negate": true,
|
||||
"resource_type": "*"
|
||||
}
|
||||
],
|
||||
"organization_permissions": [
|
||||
{
|
||||
"action": "application_connect",
|
||||
@@ -324,18 +346,19 @@ curl -X PUT http://coder-server:8080/api/v2/organizations/{organization}/members
|
||||
|
||||
Status Code **200**
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|------------------------------|----------------------------------------------------------|----------|--------------|-------------------------------------------------------------------------------------------------|
|
||||
| `[array item]` | array | false | | |
|
||||
| `» display_name` | string | false | | |
|
||||
| `» name` | string | false | | |
|
||||
| `» organization_id` | string(uuid) | false | | |
|
||||
| `» organization_permissions` | array | false | | Organization permissions are specific for the organization in the field 'OrganizationID' above. |
|
||||
| `»» action` | [codersdk.RBACAction](schemas.md#codersdkrbacaction) | false | | |
|
||||
| `»» negate` | boolean | false | | Negate makes this a negative permission |
|
||||
| `»» resource_type` | [codersdk.RBACResource](schemas.md#codersdkrbacresource) | false | | |
|
||||
| `» site_permissions` | array | false | | |
|
||||
| `» user_permissions` | array | false | | |
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|-------------------------------------|----------------------------------------------------------|----------|--------------|--------------------------------------------------------------------------------------------------------|
|
||||
| `[array item]` | array | false | | |
|
||||
| `» display_name` | string | false | | |
|
||||
| `» name` | string | false | | |
|
||||
| `» organization_id` | string(uuid) | false | | |
|
||||
| `» organization_member_permissions` | array | false | | Organization member permissions are specific for the organization in the field 'OrganizationID' above. |
|
||||
| `»» action` | [codersdk.RBACAction](schemas.md#codersdkrbacaction) | false | | |
|
||||
| `»» negate` | boolean | false | | Negate makes this a negative permission |
|
||||
| `»» resource_type` | [codersdk.RBACResource](schemas.md#codersdkrbacresource) | false | | |
|
||||
| `» organization_permissions` | array | false | | Organization permissions are specific for the organization in the field 'OrganizationID' above. |
|
||||
| `» site_permissions` | array | false | | |
|
||||
| `» user_permissions` | array | false | | |
|
||||
|
||||
#### Enumerated Values
|
||||
|
||||
@@ -423,6 +446,13 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/member
|
||||
{
|
||||
"display_name": "string",
|
||||
"name": "string",
|
||||
"organization_member_permissions": [
|
||||
{
|
||||
"action": "application_connect",
|
||||
"negate": true,
|
||||
"resource_type": "*"
|
||||
}
|
||||
],
|
||||
"organization_permissions": [
|
||||
{
|
||||
"action": "application_connect",
|
||||
@@ -464,6 +494,13 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/member
|
||||
"display_name": "string",
|
||||
"name": "string",
|
||||
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
|
||||
"organization_member_permissions": [
|
||||
{
|
||||
"action": "application_connect",
|
||||
"negate": true,
|
||||
"resource_type": "*"
|
||||
}
|
||||
],
|
||||
"organization_permissions": [
|
||||
{
|
||||
"action": "application_connect",
|
||||
@@ -499,18 +536,19 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/member
|
||||
|
||||
Status Code **200**
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|------------------------------|----------------------------------------------------------|----------|--------------|-------------------------------------------------------------------------------------------------|
|
||||
| `[array item]` | array | false | | |
|
||||
| `» display_name` | string | false | | |
|
||||
| `» name` | string | false | | |
|
||||
| `» organization_id` | string(uuid) | false | | |
|
||||
| `» organization_permissions` | array | false | | Organization permissions are specific for the organization in the field 'OrganizationID' above. |
|
||||
| `»» action` | [codersdk.RBACAction](schemas.md#codersdkrbacaction) | false | | |
|
||||
| `»» negate` | boolean | false | | Negate makes this a negative permission |
|
||||
| `»» resource_type` | [codersdk.RBACResource](schemas.md#codersdkrbacresource) | false | | |
|
||||
| `» site_permissions` | array | false | | |
|
||||
| `» user_permissions` | array | false | | |
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|-------------------------------------|----------------------------------------------------------|----------|--------------|--------------------------------------------------------------------------------------------------------|
|
||||
| `[array item]` | array | false | | |
|
||||
| `» display_name` | string | false | | |
|
||||
| `» name` | string | false | | |
|
||||
| `» organization_id` | string(uuid) | false | | |
|
||||
| `» organization_member_permissions` | array | false | | Organization member permissions are specific for the organization in the field 'OrganizationID' above. |
|
||||
| `»» action` | [codersdk.RBACAction](schemas.md#codersdkrbacaction) | false | | |
|
||||
| `»» negate` | boolean | false | | Negate makes this a negative permission |
|
||||
| `»» resource_type` | [codersdk.RBACResource](schemas.md#codersdkrbacresource) | false | | |
|
||||
| `» organization_permissions` | array | false | | Organization permissions are specific for the organization in the field 'OrganizationID' above. |
|
||||
| `» site_permissions` | array | false | | |
|
||||
| `» user_permissions` | array | false | | |
|
||||
|
||||
#### Enumerated Values
|
||||
|
||||
@@ -608,6 +646,13 @@ curl -X DELETE http://coder-server:8080/api/v2/organizations/{organization}/memb
|
||||
"display_name": "string",
|
||||
"name": "string",
|
||||
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
|
||||
"organization_member_permissions": [
|
||||
{
|
||||
"action": "application_connect",
|
||||
"negate": true,
|
||||
"resource_type": "*"
|
||||
}
|
||||
],
|
||||
"organization_permissions": [
|
||||
{
|
||||
"action": "application_connect",
|
||||
@@ -643,18 +688,19 @@ curl -X DELETE http://coder-server:8080/api/v2/organizations/{organization}/memb
|
||||
|
||||
Status Code **200**
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|------------------------------|----------------------------------------------------------|----------|--------------|-------------------------------------------------------------------------------------------------|
|
||||
| `[array item]` | array | false | | |
|
||||
| `» display_name` | string | false | | |
|
||||
| `» name` | string | false | | |
|
||||
| `» organization_id` | string(uuid) | false | | |
|
||||
| `» organization_permissions` | array | false | | Organization permissions are specific for the organization in the field 'OrganizationID' above. |
|
||||
| `»» action` | [codersdk.RBACAction](schemas.md#codersdkrbacaction) | false | | |
|
||||
| `»» negate` | boolean | false | | Negate makes this a negative permission |
|
||||
| `»» resource_type` | [codersdk.RBACResource](schemas.md#codersdkrbacresource) | false | | |
|
||||
| `» site_permissions` | array | false | | |
|
||||
| `» user_permissions` | array | false | | |
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|-------------------------------------|----------------------------------------------------------|----------|--------------|--------------------------------------------------------------------------------------------------------|
|
||||
| `[array item]` | array | false | | |
|
||||
| `» display_name` | string | false | | |
|
||||
| `» name` | string | false | | |
|
||||
| `» organization_id` | string(uuid) | false | | |
|
||||
| `» organization_member_permissions` | array | false | | Organization member permissions are specific for the organization in the field 'OrganizationID' above. |
|
||||
| `»» action` | [codersdk.RBACAction](schemas.md#codersdkrbacaction) | false | | |
|
||||
| `»» negate` | boolean | false | | Negate makes this a negative permission |
|
||||
| `»» resource_type` | [codersdk.RBACResource](schemas.md#codersdkrbacresource) | false | | |
|
||||
| `» organization_permissions` | array | false | | Organization permissions are specific for the organization in the field 'OrganizationID' above. |
|
||||
| `» site_permissions` | array | false | | |
|
||||
| `» user_permissions` | array | false | | |
|
||||
|
||||
#### Enumerated Values
|
||||
|
||||
@@ -972,6 +1018,13 @@ curl -X GET http://coder-server:8080/api/v2/users/roles \
|
||||
"display_name": "string",
|
||||
"name": "string",
|
||||
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
|
||||
"organization_member_permissions": [
|
||||
{
|
||||
"action": "application_connect",
|
||||
"negate": true,
|
||||
"resource_type": "*"
|
||||
}
|
||||
],
|
||||
"organization_permissions": [
|
||||
{
|
||||
"action": "application_connect",
|
||||
@@ -1007,20 +1060,21 @@ curl -X GET http://coder-server:8080/api/v2/users/roles \
|
||||
|
||||
Status Code **200**
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|------------------------------|----------------------------------------------------------|----------|--------------|-------------------------------------------------------------------------------------------------|
|
||||
| `[array item]` | array | false | | |
|
||||
| `» assignable` | boolean | false | | |
|
||||
| `» built_in` | boolean | false | | Built in roles are immutable |
|
||||
| `» display_name` | string | false | | |
|
||||
| `» name` | string | false | | |
|
||||
| `» organization_id` | string(uuid) | false | | |
|
||||
| `» organization_permissions` | array | false | | Organization permissions are specific for the organization in the field 'OrganizationID' above. |
|
||||
| `»» action` | [codersdk.RBACAction](schemas.md#codersdkrbacaction) | false | | |
|
||||
| `»» negate` | boolean | false | | Negate makes this a negative permission |
|
||||
| `»» resource_type` | [codersdk.RBACResource](schemas.md#codersdkrbacresource) | false | | |
|
||||
| `» site_permissions` | array | false | | |
|
||||
| `» user_permissions` | array | false | | |
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|-------------------------------------|----------------------------------------------------------|----------|--------------|--------------------------------------------------------------------------------------------------------|
|
||||
| `[array item]` | array | false | | |
|
||||
| `» assignable` | boolean | false | | |
|
||||
| `» built_in` | boolean | false | | Built in roles are immutable |
|
||||
| `» display_name` | string | false | | |
|
||||
| `» name` | string | false | | |
|
||||
| `» organization_id` | string(uuid) | false | | |
|
||||
| `» organization_member_permissions` | array | false | | Organization member permissions are specific for the organization in the field 'OrganizationID' above. |
|
||||
| `»» action` | [codersdk.RBACAction](schemas.md#codersdkrbacaction) | false | | |
|
||||
| `»» negate` | boolean | false | | Negate makes this a negative permission |
|
||||
| `»» resource_type` | [codersdk.RBACResource](schemas.md#codersdkrbacresource) | false | | |
|
||||
| `» organization_permissions` | array | false | | Organization permissions are specific for the organization in the field 'OrganizationID' above. |
|
||||
| `» site_permissions` | array | false | | |
|
||||
| `» user_permissions` | array | false | | |
|
||||
|
||||
#### Enumerated Values
|
||||
|
||||
|
||||
Generated
+49
-25
@@ -1106,6 +1106,13 @@
|
||||
"display_name": "string",
|
||||
"name": "string",
|
||||
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
|
||||
"organization_member_permissions": [
|
||||
{
|
||||
"action": "application_connect",
|
||||
"negate": true,
|
||||
"resource_type": "*"
|
||||
}
|
||||
],
|
||||
"organization_permissions": [
|
||||
{
|
||||
"action": "application_connect",
|
||||
@@ -1132,16 +1139,17 @@
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|----------------------------|-----------------------------------------------------|----------|--------------|-------------------------------------------------------------------------------------------------|
|
||||
| `assignable` | boolean | false | | |
|
||||
| `built_in` | boolean | false | | Built in roles are immutable |
|
||||
| `display_name` | string | false | | |
|
||||
| `name` | string | false | | |
|
||||
| `organization_id` | string | false | | |
|
||||
| `organization_permissions` | array of [codersdk.Permission](#codersdkpermission) | false | | Organization permissions are specific for the organization in the field 'OrganizationID' above. |
|
||||
| `site_permissions` | array of [codersdk.Permission](#codersdkpermission) | false | | |
|
||||
| `user_permissions` | array of [codersdk.Permission](#codersdkpermission) | false | | |
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|-----------------------------------|-----------------------------------------------------|----------|--------------|--------------------------------------------------------------------------------------------------------|
|
||||
| `assignable` | boolean | false | | |
|
||||
| `built_in` | boolean | false | | Built in roles are immutable |
|
||||
| `display_name` | string | false | | |
|
||||
| `name` | string | false | | |
|
||||
| `organization_id` | string | false | | |
|
||||
| `organization_member_permissions` | array of [codersdk.Permission](#codersdkpermission) | false | | Organization member permissions are specific for the organization in the field 'OrganizationID' above. |
|
||||
| `organization_permissions` | array of [codersdk.Permission](#codersdkpermission) | false | | Organization permissions are specific for the organization in the field 'OrganizationID' above. |
|
||||
| `site_permissions` | array of [codersdk.Permission](#codersdkpermission) | false | | |
|
||||
| `user_permissions` | array of [codersdk.Permission](#codersdkpermission) | false | | |
|
||||
|
||||
## codersdk.AuditAction
|
||||
|
||||
@@ -2491,6 +2499,13 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o
|
||||
{
|
||||
"display_name": "string",
|
||||
"name": "string",
|
||||
"organization_member_permissions": [
|
||||
{
|
||||
"action": "application_connect",
|
||||
"negate": true,
|
||||
"resource_type": "*"
|
||||
}
|
||||
],
|
||||
"organization_permissions": [
|
||||
{
|
||||
"action": "application_connect",
|
||||
@@ -2517,13 +2532,14 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|----------------------------|-----------------------------------------------------|----------|--------------|--------------------------------------------------------------------------------|
|
||||
| `display_name` | string | false | | |
|
||||
| `name` | string | false | | |
|
||||
| `organization_permissions` | array of [codersdk.Permission](#codersdkpermission) | false | | Organization permissions are specific to the organization the role belongs to. |
|
||||
| `site_permissions` | array of [codersdk.Permission](#codersdkpermission) | false | | |
|
||||
| `user_permissions` | array of [codersdk.Permission](#codersdkpermission) | false | | |
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|-----------------------------------|-----------------------------------------------------|----------|--------------|---------------------------------------------------------------------------------------|
|
||||
| `display_name` | string | false | | |
|
||||
| `name` | string | false | | |
|
||||
| `organization_member_permissions` | array of [codersdk.Permission](#codersdkpermission) | false | | Organization member permissions are specific to the organization the role belongs to. |
|
||||
| `organization_permissions` | array of [codersdk.Permission](#codersdkpermission) | false | | Organization permissions are specific to the organization the role belongs to. |
|
||||
| `site_permissions` | array of [codersdk.Permission](#codersdkpermission) | false | | |
|
||||
| `user_permissions` | array of [codersdk.Permission](#codersdkpermission) | false | | |
|
||||
|
||||
## codersdk.DAUEntry
|
||||
|
||||
@@ -7391,6 +7407,13 @@ Only certain features set these fields: - FeatureManagedAgentLimit|
|
||||
"display_name": "string",
|
||||
"name": "string",
|
||||
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
|
||||
"organization_member_permissions": [
|
||||
{
|
||||
"action": "application_connect",
|
||||
"negate": true,
|
||||
"resource_type": "*"
|
||||
}
|
||||
],
|
||||
"organization_permissions": [
|
||||
{
|
||||
"action": "application_connect",
|
||||
@@ -7417,14 +7440,15 @@ Only certain features set these fields: - FeatureManagedAgentLimit|
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|----------------------------|-----------------------------------------------------|----------|--------------|-------------------------------------------------------------------------------------------------|
|
||||
| `display_name` | string | false | | |
|
||||
| `name` | string | false | | |
|
||||
| `organization_id` | string | false | | |
|
||||
| `organization_permissions` | array of [codersdk.Permission](#codersdkpermission) | false | | Organization permissions are specific for the organization in the field 'OrganizationID' above. |
|
||||
| `site_permissions` | array of [codersdk.Permission](#codersdkpermission) | false | | |
|
||||
| `user_permissions` | array of [codersdk.Permission](#codersdkpermission) | false | | |
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|-----------------------------------|-----------------------------------------------------|----------|--------------|--------------------------------------------------------------------------------------------------------|
|
||||
| `display_name` | string | false | | |
|
||||
| `name` | string | false | | |
|
||||
| `organization_id` | string | false | | |
|
||||
| `organization_member_permissions` | array of [codersdk.Permission](#codersdkpermission) | false | | Organization member permissions are specific for the organization in the field 'OrganizationID' above. |
|
||||
| `organization_permissions` | array of [codersdk.Permission](#codersdkpermission) | false | | Organization permissions are specific for the organization in the field 'OrganizationID' above. |
|
||||
| `site_permissions` | array of [codersdk.Permission](#codersdkpermission) | false | | |
|
||||
| `user_permissions` | array of [codersdk.Permission](#codersdkpermission) | false | | |
|
||||
|
||||
## codersdk.RoleSyncSettings
|
||||
|
||||
|
||||
@@ -311,5 +311,13 @@ func validOrganizationRoleRequest(ctx context.Context, req codersdk.CustomRoleRe
|
||||
return false
|
||||
}
|
||||
|
||||
if len(req.OrganizationMemberPermissions) > 0 {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "Invalid request, not allowed to assign organization member permissions for an organization role.",
|
||||
Detail: "organization scoped roles may not contain organization member permissions",
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -199,6 +199,7 @@ export const createCustomRole = async (
|
||||
},
|
||||
],
|
||||
user_permissions: [],
|
||||
organization_member_permissions: [],
|
||||
});
|
||||
return role;
|
||||
};
|
||||
|
||||
Generated
+10
-2
@@ -1520,11 +1520,15 @@ export interface CustomRoleRequest {
|
||||
readonly name: string;
|
||||
readonly display_name: string;
|
||||
readonly site_permissions: readonly Permission[];
|
||||
readonly user_permissions: readonly Permission[];
|
||||
/**
|
||||
* OrganizationPermissions are specific to the organization the role belongs to.
|
||||
*/
|
||||
readonly organization_permissions: readonly Permission[];
|
||||
readonly user_permissions: readonly Permission[];
|
||||
/**
|
||||
* OrganizationMemberPermissions are specific to the organization the role belongs to.
|
||||
*/
|
||||
readonly organization_member_permissions: readonly Permission[];
|
||||
}
|
||||
|
||||
// From codersdk/deployment.go
|
||||
@@ -4140,11 +4144,15 @@ export interface Role {
|
||||
readonly organization_id?: string;
|
||||
readonly display_name: string;
|
||||
readonly site_permissions: readonly Permission[];
|
||||
readonly user_permissions: readonly Permission[];
|
||||
/**
|
||||
* OrganizationPermissions are specific for the organization in the field 'OrganizationID' above.
|
||||
*/
|
||||
readonly organization_permissions: readonly Permission[];
|
||||
readonly user_permissions: readonly Permission[];
|
||||
/**
|
||||
* OrganizationMemberPermissions are specific for the organization in the field 'OrganizationID' above.
|
||||
*/
|
||||
readonly organization_member_permissions: readonly Permission[];
|
||||
}
|
||||
|
||||
// From codersdk/rbacroles.go
|
||||
|
||||
@@ -66,9 +66,11 @@ const CreateEditRolePageView: FC<CreateEditRolePageViewProps> = ({
|
||||
initialValues: {
|
||||
name: role?.name || "",
|
||||
display_name: role?.display_name || "",
|
||||
site_permissions: role?.site_permissions || [],
|
||||
organization_permissions: role?.organization_permissions || [],
|
||||
user_permissions: role?.user_permissions || [],
|
||||
site_permissions: role?.site_permissions ?? [],
|
||||
user_permissions: role?.user_permissions ?? [],
|
||||
organization_permissions: role?.organization_permissions ?? [],
|
||||
organization_member_permissions:
|
||||
role?.organization_member_permissions ?? [],
|
||||
},
|
||||
validationSchema,
|
||||
onSubmit,
|
||||
|
||||
@@ -197,8 +197,9 @@ export const MockRoles: (AssignableRoles | Role)[] = [
|
||||
action: "stop",
|
||||
},
|
||||
],
|
||||
organization_permissions: [],
|
||||
user_permissions: [],
|
||||
organization_permissions: [],
|
||||
organization_member_permissions: [],
|
||||
assignable: true,
|
||||
built_in: true,
|
||||
},
|
||||
@@ -292,8 +293,9 @@ export const MockRoles: (AssignableRoles | Role)[] = [
|
||||
action: "read",
|
||||
},
|
||||
],
|
||||
organization_permissions: [],
|
||||
user_permissions: [],
|
||||
organization_permissions: [],
|
||||
organization_member_permissions: [],
|
||||
assignable: true,
|
||||
built_in: true,
|
||||
},
|
||||
@@ -407,8 +409,9 @@ export const MockRoles: (AssignableRoles | Role)[] = [
|
||||
action: "create",
|
||||
},
|
||||
],
|
||||
organization_permissions: [],
|
||||
user_permissions: [],
|
||||
organization_permissions: [],
|
||||
organization_member_permissions: [],
|
||||
assignable: true,
|
||||
built_in: true,
|
||||
},
|
||||
@@ -462,8 +465,9 @@ export const MockRoles: (AssignableRoles | Role)[] = [
|
||||
action: "read",
|
||||
},
|
||||
],
|
||||
organization_permissions: [],
|
||||
user_permissions: [],
|
||||
organization_permissions: [],
|
||||
organization_member_permissions: [],
|
||||
built_in: true,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -281,45 +281,50 @@ export const MockOwnerRole: TypesGen.Role = {
|
||||
name: "owner",
|
||||
display_name: "Owner",
|
||||
site_permissions: [],
|
||||
organization_permissions: [],
|
||||
user_permissions: [],
|
||||
organization_id: "",
|
||||
organization_permissions: [],
|
||||
organization_member_permissions: [],
|
||||
};
|
||||
|
||||
export const MockUserAdminRole: TypesGen.Role = {
|
||||
name: "user_admin",
|
||||
display_name: "User Admin",
|
||||
site_permissions: [],
|
||||
organization_permissions: [],
|
||||
user_permissions: [],
|
||||
organization_id: "",
|
||||
organization_permissions: [],
|
||||
organization_member_permissions: [],
|
||||
};
|
||||
|
||||
export const MockTemplateAdminRole: TypesGen.Role = {
|
||||
name: "template_admin",
|
||||
display_name: "Template Admin",
|
||||
site_permissions: [],
|
||||
organization_permissions: [],
|
||||
user_permissions: [],
|
||||
organization_id: "",
|
||||
organization_permissions: [],
|
||||
organization_member_permissions: [],
|
||||
};
|
||||
|
||||
export const MockAuditorRole: TypesGen.Role = {
|
||||
name: "auditor",
|
||||
display_name: "Auditor",
|
||||
site_permissions: [],
|
||||
organization_permissions: [],
|
||||
user_permissions: [],
|
||||
organization_id: "",
|
||||
organization_permissions: [],
|
||||
organization_member_permissions: [],
|
||||
};
|
||||
|
||||
export const MockWorkspaceCreationBanRole: TypesGen.Role = {
|
||||
name: "organization-workspace-creation-ban",
|
||||
display_name: "Organization Workspace Creation Ban",
|
||||
site_permissions: [],
|
||||
organization_permissions: [],
|
||||
user_permissions: [],
|
||||
organization_id: "",
|
||||
organization_permissions: [],
|
||||
organization_member_permissions: [],
|
||||
};
|
||||
|
||||
export const MockMemberRole: TypesGen.SlimRole = {
|
||||
@@ -331,27 +336,30 @@ export const MockOrganizationAdminRole: TypesGen.Role = {
|
||||
name: "organization-admin",
|
||||
display_name: "Organization Admin",
|
||||
site_permissions: [],
|
||||
organization_permissions: [],
|
||||
user_permissions: [],
|
||||
organization_id: MockOrganization.id,
|
||||
organization_permissions: [],
|
||||
organization_member_permissions: [],
|
||||
};
|
||||
|
||||
export const MockOrganizationUserAdminRole: TypesGen.Role = {
|
||||
name: "organization-user-admin",
|
||||
display_name: "Organization User Admin",
|
||||
site_permissions: [],
|
||||
organization_permissions: [],
|
||||
user_permissions: [],
|
||||
organization_id: MockOrganization.id,
|
||||
organization_permissions: [],
|
||||
organization_member_permissions: [],
|
||||
};
|
||||
|
||||
export const MockOrganizationTemplateAdminRole: TypesGen.Role = {
|
||||
name: "organization-template-admin",
|
||||
display_name: "Organization Template Admin",
|
||||
site_permissions: [],
|
||||
organization_permissions: [],
|
||||
user_permissions: [],
|
||||
organization_id: MockOrganization.id,
|
||||
organization_permissions: [],
|
||||
organization_member_permissions: [],
|
||||
};
|
||||
|
||||
export const MockOrganizationAuditorRole: TypesGen.AssignableRoles = {
|
||||
@@ -360,18 +368,20 @@ export const MockOrganizationAuditorRole: TypesGen.AssignableRoles = {
|
||||
assignable: true,
|
||||
built_in: false,
|
||||
site_permissions: [],
|
||||
organization_permissions: [],
|
||||
user_permissions: [],
|
||||
organization_id: MockOrganization.id,
|
||||
organization_permissions: [],
|
||||
organization_member_permissions: [],
|
||||
};
|
||||
|
||||
export const MockRoleWithOrgPermissions: TypesGen.AssignableRoles = {
|
||||
name: "my-role-1",
|
||||
display_name: "My Role 1",
|
||||
organization_id: MockOrganization.id,
|
||||
assignable: true,
|
||||
built_in: false,
|
||||
site_permissions: [],
|
||||
user_permissions: [],
|
||||
organization_id: MockOrganization.id,
|
||||
organization_permissions: [
|
||||
{
|
||||
negate: false,
|
||||
@@ -454,14 +464,15 @@ export const MockRoleWithOrgPermissions: TypesGen.AssignableRoles = {
|
||||
action: "create",
|
||||
},
|
||||
],
|
||||
user_permissions: [],
|
||||
organization_member_permissions: [],
|
||||
};
|
||||
|
||||
export const MockRole2WithOrgPermissions: TypesGen.Role = {
|
||||
name: "my-role-1",
|
||||
display_name: "My Role 1",
|
||||
organization_id: MockOrganization.id,
|
||||
site_permissions: [],
|
||||
user_permissions: [],
|
||||
organization_id: MockOrganization.id,
|
||||
organization_permissions: [
|
||||
{
|
||||
negate: false,
|
||||
@@ -469,7 +480,7 @@ export const MockRole2WithOrgPermissions: TypesGen.Role = {
|
||||
action: "create",
|
||||
},
|
||||
],
|
||||
user_permissions: [],
|
||||
organization_member_permissions: [],
|
||||
};
|
||||
|
||||
// assignableRole takes a role and a boolean. The boolean implies if the
|
||||
|
||||
Reference in New Issue
Block a user