chore: enable exhaustruct linter for database param structs (#9995)

This commit is contained in:
Cian Johnston 2023-10-03 09:23:45 +01:00 committed by GitHub
parent 352ec7bc4f
commit e55c25e037
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 186 additions and 116 deletions

View File

@ -10,6 +10,9 @@ linters-settings:
include: include:
# Gradually extend to cover more of the codebase. # Gradually extend to cover more of the codebase.
- 'httpmw\.\w+' - 'httpmw\.\w+'
# We want to enforce all values are specified when inserting or updating
# a database row. Ref: #9936
- 'github.com/coder/coder/v2/coderd/database\.[^G][^e][^t]\w+Params'
gocognit: gocognit:
min-complexity: 300 min-complexity: 300

View File

@ -76,6 +76,7 @@ func Generate(params CreateParams) (database.InsertAPIKeyParams, string, error)
return database.InsertAPIKeyParams{ return database.InsertAPIKeyParams{
ID: keyID, ID: keyID,
UserID: params.UserID, UserID: params.UserID,
LastUsed: time.Time{},
LifetimeSeconds: params.LifetimeSeconds, LifetimeSeconds: params.LifetimeSeconds,
IPAddress: pqtype.Inet{ IPAddress: pqtype.Inet{
IPNet: net.IPNet{ IPNet: net.IPNet{

View File

@ -154,6 +154,9 @@ func (api *API) generateFakeAuditLog(rw http.ResponseWriter, r *http.Request) {
Diff: diff, Diff: diff,
StatusCode: http.StatusOK, StatusCode: http.StatusOK,
AdditionalFields: params.AdditionalFields, AdditionalFields: params.AdditionalFields,
RequestID: uuid.Nil, // no request ID to attach this to
ResourceIcon: "",
OrganizationID: uuid.New(),
}) })
if err != nil { if err != nil {
httpapi.InternalServerError(rw, err) httpapi.InternalServerError(rw, err)

View File

@ -1,6 +1,7 @@
package oidctest package oidctest
import ( import (
"database/sql"
"net/http" "net/http"
"testing" "testing"
"time" "time"
@ -69,11 +70,13 @@ func (*LoginHelper) ExpireOauthToken(t *testing.T, db database.Store, user *code
// Expire the oauth link for the given user. // Expire the oauth link for the given user.
updated, err := db.UpdateUserLink(ctx, database.UpdateUserLinkParams{ updated, err := db.UpdateUserLink(ctx, database.UpdateUserLinkParams{
OAuthAccessToken: link.OAuthAccessToken, OAuthAccessToken: link.OAuthAccessToken,
OAuthRefreshToken: link.OAuthRefreshToken, OAuthAccessTokenKeyID: sql.NullString{}, // dbcrypt will update as required
OAuthExpiry: time.Now().Add(time.Hour * -1), OAuthRefreshToken: link.OAuthRefreshToken,
UserID: link.UserID, OAuthRefreshTokenKeyID: sql.NullString{}, // dbcrypt will update as required
LoginType: link.LoginType, OAuthExpiry: time.Now().Add(time.Hour * -1),
UserID: link.UserID,
LoginType: link.LoginType,
}) })
require.NoError(t, err, "expire user link") require.NoError(t, err, "expire user link")

View File

@ -4142,6 +4142,8 @@ func (q *FakeQuerier) InsertAllUsersGroup(ctx context.Context, orgID uuid.UUID)
Name: database.EveryoneGroup, Name: database.EveryoneGroup,
DisplayName: "", DisplayName: "",
OrganizationID: orgID, OrganizationID: orgID,
AvatarURL: "",
QuotaAllowance: 0,
}) })
} }

View File

@ -125,7 +125,7 @@ func APIKey(t testing.TB, db database.Store, seed database.APIKey) (key database
} }
func WorkspaceAgent(t testing.TB, db database.Store, orig database.WorkspaceAgent) database.WorkspaceAgent { func WorkspaceAgent(t testing.TB, db database.Store, orig database.WorkspaceAgent) database.WorkspaceAgent {
workspace, err := db.InsertWorkspaceAgent(genCtx, database.InsertWorkspaceAgentParams{ agt, err := db.InsertWorkspaceAgent(genCtx, database.InsertWorkspaceAgentParams{
ID: takeFirst(orig.ID, uuid.New()), ID: takeFirst(orig.ID, uuid.New()),
CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()),
UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()), UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()),
@ -154,9 +154,10 @@ func WorkspaceAgent(t testing.TB, db database.Store, orig database.WorkspaceAgen
ConnectionTimeoutSeconds: takeFirst(orig.ConnectionTimeoutSeconds, 3600), ConnectionTimeoutSeconds: takeFirst(orig.ConnectionTimeoutSeconds, 3600),
TroubleshootingURL: takeFirst(orig.TroubleshootingURL, "https://example.com"), TroubleshootingURL: takeFirst(orig.TroubleshootingURL, "https://example.com"),
MOTDFile: takeFirst(orig.TroubleshootingURL, ""), MOTDFile: takeFirst(orig.TroubleshootingURL, ""),
DisplayApps: append([]database.DisplayApp{}, orig.DisplayApps...),
}) })
require.NoError(t, err, "insert workspace agent") require.NoError(t, err, "insert workspace agent")
return workspace return agt
} }
func Workspace(t testing.TB, db database.Store, orig database.Workspace) database.Workspace { func Workspace(t testing.TB, db database.Store, orig database.Workspace) database.Workspace {
@ -204,6 +205,7 @@ func WorkspaceBuild(t testing.TB, db database.Store, orig database.WorkspaceBuil
JobID: takeFirst(orig.JobID, uuid.New()), JobID: takeFirst(orig.JobID, uuid.New()),
ProvisionerState: takeFirstSlice(orig.ProvisionerState, []byte{}), ProvisionerState: takeFirstSlice(orig.ProvisionerState, []byte{}),
Deadline: takeFirst(orig.Deadline, dbtime.Now().Add(time.Hour)), Deadline: takeFirst(orig.Deadline, dbtime.Now().Add(time.Hour)),
MaxDeadline: takeFirst(orig.MaxDeadline, time.Time{}),
Reason: takeFirst(orig.Reason, database.BuildReasonInitiator), Reason: takeFirst(orig.Reason, database.BuildReasonInitiator),
}) })
if err != nil { if err != nil {
@ -348,6 +350,7 @@ func ProvisionerJob(t testing.TB, db database.Store, ps pubsub.Pubsub, orig data
Type: takeFirst(orig.Type, database.ProvisionerJobTypeWorkspaceBuild), Type: takeFirst(orig.Type, database.ProvisionerJobTypeWorkspaceBuild),
Input: takeFirstSlice(orig.Input, []byte("{}")), Input: takeFirstSlice(orig.Input, []byte("{}")),
Tags: orig.Tags, Tags: orig.Tags,
TraceMetadata: pqtype.NullRawMessage{},
}) })
require.NoError(t, err, "insert job") require.NoError(t, err, "insert job")
if ps != nil { if ps != nil {
@ -359,6 +362,7 @@ func ProvisionerJob(t testing.TB, db database.Store, ps pubsub.Pubsub, orig data
StartedAt: orig.StartedAt, StartedAt: orig.StartedAt,
Types: []database.ProvisionerType{database.ProvisionerTypeEcho}, Types: []database.ProvisionerType{database.ProvisionerTypeEcho},
Tags: must(json.Marshal(orig.Tags)), Tags: must(json.Marshal(orig.Tags)),
WorkerID: uuid.NullUUID{},
}) })
require.NoError(t, err) require.NoError(t, err)
} }
@ -460,6 +464,8 @@ func WorkspaceProxy(t testing.TB, db database.Store, orig database.WorkspaceProx
TokenHashedSecret: hashedSecret[:], TokenHashedSecret: hashedSecret[:],
CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()),
UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()), UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()),
DerpEnabled: takeFirst(orig.DerpEnabled, false),
DerpOnly: takeFirst(orig.DerpEnabled, false),
}) })
require.NoError(t, err, "insert proxy") require.NoError(t, err, "insert proxy")

