From 007f2df0793fb092246c91964175c0b22d12b6aa Mon Sep 17 00:00:00 2001 From: Spike Curtis Date: Thu, 20 Nov 2025 13:01:50 +0400 Subject: [PATCH] fix: use API, not request context to insert audit/connection logs (#20829) Fixes: #20744 Upsert audit and connection log entries with a context derived from the API context, rather than the individual request so that we don't error out if the request is canceled or the client hangs up (e.g. if we return an error). --- coderd/coderd.go | 1 + coderd/workspaceapps/db.go | 12 +++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/coderd/coderd.go b/coderd/coderd.go index a9683ace20..872fc5359e 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -610,6 +610,7 @@ func New(options *Options) *API { dbRolluper: options.DatabaseRolluper, } api.WorkspaceAppsProvider = workspaceapps.NewDBTokenProvider( + ctx, options.Logger.Named("workspaceapps"), options.AccessURL, options.Authorizer, diff --git a/coderd/workspaceapps/db.go b/coderd/workspaceapps/db.go index 4c24cc9325..4d77dc32b1 100644 --- a/coderd/workspaceapps/db.go +++ b/coderd/workspaceapps/db.go @@ -35,6 +35,7 @@ import ( // by querying the database if the request is missing a valid token. type DBTokenProvider struct { Logger slog.Logger + ctx context.Context // DashboardURL is the main dashboard access URL for error pages. DashboardURL *url.URL @@ -50,7 +51,8 @@ type DBTokenProvider struct { var _ SignedTokenProvider = &DBTokenProvider{} -func NewDBTokenProvider(log slog.Logger, +func NewDBTokenProvider(ctx context.Context, + log slog.Logger, accessURL *url.URL, authz rbac.Authorizer, connectionLogger *atomic.Pointer[connectionlog.ConnectionLogger], @@ -70,6 +72,7 @@ func NewDBTokenProvider(log slog.Logger, return &DBTokenProvider{ Logger: log, + ctx: ctx, DashboardURL: accessURL, Authorizer: authz, ConnectionLogger: connectionLogger, @@ -94,7 +97,7 @@ func (p *DBTokenProvider) Issue(ctx context.Context, rw http.ResponseWriter, r * // // permissions. dangerousSystemCtx := dbauthz.AsSystemRestricted(ctx) - aReq, commitAudit := p.connLogInitRequest(ctx, rw, r) + aReq, commitAudit := p.connLogInitRequest(rw, r) defer commitAudit() appReq := issueReq.AppRequest.Normalize() @@ -406,7 +409,7 @@ type connLogRequest struct { // // A session is unique to the agent, app, user and users IP. If any of these // values change, a new session and connect log is created. -func (p *DBTokenProvider) connLogInitRequest(ctx context.Context, w http.ResponseWriter, r *http.Request) (aReq *connLogRequest, commit func()) { +func (p *DBTokenProvider) connLogInitRequest(w http.ResponseWriter, r *http.Request) (aReq *connLogRequest, commit func()) { // Get the status writer from the request context so we can figure // out the HTTP status and autocommit the audit log. sw, ok := w.(*tracing.StatusWriter) @@ -422,6 +425,9 @@ func (p *DBTokenProvider) connLogInitRequest(ctx context.Context, w http.Respons // this ensures that the status and response body are available. var committed bool return aReq, func() { + // We want to log/audit the connection attempt even if the request context has expired. + ctx, cancel := context.WithCancel(p.ctx) + defer cancel() if committed { return }