Skip to content

feat(organization): add suspension support#3007

Merged
Piskoo merged 4 commits intochainloop-dev:mainfrom
Piskoo:pfm-5391
Apr 9, 2026
Merged

feat(organization): add suspension support#3007
Piskoo merged 4 commits intochainloop-dev:mainfrom
Piskoo:pfm-5391

Conversation

@Piskoo
Copy link
Copy Markdown
Collaborator

@Piskoo Piskoo commented Apr 8, 2026

Summary

Added a suspended field to the Organization schema and a suspension middleware that blocks access when an org is suspended.

Examples

Non account/org related requests fail

$ chainloop att init --project myproject --workflow suspend
WRN API contacted in insecure mode
WRN User-attended mode detected. This is intended for local testing only. For CI/CD or automated workflows, please use an API token.
This command will run against the organization "myorg"
Please confirm to continue y/N
y
ERR organization is suspended, read-only access only
exit status 1

$ chainloop wf contract create --name suspended
WRN API contacted in insecure mode
ERR organization is suspended, read-only access only
exit status 1

$ chainloop org api-token create --name suspended
WRN API contacted in insecure mode
ERR organization is suspended, read-only access only
exit status 1

$ chainloop wf contract describe --name vulnerabilities -o schema
WRN API contacted in insecure mode
ERR organization is suspended, read-only access only

Org/account management requests pass

$ chainloop org member ls
WRN API contacted in insecure mode
┌──────────────────────────────────────┬───────────────────────┬────────┬─────────────────────┐
│ ID                                   │ EMAIL                 │ ROLE   │ JOINED AT           │
├──────────────────────────────────────┼───────────────────────┼────────┼─────────────────────┤
│ e0bc184d-4455-425f-9179-3cb276745329 │ john@chainloop.local  │ member │ 12 Dec 25 10:58 UTC │
├──────────────────────────────────────┼───────────────────────┼────────┼─────────────────────┤
│ c7740e0e-1a0c-467b-8bde-036b4d753bf7 │ sarah@chainloop.local │ owner  │ 16 Jun 25 09:09 UTC │
└──────────────────────────────────────┴───────────────────────┴────────┴─────────────────────┘
INF Showing [1-2] out of 2


$ chainloop org set --name myorg
WRN API contacted in insecure mode
INF Organization switched!
┌───────┬─────────┬─────────┬───────┬─────────────────────────┬─────────────────────┐
│ NAME  │ CURRENT │ DEFAULT │ ROLE  │ DEFAULT POLICY STRATEGY │ JOINED AT           │
├───────┼─────────┼─────────┼───────┼─────────────────────────┼─────────────────────┤
│ myorg │ true    │ true    │ owner │ ADVISORY                │ 16 Jun 25 09:09 UTC │
└───────┴─────────┴─────────┴───────┴─────────────────────────┴─────────────────────┘

Closes #3005

@Piskoo Piskoo marked this pull request as ready for review April 8, 2026 13:43
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 26 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="app/controlplane/pkg/authz/authz.go">

<violation number="1" location="app/controlplane/pkg/authz/authz.go:500">
P2: `regexp.MatchString` recompiles the pattern on every call. Since this runs in a request-hot loop over all `ServerOperationsMap` keys, precompile the regex patterns once at package init. Currently every OrgMetrics API call (and any unmapped operation) triggers ~55 regex compilations.

Consider extracting regex-pattern keys into a separate `map[*regexp.Regexp][]*Policy` built at init time, or precompile them with `regexp.MustCompile` in a package-level variable, similar to how `suspensionExemptRegexp` is handled in the suspension middleware.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

// This covers two cases:
// - Empty-policy reads: operations mapped with {} in ServerOperationsMap that are actually reads
// - Self-service writes: operations the user needs to leave/delete even when suspended
var suspensionExemptRegexp = regexp.MustCompile(`controlplane.v1.CASCredentialsService/Get|controlplane.v1.UserService/(ListMemberships|SetCurrentMembership|DeleteMembership)|controlplane.v1.GroupService/(List|Get|ListMembers|ListProjects|ListPendingInvitations)|controlplane.v1.AuthService/DeleteAccount|controlplane.v1.OrganizationService/Delete$`)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fragile and difficult to maintain if we add more endpoints. What if instead we extend the endpoint info in authz.go to add whether an endpoint is mutating or not? This would require adding all endpoints there though (default to mutating=true for maximum security if it's not found)
Another option is trying to get the metadata from the proto definitions, but I don't think Kratos exposes that.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevermind, I've just seen that logic below. Thanks

@migmartri
Copy link
Copy Markdown
Member

I have mixed feelings about this feature; I’m not sure why we need to support read-only.

@Piskoo
Copy link
Copy Markdown
Collaborator Author

Piskoo commented Apr 9, 2026

I have mixed feelings about this feature; I’m not sure why we need to support read-only.

It follows what for example Stripe or Github does with blocked/expired accounts. We could also completely block the access and show only the information that organization is suspended with steps to resolve that.

Updated to block all not account/org management requests as disussed

Piskoo added 3 commits April 9, 2026 11:05
Signed-off-by: Sylwester Piskozub <sylwesterpiskozub@gmail.com>
Signed-off-by: Sylwester Piskozub <sylwesterpiskozub@gmail.com>
Signed-off-by: Sylwester Piskozub <sylwesterpiskozub@gmail.com>
Signed-off-by: Sylwester Piskozub <sylwesterpiskozub@gmail.com>
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 3 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="app/controlplane/internal/server/grpc.go">

<violation number="1" location="app/controlplane/internal/server/grpc.go:214">
P3: This comment overstates behavior: suspension is not applied to all RPCs because selector matchers intentionally exempt some organization/account operations. Please make the comment explicit about the exception.

(Based on your team's feedback about documenting non-obvious authorization behavior.) [FEEDBACK_USED]</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

@Piskoo Piskoo merged commit 6981c6e into chainloop-dev:main Apr 9, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add organization suspension support

3 participants