View File

@ -123,13 +123,15 @@ func (api *API) postExternalAuthDeviceByID(rw http.ResponseWriter, r *http.Reque
} }
_, err = api.Database.InsertExternalAuthLink(ctx, database.InsertExternalAuthLinkParams{ _, err = api.Database.InsertExternalAuthLink(ctx, database.InsertExternalAuthLinkParams{
ProviderID: config.ID, ProviderID: config.ID,
UserID: apiKey.UserID, UserID: apiKey.UserID,
CreatedAt: dbtime.Now(), CreatedAt: dbtime.Now(),
UpdatedAt: dbtime.Now(), UpdatedAt: dbtime.Now(),
OAuthAccessToken: token.AccessToken, OAuthAccessToken: token.AccessToken,
OAuthRefreshToken: token.RefreshToken, OAuthAccessTokenKeyID: sql.NullString{}, // dbcrypt will set as required
OAuthExpiry: token.Expiry, OAuthRefreshToken: token.RefreshToken,
OAuthRefreshTokenKeyID: sql.NullString{}, // dbcrypt will set as required
OAuthExpiry: token.Expiry,
}) })
if err != nil { if err != nil {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
@ -140,12 +142,14 @@ func (api *API) postExternalAuthDeviceByID(rw http.ResponseWriter, r *http.Reque
} }
} else { } else {
_, err = api.Database.UpdateExternalAuthLink(ctx, database.UpdateExternalAuthLinkParams{ _, err = api.Database.UpdateExternalAuthLink(ctx, database.UpdateExternalAuthLinkParams{
ProviderID: config.ID, ProviderID: config.ID,
UserID: apiKey.UserID, UserID: apiKey.UserID,
UpdatedAt: dbtime.Now(), UpdatedAt: dbtime.Now(),
OAuthAccessToken: token.AccessToken, OAuthAccessToken: token.AccessToken,
OAuthRefreshToken: token.RefreshToken, OAuthAccessTokenKeyID: sql.NullString{}, // dbcrypt will update as required
OAuthExpiry: token.Expiry, OAuthRefreshToken: token.RefreshToken,
OAuthRefreshTokenKeyID: sql.NullString{}, // dbcrypt will update as required
OAuthExpiry: token.Expiry,
}) })
if err != nil { if err != nil {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
@ -211,13 +215,15 @@ func (api *API) externalAuthCallback(externalAuthConfig *externalauth.Config) ht
} }
_, err = api.Database.InsertExternalAuthLink(ctx, database.InsertExternalAuthLinkParams{ _, err = api.Database.InsertExternalAuthLink(ctx, database.InsertExternalAuthLinkParams{
ProviderID: externalAuthConfig.ID, ProviderID: externalAuthConfig.ID,
UserID: apiKey.UserID, UserID: apiKey.UserID,
CreatedAt: dbtime.Now(), CreatedAt: dbtime.Now(),
UpdatedAt: dbtime.Now(), UpdatedAt: dbtime.Now(),
OAuthAccessToken: state.Token.AccessToken, OAuthAccessToken: state.Token.AccessToken,
OAuthRefreshToken: state.Token.RefreshToken, OAuthAccessTokenKeyID: sql.NullString{}, // dbcrypt will set as required
OAuthExpiry: state.Token.Expiry, OAuthRefreshToken: state.Token.RefreshToken,
OAuthRefreshTokenKeyID: sql.NullString{}, // dbcrypt will set as required
OAuthExpiry: state.Token.Expiry,
}) })
if err != nil { if err != nil {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
@ -228,12 +234,14 @@ func (api *API) externalAuthCallback(externalAuthConfig *externalauth.Config) ht
} }
} else { } else {
_, err = api.Database.UpdateExternalAuthLink(ctx, database.UpdateExternalAuthLinkParams{ _, err = api.Database.UpdateExternalAuthLink(ctx, database.UpdateExternalAuthLinkParams{
ProviderID: externalAuthConfig.ID, ProviderID: externalAuthConfig.ID,
UserID: apiKey.UserID, UserID: apiKey.UserID,
UpdatedAt: dbtime.Now(), UpdatedAt: dbtime.Now(),
OAuthAccessToken: state.Token.AccessToken, OAuthAccessToken: state.Token.AccessToken,
OAuthRefreshToken: state.Token.RefreshToken, OAuthAccessTokenKeyID: sql.NullString{}, // dbcrypt will update as required
OAuthExpiry: state.Token.Expiry, OAuthRefreshToken: state.Token.RefreshToken,
OAuthRefreshTokenKeyID: sql.NullString{}, // dbcrypt will update as required
OAuthExpiry: state.Token.Expiry,
}) })
if err != nil { if err != nil {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{

View File

@ -2,6 +2,7 @@ package externalauth
import ( import (
"context" "context"
"database/sql"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
@ -120,18 +121,20 @@ validate:
} }
if token.AccessToken != externalAuthLink.OAuthAccessToken { if token.AccessToken != externalAuthLink.OAuthAccessToken {
// Update it updatedAuthLink, err := db.UpdateExternalAuthLink(ctx, database.UpdateExternalAuthLinkParams{
externalAuthLink, err = db.UpdateExternalAuthLink(ctx, database.UpdateExternalAuthLinkParams{ ProviderID: c.ID,
ProviderID: c.ID, UserID: externalAuthLink.UserID,
UserID: externalAuthLink.UserID, UpdatedAt: dbtime.Now(),
UpdatedAt: dbtime.Now(), OAuthAccessToken: token.AccessToken,
OAuthAccessToken: token.AccessToken, OAuthAccessTokenKeyID: sql.NullString{}, // dbcrypt will update as required
OAuthRefreshToken: token.RefreshToken, OAuthRefreshToken: token.RefreshToken,
OAuthExpiry: token.Expiry, OAuthRefreshTokenKeyID: sql.NullString{}, // dbcrypt will update as required
OAuthExpiry: token.Expiry,
}) })
if err != nil { if err != nil {
return externalAuthLink, false, xerrors.Errorf("update external auth link: %w", err) return updatedAuthLink, false, xerrors.Errorf("update external auth link: %w", err)
} }
externalAuthLink = updatedAuthLink
} }
return externalAuthLink, true, nil return externalAuthLink, true, nil
} }

