feat: add allow_list to resource-scoped API tokens (#19964)

# Add API key allow_list for resource-scoped tokens

This PR adds support for API key allow lists, enabling tokens to be scoped to specific resources. The implementation:

1. Adds a new `allow_list` field to the `CreateTokenRequest` struct, allowing clients to specify resource-specific scopes when creating API tokens
2. Implements `APIAllowListTarget` type to represent resource targets in the format `<type>:<id>` with support for wildcards
3. Adds validation and normalization logic for allow lists to handle wildcards and deduplication
4. Integrates with RBAC by creating an `APIKeyEffectiveScope` that merges API key scopes with allow list restrictions
5. Updates API documentation and TypeScript types to reflect the new functionality

This feature enables creating tokens that are limited to specific resources (like workspaces or templates) by ID, making it possible to create more granular API tokens with limited access.
This commit is contained in:
Thomas Kosiewski
2025-10-09 14:53:08 +02:00
committed by GitHub
parent f31e6e09ba
commit ed90ecf00e
25 changed files with 930 additions and 94 deletions
+1 -1
View File
@@ -434,7 +434,7 @@ func ExtractAPIKey(rw http.ResponseWriter, r *http.Request, cfg ExtractAPIKeyCon
// If the key is valid, we also fetch the user roles and status.
// The roles are used for RBAC authorize checks, and the status
// is to block 'suspended' users from accessing the platform.
actor, userStatus, err := UserRBACSubject(ctx, cfg.DB, key.UserID, key.Scopes)
actor, userStatus, err := UserRBACSubject(ctx, cfg.DB, key.UserID, key.ScopeSet())
if err != nil {
return write(http.StatusUnauthorized, codersdk.Response{
Message: internalErrorMessage,
+4 -1
View File
@@ -20,6 +20,7 @@ import (
"github.com/coder/coder/v2/coderd/database/dbtime"
"github.com/coder/coder/v2/coderd/httpmw"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/coderd/rbac/policy"
"github.com/coder/coder/v2/codersdk"
)
@@ -173,7 +174,9 @@ func addUser(t *testing.T, db database.Store, roles ...string) (database.User, s
ExpiresAt: dbtime.Now().Add(time.Minute),
LoginType: database.LoginTypePassword,
Scopes: database.APIKeyScopes{database.ApiKeyScopeCoderAll},
AllowList: database.AllowList{database.AllowListWildcard()},
AllowList: database.AllowList{
{Type: policy.WildcardSymbol, ID: policy.WildcardSymbol},
},
IPAddress: pqtype.Inet{
IPNet: net.IPNet{
IP: net.ParseIP("0.0.0.0"),
+4 -1
View File
@@ -22,6 +22,7 @@ import (
"github.com/coder/coder/v2/coderd/database/dbtestutil"
"github.com/coder/coder/v2/coderd/database/dbtime"
"github.com/coder/coder/v2/coderd/httpmw"
"github.com/coder/coder/v2/coderd/rbac/policy"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/cryptorand"
)
@@ -67,7 +68,9 @@ func TestWorkspaceParam(t *testing.T) {
ExpiresAt: dbtime.Now().Add(time.Minute),
LoginType: database.LoginTypePassword,
Scopes: database.APIKeyScopes{database.ApiKeyScopeCoderAll},
AllowList: database.AllowList{database.AllowListWildcard()},
AllowList: database.AllowList{
{Type: policy.WildcardSymbol, ID: policy.WildcardSymbol},
},
IPAddress: pqtype.Inet{
IPNet: net.IPNet{
IP: net.IPv4(127, 0, 0, 1),