`POST /api/v2/authcheck` evaluated every check with a full policy
evaluation in a serial loop. A subject in many organizations (100+)
produced hundreds of full evaluations, taking seconds on a cold cache
(DEVEX-608).
Group the checks by `(action, resource type)` and authorize each group
with the existing `rbac.Filter`, which amortizes a single partial
evaluation across the group once it is large enough. Each check is
wrapped in a small value struct that carries its response key, so
`Filter`'s returned subset maps back to keys by reading a field rather
than relying on element identity.
`Filter` now takes an explicit `prepareThreshold`; existing callers pass
the new `rbac.DefaultFilterThreshold` (10), and `checkAuthorization`
passes 50, above the ~35-group crossover measured for this workload, so
subjects with few objects of a given type keep the per-object path and
cannot regress.
## Stacking
This is stacked on top of #27244. `Filter` runs `Prepare` (partial
evaluation), and those residuals are only compact once #27244's
set-membership residuals land. On plain `main` the existing O(N)
residual fanout means batching can regress at high org counts, so this
change should land with or after #27244.
<details>
<summary>Decision log</summary>
### Bottleneck
- `site/src/modules/permissions/organizations.ts` defines ~14 permission
checks per org; `organizationsPermissions()` flattens them across all
orgs into one `POST /api/v2/authcheck`. A 100-org request is ~1400
checks.
- `checkAuthorization` looped serially, calling `Authorizer.Authorize`
(full eval) once per check.
- The endpoint's `maxFetch = 10` only caps checks that carry a
`resource_id`, not total checks, so it does not bound this workload.
### Approach
- Group checks by `(action, resource type)` and run each group through
`rbac.Filter`, which does one partial evaluation (`Prepare`) and reuses
it across the group.
- Carry the response key as data in a small value struct implementing
`RBACObject()`, so allowed results map back to keys without pointer
identity:
```go
type authorizeCheck struct {
key string
object rbac.Object
}
func (c authorizeCheck) RBACObject() rbac.Object { return c.object }
```
- `Filter` takes a required `prepareThreshold int` (no functional
options). Generic callers pass `rbac.DefaultFilterThreshold = 10`;
`/authcheck` passes 50 because the measured crossover for this workload
is ~35 groups.
### Alternatives rejected
- **Bounded `errgroup` parallelism**: reduced wall time at high org
counts but not aggregate work (allocations flat). Discarded in favor of
reducing work via partial evaluation.
- **Symmetric-deny Rego simplification** (on the #27244 branch):
replacing the known-org deny-fold with symmetric `org := -1` /
`scope_org := -1` rules failed existing SQL-compile tests. A `-1`
known-org vote gated by `not org = -1` produces a negated membership
test over the unknown org id, which OPA emits as an unconvertible
support rule. #27244's fold (`member_allow - org_deny`, a positive
set-difference membership test) is therefore load-bearing, not
incidental.
</details>
---
Authored with Coder Agents.
---------
Co-authored-by: Steven Masley <Emyrk@users.noreply.github.com>
Organization member's table is already scoped to an organization.
Rolename should avoid having the org_id appended.
Wipes all existing organization role assignments, which should not be used anyway.
* chore: add /v2 to import module path
go mod requires semantic versioning with versions greater than 1.x
This was a mechanical update by running:
```
go install github.com/marwan-at-work/mod/cmd/mod@latest
mod upgrade
```
Migrate generated files to import /v2
* Fix gen
* feat: drop reading other 'user' permission
Members of the platform can no longer read or list other users.
Resources that have "created_by" or "initiated_by" still retain
user context, but only include username and avatar url.
Attempting to read a user found via those means will result in
a 404.
* Hide /users page for regular users
* make groups a privledged endpoint
* Permissions page for template perms
* Admin for a given template enables an endpoint for listing users/groups.
* chore: rename `AgentConn` to `WorkspaceAgentConn`
The codersdk was becoming bloated with consts for the workspace
agent that made no sense to a reader. `Tailnet*` is an example
of these consts.
* chore: remove `Get` prefix from *Client functions
* chore: remove `BypassRatelimits` option in `codersdk.Client`
It feels wrong to have this as a direct option because it's so infrequently
needed by API callers. It's better to directly modify headers in the two
places that we actually use it.
* Merge `appearance.go` and `buildinfo.go` into `deployment.go`
* Merge `experiments.go` and `features.go` into `deployment.go`
* Fix `make gen` referencing old type names
* Merge `error.go` into `client.go`
`codersdk.Response` lived in `error.go`, which is wrong.
* chore: refactor workspace agent functions into agentsdk
It was odd conflating the codersdk that clients should use
with functions that only the agent should use. This separates
them into two SDKs that are closely coupled, but separate.
* Merge `insights.go` into `deployment.go`
* Merge `organizationmember.go` into `organizations.go`
* Merge `quota.go` into `workspaces.go`
* Rename `sse.go` to `serversentevents.go`
* Rename `codersdk.WorkspaceAppHostResponse` to `codersdk.AppHostResponse`
* Format `.vscode/settings.json`
* Fix outdated naming in `api.ts`
* Fix app host response
* Fix unsupported type
* Fix imported type