mirror of https://github.com/coder/coder.git
chore: pass lifetime directly into api key generate (#11715)
Rather than passing all the deployment values. This is to make it easier to generate API keys as part of the oauth flow. I also added and fixed a test for when the lifetime is set and the default and expiration are unset. Co-authored-by: Steven Masley <stevenmasley@gmail.com>
This commit is contained in:
parent
a31d19d538
commit
16c6cefde8
|
@ -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),
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 != "" {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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{
|
||||
|
|
Loading…
Reference in New Issue