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

Commit9f15ef9

Browse files
committed
feat(api): add max admin token lifetime configuration and validation
Change-Id: I4540ce3eeb46ab58909ac37e60c3ece93668212aSigned-off-by: Thomas Kosiewski <tk@coder.com>
1 parentd47a53d commit9f15ef9

File tree

12 files changed

+166
-13
lines changed

12 files changed

+166
-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: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,76 @@ 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+
182+
funcTestTokenAdminSetMaxLifetimeShorter(t*testing.T) {
183+
t.Parallel()
184+
185+
ctx,cancel:=context.WithTimeout(context.Background(),testutil.WaitLong)
186+
defercancel()
187+
dc:=coderdtest.DeploymentValues(t)
188+
dc.Sessions.MaximumTokenDuration=serpent.Duration(time.Hour*24*14)
189+
dc.Sessions.MaximumAdminTokenDuration=serpent.Duration(time.Hour*24*7)
190+
client:=coderdtest.New(t,&coderdtest.Options{
191+
DeploymentValues:dc,
192+
})
193+
adminUser:=coderdtest.CreateFirstUser(t,client)
194+
nonAdminClient,_:=coderdtest.CreateAnotherUser(t,client,adminUser.OrganizationID)
195+
196+
// Admin should NOT be able to create a token with a lifetime longer than the admin max.
197+
_,err:=client.CreateToken(ctx,codersdk.Me, codersdk.CreateTokenRequest{
198+
Lifetime:time.Hour*24*8,
199+
})
200+
require.Error(t,err)
201+
require.Contains(t,err.Error(),"lifetime must be less")
202+
203+
// Non-admin should be able to create a token with a lifetime longer than the admin max.
204+
_,err=nonAdminClient.CreateToken(ctx,codersdk.Me, codersdk.CreateTokenRequest{
205+
Lifetime:time.Hour*24*10,
206+
})
207+
require.NoError(t,err)
208+
209+
// Non-admin should NOT be able to create a token with a lifetime longer than the non-admin max.
210+
_,err=nonAdminClient.CreateToken(ctx,codersdk.Me, codersdk.CreateTokenRequest{
211+
Lifetime:time.Hour*24*15,
212+
})
213+
require.Error(t,err)
214+
require.Contains(t,err.Error(),"lifetime must be less")
215+
}
216+
147217
funcTestTokenCustomDefaultLifetime(t*testing.T) {
148218
t.Parallel()
149219

‎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