diff --git a/coderd/apikey.go b/coderd/apikey.go index 02db2029d15db..b1d31ff613f65 100644 --- a/coderd/apikey.go +++ b/coderd/apikey.go @@ -82,13 +82,13 @@ func (api *API) postToken(rw http.ResponseWriter, r *http.Request) { } cookie, key, err := api.createAPIKey(ctx, apikey.CreateParams{ - UserID: user.ID, - LoginType: database.LoginTypeToken, - DeploymentValues: api.DeploymentValues, - ExpiresAt: dbtime.Now().Add(lifeTime), - Scope: scope, - LifetimeSeconds: int64(lifeTime.Seconds()), - TokenName: tokenName, + UserID: user.ID, + LoginType: database.LoginTypeToken, + DefaultLifetime: api.DeploymentValues.SessionDuration.Value(), + ExpiresAt: dbtime.Now().Add(lifeTime), + Scope: scope, + LifetimeSeconds: int64(lifeTime.Seconds()), + TokenName: tokenName, }) if err != nil { if database.IsUniqueViolation(err, database.UniqueIndexAPIKeyName) { @@ -127,10 +127,10 @@ 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, - DeploymentValues: api.DeploymentValues, - LoginType: database.LoginTypePassword, - RemoteAddr: r.RemoteAddr, + UserID: user.ID, + DefaultLifetime: api.DeploymentValues.SessionDuration.Value(), + LoginType: database.LoginTypePassword, + RemoteAddr: r.RemoteAddr, // All api generated keys will last 1 week. Browser login tokens have // a shorter life. ExpiresAt: dbtime.Now().Add(lifeTime), diff --git a/coderd/apikey/apikey.go b/coderd/apikey/apikey.go index 3ae3c0d6e9bb8..ce6960dd53412 100644 --- a/coderd/apikey/apikey.go +++ b/coderd/apikey/apikey.go @@ -12,14 +12,15 @@ import ( "github.com/coder/coder/v2/coderd/database" "github.com/coder/coder/v2/coderd/database/dbtime" - "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/cryptorand" ) type CreateParams struct { - UserID uuid.UUID - LoginType database.LoginType - DeploymentValues *codersdk.DeploymentValues + UserID uuid.UUID + LoginType database.LoginType + // DefaultLifetime is configured in DeploymentValues. + // It is used if both ExpiresAt and LifetimeSeconds are not set. + DefaultLifetime time.Duration // Optional. ExpiresAt time.Time @@ -46,8 +47,8 @@ func Generate(params CreateParams) (database.InsertAPIKeyParams, string, error) if params.LifetimeSeconds != 0 { params.ExpiresAt = dbtime.Now().Add(time.Duration(params.LifetimeSeconds) * time.Second) } else { - params.ExpiresAt = dbtime.Now().Add(params.DeploymentValues.SessionDuration.Value()) - params.LifetimeSeconds = int64(params.DeploymentValues.SessionDuration.Value().Seconds()) + params.ExpiresAt = dbtime.Now().Add(params.DefaultLifetime) + params.LifetimeSeconds = int64(params.DefaultLifetime.Seconds()) } } if params.LifetimeSeconds == 0 { diff --git a/coderd/apikey/apikey_test.go b/coderd/apikey/apikey_test.go index b2d8a7768b76f..734a187219bf5 100644 --- a/coderd/apikey/apikey_test.go +++ b/coderd/apikey/apikey_test.go @@ -10,11 +10,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/coder/coder/v2/cli/clibase" "github.com/coder/coder/v2/coderd/apikey" "github.com/coder/coder/v2/coderd/database" "github.com/coder/coder/v2/coderd/database/dbtime" - "github.com/coder/coder/v2/codersdk" ) func TestGenerate(t *testing.T) { @@ -30,38 +28,36 @@ func TestGenerate(t *testing.T) { { name: "OK", params: apikey.CreateParams{ - UserID: uuid.New(), - LoginType: database.LoginTypeOIDC, - DeploymentValues: &codersdk.DeploymentValues{}, - ExpiresAt: time.Now().Add(time.Hour), - LifetimeSeconds: int64(time.Hour.Seconds()), - TokenName: "hello", - RemoteAddr: "1.2.3.4", - Scope: database.APIKeyScopeApplicationConnect, + UserID: uuid.New(), + LoginType: database.LoginTypeOIDC, + DefaultLifetime: time.Duration(0), + ExpiresAt: time.Now().Add(time.Hour), + LifetimeSeconds: int64(time.Hour.Seconds()), + TokenName: "hello", + RemoteAddr: "1.2.3.4", + Scope: database.APIKeyScopeApplicationConnect, }, }, { name: "InvalidScope", params: apikey.CreateParams{ - UserID: uuid.New(), - LoginType: database.LoginTypeOIDC, - DeploymentValues: &codersdk.DeploymentValues{}, - ExpiresAt: time.Now().Add(time.Hour), - LifetimeSeconds: int64(time.Hour.Seconds()), - TokenName: "hello", - RemoteAddr: "1.2.3.4", - Scope: database.APIKeyScope("test"), + UserID: uuid.New(), + LoginType: database.LoginTypeOIDC, + DefaultLifetime: time.Duration(0), + ExpiresAt: time.Now().Add(time.Hour), + LifetimeSeconds: int64(time.Hour.Seconds()), + TokenName: "hello", + RemoteAddr: "1.2.3.4", + Scope: database.APIKeyScope("test"), }, fail: true, }, { name: "DeploymentSessionDuration", params: apikey.CreateParams{ - UserID: uuid.New(), - LoginType: database.LoginTypeOIDC, - DeploymentValues: &codersdk.DeploymentValues{ - SessionDuration: clibase.Duration(time.Hour), - }, + UserID: uuid.New(), + LoginType: database.LoginTypeOIDC, + DefaultLifetime: time.Hour, LifetimeSeconds: 0, ExpiresAt: time.Time{}, TokenName: "hello", @@ -69,30 +65,43 @@ func TestGenerate(t *testing.T) { Scope: database.APIKeyScopeApplicationConnect, }, }, + { + name: "LifetimeSeconds", + params: apikey.CreateParams{ + UserID: uuid.New(), + LoginType: database.LoginTypeOIDC, + DefaultLifetime: time.Duration(0), + LifetimeSeconds: int64(time.Hour.Seconds()), + ExpiresAt: time.Time{}, + TokenName: "hello", + RemoteAddr: "1.2.3.4", + Scope: database.APIKeyScopeApplicationConnect, + }, + }, { name: "DefaultIP", params: apikey.CreateParams{ - UserID: uuid.New(), - LoginType: database.LoginTypeOIDC, - DeploymentValues: &codersdk.DeploymentValues{}, - ExpiresAt: time.Now().Add(time.Hour), - LifetimeSeconds: int64(time.Hour.Seconds()), - TokenName: "hello", - RemoteAddr: "", - Scope: database.APIKeyScopeApplicationConnect, + UserID: uuid.New(), + LoginType: database.LoginTypeOIDC, + DefaultLifetime: time.Duration(0), + ExpiresAt: time.Now().Add(time.Hour), + LifetimeSeconds: int64(time.Hour.Seconds()), + TokenName: "hello", + RemoteAddr: "", + Scope: database.APIKeyScopeApplicationConnect, }, }, { name: "DefaultScope", params: apikey.CreateParams{ - UserID: uuid.New(), - LoginType: database.LoginTypeOIDC, - DeploymentValues: &codersdk.DeploymentValues{}, - ExpiresAt: time.Now().Add(time.Hour), - LifetimeSeconds: int64(time.Hour.Seconds()), - TokenName: "hello", - RemoteAddr: "1.2.3.4", - Scope: "", + UserID: uuid.New(), + LoginType: database.LoginTypeOIDC, + DefaultLifetime: time.Duration(0), + ExpiresAt: time.Now().Add(time.Hour), + LifetimeSeconds: int64(time.Hour.Seconds()), + TokenName: "hello", + RemoteAddr: "1.2.3.4", + Scope: "", }, }, } @@ -131,15 +140,15 @@ func TestGenerate(t *testing.T) { // Should not be a delta greater than 5 seconds. assert.InDelta(t, time.Until(tc.params.ExpiresAt).Seconds(), key.LifetimeSeconds, 5) } else { - assert.Equal(t, int64(tc.params.DeploymentValues.SessionDuration.Value().Seconds()), key.LifetimeSeconds) + assert.Equal(t, int64(tc.params.DefaultLifetime.Seconds()), key.LifetimeSeconds) } if !tc.params.ExpiresAt.IsZero() { assert.Equal(t, tc.params.ExpiresAt.UTC(), key.ExpiresAt) } else if tc.params.LifetimeSeconds > 0 { - assert.WithinDuration(t, dbtime.Now().Add(time.Duration(tc.params.LifetimeSeconds)), key.ExpiresAt, time.Second*5) + assert.WithinDuration(t, dbtime.Now().Add(time.Duration(tc.params.LifetimeSeconds)*time.Second), key.ExpiresAt, time.Second*5) } else { - assert.WithinDuration(t, dbtime.Now().Add(tc.params.DeploymentValues.SessionDuration.Value()), key.ExpiresAt, time.Second*5) + assert.WithinDuration(t, dbtime.Now().Add(tc.params.DefaultLifetime), key.ExpiresAt, time.Second*5) } if tc.params.RemoteAddr != "" { diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index 0619e99f1cb76..9949e8dccf96e 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -1683,11 +1683,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, - DeploymentValues: s.DeploymentValues, - TokenName: workspaceSessionTokenName(workspace), - LifetimeSeconds: int64(s.DeploymentValues.MaxTokenLifetime.Value().Seconds()), + UserID: user.ID, + LoginType: user.LoginType, + DefaultLifetime: s.DeploymentValues.SessionDuration.Value(), + TokenName: workspaceSessionTokenName(workspace), + LifetimeSeconds: int64(s.DeploymentValues.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 4c160c883e6e1..e6819c3ffb721 100644 --- a/coderd/userauth.go +++ b/coderd/userauth.go @@ -247,10 +247,10 @@ func (api *API) postLogin(rw http.ResponseWriter, r *http.Request) { //nolint:gocritic // Creating the API key as the user instead of as system. cookie, key, err := api.createAPIKey(dbauthz.As(ctx, userSubj), apikey.CreateParams{ - UserID: user.ID, - LoginType: database.LoginTypePassword, - RemoteAddr: r.RemoteAddr, - DeploymentValues: api.DeploymentValues, + UserID: user.ID, + LoginType: database.LoginTypePassword, + RemoteAddr: r.RemoteAddr, + DefaultLifetime: api.DeploymentValues.SessionDuration.Value(), }) if err != nil { logger.Error(ctx, "unable to create API key", slog.Error(err)) @@ -1545,10 +1545,10 @@ func (api *API) oauthLogin(r *http.Request, params *oauthLoginParams) ([]*http.C } else { //nolint:gocritic cookie, newKey, err := api.createAPIKey(dbauthz.AsSystemRestricted(ctx), apikey.CreateParams{ - UserID: user.ID, - LoginType: params.LoginType, - DeploymentValues: api.DeploymentValues, - RemoteAddr: r.RemoteAddr, + UserID: user.ID, + LoginType: params.LoginType, + DefaultLifetime: api.DeploymentValues.SessionDuration.Value(), + RemoteAddr: r.RemoteAddr, }) if err != nil { return nil, database.APIKey{}, xerrors.Errorf("create API key: %w", err) diff --git a/coderd/workspaceapps.go b/coderd/workspaceapps.go index b519bc2a29028..d4a31e18224d3 100644 --- a/coderd/workspaceapps.go +++ b/coderd/workspaceapps.go @@ -107,12 +107,12 @@ func (api *API) workspaceApplicationAuth(rw http.ResponseWriter, r *http.Request lifetimeSeconds = int64(api.DeploymentValues.SessionDuration.Value().Seconds()) } cookie, _, err := api.createAPIKey(ctx, apikey.CreateParams{ - UserID: apiKey.UserID, - LoginType: database.LoginTypePassword, - DeploymentValues: api.DeploymentValues, - ExpiresAt: exp, - LifetimeSeconds: lifetimeSeconds, - Scope: database.APIKeyScopeApplicationConnect, + UserID: apiKey.UserID, + LoginType: database.LoginTypePassword, + DefaultLifetime: api.DeploymentValues.SessionDuration.Value(), + ExpiresAt: exp, + LifetimeSeconds: lifetimeSeconds, + Scope: database.APIKeyScopeApplicationConnect, }) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{