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

Commitc5117c4

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 commitc5117c4

25 files changed

+1173
-987
lines changed

‎.swaggo

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@ replace time.Duration int64
66
replace github.com/coder/coder/v2/codersdk.ProvisionerType string
77
// Do not render netip.Addr
88
replace netip.Addr string
9+
// Do not render cel.Program
10+
replace github.com/google/cel-go/cel.Program object

‎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+
if_,err:=vals.Sessions.CompiledMaximumTokenDurationProgram();err!=nil {
818+
returnxerrors.Errorf("failed tocompiletokenlifetime expression: %w",err)
819819
}
820820

821821
// Manage push notifications.

‎cli/testdata/coder_server_--help.golden

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,17 @@ 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)'. See
61+
https://github.com/google/cel-spec for CEL expression syntax and
62+
examples.
63+
5364
--postgres-auth password|awsiamrds, $CODER_PG_AUTH (default: password)
5465
Type of auth to use when connecting to postgres. For AWS RDS, using
5566
IAM authentication (awsiamrds) is recommended.
@@ -61,15 +72,6 @@ OPTIONS:
6172
server postgres-builtin-url". Note that any special characters in the
6273
URL must be URL-encoded.
6374

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-
7375
--ssh-keygen-algorithm string, $CODER_SSH_KEYGEN_ALGORITHM (default: ed25519)
7476
The algorithm to use for generating ssh keys. Accepted values are
7577
"ed25519", "ecdsa", or "rsa4096".

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

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -445,14 +445,15 @@ experiments: []
445445
# performed once per day.
446446
# (default: false, type: bool)
447447
updateCheck: false
448-
# A JSON mapping of role names to maximum token lifetimes (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+
# A CEL expression that determines the maximum token lifetime 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)'. See
454+
# https://github.com/google/cel-spec for CEL expression syntax and examples.
455+
# (default: <unset>, type: string)
456+
maxTokenLifetimeExpression: ""
456457
# The default lifetime duration for API tokens. This value is used when creating a
457458
# token without specifying a duration, such as when authenticating the CLI or an
458459
# IDE plugin.

‎coderd/apidoc/docs.go

Lines changed: 7 additions & 4 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/apidoc/swagger.json

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/apikey.go

Lines changed: 49 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@ 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

17+
"cdr.dev/slog"
1618
"github.com/coder/coder/v2/coderd/apikey"
1719
"github.com/coder/coder/v2/coderd/audit"
20+
celtoken"github.com/coder/coder/v2/coderd/cel"
1821
"github.com/coder/coder/v2/coderd/database"
1922
"github.com/coder/coder/v2/coderd/database/dbtime"
2023
"github.com/coder/coder/v2/coderd/httpapi"
@@ -340,28 +343,14 @@ func (api *API) deleteAPIKey(rw http.ResponseWriter, r *http.Request) {
340343
// @Router /users/{user}/keys/tokens/tokenconfig [get]
341344
func (api*API)tokenConfig(rw http.ResponseWriter,r*http.Request) {
342345
ctx:=r.Context()
343-
user:=httpmw.UserParam(r)
344346

345-
varroleIdentifiers []rbac.RoleIdentifier
346-
347-
ifuser.ID!=uuid.Nil {
348-
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()
358-
}
359-
}else {
360-
api.Logger.Warn(ctx,"user ID is nil in token config request context")
361-
roleIdentifiers= []rbac.RoleIdentifier{}
362-
}
347+
maxLifetime:=api.DeploymentValues.Sessions.MaximumTokenDuration.Value()
363348

364-
maxLifetime:=api.getMaxTokenLifetimeForUserRoles(roleIdentifiers)
349+
user:=httpmw.UserParam(r)
350+
subject,_,err:=httpmw.UserRBACSubject(ctx,api.Database,user.ID,rbac.ScopeAll)
351+
iferr==nil {
352+
maxLifetime=api.getMaxTokenLifetimeForUser(ctx,subject)
353+
}
365354

366355
httpapi.Write(
367356
ctx,rw,http.StatusOK,
@@ -378,20 +367,19 @@ func (api *API) validateAPIKeyLifetime(ctx context.Context, lifetime time.Durati
378367

379368
subject,userStatus,err:=httpmw.UserRBACSubject(ctx,api.Database,userID,rbac.ScopeAll)
380369
iferr!=nil {
381-
api.Logger.Error(ctx,"failed to get user RBAC subject during token validation","user_id",userID.String(),"error",err)
370+
api.Logger.Error(ctx,"failed to get user RBAC subject during token validation",slog.F("user_id",userID.String()),slog.Error(err))
382371
ifxerrors.Is(err,sql.ErrNoRows) {
383372
returnxerrors.Errorf("user %s not found",userID)
384373
}
385374
returnxerrors.Errorf("internal server error validating token lifetime for user %s",userID)
386375
}
387376

388-
ifuserStatus==database.UserStatusSuspended {
389-
returnxerrors.Errorf("user %s is suspended and cannot create tokens",userID)
377+
ifuserStatus!=database.UserStatusActive {
378+
returnxerrors.Errorf("user %s is suspendedor inactive,and cannot create tokens",userID)
390379
}
391380

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

396384
iflifetime>maxAllowedLifetime {
397385
returnxerrors.Errorf(
@@ -403,35 +391,47 @@ func (api *API) validateAPIKeyLifetime(ctx context.Context, lifetime time.Durati
403391
returnnil
404392
}
405393

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
394+
// getMaxTokenLifetimeForUser determines the maximum token lifetime a user is entitled to
395+
// based on their attributes and the CEL expression configuration.
396+
func (api*API)getMaxTokenLifetimeForUser(ctx context.Context,subject rbac.Subject) time.Duration {
397+
// Compiled at startup no need to recheck here.
398+
program,_:=api.DeploymentValues.Sessions.CompiledMaximumTokenDurationProgram()
399+
ifprogram==nil {
400+
// No expression configured, use global max
401+
returnapi.DeploymentValues.Sessions.MaximumTokenDuration.Value()
416402
}
417403

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

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

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

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

437437
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