View File

@ -369,13 +369,15 @@ func ExtractAPIKey(rw http.ResponseWriter, r *http.Request, cfg ExtractAPIKeyCon
// If the API Key is associated with a user_link (e.g. Github/OIDC) // If the API Key is associated with a user_link (e.g. Github/OIDC)
// then we want to update the relevant oauth fields. // then we want to update the relevant oauth fields.
if link.UserID != uuid.Nil { if link.UserID != uuid.Nil {
// nolint:gocritic //nolint:gocritic // system needs to update user link
link, err = cfg.DB.UpdateUserLink(dbauthz.AsSystemRestricted(ctx), database.UpdateUserLinkParams{ link, err = cfg.DB.UpdateUserLink(dbauthz.AsSystemRestricted(ctx), database.UpdateUserLinkParams{
UserID: link.UserID, UserID: link.UserID,
LoginType: link.LoginType, LoginType: link.LoginType,
OAuthAccessToken: link.OAuthAccessToken, OAuthAccessToken: link.OAuthAccessToken,
OAuthRefreshToken: link.OAuthRefreshToken, OAuthAccessTokenKeyID: sql.NullString{}, // dbcrypt will update as required
OAuthExpiry: link.OAuthExpiry, OAuthRefreshToken: link.OAuthRefreshToken,
OAuthRefreshTokenKeyID: sql.NullString{}, // dbcrypt will update as required
OAuthExpiry: link.OAuthExpiry,
}) })
if err != nil { if err != nil {
return write(http.StatusInternalServerError, codersdk.Response{ return write(http.StatusInternalServerError, codersdk.Response{
@ -388,7 +390,7 @@ func ExtractAPIKey(rw http.ResponseWriter, r *http.Request, cfg ExtractAPIKeyCon
// We only want to update this occasionally to reduce DB write // We only want to update this occasionally to reduce DB write
// load. We update alongside the UserLink and APIKey since it's // load. We update alongside the UserLink and APIKey since it's
// easier on the DB to colocate writes. // easier on the DB to colocate writes.
// nolint:gocritic //nolint:gocritic // system needs to update user last seen at
_, err = cfg.DB.UpdateUserLastSeenAt(dbauthz.AsSystemRestricted(ctx), database.UpdateUserLastSeenAtParams{ _, err = cfg.DB.UpdateUserLastSeenAt(dbauthz.AsSystemRestricted(ctx), database.UpdateUserLastSeenAtParams{
ID: key.UserID, ID: key.UserID,
LastSeenAt: dbtime.Now(), LastSeenAt: dbtime.Now(),
@ -405,7 +407,7 @@ func ExtractAPIKey(rw http.ResponseWriter, r *http.Request, cfg ExtractAPIKeyCon
// If the key is valid, we also fetch the user roles and status. // If the key is valid, we also fetch the user roles and status.
// The roles are used for RBAC authorize checks, and the status // The roles are used for RBAC authorize checks, and the status
// is to block 'suspended' users from accessing the platform. // is to block 'suspended' users from accessing the platform.
// nolint:gocritic //nolint:gocritic // system needs to update user roles
roles, err := cfg.DB.GetAuthorizationUserRoles(dbauthz.AsSystemRestricted(ctx), key.UserID) roles, err := cfg.DB.GetAuthorizationUserRoles(dbauthz.AsSystemRestricted(ctx), key.UserID)
if err != nil { if err != nil {
return write(http.StatusUnauthorized, codersdk.Response{ return write(http.StatusUnauthorized, codersdk.Response{

View File

@ -68,10 +68,11 @@ func (api *API) postOrganizations(rw http.ResponseWriter, r *http.Request) {
var organization database.Organization var organization database.Organization
err = api.Database.InTx(func(tx database.Store) error { err = api.Database.InTx(func(tx database.Store) error {
organization, err = tx.InsertOrganization(ctx, database.InsertOrganizationParams{ organization, err = tx.InsertOrganization(ctx, database.InsertOrganizationParams{
ID: uuid.New(), ID: uuid.New(),
Name: req.Name, Name: req.Name,
CreatedAt: dbtime.Now(), CreatedAt: dbtime.Now(),
UpdatedAt: dbtime.Now(), UpdatedAt: dbtime.Now(),
Description: "",
}) })
if err != nil { if err != nil {
return xerrors.Errorf("create organization: %w", err) return xerrors.Errorf("create organization: %w", err)

View File

@ -263,18 +263,21 @@ func (s *server) AcquireJobWithCancel(stream proto.DRPCProvisionerDaemon_Acquire
logger.Error(streamCtx, "recv error and failed to cancel acquire job", slog.Error(recvErr)) logger.Error(streamCtx, "recv error and failed to cancel acquire job", slog.Error(recvErr))
// Well, this is awkward. We hit an error receiving from the stream, but didn't cancel before we locked a job // Well, this is awkward. We hit an error receiving from the stream, but didn't cancel before we locked a job
// in the database. We need to mark this job as failed so the end user can retry if they want to. // in the database. We need to mark this job as failed so the end user can retry if they want to.
now := dbtime.Now()
err := s.Database.UpdateProvisionerJobWithCompleteByID( err := s.Database.UpdateProvisionerJobWithCompleteByID(
context.Background(), context.Background(),
database.UpdateProvisionerJobWithCompleteByIDParams{ database.UpdateProvisionerJobWithCompleteByIDParams{
ID: je.job.ID, ID: je.job.ID,
CompletedAt: sql.NullTime{ CompletedAt: sql.NullTime{
Time: dbtime.Now(), Time: now,
Valid: true, Valid: true,
}, },
UpdatedAt: now,
Error: sql.NullString{ Error: sql.NullString{
String: "connection to provisioner daemon broken", String: "connection to provisioner daemon broken",
Valid: true, Valid: true,
}, },
ErrorCode: sql.NullString{},
}) })
if err != nil { if err != nil {
logger.Error(streamCtx, "error updating failed job", slog.Error(err)) logger.Error(streamCtx, "error updating failed job", slog.Error(err))
@ -308,6 +311,7 @@ func (s *server) acquireProtoJob(ctx context.Context, job database.ProvisionerJo
Valid: true, Valid: true,
}, },
ErrorCode: job.ErrorCode, ErrorCode: job.ErrorCode,
UpdatedAt: dbtime.Now(),
}) })
if err != nil { if err != nil {
return xerrors.Errorf("update provisioner job: %w", err) return xerrors.Errorf("update provisioner job: %w", err)
@ -651,6 +655,7 @@ func (s *server) UpdateJob(ctx context.Context, request *proto.UpdateJobRequest)
} }
if len(request.Logs) > 0 { if len(request.Logs) > 0 {
//nolint:exhaustruct // We append to the additional fields below.
insertParams := database.InsertProvisionerJobLogsParams{ insertParams := database.InsertProvisionerJobLogsParams{
JobID: parsedID, JobID: parsedID,
} }
@ -1061,7 +1066,8 @@ func (s *server) CompleteJob(ctx context.Context, completed *proto.CompletedJob)
Time: dbtime.Now(), Time: dbtime.Now(),
Valid: true, Valid: true,
}, },
Error: completedError, Error: completedError,
ErrorCode: sql.NullString{},
}) })
if err != nil { if err != nil {
return nil, xerrors.Errorf("update provisioner job: %w", err) return nil, xerrors.Errorf("update provisioner job: %w", err)
@ -1118,6 +1124,8 @@ func (s *server) CompleteJob(ctx context.Context, completed *proto.CompletedJob)
Time: dbtime.Now(), Time: dbtime.Now(),
Valid: true, Valid: true,
}, },
Error: sql.NullString{},
ErrorCode: sql.NullString{},
}) })
if err != nil { if err != nil {
return xerrors.Errorf("update provisioner job: %w", err) return xerrors.Errorf("update provisioner job: %w", err)
@ -1275,6 +1283,8 @@ func (s *server) CompleteJob(ctx context.Context, completed *proto.CompletedJob)
Time: dbtime.Now(), Time: dbtime.Now(),
Valid: true, Valid: true,
}, },
Error: sql.NullString{},
ErrorCode: sql.NullString{},
}) })
if err != nil { if err != nil {
return nil, xerrors.Errorf("update provisioner job: %w", err) return nil, xerrors.Errorf("update provisioner job: %w", err)
@ -1386,6 +1396,8 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid.
TroubleshootingURL: prAgent.GetTroubleshootingUrl(), TroubleshootingURL: prAgent.GetTroubleshootingUrl(),
MOTDFile: prAgent.GetMotdFile(), MOTDFile: prAgent.GetMotdFile(),
DisplayApps: convertDisplayApps(prAgent.GetDisplayApps()), DisplayApps: convertDisplayApps(prAgent.GetDisplayApps()),
InstanceMetadata: pqtype.NullRawMessage{},
ResourceMetadata: pqtype.NullRawMessage{},
}) })
if err != nil { if err != nil {
return xerrors.Errorf("insert agent: %w", err) return xerrors.Errorf("insert agent: %w", err)
@ -1628,11 +1640,13 @@ func obtainOIDCAccessToken(ctx context.Context, db database.Store, oidcConfig ht
link.OAuthExpiry = token.Expiry link.OAuthExpiry = token.Expiry
link, err = db.UpdateUserLink(ctx, database.UpdateUserLinkParams{ link, err = db.UpdateUserLink(ctx, database.UpdateUserLinkParams{
UserID: userID, UserID: userID,
LoginType: database.LoginTypeOIDC, LoginType: database.LoginTypeOIDC,
OAuthAccessToken: link.OAuthAccessToken, OAuthAccessToken: link.OAuthAccessToken,
OAuthRefreshToken: link.OAuthRefreshToken, OAuthAccessTokenKeyID: sql.NullString{}, // set by dbcrypt if required
OAuthExpiry: link.OAuthExpiry, OAuthRefreshToken: link.OAuthRefreshToken,
OAuthRefreshTokenKeyID: sql.NullString{}, // set by dbcrypt if required
OAuthExpiry: link.OAuthExpiry,
}) })
if err != nil { if err != nil {
return "", xerrors.Errorf("update user link: %w", err) return "", xerrors.Errorf("update user link: %w", err)

View File

@ -273,7 +273,12 @@ func unhangJob(ctx context.Context, log slog.Logger, db database.Store, pub pubs
// Insert the messages into the build log. // Insert the messages into the build log.
insertParams := database.InsertProvisionerJobLogsParams{ insertParams := database.InsertProvisionerJobLogsParams{
JobID: job.ID, JobID: job.ID,
CreatedAt: nil,
Source: nil,
Level: nil,
Stage: nil,
Output: nil,
} }
now := dbtime.Now() now := dbtime.Now()
for i, msg := range HungJobLogMessages { for i, msg := range HungJobLogMessages {

View File

@ -1327,14 +1327,16 @@ func (api *API) oauthLogin(r *http.Request, params *oauthLoginParams) ([]*http.C
} }
if link.UserID == uuid.Nil { if link.UserID == uuid.Nil {
//nolint:gocritic //nolint:gocritic // System needs to insert the user link (linked_id, oauth_token, oauth_expiry).
link, err = tx.InsertUserLink(dbauthz.AsSystemRestricted(ctx), database.InsertUserLinkParams{ link, err = tx.InsertUserLink(dbauthz.AsSystemRestricted(ctx), database.InsertUserLinkParams{
UserID: user.ID, UserID: user.ID,
LoginType: params.LoginType, LoginType: params.LoginType,
LinkedID: params.LinkedID, LinkedID: params.LinkedID,
OAuthAccessToken: params.State.Token.AccessToken, OAuthAccessToken: params.State.Token.AccessToken,
OAuthRefreshToken: params.State.Token.RefreshToken, OAuthAccessTokenKeyID: sql.NullString{}, // set by dbcrypt if required
OAuthExpiry: params.State.Token.Expiry, OAuthRefreshToken: params.State.Token.RefreshToken,
OAuthRefreshTokenKeyID: sql.NullString{}, // set by dbcrypt if required
OAuthExpiry: params.State.Token.Expiry,
}) })
if err != nil { if err != nil {
return xerrors.Errorf("insert user link: %w", err) return xerrors.Errorf("insert user link: %w", err)
@ -1342,13 +1344,15 @@ func (api *API) oauthLogin(r *http.Request, params *oauthLoginParams) ([]*http.C
} }
if link.UserID != uuid.Nil { if link.UserID != uuid.Nil {
//nolint:gocritic //nolint:gocritic // System needs to update the user link (linked_id, oauth_token, oauth_expiry).
link, err = tx.UpdateUserLink(dbauthz.AsSystemRestricted(ctx), database.UpdateUserLinkParams{ link, err = tx.UpdateUserLink(dbauthz.AsSystemRestricted(ctx), database.UpdateUserLinkParams{
UserID: user.ID, UserID: user.ID,
LoginType: params.LoginType, LoginType: params.LoginType,
OAuthAccessToken: params.State.Token.AccessToken, OAuthAccessToken: params.State.Token.AccessToken,
OAuthRefreshToken: params.State.Token.RefreshToken, OAuthAccessTokenKeyID: sql.NullString{}, // set by dbcrypt if required
OAuthExpiry: params.State.Token.Expiry, OAuthRefreshToken: params.State.Token.RefreshToken,
OAuthRefreshTokenKeyID: sql.NullString{}, // set by dbcrypt if required
OAuthExpiry: params.State.Token.Expiry,
}) })
if err != nil { if err != nil {
return xerrors.Errorf("update user link: %w", err) return xerrors.Errorf("update user link: %w", err)

View File

@ -1079,10 +1079,11 @@ func (api *API) CreateUser(ctx context.Context, store database.Store, req Create
} }
organization, err := tx.InsertOrganization(ctx, database.InsertOrganizationParams{ organization, err := tx.InsertOrganization(ctx, database.InsertOrganizationParams{
ID: uuid.New(), ID: uuid.New(),
Name: req.Username, Name: req.Username,
CreatedAt: dbtime.Now(), CreatedAt: dbtime.Now(),
UpdatedAt: dbtime.Now(), UpdatedAt: dbtime.Now(),
Description: "",
}) })
if err != nil { if err != nil {
return xerrors.Errorf("create organization: %w", err) return xerrors.Errorf("create organization: %w", err)
@ -1101,11 +1102,12 @@ func (api *API) CreateUser(ctx context.Context, store database.Store, req Create
} }
params := database.InsertUserParams{ params := database.InsertUserParams{
ID: uuid.New(), ID: uuid.New(),
Email: req.Email, Email: req.Email,
Username: req.Username, Username: req.Username,
CreatedAt: dbtime.Now(), CreatedAt: dbtime.Now(),
UpdatedAt: dbtime.Now(), UpdatedAt: dbtime.Now(),
HashedPassword: []byte{},
// All new users are defaulted to members of the site. // All new users are defaulted to members of the site.
RBACRoles: []string{}, RBACRoles: []string{},
LoginType: req.LoginType, LoginType: req.LoginType,

View File

@ -8,6 +8,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"time"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/lib/pq" "github.com/lib/pq"
@ -350,6 +351,8 @@ func (b *Builder) buildTx(authFunc func(action rbac.Action, object rbac.Objecter
Transition: b.trans, Transition: b.trans,
JobID: provisionerJob.ID, JobID: provisionerJob.ID,
Reason: b.reason, Reason: b.reason,
Deadline: time.Time{}, // set by provisioner upon completion
MaxDeadline: time.Time{}, // set by provisioner upon completion
}) })
if err != nil { if err != nil {
return BuildError{http.StatusInternalServerError, "insert workspace build", err} return BuildError{http.StatusInternalServerError, "insert workspace build", err}

View File

@ -25,8 +25,8 @@ func Rotate(ctx context.Context, log slog.Logger, sqlDB *sql.DB, ciphers []Ciphe
} }
log.Info(ctx, "encrypting user tokens", slog.F("user_count", len(userIDs))) log.Info(ctx, "encrypting user tokens", slog.F("user_count", len(userIDs)))
for idx, uid := range userIDs { for idx, uid := range userIDs {
err := cryptDB.InTx(func(tx database.Store) error { err := cryptDB.InTx(func(cryptTx database.Store) error {
userLinks, err := tx.GetUserLinksByUserID(ctx, uid) userLinks, err := cryptTx.GetUserLinksByUserID(ctx, uid)
if err != nil { if err != nil {
return xerrors.Errorf("get user links for user: %w", err) return xerrors.Errorf("get user links for user: %w", err)
} }
@ -35,18 +35,20 @@ func Rotate(ctx context.Context, log slog.Logger, sqlDB *sql.DB, ciphers []Ciphe
log.Debug(ctx, "skipping user link", slog.F("user_id", uid), slog.F("current", idx+1), slog.F("cipher", ciphers[0].HexDigest())) log.Debug(ctx, "skipping user link", slog.F("user_id", uid), slog.F("current", idx+1), slog.F("cipher", ciphers[0].HexDigest()))
continue continue
} }
if _, err := tx.UpdateUserLink(ctx, database.UpdateUserLinkParams{ if _, err := cryptTx.UpdateUserLink(ctx, database.UpdateUserLinkParams{
OAuthAccessToken: userLink.OAuthAccessToken, OAuthAccessToken: userLink.OAuthAccessToken,
OAuthRefreshToken: userLink.OAuthRefreshToken, OAuthAccessTokenKeyID: sql.NullString{}, // dbcrypt will update as required
OAuthExpiry: userLink.OAuthExpiry, OAuthRefreshToken: userLink.OAuthRefreshToken,
UserID: uid, OAuthRefreshTokenKeyID: sql.NullString{}, // dbcrypt will update as required
LoginType: userLink.LoginType, OAuthExpiry: userLink.OAuthExpiry,
UserID: uid,
LoginType: userLink.LoginType,
}); err != nil { }); err != nil {
return xerrors.Errorf("update user link user_id=%s linked_id=%s: %w", userLink.UserID, userLink.LinkedID, err) return xerrors.Errorf("update user link user_id=%s linked_id=%s: %w", userLink.UserID, userLink.LinkedID, err)
} }
} }
gitAuthLinks, err := tx.GetExternalAuthLinksByUserID(ctx, uid) gitAuthLinks, err := cryptTx.GetExternalAuthLinksByUserID(ctx, uid)
if err != nil { if err != nil {
return xerrors.Errorf("get git auth links for user: %w", err) return xerrors.Errorf("get git auth links for user: %w", err)
} }
@ -55,13 +57,15 @@ func Rotate(ctx context.Context, log slog.Logger, sqlDB *sql.DB, ciphers []Ciphe
log.Debug(ctx, "skipping git auth link", slog.F("user_id", uid), slog.F("current", idx+1), slog.F("cipher", ciphers[0].HexDigest())) log.Debug(ctx, "skipping git auth link", slog.F("user_id", uid), slog.F("current", idx+1), slog.F("cipher", ciphers[0].HexDigest()))
continue continue
} }
if _, err := tx.UpdateExternalAuthLink(ctx, database.UpdateExternalAuthLinkParams{ if _, err := cryptTx.UpdateExternalAuthLink(ctx, database.UpdateExternalAuthLinkParams{
ProviderID: gitAuthLink.ProviderID, ProviderID: gitAuthLink.ProviderID,
UserID: uid, UserID: uid,
UpdatedAt: gitAuthLink.UpdatedAt, UpdatedAt: gitAuthLink.UpdatedAt,
OAuthAccessToken: gitAuthLink.OAuthAccessToken, OAuthAccessToken: gitAuthLink.OAuthAccessToken,
OAuthRefreshToken: gitAuthLink.OAuthRefreshToken, OAuthAccessTokenKeyID: sql.NullString{}, // dbcrypt will update as required
OAuthExpiry: gitAuthLink.OAuthExpiry, OAuthRefreshToken: gitAuthLink.OAuthRefreshToken,
OAuthRefreshTokenKeyID: sql.NullString{}, // dbcrypt will update as required
OAuthExpiry: gitAuthLink.OAuthExpiry,
}); err != nil { }); err != nil {
return xerrors.Errorf("update git auth link user_id=%s provider_id=%s: %w", gitAuthLink.UserID, gitAuthLink.ProviderID, err) return xerrors.Errorf("update git auth link user_id=%s provider_id=%s: %w", gitAuthLink.UserID, gitAuthLink.ProviderID, err)
} }
@ -120,11 +124,13 @@ func Decrypt(ctx context.Context, log slog.Logger, sqlDB *sql.DB, ciphers []Ciph
continue continue
} }
if _, err := tx.UpdateUserLink(ctx, database.UpdateUserLinkParams{ if _, err := tx.UpdateUserLink(ctx, database.UpdateUserLinkParams{
OAuthAccessToken: userLink.OAuthAccessToken, OAuthAccessToken: userLink.OAuthAccessToken,
OAuthRefreshToken: userLink.OAuthRefreshToken, OAuthAccessTokenKeyID: sql.NullString{}, // we explicitly want to clear the key id
OAuthExpiry: userLink.OAuthExpiry, OAuthRefreshToken: userLink.OAuthRefreshToken,
UserID: uid, OAuthRefreshTokenKeyID: sql.NullString{}, // we explicitly want to clear the key id
LoginType: userLink.LoginType, OAuthExpiry: userLink.OAuthExpiry,
UserID: uid,
LoginType: userLink.LoginType,
}); err != nil { }); err != nil {
return xerrors.Errorf("update user link user_id=%s linked_id=%s: %w", userLink.UserID, userLink.LinkedID, err) return xerrors.Errorf("update user link user_id=%s linked_id=%s: %w", userLink.UserID, userLink.LinkedID, err)
} }
@ -140,12 +146,14 @@ func Decrypt(ctx context.Context, log slog.Logger, sqlDB *sql.DB, ciphers []Ciph
continue continue
} }
if _, err := tx.UpdateExternalAuthLink(ctx, database.UpdateExternalAuthLinkParams{ if _, err := tx.UpdateExternalAuthLink(ctx, database.UpdateExternalAuthLinkParams{
ProviderID: gitAuthLink.ProviderID, ProviderID: gitAuthLink.ProviderID,
UserID: uid, UserID: uid,
UpdatedAt: gitAuthLink.UpdatedAt, UpdatedAt: gitAuthLink.UpdatedAt,
OAuthAccessToken: gitAuthLink.OAuthAccessToken, OAuthAccessToken: gitAuthLink.OAuthAccessToken,
OAuthRefreshToken: gitAuthLink.OAuthRefreshToken, OAuthAccessTokenKeyID: sql.NullString{}, // we explicitly want to clear the key id
OAuthExpiry: gitAuthLink.OAuthExpiry, OAuthRefreshToken: gitAuthLink.OAuthRefreshToken,
OAuthRefreshTokenKeyID: sql.NullString{}, // we explicitly want to clear the key id
OAuthExpiry: gitAuthLink.OAuthExpiry,
}); err != nil { }); err != nil {
return xerrors.Errorf("update git auth link user_id=%s provider_id=%s: %w", gitAuthLink.UserID, gitAuthLink.ProviderID, err) return xerrors.Errorf("update git auth link user_id=%s provider_id=%s: %w", gitAuthLink.UserID, gitAuthLink.ProviderID, err)
} }

View File

@ -420,11 +420,13 @@ func (m *Manager) Close() error {
Time: dbtime.Now(), Time: dbtime.Now(),
Valid: true, Valid: true,
}, },
RelayAddress: m.self.RelayAddress, RelayAddress: m.self.RelayAddress,
RegionID: m.self.RegionID, RegionID: m.self.RegionID,
Hostname: m.self.Hostname, Hostname: m.self.Hostname,
Version: m.self.Version, Version: m.self.Version,
Error: m.self.Error, Error: m.self.Error,
DatabaseLatency: 0, // A stopped replica has no latency.
Primary: false, // A stopped replica cannot be primary.
}) })
if err != nil { if err != nil {
return xerrors.Errorf("update replica: %w", err) return xerrors.Errorf("update replica: %w", err)