feat: add session expiry control flags (#5976)

Adds --session-duration which lets admins customize the default session
expiration for browser sessions.

Adds --disable-session-expiry-refresh which allows admins to prevent
session expiry from being automatically bumped upon the API key being
used.
This commit is contained in:
Dean Sheather
2023-02-04 04:38:36 +11:00
committed by GitHub
parent 2285a5e8a0
commit cf9abe3a6c
16 changed files with 225 additions and 37 deletions

View File

@ -88,9 +88,10 @@ const (
)
type ExtractAPIKeyConfig struct {
DB database.Store
OAuth2Configs *OAuth2Configs
RedirectToLogin bool
DB database.Store
OAuth2Configs *OAuth2Configs
RedirectToLogin bool
DisableSessionExpiryRefresh bool
// Optional governs whether the API key is optional. Use this if you want to
// allow unauthenticated requests.
@ -266,10 +267,12 @@ func ExtractAPIKey(cfg ExtractAPIKeyConfig) func(http.Handler) http.Handler {
}
// Only update the ExpiresAt once an hour to prevent database spam.
// We extend the ExpiresAt to reduce re-authentication.
apiKeyLifetime := time.Duration(key.LifetimeSeconds) * time.Second
if key.ExpiresAt.Sub(now) <= apiKeyLifetime-time.Hour {
key.ExpiresAt = now.Add(apiKeyLifetime)
changed = true
if !cfg.DisableSessionExpiryRefresh {
apiKeyLifetime := time.Duration(key.LifetimeSeconds) * time.Second
if key.ExpiresAt.Sub(now) <= apiKeyLifetime-time.Hour {
key.ExpiresAt = now.Add(apiKeyLifetime)
changed = true
}
}
if changed {
err := cfg.DB.UpdateAPIKeyByID(r.Context(), database.UpdateAPIKeyByIDParams{

View File

@ -363,6 +363,38 @@ func TestAPIKey(t *testing.T) {
require.NotEqual(t, sentAPIKey.ExpiresAt, gotAPIKey.ExpiresAt)
})
t.Run("NoRefresh", func(t *testing.T) {
t.Parallel()
var (
db = dbfake.New()
user = dbgen.User(t, db, database.User{})
sentAPIKey, token = dbgen.APIKey(t, db, database.APIKey{
UserID: user.ID,
LastUsed: database.Now().AddDate(0, 0, -1),
ExpiresAt: database.Now().AddDate(0, 0, 1),
})
r = httptest.NewRequest("GET", "/", nil)
rw = httptest.NewRecorder()
)
r.Header.Set(codersdk.SessionTokenHeader, token)
httpmw.ExtractAPIKey(httpmw.ExtractAPIKeyConfig{
DB: db,
RedirectToLogin: false,
DisableSessionExpiryRefresh: true,
})(successHandler).ServeHTTP(rw, r)
res := rw.Result()
defer res.Body.Close()
require.Equal(t, http.StatusOK, res.StatusCode)
gotAPIKey, err := db.GetAPIKeyByID(r.Context(), sentAPIKey.ID)
require.NoError(t, err)
require.NotEqual(t, sentAPIKey.LastUsed, gotAPIKey.LastUsed)
require.Equal(t, sentAPIKey.ExpiresAt, gotAPIKey.ExpiresAt)
})
t.Run("OAuthNotExpired", func(t *testing.T) {
t.Parallel()
var (