Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit3ea7737

Browse files
committed
refactor: replace role token lifetimes JSON config with CEL expressions
Change-Id: I9693c219e2e6268662d2ced209bc75744c07cf67Signed-off-by: Thomas Kosiewski <tk@coder.com>
1 parent63eaf5c commit3ea7737

18 files changed

+2578
-2615
lines changed

‎cli/server.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -813,9 +813,9 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
813813
returnxerrors.Errorf("set deployment id: %w",err)
814814
}
815815

816-
//Process the role-based token lifetimeconfigurations
817-
iferr:=coderd.ProcessRoleTokenLifetimesConfig(ctx,&vals.Sessions,options.Database,options.Logger);err!=nil {
818-
returnxerrors.Errorf("failed toinitialize roletokenlifetimes configuration: %w",err)
816+
//Compile the role token lifetimeCEL expression
817+
iferr:=coderd.CompileTokenLifetimeExpression(ctx,&vals.Sessions,options.Logger);err!=nil {
818+
returnxerrors.Errorf("failed tocompiletokenlifetime expression: %w",err)
819819
}
820820

821821
// Manage push notifications.

‎cli/testdata/coder_server_--help.golden

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ OPTIONS:
5050
Separate multiple experiments with commas, or enter '*' to opt-in to
5151
all available experiments.
5252

53+
--max-token-lifetime-expression string, $CODER_MAX_TOKEN_LIFETIME_EXPRESSION
54+
A CEL expression that determines the maximum token lifetime based on
55+
user attributes. The expression has access to 'subject'
56+
(rbac.Subject), 'requestedLifetime' (string), 'globalMaxDuration'
57+
(string), 'defaultDuration' (string), and 'tokenScope' (string). Must
58+
return a duration string (e.g., duration("168h")). Example:
59+
'subject.roles.exists(r, r.name == "owner") ?
60+
duration(globalMaxDuration) : duration(defaultDuration)'.
61+
5362
--postgres-auth password|awsiamrds, $CODER_PG_AUTH (default: password)
5463
Type of auth to use when connecting to postgres. For AWS RDS, using
5564
IAM authentication (awsiamrds) is recommended.
@@ -61,15 +70,6 @@ OPTIONS:
6170
server postgres-builtin-url". Note that any special characters in the
6271
URL must be URL-encoded.
6372

64-
--role-token-lifetimes string, $CODER_ROLE_TOKEN_LIFETIMES (default: {})
65-
A JSON mapping of role names to maximum token lifetimes (e.g.,
66-
`{"owner": "720h", "MyOrg/admin": "168h"}`). Overrides the global
67-
max_token_lifetime for specified roles. Site-level roles are direct
68-
names (e.g., 'admin'). Organization-level roles use the format
69-
'OrgName/rolename'. Durations use Go duration format: hours (h),
70-
minutes (m), seconds (s). Common conversions: 1 day = 24h, 7 days =
71-
168h, 30 days = 720h.
72-
7373
--ssh-keygen-algorithm string, $CODER_SSH_KEYGEN_ALGORITHM (default: ed25519)
7474
The algorithm to use for generating ssh keys. Accepted values are
7575
"ed25519", "ecdsa", or "rsa4096".

‎cli/testdata/server-config.yaml.golden

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -445,14 +445,14 @@ experiments: []
445445
# performed once per day.
446446
# (default: false, type: bool)
447447
updateCheck: false
448-
# AJSON mapping of role names tomaximum tokenlifetimes (e.g., `{"owner":
449-
#"720h", "MyOrg/admin": "168h"}`). Overrides the global max_token_lifetime for
450-
#specified roles. Site-level roles are direct names (e.g., 'admin').
451-
#Organization-level roles use the format 'OrgName/rolename'. Durations use Go
452-
# duration format: hours (h), minutes (m), seconds (s). Common conversions: 1 day
453-
#= 24h, 7 days = 168h, 30 days = 720h.
454-
# (default:{}, type: string)
455-
roleTokenLifetimes: '{}'
448+
# ACEL expression that determines themaximum tokenlifetime based on user
449+
#attributes. The expression has access to 'subject' (rbac.Subject),
450+
#'requestedLifetime' (string), 'globalMaxDuration' (string), 'defaultDuration'
451+
#(string), and 'tokenScope' (string). Must return a duration string (e.g.,
452+
# duration("168h")). Example: 'subject.roles.exists(r, r.name == "owner") ?
453+
#duration(globalMaxDuration) : duration(defaultDuration)'.
454+
# (default:<unset>, type: string)
455+
maxTokenLifetimeExpression: ""
456456
# The default lifetime duration for API tokens. This value is used when creating a
457457
# token without specifying a duration, such as when authenticating the CLI or an
458458
# IDE plugin.

‎coderd/apikey.go

Lines changed: 42 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ import (
99
"time"
1010

1111
"github.com/go-chi/chi/v5"
12+
"github.com/google/cel-go/common/types"
1213
"github.com/google/uuid"
1314
"github.com/moby/moby/pkg/namesgenerator"
1415
"golang.org/x/xerrors"
1516

1617
"github.com/coder/coder/v2/coderd/apikey"
1718
"github.com/coder/coder/v2/coderd/audit"
19+
celtoken"github.com/coder/coder/v2/coderd/cel"
1820
"github.com/coder/coder/v2/coderd/database"
1921
"github.com/coder/coder/v2/coderd/database/dbtime"
2022
"github.com/coder/coder/v2/coderd/httpapi"
@@ -340,29 +342,19 @@ func (api *API) deleteAPIKey(rw http.ResponseWriter, r *http.Request) {
340342
// @Router /users/{user}/keys/tokens/tokenconfig [get]
341343
func (api*API)tokenConfig(rw http.ResponseWriter,r*http.Request) {
342344
ctx:=r.Context()
343-
user:=httpmw.UserParam(r)
344345

345-
varroleIdentifiers []rbac.RoleIdentifier
346+
maxLifetime:=api.DeploymentValues.Sessions.MaximumTokenDuration.Value()
346347

348+
user:=httpmw.UserParam(r)
347349
ifuser.ID!=uuid.Nil {
348350
subject,userStatus,err:=httpmw.UserRBACSubject(ctx,api.Database,user.ID,rbac.ScopeAll)
349-
switch {
350-
caseerr!=nil:
351-
api.Logger.Error(ctx,"failed to get user RBAC subject for token config","user_id",user.ID.String(),"error",err)
352-
roleIdentifiers= []rbac.RoleIdentifier{}
353-
caseuserStatus==database.UserStatusSuspended:
354-
roleIdentifiers= []rbac.RoleIdentifier{}
355-
default:
356-
// Extract role names from the RBAC subject and convert to internal format
357-
roleIdentifiers=subject.Roles.Names()
351+
iferr==nil&&userStatus!=database.UserStatusSuspended {
352+
maxLifetime=api.getMaxTokenLifetimeForUser(ctx,subject)
358353
}
359354
}else {
360355
api.Logger.Warn(ctx,"user ID is nil in token config request context")
361-
roleIdentifiers= []rbac.RoleIdentifier{}
362356
}
363357

364-
maxLifetime:=api.getMaxTokenLifetimeForUserRoles(roleIdentifiers)
365-
366358
httpapi.Write(
367359
ctx,rw,http.StatusOK,
368360
codersdk.TokenConfig{
@@ -389,9 +381,8 @@ func (api *API) validateAPIKeyLifetime(ctx context.Context, lifetime time.Durati
389381
returnxerrors.Errorf("user %s is suspended and cannot create tokens",userID)
390382
}
391383

392-
// Extract role names from the RBAC subject and convert to internal format
393-
roleIdentifiers:=subject.Roles.Names()
394-
maxAllowedLifetime:=api.getMaxTokenLifetimeForUserRoles(roleIdentifiers)
384+
// Get the maximum token lifetime for this user based on CEL expression
385+
maxAllowedLifetime:=api.getMaxTokenLifetimeForUser(ctx,subject)
395386

396387
iflifetime>maxAllowedLifetime {
397388
returnxerrors.Errorf(
@@ -403,35 +394,46 @@ func (api *API) validateAPIKeyLifetime(ctx context.Context, lifetime time.Durati
403394
returnnil
404395
}
405396

406-
// getMaxTokenLifetimeForUserRoles determines the most generous token lifetime a user is entitled to
407-
// based on their roles and the CODER_ROLE_TOKEN_LIFETIMES configuration.
408-
// Roles are expected in the internal format ("rolename" or "rolename:org_id").
409-
func (api*API)getMaxTokenLifetimeForUserRoles(roles []rbac.RoleIdentifier) time.Duration {
410-
globalMaxDefault:=api.DeploymentValues.Sessions.MaximumTokenDuration.Value()
411-
412-
// Early return for empty config
413-
ifapi.DeploymentValues.Sessions.RoleTokenLifetimes.Value()==""||
414-
api.DeploymentValues.Sessions.RoleTokenLifetimes.Value()=="{}" {
415-
returnglobalMaxDefault
397+
// getMaxTokenLifetimeForUser determines the maximum token lifetime a user is entitled to
398+
// based on their attributes and the CEL expression configuration.
399+
func (api*API)getMaxTokenLifetimeForUser(ctx context.Context,subject rbac.Subject) time.Duration {
400+
program:=api.DeploymentValues.Sessions.CompiledMaximumTokenDurationProgram()
401+
ifprogram==nil {
402+
// No expression configured, use global max
403+
returnapi.DeploymentValues.Sessions.MaximumTokenDuration.Value()
416404
}
417405

418-
// Early return for no roles
419-
iflen(roles)==0 {
420-
returnglobalMaxDefault
421-
}
406+
globalMax:=api.DeploymentValues.Sessions.MaximumTokenDuration.Value()
407+
defaultDuration:=api.DeploymentValues.Sessions.DefaultTokenDuration.Value()
422408

423-
// Find the maximum lifetime among all roles
424-
// This includes both role-specific lifetimes and the global default
425-
maxLifetime:=globalMaxDefault
409+
// Convert subject to CEL-friendly format
410+
celSubject:=celtoken.ConvertSubjectToCEL(subject)
426411

427-
for_,role:=rangeroles {
428-
roleDuration:=api.DeploymentValues.Sessions.MaxTokenLifetimeForRole(role)
429-
ifroleDuration>maxLifetime {
430-
maxLifetime=roleDuration
431-
}
412+
// Evaluate CEL expression with typed struct
413+
// TODO: Consider adding timeout protection in future iterations
414+
out,_,err:=program.Eval(map[string]interface{}{
415+
"subject":celSubject,
416+
"globalMaxDuration":globalMax,
417+
"defaultDuration":defaultDuration,
418+
})
419+
iferr!=nil {
420+
api.Logger.Error(ctx,"cel evaluation failed, using default duration","error",err)
421+
returndefaultDuration
432422
}
433423

434-
returnmaxLifetime
424+
// Convert result to time.Duration
425+
// CEL returns types.Duration, not time.Duration directly
426+
switchv:=out.Value().(type) {
427+
case types.Duration:
428+
returnv.Duration
429+
case time.Duration:
430+
returnv
431+
default:
432+
api.Logger.Error(ctx,"cel expression did not return a duration, using default duration",
433+
"result_type",fmt.Sprintf("%T",out.Value()),
434+
"result_value",out.Value())
435+
returndefaultDuration
436+
}
435437
}
436438

437439
func (api*API)createAPIKey(ctx context.Context,params apikey.CreateParams) (*http.Cookie,*database.APIKey,error) {

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp