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

Commitf569d9c

Browse files
authored
feat: add separate max token lifetime for administrators (#18267)
# Add separate token lifetime limits for administratorsThis PR introduces a new configuration option `--max-admin-token-lifetime` that allows administrators to create API tokens with longer lifetimes than regular users. By default, administrators can create tokens with a lifetime of up to 7 days (168 hours), while the existing `--max-token-lifetime` setting continues to apply to regular users.The implementation:- Adds a new `MaximumAdminTokenDuration` field to the session configuration- Modifies the token validation logic to check the user's role and apply the appropriate lifetime limit- Updates the token configuration endpoint to return the correct maximum lifetime based on the user's role- Adds tests to verify that administrators can create tokens with longer and shorter lifetimes- Updates documentation and help text to reflect the new optionThis change allows organizations to grant administrators extended token lifetimes while maintaining tighter security controls for regular users.Fixes#17395
1 parent348d19d commitf569d9c

File tree

12 files changed

+178
-13
lines changed

12 files changed

+178
-13
lines changed

‎cli/testdata/coder_server_--help.golden

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,10 @@ NETWORKING / HTTP OPTIONS:
332332
The maximum lifetime duration users can specify when creating an API
333333
token.
334334

335+
--max-admin-token-lifetime duration, $CODER_MAX_ADMIN_TOKEN_LIFETIME (default: 168h0m0s)
336+
The maximum lifetime duration administrators can specify when creating
337+
an API token.
338+
335339
--proxy-health-interval duration, $CODER_PROXY_HEALTH_INTERVAL (default: 1m0s)
336340
The interval in which coderd should be checking the status of
337341
workspace proxies.

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ networking:
2525
# The maximum lifetime duration users can specify when creating an API token.
2626
# (default: 876600h0m0s, type: duration)
2727
maxTokenLifetime: 876600h0m0s
28+
# The maximum lifetime duration administrators can specify when creating an API
29+
# token.
30+
# (default: 168h0m0s, type: duration)
31+
maxAdminTokenLifetime: 168h0m0s
2832
# The token expiry duration for browser sessions. Sessions may last longer if they
2933
# are actively making requests, but this functionality can be disabled via
3034
# --disable-session-expiry-refresh.

‎coderd/apidoc/docs.go

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

‎coderd/apidoc/swagger.json

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

‎coderd/apikey.go

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/coder/coder/v2/coderd/database/dbtime"
1919
"github.com/coder/coder/v2/coderd/httpapi"
2020
"github.com/coder/coder/v2/coderd/httpmw"
21+
"github.com/coder/coder/v2/coderd/rbac"
2122
"github.com/coder/coder/v2/coderd/rbac/policy"
2223
"github.com/coder/coder/v2/coderd/telemetry"
2324
"github.com/coder/coder/v2/codersdk"
@@ -75,7 +76,7 @@ func (api *API) postToken(rw http.ResponseWriter, r *http.Request) {
7576
}
7677

7778
ifcreateToken.Lifetime!=0 {
78-
err:=api.validateAPIKeyLifetime(createToken.Lifetime)
79+
err:=api.validateAPIKeyLifetime(ctx,user.ID,createToken.Lifetime)
7980
iferr!=nil {
8081
httpapi.Write(ctx,rw,http.StatusBadRequest, codersdk.Response{
8182
Message:"Failed to validate create API key request.",
@@ -338,35 +339,69 @@ func (api *API) deleteAPIKey(rw http.ResponseWriter, r *http.Request) {
338339
// @Success 200 {object} codersdk.TokenConfig
339340
// @Router /users/{user}/keys/tokens/tokenconfig [get]
340341
func (api*API)tokenConfig(rw http.ResponseWriter,r*http.Request) {
341-
values,err:=api.DeploymentValues.WithoutSecrets()
342+
user:=httpmw.UserParam(r)
343+
maxLifetime,err:=api.getMaxTokenLifetime(r.Context(),user.ID)
342344
iferr!=nil {
343-
httpapi.InternalServerError(rw,err)
345+
httpapi.Write(r.Context(),rw,http.StatusInternalServerError, codersdk.Response{
346+
Message:"Failed to get token configuration.",
347+
Detail:err.Error(),
348+
})
344349
return
345350
}
346351

347352
httpapi.Write(
348353
r.Context(),rw,http.StatusOK,
349354
codersdk.TokenConfig{
350-
MaxTokenLifetime:values.Sessions.MaximumTokenDuration.Value(),
355+
MaxTokenLifetime:maxLifetime,
351356
},
352357
)
353358
}
354359

355-
func (api*API)validateAPIKeyLifetime(lifetime time.Duration)error {
360+
func (api*API)validateAPIKeyLifetime(ctx context.Context,userID uuid.UUID,lifetime time.Duration)error {
356361
iflifetime<=0 {
357362
returnxerrors.New("lifetime must be positive number greater than 0")
358363
}
359364

360-
iflifetime>api.DeploymentValues.Sessions.MaximumTokenDuration.Value() {
365+
maxLifetime,err:=api.getMaxTokenLifetime(ctx,userID)
366+
iferr!=nil {
367+
returnxerrors.Errorf("failed to get max token lifetime: %w",err)
368+
}
369+
370+
iflifetime>maxLifetime {
361371
returnxerrors.Errorf(
362372
"lifetime must be less than %v",
363-
api.DeploymentValues.Sessions.MaximumTokenDuration,
373+
maxLifetime,
364374
)
365375
}
366376

367377
returnnil
368378
}
369379

380+
// getMaxTokenLifetime returns the maximum allowed token lifetime for a user.
381+
// It distinguishes between regular users and owners.
382+
func (api*API)getMaxTokenLifetime(ctx context.Context,userID uuid.UUID) (time.Duration,error) {
383+
subject,_,err:=httpmw.UserRBACSubject(ctx,api.Database,userID,rbac.ScopeAll)
384+
iferr!=nil {
385+
return0,xerrors.Errorf("failed to get user rbac subject: %w",err)
386+
}
387+
388+
roles,err:=subject.Roles.Expand()
389+
iferr!=nil {
390+
return0,xerrors.Errorf("failed to expand user roles: %w",err)
391+
}
392+
393+
maxLifetime:=api.DeploymentValues.Sessions.MaximumTokenDuration.Value()
394+
for_,role:=rangeroles {
395+
ifrole.Identifier.Name==codersdk.RoleOwner {
396+
// Owners have a different max lifetime.
397+
maxLifetime=api.DeploymentValues.Sessions.MaximumAdminTokenDuration.Value()
398+
break
399+
}
400+
}
401+
402+
returnmaxLifetime,nil
403+
}
404+
370405
func (api*API)createAPIKey(ctx context.Context,params apikey.CreateParams) (*http.Cookie,*database.APIKey,error) {
371406
key,sessionToken,err:=apikey.Generate(params)
372407
iferr!=nil {

‎coderd/apikey_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,88 @@ func TestTokenUserSetMaxLifetime(t *testing.T) {
144144
require.ErrorContains(t,err,"lifetime must be less")
145145
}
146146

147+
funcTestTokenAdminSetMaxLifetime(t*testing.T) {
148+
t.Parallel()
149+
150+
ctx,cancel:=context.WithTimeout(context.Background(),testutil.WaitLong)
151+
defercancel()
152+
dc:=coderdtest.DeploymentValues(t)
153+
dc.Sessions.MaximumTokenDuration=serpent.Duration(time.Hour*24*7)
154+
dc.Sessions.MaximumAdminTokenDuration=serpent.Duration(time.Hour*24*14)
155+
client:=coderdtest.New(t,&coderdtest.Options{
156+
DeploymentValues:dc,
157+
})
158+
adminUser:=coderdtest.CreateFirstUser(t,client)
159+
nonAdminClient,_:=coderdtest.CreateAnotherUser(t,client,adminUser.OrganizationID)
160+
161+
// Admin should be able to create a token with a lifetime longer than the non-admin max.
162+
_,err:=client.CreateToken(ctx,codersdk.Me, codersdk.CreateTokenRequest{
163+
Lifetime:time.Hour*24*10,
164+
})
165+
require.NoError(t,err)
166+
167+
// Admin should NOT be able to create a token with a lifetime longer than the admin max.
168+
_,err=client.CreateToken(ctx,codersdk.Me, codersdk.CreateTokenRequest{
169+
Lifetime:time.Hour*24*15,
170+
})
171+
require.Error(t,err)
172+
require.Contains(t,err.Error(),"lifetime must be less")
173+
174+
// Non-admin should NOT be able to create a token with a lifetime longer than the non-admin max.
175+
_,err=nonAdminClient.CreateToken(ctx,codersdk.Me, codersdk.CreateTokenRequest{
176+
Lifetime:time.Hour*24*8,
177+
})
178+
require.Error(t,err)
179+
require.Contains(t,err.Error(),"lifetime must be less")
180+
181+
// Non-admin should be able to create a token with a lifetime shorter than the non-admin max.
182+
_,err=nonAdminClient.CreateToken(ctx,codersdk.Me, codersdk.CreateTokenRequest{
183+
Lifetime:time.Hour*24*6,
184+
})
185+
require.NoError(t,err)
186+
}
187+
188+
funcTestTokenAdminSetMaxLifetimeShorter(t*testing.T) {
189+
t.Parallel()
190+
191+
ctx,cancel:=context.WithTimeout(context.Background(),testutil.WaitLong)
192+
defercancel()
193+
dc:=coderdtest.DeploymentValues(t)
194+
dc.Sessions.MaximumTokenDuration=serpent.Duration(time.Hour*24*14)
195+
dc.Sessions.MaximumAdminTokenDuration=serpent.Duration(time.Hour*24*7)
196+
client:=coderdtest.New(t,&coderdtest.Options{
197+
DeploymentValues:dc,
198+
})
199+
adminUser:=coderdtest.CreateFirstUser(t,client)
200+
nonAdminClient,_:=coderdtest.CreateAnotherUser(t,client,adminUser.OrganizationID)
201+
202+
// Admin should NOT be able to create a token with a lifetime longer than the admin max.
203+
_,err:=client.CreateToken(ctx,codersdk.Me, codersdk.CreateTokenRequest{
204+
Lifetime:time.Hour*24*8,
205+
})
206+
require.Error(t,err)
207+
require.Contains(t,err.Error(),"lifetime must be less")
208+
209+
// Admin should be able to create a token with a lifetime shorter than the admin max.
210+
_,err=client.CreateToken(ctx,codersdk.Me, codersdk.CreateTokenRequest{
211+
Lifetime:time.Hour*24*6,
212+
})
213+
require.NoError(t,err)
214+
215+
// Non-admin should be able to create a token with a lifetime longer than the admin max.
216+
_,err=nonAdminClient.CreateToken(ctx,codersdk.Me, codersdk.CreateTokenRequest{
217+
Lifetime:time.Hour*24*10,
218+
})
219+
require.NoError(t,err)
220+
221+
// Non-admin should NOT be able to create a token with a lifetime longer than the non-admin max.
222+
_,err=nonAdminClient.CreateToken(ctx,codersdk.Me, codersdk.CreateTokenRequest{
223+
Lifetime:time.Hour*24*15,
224+
})
225+
require.Error(t,err)
226+
require.Contains(t,err.Error(),"lifetime must be less")
227+
}
228+
147229
funcTestTokenCustomDefaultLifetime(t*testing.T) {
148230
t.Parallel()
149231

‎codersdk/deployment.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,8 @@ type SessionLifetime struct {
468468
DefaultTokenDuration serpent.Duration`json:"default_token_lifetime,omitempty" typescript:",notnull"`
469469

470470
MaximumTokenDuration serpent.Duration`json:"max_token_lifetime,omitempty" typescript:",notnull"`
471+
472+
MaximumAdminTokenDuration serpent.Duration`json:"max_admin_token_lifetime,omitempty" typescript:",notnull"`
471473
}
472474

473475
typeDERPstruct {
@@ -2340,6 +2342,17 @@ func (c *DeploymentValues) Options() serpent.OptionSet {
23402342
YAML:"maxTokenLifetime",
23412343
Annotations: serpent.Annotations{}.Mark(annotationFormatDuration,"true"),
23422344
},
2345+
{
2346+
Name:"Maximum Admin Token Lifetime",
2347+
Description:"The maximum lifetime duration administrators can specify when creating an API token.",
2348+
Flag:"max-admin-token-lifetime",
2349+
Env:"CODER_MAX_ADMIN_TOKEN_LIFETIME",
2350+
Default: (7*24*time.Hour).String(),
2351+
Value:&c.Sessions.MaximumAdminTokenDuration,
2352+
Group:&deploymentGroupNetworkingHTTP,
2353+
YAML:"maxAdminTokenLifetime",
2354+
Annotations: serpent.Annotations{}.Mark(annotationFormatDuration,"true"),
2355+
},
23432356
{
23442357
Name:"Default Token Lifetime",
23452358
Description:"The default lifetime duration for API tokens. This value is used when creating a token without specifying a duration, such as when authenticating the CLI or an IDE plugin.",

‎docs/reference/api/general.md

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎docs/reference/api/schemas.md

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

‎docs/reference/cli/server.md

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

‎enterprise/cli/testdata/coder_server_--help.golden

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,10 @@ NETWORKING / HTTP OPTIONS:
333333
The maximum lifetime duration users can specify when creating an API
334334
token.
335335

336+
--max-admin-token-lifetime duration, $CODER_MAX_ADMIN_TOKEN_LIFETIME (default: 168h0m0s)
337+
The maximum lifetime duration administrators can specify when creating
338+
an API token.
339+
336340
--proxy-health-interval duration, $CODER_PROXY_HEALTH_INTERVAL (default: 1m0s)
337341
The interval in which coderd should be checking the status of
338342
workspace proxies.

‎site/src/api/typesGenerated.ts

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp