From f0710d916882af947c1c76774bfc4f0fbe57049b Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 29 Mar 2024 10:38:16 -0500 Subject: [PATCH 1/8] chore: merge apikey/token session config values There is a confusing difference between an apikey and a token. This difference leaks into our configs. This change does not resolve the difference. It only groups the config values to try and manage any bloat that occurs from adding more similar config values --- coderd/apikey.go | 6 ++-- coderd/identityprovider/tokens.go | 30 +++++++++---------- coderd/oauth2.go | 2 +- .../provisionerdserver/provisionerdserver.go | 9 +++--- coderd/workspaceapps/db.go | 2 +- codersdk/deployment.go | 19 ++++++++---- 6 files changed, 37 insertions(+), 31 deletions(-) diff --git a/coderd/apikey.go b/coderd/apikey.go index b1d31ff613f65..694e68e655609 100644 --- a/coderd/apikey.go +++ b/coderd/apikey.go @@ -354,7 +354,7 @@ func (api *API) tokenConfig(rw http.ResponseWriter, r *http.Request) { httpapi.Write( r.Context(), rw, http.StatusOK, codersdk.TokenConfig{ - MaxTokenLifetime: values.MaxTokenLifetime.Value(), + MaxTokenLifetime: values.Sessions.MaxTokenLifetime.Value(), }, ) } @@ -364,10 +364,10 @@ func (api *API) validateAPIKeyLifetime(lifetime time.Duration) error { return xerrors.New("lifetime must be positive number greater than 0") } - if lifetime > api.DeploymentValues.MaxTokenLifetime.Value() { + if lifetime > api.DeploymentValues.Sessions.MaxTokenLifetime.Value() { return xerrors.Errorf( "lifetime must be less than %v", - api.DeploymentValues.MaxTokenLifetime, + api.DeploymentValues.Sessions.MaxTokenLifetime, ) } diff --git a/coderd/identityprovider/tokens.go b/coderd/identityprovider/tokens.go index 0673eb7d1af7c..58fde8a12dc4e 100644 --- a/coderd/identityprovider/tokens.go +++ b/coderd/identityprovider/tokens.go @@ -75,7 +75,11 @@ func extractTokenParams(r *http.Request, callbackURL *url.URL) (tokenParams, []c return params, nil, nil } -func Tokens(db database.Store, defaultLifetime time.Duration) http.HandlerFunc { +// Tokens +// TODO: the sessions lifetime config passed is for coder api tokens. +// Should er have a separate config for oauth2 tokens? They are related, +// but they are not the same. +func Tokens(db database.Store, lifetimes codersdk.SessionLifetime) http.HandlerFunc { return func(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() app := httpmw.OAuth2ProviderApp(r) @@ -104,9 +108,9 @@ func Tokens(db database.Store, defaultLifetime time.Duration) http.HandlerFunc { switch params.grantType { // TODO: Client creds, device code. case codersdk.OAuth2ProviderGrantTypeRefreshToken: - token, err = refreshTokenGrant(ctx, db, app, defaultLifetime, params) + token, err = refreshTokenGrant(ctx, db, app, lifetimes, params) case codersdk.OAuth2ProviderGrantTypeAuthorizationCode: - token, err = authorizationCodeGrant(ctx, db, app, defaultLifetime, params) + token, err = authorizationCodeGrant(ctx, db, app, lifetimes, params) default: // Grant types are validated by the parser, so getting through here means // the developer added a type but forgot to add a case here. @@ -137,7 +141,7 @@ func Tokens(db database.Store, defaultLifetime time.Duration) http.HandlerFunc { } } -func authorizationCodeGrant(ctx context.Context, db database.Store, app database.OAuth2ProviderApp, defaultLifetime time.Duration, params tokenParams) (oauth2.Token, error) { +func authorizationCodeGrant(ctx context.Context, db database.Store, app database.OAuth2ProviderApp, lifetimes codersdk.SessionLifetime, params tokenParams) (oauth2.Token, error) { // Validate the client secret. secret, err := parseSecret(params.clientSecret) if err != nil { @@ -195,11 +199,9 @@ func authorizationCodeGrant(ctx context.Context, db database.Store, app database // TODO: We are ignoring scopes for now. tokenName := fmt.Sprintf("%s_%s_oauth_session_token", dbCode.UserID, app.ID) key, sessionToken, err := apikey.Generate(apikey.CreateParams{ - UserID: dbCode.UserID, - LoginType: database.LoginTypeOAuth2ProviderApp, - // TODO: This is just the lifetime for api keys, maybe have its own config - // settings. #11693 - DefaultLifetime: defaultLifetime, + UserID: dbCode.UserID, + LoginType: database.LoginTypeOAuth2ProviderApp, + SessionCfg: lifetimes, // For now, we allow only one token per app and user at a time. TokenName: tokenName, }) @@ -271,7 +273,7 @@ func authorizationCodeGrant(ctx context.Context, db database.Store, app database }, nil } -func refreshTokenGrant(ctx context.Context, db database.Store, app database.OAuth2ProviderApp, defaultLifetime time.Duration, params tokenParams) (oauth2.Token, error) { +func refreshTokenGrant(ctx context.Context, db database.Store, app database.OAuth2ProviderApp, lifetimes codersdk.SessionLifetime, params tokenParams) (oauth2.Token, error) { // Validate the token. token, err := parseSecret(params.refreshToken) if err != nil { @@ -326,11 +328,9 @@ func refreshTokenGrant(ctx context.Context, db database.Store, app database.OAut // TODO: We are ignoring scopes for now. tokenName := fmt.Sprintf("%s_%s_oauth_session_token", prevKey.UserID, app.ID) key, sessionToken, err := apikey.Generate(apikey.CreateParams{ - UserID: prevKey.UserID, - LoginType: database.LoginTypeOAuth2ProviderApp, - // TODO: This is just the lifetime for api keys, maybe have its own config - // settings. #11693 - DefaultLifetime: defaultLifetime, + UserID: prevKey.UserID, + LoginType: database.LoginTypeOAuth2ProviderApp, + SessionCfg: lifetimes, // For now, we allow only one token per app and user at a time. TokenName: tokenName, }) diff --git a/coderd/oauth2.go b/coderd/oauth2.go index 9e2df641bf7d3..ef68e93a1fc47 100644 --- a/coderd/oauth2.go +++ b/coderd/oauth2.go @@ -354,7 +354,7 @@ func (api *API) getOAuth2ProviderAppAuthorize() http.HandlerFunc { // @Success 200 {object} oauth2.Token // @Router /oauth2/tokens [post] func (api *API) postOAuth2ProviderAppToken() http.HandlerFunc { - return identityprovider.Tokens(api.Database, api.DeploymentValues.SessionDuration.Value()) + return identityprovider.Tokens(api.Database, api.DeploymentValues.Sessions) } // @Summary Delete OAuth2 application tokens. diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index 6183ffc02862a..1f90700a7d536 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -1723,11 +1723,10 @@ func workspaceSessionTokenName(workspace database.Workspace) string { func (s *server) regenerateSessionToken(ctx context.Context, user database.User, workspace database.Workspace) (string, error) { newkey, sessionToken, err := apikey.Generate(apikey.CreateParams{ - UserID: user.ID, - LoginType: user.LoginType, - DefaultLifetime: s.DeploymentValues.SessionDuration.Value(), - TokenName: workspaceSessionTokenName(workspace), - LifetimeSeconds: int64(s.DeploymentValues.MaxTokenLifetime.Value().Seconds()), + UserID: user.ID, + LoginType: user.LoginType, + SessionCfg: s.DeploymentValues.Sessions, + TokenName: workspaceSessionTokenName(workspace), }) if err != nil { return "", xerrors.Errorf("generate API key: %w", err) diff --git a/coderd/workspaceapps/db.go b/coderd/workspaceapps/db.go index 32eaec1cf0f57..6818ae2885be5 100644 --- a/coderd/workspaceapps/db.go +++ b/coderd/workspaceapps/db.go @@ -85,7 +85,7 @@ func (p *DBTokenProvider) Issue(ctx context.Context, rw http.ResponseWriter, r * DB: p.Database, OAuth2Configs: p.OAuth2Configs, RedirectToLogin: false, - DisableSessionExpiryRefresh: p.DeploymentValues.DisableSessionExpiryRefresh.Value(), + DisableSessionExpiryRefresh: p.DeploymentValues.Sessions.DisableSessionExpiryRefresh.Value(), // Optional is true to allow for public apps. If the authorization check // (later on) fails and the user is not authenticated, they will be // redirected to the login page or app auth endpoint using code below. diff --git a/codersdk/deployment.go b/codersdk/deployment.go index ee174075a72e4..536f3d9bd721c 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -182,13 +182,11 @@ type DeploymentValues struct { RateLimit RateLimitConfig `json:"rate_limit,omitempty" typescript:",notnull"` Experiments serpent.StringArray `json:"experiments,omitempty" typescript:",notnull"` UpdateCheck serpent.Bool `json:"update_check,omitempty" typescript:",notnull"` - MaxTokenLifetime serpent.Duration `json:"max_token_lifetime,omitempty" typescript:",notnull"` Swagger SwaggerConfig `json:"swagger,omitempty" typescript:",notnull"` Logging LoggingConfig `json:"logging,omitempty" typescript:",notnull"` Dangerous DangerousConfig `json:"dangerous,omitempty" typescript:",notnull"` DisablePathApps serpent.Bool `json:"disable_path_apps,omitempty" typescript:",notnull"` - SessionDuration serpent.Duration `json:"max_session_expiry,omitempty" typescript:",notnull"` - DisableSessionExpiryRefresh serpent.Bool `json:"disable_session_expiry_refresh,omitempty" typescript:",notnull"` + Sessions SessionLifetime `json:"session_lifetime,omitempty" typescript:",notnull"` DisablePasswordAuth serpent.Bool `json:"disable_password_auth,omitempty" typescript:",notnull"` Support SupportConfig `json:"support,omitempty" typescript:",notnull"` ExternalAuthConfigs serpent.Struct[[]ExternalAuthConfig] `json:"external_auth,omitempty" typescript:",notnull"` @@ -244,6 +242,15 @@ func ParseSSHConfigOption(opt string) (key string, value string, err error) { return opt[:idx], opt[idx+1:], nil } +// SessionLifetime should be any configuration related to creating apikeys and tokens. +type SessionLifetime struct { + // DefaultSessionDuration is for api keys, not tokens. + DefaultSessionDuration serpent.Duration `json:"max_session_expiry" typescript:",notnull"` + DisableSessionExpiryRefresh serpent.Bool `json:"disable_session_expiry_refresh,omitempty" typescript:",notnull"` + + MaxTokenLifetime serpent.Duration `json:"max_token_lifetime,omitempty" typescript:",notnull"` +} + type DERP struct { Server DERPServerConfig `json:"server" typescript:",notnull"` Config DERPConfig `json:"config" typescript:",notnull"` @@ -1579,7 +1586,7 @@ when required by your organization's security policy.`, // We have to add in the 25 leap days for the frontend to show the // "100 years" correctly. Default: ((100 * 365 * time.Hour * 24) + (25 * time.Hour * 24)).String(), - Value: &c.MaxTokenLifetime, + Value: &c.Sessions.MaxTokenLifetime, Group: &deploymentGroupNetworkingHTTP, YAML: "maxTokenLifetime", Annotations: serpent.Annotations{}.Mark(annotationFormatDuration, "true"), @@ -1773,7 +1780,7 @@ when required by your organization's security policy.`, Flag: "session-duration", Env: "CODER_SESSION_DURATION", Default: (24 * time.Hour).String(), - Value: &c.SessionDuration, + Value: &c.Sessions.DefaultSessionDuration, Group: &deploymentGroupNetworkingHTTP, YAML: "sessionDuration", Annotations: serpent.Annotations{}.Mark(annotationFormatDuration, "true"), @@ -1784,7 +1791,7 @@ when required by your organization's security policy.`, Flag: "disable-session-expiry-refresh", Env: "CODER_DISABLE_SESSION_EXPIRY_REFRESH", - Value: &c.DisableSessionExpiryRefresh, + Value: &c.Sessions.DisableSessionExpiryRefresh, Group: &deploymentGroupNetworkingHTTP, YAML: "disableSessionExpiryRefresh", }, From cdb9ed3772138feb80a3007b438e20c640896d92 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 29 Mar 2024 10:53:12 -0500 Subject: [PATCH 2/8] chore: add comment explaining all the ambiguity --- coderd/apikey.go | 4 ++-- coderd/coderd.go | 6 ++--- coderd/identityprovider/tokens.go | 13 +++++----- .../provisionerdserver/provisionerdserver.go | 9 +++---- coderd/userauth.go | 4 ++-- coderd/workspaceapps.go | 8 +++---- codersdk/deployment.go | 24 ++++++++++++++++--- 7 files changed, 43 insertions(+), 25 deletions(-) diff --git a/coderd/apikey.go b/coderd/apikey.go index 694e68e655609..43ff8ffaf7597 100644 --- a/coderd/apikey.go +++ b/coderd/apikey.go @@ -84,7 +84,7 @@ func (api *API) postToken(rw http.ResponseWriter, r *http.Request) { cookie, key, err := api.createAPIKey(ctx, apikey.CreateParams{ UserID: user.ID, LoginType: database.LoginTypeToken, - DefaultLifetime: api.DeploymentValues.SessionDuration.Value(), + DefaultLifetime: api.DeploymentValues.Sessions.DefaultSessionDuration.Value(), ExpiresAt: dbtime.Now().Add(lifeTime), Scope: scope, LifetimeSeconds: int64(lifeTime.Seconds()), @@ -128,7 +128,7 @@ func (api *API) postAPIKey(rw http.ResponseWriter, r *http.Request) { lifeTime := time.Hour * 24 * 7 cookie, _, err := api.createAPIKey(ctx, apikey.CreateParams{ UserID: user.ID, - DefaultLifetime: api.DeploymentValues.SessionDuration.Value(), + DefaultLifetime: api.DeploymentValues.Sessions.DefaultSessionDuration.Value(), LoginType: database.LoginTypePassword, RemoteAddr: r.RemoteAddr, // All api generated keys will last 1 week. Browser login tokens have diff --git a/coderd/coderd.go b/coderd/coderd.go index 0cc0962316571..7650831321d36 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -566,7 +566,7 @@ func New(options *Options) *API { DB: options.Database, OAuth2Configs: oauthConfigs, RedirectToLogin: false, - DisableSessionExpiryRefresh: options.DeploymentValues.DisableSessionExpiryRefresh.Value(), + DisableSessionExpiryRefresh: options.DeploymentValues.Sessions.DisableSessionExpiryRefresh.Value(), Optional: false, SessionTokenFunc: nil, // Default behavior PostAuthAdditionalHeadersFunc: options.PostAuthAdditionalHeadersFunc, @@ -576,7 +576,7 @@ func New(options *Options) *API { DB: options.Database, OAuth2Configs: oauthConfigs, RedirectToLogin: true, - DisableSessionExpiryRefresh: options.DeploymentValues.DisableSessionExpiryRefresh.Value(), + DisableSessionExpiryRefresh: options.DeploymentValues.Sessions.DisableSessionExpiryRefresh.Value(), Optional: false, SessionTokenFunc: nil, // Default behavior PostAuthAdditionalHeadersFunc: options.PostAuthAdditionalHeadersFunc, @@ -586,7 +586,7 @@ func New(options *Options) *API { DB: options.Database, OAuth2Configs: oauthConfigs, RedirectToLogin: false, - DisableSessionExpiryRefresh: options.DeploymentValues.DisableSessionExpiryRefresh.Value(), + DisableSessionExpiryRefresh: options.DeploymentValues.Sessions.DisableSessionExpiryRefresh.Value(), Optional: true, SessionTokenFunc: nil, // Default behavior PostAuthAdditionalHeadersFunc: options.PostAuthAdditionalHeadersFunc, diff --git a/coderd/identityprovider/tokens.go b/coderd/identityprovider/tokens.go index 58fde8a12dc4e..a4f2a2f39595c 100644 --- a/coderd/identityprovider/tokens.go +++ b/coderd/identityprovider/tokens.go @@ -7,7 +7,6 @@ import ( "fmt" "net/http" "net/url" - "time" "github.com/google/uuid" "golang.org/x/oauth2" @@ -199,9 +198,9 @@ func authorizationCodeGrant(ctx context.Context, db database.Store, app database // TODO: We are ignoring scopes for now. tokenName := fmt.Sprintf("%s_%s_oauth_session_token", dbCode.UserID, app.ID) key, sessionToken, err := apikey.Generate(apikey.CreateParams{ - UserID: dbCode.UserID, - LoginType: database.LoginTypeOAuth2ProviderApp, - SessionCfg: lifetimes, + UserID: dbCode.UserID, + LoginType: database.LoginTypeOAuth2ProviderApp, + DefaultLifetime: lifetimes.DefaultSessionDuration.Value(), // For now, we allow only one token per app and user at a time. TokenName: tokenName, }) @@ -328,9 +327,9 @@ func refreshTokenGrant(ctx context.Context, db database.Store, app database.OAut // TODO: We are ignoring scopes for now. tokenName := fmt.Sprintf("%s_%s_oauth_session_token", prevKey.UserID, app.ID) key, sessionToken, err := apikey.Generate(apikey.CreateParams{ - UserID: prevKey.UserID, - LoginType: database.LoginTypeOAuth2ProviderApp, - SessionCfg: lifetimes, + UserID: prevKey.UserID, + LoginType: database.LoginTypeOAuth2ProviderApp, + DefaultLifetime: lifetimes.DefaultSessionDuration.Value(), // For now, we allow only one token per app and user at a time. TokenName: tokenName, }) diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index 1f90700a7d536..3269711d4b29a 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -1723,10 +1723,11 @@ func workspaceSessionTokenName(workspace database.Workspace) string { func (s *server) regenerateSessionToken(ctx context.Context, user database.User, workspace database.Workspace) (string, error) { newkey, sessionToken, err := apikey.Generate(apikey.CreateParams{ - UserID: user.ID, - LoginType: user.LoginType, - SessionCfg: s.DeploymentValues.Sessions, - TokenName: workspaceSessionTokenName(workspace), + UserID: user.ID, + LoginType: user.LoginType, + TokenName: workspaceSessionTokenName(workspace), + DefaultLifetime: s.DeploymentValues.Sessions.DefaultSessionDuration.Value(), + LifetimeSeconds: int64(s.DeploymentValues.Sessions.MaxTokenLifetime.Value().Seconds()), }) if err != nil { return "", xerrors.Errorf("generate API key: %w", err) diff --git a/coderd/userauth.go b/coderd/userauth.go index 366f566c59349..98749e6a6979a 100644 --- a/coderd/userauth.go +++ b/coderd/userauth.go @@ -252,7 +252,7 @@ func (api *API) postLogin(rw http.ResponseWriter, r *http.Request) { UserID: user.ID, LoginType: database.LoginTypePassword, RemoteAddr: r.RemoteAddr, - DefaultLifetime: api.DeploymentValues.SessionDuration.Value(), + DefaultLifetime: api.DeploymentValues.Sessions.DefaultSessionDuration.Value(), }) if err != nil { logger.Error(ctx, "unable to create API key", slog.Error(err)) @@ -1612,7 +1612,7 @@ func (api *API) oauthLogin(r *http.Request, params *oauthLoginParams) ([]*http.C cookie, newKey, err := api.createAPIKey(dbauthz.AsSystemRestricted(ctx), apikey.CreateParams{ UserID: user.ID, LoginType: params.LoginType, - DefaultLifetime: api.DeploymentValues.SessionDuration.Value(), + DefaultLifetime: api.DeploymentValues.Sessions.DefaultSessionDuration.Value(), RemoteAddr: r.RemoteAddr, }) if err != nil { diff --git a/coderd/workspaceapps.go b/coderd/workspaceapps.go index d4a31e18224d3..55cafbc0ce860 100644 --- a/coderd/workspaceapps.go +++ b/coderd/workspaceapps.go @@ -102,14 +102,14 @@ func (api *API) workspaceApplicationAuth(rw http.ResponseWriter, r *http.Request // the current session. exp := apiKey.ExpiresAt lifetimeSeconds := apiKey.LifetimeSeconds - if exp.IsZero() || time.Until(exp) > api.DeploymentValues.SessionDuration.Value() { - exp = dbtime.Now().Add(api.DeploymentValues.SessionDuration.Value()) - lifetimeSeconds = int64(api.DeploymentValues.SessionDuration.Value().Seconds()) + if exp.IsZero() || time.Until(exp) > api.DeploymentValues.Sessions.DefaultSessionDuration.Value() { + exp = dbtime.Now().Add(api.DeploymentValues.Sessions.DefaultSessionDuration.Value()) + lifetimeSeconds = int64(api.DeploymentValues.Sessions.DefaultSessionDuration.Value().Seconds()) } cookie, _, err := api.createAPIKey(ctx, apikey.CreateParams{ UserID: apiKey.UserID, LoginType: database.LoginTypePassword, - DefaultLifetime: api.DeploymentValues.SessionDuration.Value(), + DefaultLifetime: api.DeploymentValues.Sessions.DefaultSessionDuration.Value(), ExpiresAt: exp, LifetimeSeconds: lifetimeSeconds, Scope: database.APIKeyScopeApplicationConnect, diff --git a/codersdk/deployment.go b/codersdk/deployment.go index 536f3d9bd721c..f4daf3969fbf3 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -242,11 +242,29 @@ func ParseSSHConfigOption(opt string) (key string, value string, err error) { return opt[:idx], opt[idx+1:], nil } -// SessionLifetime should be any configuration related to creating apikeys and tokens. +// SessionLifetime refers to "sessions" authenticating into Coderd. Coder has +// multiple different session types: api keys, tokens, workspace app tokens, +// agent tokens, etc. This configuration struct should be used to group all +// settings referring to any of these session lifetime controls. +// TODO: These config options were created back when coder only had api keys. +// Today, the config is ambigously used for all of them. For example: +// - cli based api keys ignore all settings +// - login uses the default lifetime, not the MaxTokenLifetime +// - Tokens use the Default & MaxTokenLifetime +// - ... etc ... +// The rational behind each decision is undocumented. The naming behind these +// config options is also confusing without any clear documentation. +// 'CreateAPIKey' is used to make all sessions, and it's parameters are just +// 'LifetimeSeconds' and 'DefaultLifetime'. Which does not directly correlate to +// the config options here. type SessionLifetime struct { + // DisableSessionExpiryRefresh will disable automatically refreshing api + // keys when they are used from the api. This means the api key lifetime at + // creation is the lifetime of the api key. + DisableSessionExpiryRefresh serpent.Bool `json:"disable_session_expiry_refresh,omitempty" typescript:",notnull"` + // DefaultSessionDuration is for api keys, not tokens. - DefaultSessionDuration serpent.Duration `json:"max_session_expiry" typescript:",notnull"` - DisableSessionExpiryRefresh serpent.Bool `json:"disable_session_expiry_refresh,omitempty" typescript:",notnull"` + DefaultSessionDuration serpent.Duration `json:"max_session_expiry" typescript:",notnull"` MaxTokenLifetime serpent.Duration `json:"max_token_lifetime,omitempty" typescript:",notnull"` } From 282d0cdeacd6999a2647cf0d2ebd75e2426131fa Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 29 Mar 2024 10:54:03 -0500 Subject: [PATCH 3/8] fix typo --- coderd/identityprovider/tokens.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coderd/identityprovider/tokens.go b/coderd/identityprovider/tokens.go index a4f2a2f39595c..c80be7b38a76d 100644 --- a/coderd/identityprovider/tokens.go +++ b/coderd/identityprovider/tokens.go @@ -76,7 +76,7 @@ func extractTokenParams(r *http.Request, callbackURL *url.URL) (tokenParams, []c // Tokens // TODO: the sessions lifetime config passed is for coder api tokens. -// Should er have a separate config for oauth2 tokens? They are related, +// Should there be a separate config for oauth2 tokens? They are related, // but they are not the same. func Tokens(db database.Store, lifetimes codersdk.SessionLifetime) http.HandlerFunc { return func(rw http.ResponseWriter, r *http.Request) { From 3aa8309329f54b4b687e425267608a1b386bb4fe Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 29 Mar 2024 11:33:16 -0500 Subject: [PATCH 4/8] fix references --- coderd/apikey_test.go | 8 ++++---- coderd/provisionerdserver/provisionerdserver_test.go | 9 ++++++--- enterprise/coderd/coderd.go | 4 ++-- site/src/api/typesGenerated.ts | 11 ++++++++--- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/coderd/apikey_test.go b/coderd/apikey_test.go index a20acf5ff3fbd..3672b0e38ee8b 100644 --- a/coderd/apikey_test.go +++ b/coderd/apikey_test.go @@ -125,7 +125,7 @@ func TestTokenUserSetMaxLifetime(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) defer cancel() dc := coderdtest.DeploymentValues(t) - dc.MaxTokenLifetime = serpent.Duration(time.Hour * 24 * 7) + dc.Sessions.MaxTokenLifetime = serpent.Duration(time.Hour * 24 * 7) client := coderdtest.New(t, &coderdtest.Options{ DeploymentValues: dc, }) @@ -165,7 +165,7 @@ func TestSessionExpiry(t *testing.T) { // // We don't support updating the deployment config after startup, but for // this test it works because we don't copy the value (and we use pointers). - dc.SessionDuration = serpent.Duration(time.Second) + dc.Sessions.DefaultSessionDuration = serpent.Duration(time.Second) userClient, _ := coderdtest.CreateAnotherUser(t, adminClient, adminUser.OrganizationID) @@ -174,8 +174,8 @@ func TestSessionExpiry(t *testing.T) { apiKey, err := db.GetAPIKeyByID(ctx, strings.Split(token, "-")[0]) require.NoError(t, err) - require.EqualValues(t, dc.SessionDuration.Value().Seconds(), apiKey.LifetimeSeconds) - require.WithinDuration(t, apiKey.CreatedAt.Add(dc.SessionDuration.Value()), apiKey.ExpiresAt, 2*time.Second) + require.EqualValues(t, dc.Sessions.DefaultSessionDuration.Value().Seconds(), apiKey.LifetimeSeconds) + require.WithinDuration(t, apiKey.CreatedAt.Add(dc.Sessions.DefaultSessionDuration.Value()), apiKey.ExpiresAt, 2*time.Second) // Update the session token to be expired so we can test that it is // rejected for extra points. diff --git a/coderd/provisionerdserver/provisionerdserver_test.go b/coderd/provisionerdserver/provisionerdserver_test.go index 05572d381ea00..6d8578af858db 100644 --- a/coderd/provisionerdserver/provisionerdserver_test.go +++ b/coderd/provisionerdserver/provisionerdserver_test.go @@ -166,7 +166,10 @@ func TestAcquireJob(t *testing.T) { // Set the max session token lifetime so we can assert we // create an API key with an expiration within the bounds of the // deployment config. - dv := &codersdk.DeploymentValues{MaxTokenLifetime: serpent.Duration(time.Hour)} + dv := &codersdk.DeploymentValues{ + Sessions: codersdk.SessionLifetime{ + MaxTokenLifetime: serpent.Duration(time.Hour), + }} gitAuthProvider := &sdkproto.ExternalAuthProviderResource{ Id: "github", } @@ -310,8 +313,8 @@ func TestAcquireJob(t *testing.T) { require.Len(t, toks, 2, "invalid api key") key, err := db.GetAPIKeyByID(ctx, toks[0]) require.NoError(t, err) - require.Equal(t, int64(dv.MaxTokenLifetime.Value().Seconds()), key.LifetimeSeconds) - require.WithinDuration(t, time.Now().Add(dv.MaxTokenLifetime.Value()), key.ExpiresAt, time.Minute) + require.Equal(t, int64(dv.Sessions.MaxTokenLifetime.Value().Seconds()), key.LifetimeSeconds) + require.WithinDuration(t, time.Now().Add(dv.Sessions.MaxTokenLifetime.Value()), key.ExpiresAt, time.Minute) want, err := json.Marshal(&proto.AcquiredJob_WorkspaceBuild_{ WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{ diff --git a/enterprise/coderd/coderd.go b/enterprise/coderd/coderd.go index c3b8cc019989e..7cdcc1432357c 100644 --- a/enterprise/coderd/coderd.go +++ b/enterprise/coderd/coderd.go @@ -148,7 +148,7 @@ func New(ctx context.Context, options *Options) (_ *API, err error) { DB: options.Database, OAuth2Configs: oauthConfigs, RedirectToLogin: false, - DisableSessionExpiryRefresh: options.DeploymentValues.DisableSessionExpiryRefresh.Value(), + DisableSessionExpiryRefresh: options.DeploymentValues.Sessions.DisableSessionExpiryRefresh.Value(), Optional: false, SessionTokenFunc: nil, // Default behavior PostAuthAdditionalHeadersFunc: options.PostAuthAdditionalHeadersFunc, @@ -157,7 +157,7 @@ func New(ctx context.Context, options *Options) (_ *API, err error) { DB: options.Database, OAuth2Configs: oauthConfigs, RedirectToLogin: false, - DisableSessionExpiryRefresh: options.DeploymentValues.DisableSessionExpiryRefresh.Value(), + DisableSessionExpiryRefresh: options.DeploymentValues.Sessions.DisableSessionExpiryRefresh.Value(), Optional: true, SessionTokenFunc: nil, // Default behavior PostAuthAdditionalHeadersFunc: options.PostAuthAdditionalHeadersFunc, diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index bdf744e104b39..0833f7f9588b9 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -427,13 +427,11 @@ export interface DeploymentValues { readonly rate_limit?: RateLimitConfig; readonly experiments?: string[]; readonly update_check?: boolean; - readonly max_token_lifetime?: number; readonly swagger?: SwaggerConfig; readonly logging?: LoggingConfig; readonly dangerous?: DangerousConfig; readonly disable_path_apps?: boolean; - readonly max_session_expiry?: number; - readonly disable_session_expiry_refresh?: boolean; + readonly session_lifetime?: SessionLifetime; readonly disable_password_auth?: boolean; readonly support?: SupportConfig; readonly external_auth?: ExternalAuthConfig[]; @@ -998,6 +996,13 @@ export interface SessionCountDeploymentStats { readonly reconnecting_pty: number; } +// From codersdk/deployment.go +export interface SessionLifetime { + readonly disable_session_expiry_refresh?: boolean; + readonly max_session_expiry: number; + readonly max_token_lifetime?: number; +} + // From codersdk/deployment.go export interface SupportConfig { readonly links: LinkConfig[]; From 863688facb1fa513c8812ec643e528c8fd4bbedb Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 29 Mar 2024 13:40:01 -0500 Subject: [PATCH 5/8] make gen --- coderd/apidoc/docs.go | 28 +++++++++++++++++++--------- coderd/apidoc/swagger.json | 28 +++++++++++++++++++--------- docs/api/general.md | 8 +++++--- docs/api/schemas.md | 38 +++++++++++++++++++++++++++++--------- 4 files changed, 72 insertions(+), 30 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 7bfd521b093ce..dc29f0c6055c3 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -9295,9 +9295,6 @@ const docTemplate = `{ "disable_path_apps": { "type": "boolean" }, - "disable_session_expiry_refresh": { - "type": "boolean" - }, "docs_url": { "$ref": "#/definitions/serpent.URL" }, @@ -9335,12 +9332,6 @@ const docTemplate = `{ "logging": { "$ref": "#/definitions/codersdk.LoggingConfig" }, - "max_session_expiry": { - "type": "integer" - }, - "max_token_lifetime": { - "type": "integer" - }, "metrics_cache_refresh_interval": { "type": "integer" }, @@ -9392,6 +9383,9 @@ const docTemplate = `{ "secure_auth_cookie": { "type": "boolean" }, + "session_lifetime": { + "$ref": "#/definitions/codersdk.SessionLifetime" + }, "ssh_keygen_algorithm": { "type": "string" }, @@ -11084,6 +11078,22 @@ const docTemplate = `{ } } }, + "codersdk.SessionLifetime": { + "type": "object", + "properties": { + "disable_session_expiry_refresh": { + "description": "DisableSessionExpiryRefresh will disable automatically refreshing api\nkeys when they are used from the api. This means the api key lifetime at\ncreation is the lifetime of the api key.", + "type": "boolean" + }, + "max_session_expiry": { + "description": "DefaultSessionDuration is for api keys, not tokens.", + "type": "integer" + }, + "max_token_lifetime": { + "type": "integer" + } + } + }, "codersdk.SupportConfig": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index c4dabcacaf6ba..6a3af6cd6ccb0 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -8300,9 +8300,6 @@ "disable_path_apps": { "type": "boolean" }, - "disable_session_expiry_refresh": { - "type": "boolean" - }, "docs_url": { "$ref": "#/definitions/serpent.URL" }, @@ -8340,12 +8337,6 @@ "logging": { "$ref": "#/definitions/codersdk.LoggingConfig" }, - "max_session_expiry": { - "type": "integer" - }, - "max_token_lifetime": { - "type": "integer" - }, "metrics_cache_refresh_interval": { "type": "integer" }, @@ -8397,6 +8388,9 @@ "secure_auth_cookie": { "type": "boolean" }, + "session_lifetime": { + "$ref": "#/definitions/codersdk.SessionLifetime" + }, "ssh_keygen_algorithm": { "type": "string" }, @@ -9986,6 +9980,22 @@ } } }, + "codersdk.SessionLifetime": { + "type": "object", + "properties": { + "disable_session_expiry_refresh": { + "description": "DisableSessionExpiryRefresh will disable automatically refreshing api\nkeys when they are used from the api. This means the api key lifetime at\ncreation is the lifetime of the api key.", + "type": "boolean" + }, + "max_session_expiry": { + "description": "DefaultSessionDuration is for api keys, not tokens.", + "type": "integer" + }, + "max_token_lifetime": { + "type": "integer" + } + } + }, "codersdk.SupportConfig": { "type": "object", "properties": { diff --git a/docs/api/general.md b/docs/api/general.md index 69f57b9a9975c..02d925bf767b9 100644 --- a/docs/api/general.md +++ b/docs/api/general.md @@ -200,7 +200,6 @@ curl -X GET http://coder-server:8080/api/v2/deployment/config \ "disable_owner_workspace_exec": true, "disable_password_auth": true, "disable_path_apps": true, - "disable_session_expiry_refresh": true, "docs_url": { "forceQuery": true, "fragment": "string", @@ -252,8 +251,6 @@ curl -X GET http://coder-server:8080/api/v2/deployment/config \ "log_filter": ["string"], "stackdriver": "string" }, - "max_session_expiry": 0, - "max_token_lifetime": 0, "metrics_cache_refresh_interval": 0, "oauth2": { "github": { @@ -341,6 +338,11 @@ curl -X GET http://coder-server:8080/api/v2/deployment/config \ "redirect_to_access_url": true, "scim_api_key": "string", "secure_auth_cookie": true, + "session_lifetime": { + "disable_session_expiry_refresh": true, + "max_session_expiry": 0, + "max_token_lifetime": 0 + }, "ssh_keygen_algorithm": "string", "strict_transport_security": 0, "strict_transport_security_options": ["string"], diff --git a/docs/api/schemas.md b/docs/api/schemas.md index f0b5646fea240..2ea5d28120c45 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -1925,7 +1925,6 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "disable_owner_workspace_exec": true, "disable_password_auth": true, "disable_path_apps": true, - "disable_session_expiry_refresh": true, "docs_url": { "forceQuery": true, "fragment": "string", @@ -1977,8 +1976,6 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "log_filter": ["string"], "stackdriver": "string" }, - "max_session_expiry": 0, - "max_token_lifetime": 0, "metrics_cache_refresh_interval": 0, "oauth2": { "github": { @@ -2066,6 +2063,11 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "redirect_to_access_url": true, "scim_api_key": "string", "secure_auth_cookie": true, + "session_lifetime": { + "disable_session_expiry_refresh": true, + "max_session_expiry": 0, + "max_token_lifetime": 0 + }, "ssh_keygen_algorithm": "string", "strict_transport_security": 0, "strict_transport_security_options": ["string"], @@ -2295,7 +2297,6 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "disable_owner_workspace_exec": true, "disable_password_auth": true, "disable_path_apps": true, - "disable_session_expiry_refresh": true, "docs_url": { "forceQuery": true, "fragment": "string", @@ -2347,8 +2348,6 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "log_filter": ["string"], "stackdriver": "string" }, - "max_session_expiry": 0, - "max_token_lifetime": 0, "metrics_cache_refresh_interval": 0, "oauth2": { "github": { @@ -2436,6 +2435,11 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "redirect_to_access_url": true, "scim_api_key": "string", "secure_auth_cookie": true, + "session_lifetime": { + "disable_session_expiry_refresh": true, + "max_session_expiry": 0, + "max_token_lifetime": 0 + }, "ssh_keygen_algorithm": "string", "strict_transport_security": 0, "strict_transport_security_options": ["string"], @@ -2526,7 +2530,6 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o | `disable_owner_workspace_exec` | boolean | false | | | | `disable_password_auth` | boolean | false | | | | `disable_path_apps` | boolean | false | | | -| `disable_session_expiry_refresh` | boolean | false | | | | `docs_url` | [serpent.URL](#serpenturl) | false | | | | `enable_terraform_debug_mode` | boolean | false | | | | `experiments` | array of string | false | | | @@ -2537,8 +2540,6 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o | `in_memory_database` | boolean | false | | | | `job_hang_detector_interval` | integer | false | | | | `logging` | [codersdk.LoggingConfig](#codersdkloggingconfig) | false | | | -| `max_session_expiry` | integer | false | | | -| `max_token_lifetime` | integer | false | | | | `metrics_cache_refresh_interval` | integer | false | | | | `oauth2` | [codersdk.OAuth2Config](#codersdkoauth2config) | false | | | | `oidc` | [codersdk.OIDCConfig](#codersdkoidcconfig) | false | | | @@ -2554,6 +2555,7 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o | `redirect_to_access_url` | boolean | false | | | | `scim_api_key` | string | false | | | | `secure_auth_cookie` | boolean | false | | | +| `session_lifetime` | [codersdk.SessionLifetime](#codersdksessionlifetime) | false | | | | `ssh_keygen_algorithm` | string | false | | | | `strict_transport_security` | integer | false | | | | `strict_transport_security_options` | array of string | false | | | @@ -4294,6 +4296,24 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o | `ssh` | integer | false | | | | `vscode` | integer | false | | | +## codersdk.SessionLifetime + +```json +{ + "disable_session_expiry_refresh": true, + "max_session_expiry": 0, + "max_token_lifetime": 0 +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| -------------------------------- | ------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `disable_session_expiry_refresh` | boolean | false | | Disable session expiry refresh will disable automatically refreshing api keys when they are used from the api. This means the api key lifetime at creation is the lifetime of the api key. | +| `max_session_expiry` | integer | false | | Max session expiry is for api keys, not tokens. | +| `max_token_lifetime` | integer | false | | | + ## codersdk.SupportConfig ```json From 9ecae23026e2b5ce09ad821cc684e2516cae8cdc Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 29 Mar 2024 16:20:19 -0500 Subject: [PATCH 6/8] make fmt --- coderd/provisionerdserver/provisionerdserver_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/coderd/provisionerdserver/provisionerdserver_test.go b/coderd/provisionerdserver/provisionerdserver_test.go index 6d8578af858db..8d0bd63127653 100644 --- a/coderd/provisionerdserver/provisionerdserver_test.go +++ b/coderd/provisionerdserver/provisionerdserver_test.go @@ -169,7 +169,8 @@ func TestAcquireJob(t *testing.T) { dv := &codersdk.DeploymentValues{ Sessions: codersdk.SessionLifetime{ MaxTokenLifetime: serpent.Duration(time.Hour), - }} + }, + } gitAuthProvider := &sdkproto.ExternalAuthProviderResource{ Id: "github", } From d609db3e6fc68fdba48693027ab8bc138e6fa0ee Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 2 Apr 2024 11:47:37 -0500 Subject: [PATCH 7/8] renames --- coderd/apidoc/docs.go | 2 +- coderd/apikey.go | 10 +++++----- coderd/apikey_test.go | 8 ++++---- coderd/coderd.go | 6 +++--- coderd/identityprovider/tokens.go | 4 ++-- .../provisionerdserver/provisionerdserver.go | 4 ++-- .../provisionerdserver_test.go | 6 +++--- coderd/userauth.go | 4 ++-- coderd/workspaceapps.go | 8 ++++---- coderd/workspaceapps/db.go | 2 +- codersdk/deployment.go | 20 +++++++++---------- enterprise/coderd/coderd.go | 4 ++-- 12 files changed, 39 insertions(+), 39 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index dc29f0c6055c3..b1420083f2955 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -11086,7 +11086,7 @@ const docTemplate = `{ "type": "boolean" }, "max_session_expiry": { - "description": "DefaultSessionDuration is for api keys, not tokens.", + "description": "DefaultDuration is for api keys, not tokens.", "type": "integer" }, "max_token_lifetime": { diff --git a/coderd/apikey.go b/coderd/apikey.go index 43ff8ffaf7597..10a83a05f4a24 100644 --- a/coderd/apikey.go +++ b/coderd/apikey.go @@ -84,7 +84,7 @@ func (api *API) postToken(rw http.ResponseWriter, r *http.Request) { cookie, key, err := api.createAPIKey(ctx, apikey.CreateParams{ UserID: user.ID, LoginType: database.LoginTypeToken, - DefaultLifetime: api.DeploymentValues.Sessions.DefaultSessionDuration.Value(), + DefaultLifetime: api.DeploymentValues.Sessions.DefaultDuration.Value(), ExpiresAt: dbtime.Now().Add(lifeTime), Scope: scope, LifetimeSeconds: int64(lifeTime.Seconds()), @@ -128,7 +128,7 @@ func (api *API) postAPIKey(rw http.ResponseWriter, r *http.Request) { lifeTime := time.Hour * 24 * 7 cookie, _, err := api.createAPIKey(ctx, apikey.CreateParams{ UserID: user.ID, - DefaultLifetime: api.DeploymentValues.Sessions.DefaultSessionDuration.Value(), + DefaultLifetime: api.DeploymentValues.Sessions.DefaultDuration.Value(), LoginType: database.LoginTypePassword, RemoteAddr: r.RemoteAddr, // All api generated keys will last 1 week. Browser login tokens have @@ -354,7 +354,7 @@ func (api *API) tokenConfig(rw http.ResponseWriter, r *http.Request) { httpapi.Write( r.Context(), rw, http.StatusOK, codersdk.TokenConfig{ - MaxTokenLifetime: values.Sessions.MaxTokenLifetime.Value(), + MaxTokenLifetime: values.Sessions.MaximumTokenDuration.Value(), }, ) } @@ -364,10 +364,10 @@ func (api *API) validateAPIKeyLifetime(lifetime time.Duration) error { return xerrors.New("lifetime must be positive number greater than 0") } - if lifetime > api.DeploymentValues.Sessions.MaxTokenLifetime.Value() { + if lifetime > api.DeploymentValues.Sessions.MaximumTokenDuration.Value() { return xerrors.Errorf( "lifetime must be less than %v", - api.DeploymentValues.Sessions.MaxTokenLifetime, + api.DeploymentValues.Sessions.MaximumTokenDuration, ) } diff --git a/coderd/apikey_test.go b/coderd/apikey_test.go index 3672b0e38ee8b..29d0f01126b7a 100644 --- a/coderd/apikey_test.go +++ b/coderd/apikey_test.go @@ -125,7 +125,7 @@ func TestTokenUserSetMaxLifetime(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) defer cancel() dc := coderdtest.DeploymentValues(t) - dc.Sessions.MaxTokenLifetime = serpent.Duration(time.Hour * 24 * 7) + dc.Sessions.MaximumTokenDuration = serpent.Duration(time.Hour * 24 * 7) client := coderdtest.New(t, &coderdtest.Options{ DeploymentValues: dc, }) @@ -165,7 +165,7 @@ func TestSessionExpiry(t *testing.T) { // // We don't support updating the deployment config after startup, but for // this test it works because we don't copy the value (and we use pointers). - dc.Sessions.DefaultSessionDuration = serpent.Duration(time.Second) + dc.Sessions.DefaultDuration = serpent.Duration(time.Second) userClient, _ := coderdtest.CreateAnotherUser(t, adminClient, adminUser.OrganizationID) @@ -174,8 +174,8 @@ func TestSessionExpiry(t *testing.T) { apiKey, err := db.GetAPIKeyByID(ctx, strings.Split(token, "-")[0]) require.NoError(t, err) - require.EqualValues(t, dc.Sessions.DefaultSessionDuration.Value().Seconds(), apiKey.LifetimeSeconds) - require.WithinDuration(t, apiKey.CreatedAt.Add(dc.Sessions.DefaultSessionDuration.Value()), apiKey.ExpiresAt, 2*time.Second) + require.EqualValues(t, dc.Sessions.DefaultDuration.Value().Seconds(), apiKey.LifetimeSeconds) + require.WithinDuration(t, apiKey.CreatedAt.Add(dc.Sessions.DefaultDuration.Value()), apiKey.ExpiresAt, 2*time.Second) // Update the session token to be expired so we can test that it is // rejected for extra points. diff --git a/coderd/coderd.go b/coderd/coderd.go index 7650831321d36..67b16e9032bfe 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -566,7 +566,7 @@ func New(options *Options) *API { DB: options.Database, OAuth2Configs: oauthConfigs, RedirectToLogin: false, - DisableSessionExpiryRefresh: options.DeploymentValues.Sessions.DisableSessionExpiryRefresh.Value(), + DisableSessionExpiryRefresh: options.DeploymentValues.Sessions.DisableExpiryRefresh.Value(), Optional: false, SessionTokenFunc: nil, // Default behavior PostAuthAdditionalHeadersFunc: options.PostAuthAdditionalHeadersFunc, @@ -576,7 +576,7 @@ func New(options *Options) *API { DB: options.Database, OAuth2Configs: oauthConfigs, RedirectToLogin: true, - DisableSessionExpiryRefresh: options.DeploymentValues.Sessions.DisableSessionExpiryRefresh.Value(), + DisableSessionExpiryRefresh: options.DeploymentValues.Sessions.DisableExpiryRefresh.Value(), Optional: false, SessionTokenFunc: nil, // Default behavior PostAuthAdditionalHeadersFunc: options.PostAuthAdditionalHeadersFunc, @@ -586,7 +586,7 @@ func New(options *Options) *API { DB: options.Database, OAuth2Configs: oauthConfigs, RedirectToLogin: false, - DisableSessionExpiryRefresh: options.DeploymentValues.Sessions.DisableSessionExpiryRefresh.Value(), + DisableSessionExpiryRefresh: options.DeploymentValues.Sessions.DisableExpiryRefresh.Value(), Optional: true, SessionTokenFunc: nil, // Default behavior PostAuthAdditionalHeadersFunc: options.PostAuthAdditionalHeadersFunc, diff --git a/coderd/identityprovider/tokens.go b/coderd/identityprovider/tokens.go index c80be7b38a76d..e9c9e743e7225 100644 --- a/coderd/identityprovider/tokens.go +++ b/coderd/identityprovider/tokens.go @@ -200,7 +200,7 @@ func authorizationCodeGrant(ctx context.Context, db database.Store, app database key, sessionToken, err := apikey.Generate(apikey.CreateParams{ UserID: dbCode.UserID, LoginType: database.LoginTypeOAuth2ProviderApp, - DefaultLifetime: lifetimes.DefaultSessionDuration.Value(), + DefaultLifetime: lifetimes.DefaultDuration.Value(), // For now, we allow only one token per app and user at a time. TokenName: tokenName, }) @@ -329,7 +329,7 @@ func refreshTokenGrant(ctx context.Context, db database.Store, app database.OAut key, sessionToken, err := apikey.Generate(apikey.CreateParams{ UserID: prevKey.UserID, LoginType: database.LoginTypeOAuth2ProviderApp, - DefaultLifetime: lifetimes.DefaultSessionDuration.Value(), + DefaultLifetime: lifetimes.DefaultDuration.Value(), // For now, we allow only one token per app and user at a time. TokenName: tokenName, }) diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index 3269711d4b29a..965e7c6f78ed3 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -1726,8 +1726,8 @@ func (s *server) regenerateSessionToken(ctx context.Context, user database.User, UserID: user.ID, LoginType: user.LoginType, TokenName: workspaceSessionTokenName(workspace), - DefaultLifetime: s.DeploymentValues.Sessions.DefaultSessionDuration.Value(), - LifetimeSeconds: int64(s.DeploymentValues.Sessions.MaxTokenLifetime.Value().Seconds()), + DefaultLifetime: s.DeploymentValues.Sessions.DefaultDuration.Value(), + LifetimeSeconds: int64(s.DeploymentValues.Sessions.MaximumTokenDuration.Value().Seconds()), }) if err != nil { return "", xerrors.Errorf("generate API key: %w", err) diff --git a/coderd/provisionerdserver/provisionerdserver_test.go b/coderd/provisionerdserver/provisionerdserver_test.go index 8d0bd63127653..3f5f344dea5ce 100644 --- a/coderd/provisionerdserver/provisionerdserver_test.go +++ b/coderd/provisionerdserver/provisionerdserver_test.go @@ -168,7 +168,7 @@ func TestAcquireJob(t *testing.T) { // deployment config. dv := &codersdk.DeploymentValues{ Sessions: codersdk.SessionLifetime{ - MaxTokenLifetime: serpent.Duration(time.Hour), + MaximumTokenDuration: serpent.Duration(time.Hour), }, } gitAuthProvider := &sdkproto.ExternalAuthProviderResource{ @@ -314,8 +314,8 @@ func TestAcquireJob(t *testing.T) { require.Len(t, toks, 2, "invalid api key") key, err := db.GetAPIKeyByID(ctx, toks[0]) require.NoError(t, err) - require.Equal(t, int64(dv.Sessions.MaxTokenLifetime.Value().Seconds()), key.LifetimeSeconds) - require.WithinDuration(t, time.Now().Add(dv.Sessions.MaxTokenLifetime.Value()), key.ExpiresAt, time.Minute) + require.Equal(t, int64(dv.Sessions.MaximumTokenDuration.Value().Seconds()), key.LifetimeSeconds) + require.WithinDuration(t, time.Now().Add(dv.Sessions.MaximumTokenDuration.Value()), key.ExpiresAt, time.Minute) want, err := json.Marshal(&proto.AcquiredJob_WorkspaceBuild_{ WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{ diff --git a/coderd/userauth.go b/coderd/userauth.go index 98749e6a6979a..eda4dd60abfa2 100644 --- a/coderd/userauth.go +++ b/coderd/userauth.go @@ -252,7 +252,7 @@ func (api *API) postLogin(rw http.ResponseWriter, r *http.Request) { UserID: user.ID, LoginType: database.LoginTypePassword, RemoteAddr: r.RemoteAddr, - DefaultLifetime: api.DeploymentValues.Sessions.DefaultSessionDuration.Value(), + DefaultLifetime: api.DeploymentValues.Sessions.DefaultDuration.Value(), }) if err != nil { logger.Error(ctx, "unable to create API key", slog.Error(err)) @@ -1612,7 +1612,7 @@ func (api *API) oauthLogin(r *http.Request, params *oauthLoginParams) ([]*http.C cookie, newKey, err := api.createAPIKey(dbauthz.AsSystemRestricted(ctx), apikey.CreateParams{ UserID: user.ID, LoginType: params.LoginType, - DefaultLifetime: api.DeploymentValues.Sessions.DefaultSessionDuration.Value(), + DefaultLifetime: api.DeploymentValues.Sessions.DefaultDuration.Value(), RemoteAddr: r.RemoteAddr, }) if err != nil { diff --git a/coderd/workspaceapps.go b/coderd/workspaceapps.go index 55cafbc0ce860..8c6ffdb62e34a 100644 --- a/coderd/workspaceapps.go +++ b/coderd/workspaceapps.go @@ -102,14 +102,14 @@ func (api *API) workspaceApplicationAuth(rw http.ResponseWriter, r *http.Request // the current session. exp := apiKey.ExpiresAt lifetimeSeconds := apiKey.LifetimeSeconds - if exp.IsZero() || time.Until(exp) > api.DeploymentValues.Sessions.DefaultSessionDuration.Value() { - exp = dbtime.Now().Add(api.DeploymentValues.Sessions.DefaultSessionDuration.Value()) - lifetimeSeconds = int64(api.DeploymentValues.Sessions.DefaultSessionDuration.Value().Seconds()) + if exp.IsZero() || time.Until(exp) > api.DeploymentValues.Sessions.DefaultDuration.Value() { + exp = dbtime.Now().Add(api.DeploymentValues.Sessions.DefaultDuration.Value()) + lifetimeSeconds = int64(api.DeploymentValues.Sessions.DefaultDuration.Value().Seconds()) } cookie, _, err := api.createAPIKey(ctx, apikey.CreateParams{ UserID: apiKey.UserID, LoginType: database.LoginTypePassword, - DefaultLifetime: api.DeploymentValues.Sessions.DefaultSessionDuration.Value(), + DefaultLifetime: api.DeploymentValues.Sessions.DefaultDuration.Value(), ExpiresAt: exp, LifetimeSeconds: lifetimeSeconds, Scope: database.APIKeyScopeApplicationConnect, diff --git a/coderd/workspaceapps/db.go b/coderd/workspaceapps/db.go index 6818ae2885be5..619bdd95ba165 100644 --- a/coderd/workspaceapps/db.go +++ b/coderd/workspaceapps/db.go @@ -85,7 +85,7 @@ func (p *DBTokenProvider) Issue(ctx context.Context, rw http.ResponseWriter, r * DB: p.Database, OAuth2Configs: p.OAuth2Configs, RedirectToLogin: false, - DisableSessionExpiryRefresh: p.DeploymentValues.Sessions.DisableSessionExpiryRefresh.Value(), + DisableSessionExpiryRefresh: p.DeploymentValues.Sessions.DisableExpiryRefresh.Value(), // Optional is true to allow for public apps. If the authorization check // (later on) fails and the user is not authenticated, they will be // redirected to the login page or app auth endpoint using code below. diff --git a/codersdk/deployment.go b/codersdk/deployment.go index f4daf3969fbf3..34eaa4edd4c40 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -249,8 +249,8 @@ func ParseSSHConfigOption(opt string) (key string, value string, err error) { // TODO: These config options were created back when coder only had api keys. // Today, the config is ambigously used for all of them. For example: // - cli based api keys ignore all settings -// - login uses the default lifetime, not the MaxTokenLifetime -// - Tokens use the Default & MaxTokenLifetime +// - login uses the default lifetime, not the MaximumTokenDuration +// - Tokens use the Default & MaximumTokenDuration // - ... etc ... // The rational behind each decision is undocumented. The naming behind these // config options is also confusing without any clear documentation. @@ -258,15 +258,15 @@ func ParseSSHConfigOption(opt string) (key string, value string, err error) { // 'LifetimeSeconds' and 'DefaultLifetime'. Which does not directly correlate to // the config options here. type SessionLifetime struct { - // DisableSessionExpiryRefresh will disable automatically refreshing api + // DisableExpiryRefresh will disable automatically refreshing api // keys when they are used from the api. This means the api key lifetime at // creation is the lifetime of the api key. - DisableSessionExpiryRefresh serpent.Bool `json:"disable_session_expiry_refresh,omitempty" typescript:",notnull"` + DisableExpiryRefresh serpent.Bool `json:"disable_expiry_refresh,omitempty" typescript:",notnull"` - // DefaultSessionDuration is for api keys, not tokens. - DefaultSessionDuration serpent.Duration `json:"max_session_expiry" typescript:",notnull"` + // DefaultDuration is for api keys, not tokens. + DefaultDuration serpent.Duration `json:"default_duration" typescript:",notnull"` - MaxTokenLifetime serpent.Duration `json:"max_token_lifetime,omitempty" typescript:",notnull"` + MaximumTokenDuration serpent.Duration `json:"max_token_lifetime,omitempty" typescript:",notnull"` } type DERP struct { @@ -1604,7 +1604,7 @@ when required by your organization's security policy.`, // We have to add in the 25 leap days for the frontend to show the // "100 years" correctly. Default: ((100 * 365 * time.Hour * 24) + (25 * time.Hour * 24)).String(), - Value: &c.Sessions.MaxTokenLifetime, + Value: &c.Sessions.MaximumTokenDuration, Group: &deploymentGroupNetworkingHTTP, YAML: "maxTokenLifetime", Annotations: serpent.Annotations{}.Mark(annotationFormatDuration, "true"), @@ -1798,7 +1798,7 @@ when required by your organization's security policy.`, Flag: "session-duration", Env: "CODER_SESSION_DURATION", Default: (24 * time.Hour).String(), - Value: &c.Sessions.DefaultSessionDuration, + Value: &c.Sessions.DefaultDuration, Group: &deploymentGroupNetworkingHTTP, YAML: "sessionDuration", Annotations: serpent.Annotations{}.Mark(annotationFormatDuration, "true"), @@ -1809,7 +1809,7 @@ when required by your organization's security policy.`, Flag: "disable-session-expiry-refresh", Env: "CODER_DISABLE_SESSION_EXPIRY_REFRESH", - Value: &c.Sessions.DisableSessionExpiryRefresh, + Value: &c.Sessions.DisableExpiryRefresh, Group: &deploymentGroupNetworkingHTTP, YAML: "disableSessionExpiryRefresh", }, diff --git a/enterprise/coderd/coderd.go b/enterprise/coderd/coderd.go index 7cdcc1432357c..0ac2086f8699e 100644 --- a/enterprise/coderd/coderd.go +++ b/enterprise/coderd/coderd.go @@ -148,7 +148,7 @@ func New(ctx context.Context, options *Options) (_ *API, err error) { DB: options.Database, OAuth2Configs: oauthConfigs, RedirectToLogin: false, - DisableSessionExpiryRefresh: options.DeploymentValues.Sessions.DisableSessionExpiryRefresh.Value(), + DisableSessionExpiryRefresh: options.DeploymentValues.Sessions.DisableExpiryRefresh.Value(), Optional: false, SessionTokenFunc: nil, // Default behavior PostAuthAdditionalHeadersFunc: options.PostAuthAdditionalHeadersFunc, @@ -157,7 +157,7 @@ func New(ctx context.Context, options *Options) (_ *API, err error) { DB: options.Database, OAuth2Configs: oauthConfigs, RedirectToLogin: false, - DisableSessionExpiryRefresh: options.DeploymentValues.Sessions.DisableSessionExpiryRefresh.Value(), + DisableSessionExpiryRefresh: options.DeploymentValues.Sessions.DisableExpiryRefresh.Value(), Optional: true, SessionTokenFunc: nil, // Default behavior PostAuthAdditionalHeadersFunc: options.PostAuthAdditionalHeadersFunc, From d3b5575aa58188848e3c83ffe600822d378c097c Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 2 Apr 2024 11:49:40 -0500 Subject: [PATCH 8/8] make gen --- coderd/apidoc/docs.go | 10 +++++----- coderd/apidoc/swagger.json | 12 ++++++------ docs/api/general.md | 4 ++-- docs/api/schemas.md | 22 +++++++++++----------- site/src/api/typesGenerated.ts | 4 ++-- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index b1420083f2955..0881eac5b7c31 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -11081,14 +11081,14 @@ const docTemplate = `{ "codersdk.SessionLifetime": { "type": "object", "properties": { - "disable_session_expiry_refresh": { - "description": "DisableSessionExpiryRefresh will disable automatically refreshing api\nkeys when they are used from the api. This means the api key lifetime at\ncreation is the lifetime of the api key.", - "type": "boolean" - }, - "max_session_expiry": { + "default_duration": { "description": "DefaultDuration is for api keys, not tokens.", "type": "integer" }, + "disable_expiry_refresh": { + "description": "DisableExpiryRefresh will disable automatically refreshing api\nkeys when they are used from the api. This means the api key lifetime at\ncreation is the lifetime of the api key.", + "type": "boolean" + }, "max_token_lifetime": { "type": "integer" } diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 6a3af6cd6ccb0..88c025eaec71b 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -9983,14 +9983,14 @@ "codersdk.SessionLifetime": { "type": "object", "properties": { - "disable_session_expiry_refresh": { - "description": "DisableSessionExpiryRefresh will disable automatically refreshing api\nkeys when they are used from the api. This means the api key lifetime at\ncreation is the lifetime of the api key.", - "type": "boolean" - }, - "max_session_expiry": { - "description": "DefaultSessionDuration is for api keys, not tokens.", + "default_duration": { + "description": "DefaultDuration is for api keys, not tokens.", "type": "integer" }, + "disable_expiry_refresh": { + "description": "DisableExpiryRefresh will disable automatically refreshing api\nkeys when they are used from the api. This means the api key lifetime at\ncreation is the lifetime of the api key.", + "type": "boolean" + }, "max_token_lifetime": { "type": "integer" } diff --git a/docs/api/general.md b/docs/api/general.md index 02d925bf767b9..330c41a335b9b 100644 --- a/docs/api/general.md +++ b/docs/api/general.md @@ -339,8 +339,8 @@ curl -X GET http://coder-server:8080/api/v2/deployment/config \ "scim_api_key": "string", "secure_auth_cookie": true, "session_lifetime": { - "disable_session_expiry_refresh": true, - "max_session_expiry": 0, + "default_duration": 0, + "disable_expiry_refresh": true, "max_token_lifetime": 0 }, "ssh_keygen_algorithm": "string", diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 2ea5d28120c45..efc3a38f01219 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -2064,8 +2064,8 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "scim_api_key": "string", "secure_auth_cookie": true, "session_lifetime": { - "disable_session_expiry_refresh": true, - "max_session_expiry": 0, + "default_duration": 0, + "disable_expiry_refresh": true, "max_token_lifetime": 0 }, "ssh_keygen_algorithm": "string", @@ -2436,8 +2436,8 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "scim_api_key": "string", "secure_auth_cookie": true, "session_lifetime": { - "disable_session_expiry_refresh": true, - "max_session_expiry": 0, + "default_duration": 0, + "disable_expiry_refresh": true, "max_token_lifetime": 0 }, "ssh_keygen_algorithm": "string", @@ -4300,19 +4300,19 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o ```json { - "disable_session_expiry_refresh": true, - "max_session_expiry": 0, + "default_duration": 0, + "disable_expiry_refresh": true, "max_token_lifetime": 0 } ``` ### Properties -| Name | Type | Required | Restrictions | Description | -| -------------------------------- | ------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `disable_session_expiry_refresh` | boolean | false | | Disable session expiry refresh will disable automatically refreshing api keys when they are used from the api. This means the api key lifetime at creation is the lifetime of the api key. | -| `max_session_expiry` | integer | false | | Max session expiry is for api keys, not tokens. | -| `max_token_lifetime` | integer | false | | | +| Name | Type | Required | Restrictions | Description | +| ------------------------ | ------- | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `default_duration` | integer | false | | Default duration is for api keys, not tokens. | +| `disable_expiry_refresh` | boolean | false | | Disable expiry refresh will disable automatically refreshing api keys when they are used from the api. This means the api key lifetime at creation is the lifetime of the api key. | +| `max_token_lifetime` | integer | false | | | ## codersdk.SupportConfig diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 0833f7f9588b9..be751559f21d9 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -998,8 +998,8 @@ export interface SessionCountDeploymentStats { // From codersdk/deployment.go export interface SessionLifetime { - readonly disable_session_expiry_refresh?: boolean; - readonly max_session_expiry: number; + readonly disable_expiry_refresh?: boolean; + readonly default_duration: number; readonly max_token_lifetime?: number; }