@@ -372,8 +372,7 @@ func Test_ResolveRequest(t *testing.T) {
372
372
require .WithinDuration (t , token .Expiry .Time (), secondToken .Expiry .Time (), 2 * time .Second )
373
373
secondToken .Expiry = token .Expiry
374
374
require .Equal (t , token , secondToken )
375
-
376
- require .Len (t , auditor .AuditLogs (), 1 , "single audit log, same user and app audit session is active" )
375
+ require .Len (t , auditor .AuditLogs (), 1 , "no new audit log, FromRequest returned the same token and is not audited" )
377
376
}
378
377
})
379
378
}
@@ -1248,6 +1247,134 @@ func Test_ResolveRequest(t *testing.T) {
1248
1247
}), "audit log unhealthy app" )
1249
1248
require .Len (t , auditor .AuditLogs (), 1 , "single audit log" )
1250
1249
})
1250
+
1251
+ t .Run ("AuditLogging" , func (t * testing.T ) {
1252
+ t .Parallel ()
1253
+
1254
+ for _ , app := range allApps {
1255
+ req := (workspaceapps.Request {
1256
+ AccessMethod : workspaceapps .AccessMethodPath ,
1257
+ BasePath : "/app" ,
1258
+ UsernameOrID : me .Username ,
1259
+ WorkspaceNameOrID : workspace .Name ,
1260
+ AgentNameOrID : agentName ,
1261
+ AppSlugOrPort : app ,
1262
+ }).Normalize ()
1263
+
1264
+ auditor := audit .NewMock ()
1265
+ auditableIP := randomIPv6 (t )
1266
+
1267
+ t .Log ("app" , app )
1268
+
1269
+ // First request, new audit log.
1270
+ rw := httptest .NewRecorder ()
1271
+ r := httptest .NewRequest ("GET" , "/app" , nil )
1272
+ r .Header .Set (codersdk .SessionTokenHeader , client .SessionToken ())
1273
+ r = requestWithAuditorAndRemoteAddr (r , auditor , auditableIP )
1274
+
1275
+ _ , ok := workspaceappsResolveRequest (t , rw , r , workspaceapps.ResolveRequestOptions {
1276
+ Logger : api .Logger ,
1277
+ SignedTokenProvider : api .WorkspaceAppsProvider ,
1278
+ DashboardURL : api .AccessURL ,
1279
+ PathAppBaseURL : api .AccessURL ,
1280
+ AppHostname : api .AppHostname ,
1281
+ AppRequest : req ,
1282
+ })
1283
+ require .True (t , ok )
1284
+ w := rw .Result ()
1285
+ _ = w .Body .Close ()
1286
+ require .True (t , auditor .Contains (t , database.AuditLog {
1287
+ OrganizationID : workspace .OrganizationID ,
1288
+ Action : database .AuditActionOpen ,
1289
+ ResourceType : audit .ResourceType (appsBySlug [app ]),
1290
+ ResourceID : audit .ResourceID (appsBySlug [app ]),
1291
+ ResourceTarget : audit .ResourceTarget (appsBySlug [app ]),
1292
+ UserID : me .ID ,
1293
+ Ip : audit .ParseIP (auditableIP ),
1294
+ StatusCode : int32 (w .StatusCode ), //nolint:gosec
1295
+ }), "audit log 1" )
1296
+ require .Len (t , auditor .AuditLogs (), 1 , "single audit log" )
1297
+
1298
+ // Second request, no audit log because the session is active.
1299
+ rw = httptest .NewRecorder ()
1300
+ r = httptest .NewRequest ("GET" , "/app" , nil )
1301
+ r .Header .Set (codersdk .SessionTokenHeader , client .SessionToken ())
1302
+ r = requestWithAuditorAndRemoteAddr (r , auditor , auditableIP )
1303
+
1304
+ _ , ok = workspaceappsResolveRequest (t , rw , r , workspaceapps.ResolveRequestOptions {
1305
+ Logger : api .Logger ,
1306
+ SignedTokenProvider : api .WorkspaceAppsProvider ,
1307
+ DashboardURL : api .AccessURL ,
1308
+ PathAppBaseURL : api .AccessURL ,
1309
+ AppHostname : api .AppHostname ,
1310
+ AppRequest : req ,
1311
+ })
1312
+ require .True (t , ok )
1313
+ w = rw .Result ()
1314
+ _ = w .Body .Close ()
1315
+ require .Len (t , auditor .AuditLogs (), 1 , "single audit log, previous session active" )
1316
+
1317
+ // Third request, session timed out, new audit log.
1318
+ rw = httptest .NewRecorder ()
1319
+ r = httptest .NewRequest ("GET" , "/app" , nil )
1320
+ r .Header .Set (codersdk .SessionTokenHeader , client .SessionToken ())
1321
+ r .RemoteAddr = auditableIP
1322
+
1323
+ sessionTimeoutTokenProvider := signedTokenProviderWithAuditor (t , api .WorkspaceAppsProvider , auditor , 0 )
1324
+ _ , ok = workspaceappsResolveRequest (t , rw , r , workspaceapps.ResolveRequestOptions {
1325
+ Logger : api .Logger ,
1326
+ SignedTokenProvider : sessionTimeoutTokenProvider ,
1327
+ DashboardURL : api .AccessURL ,
1328
+ PathAppBaseURL : api .AccessURL ,
1329
+ AppHostname : api .AppHostname ,
1330
+ AppRequest : req ,
1331
+ })
1332
+ require .True (t , ok )
1333
+ w = rw .Result ()
1334
+ _ = w .Body .Close ()
1335
+ require .True (t , auditor .Contains (t , database.AuditLog {
1336
+ OrganizationID : workspace .OrganizationID ,
1337
+ Action : database .AuditActionOpen ,
1338
+ ResourceType : audit .ResourceType (appsBySlug [app ]),
1339
+ ResourceID : audit .ResourceID (appsBySlug [app ]),
1340
+ ResourceTarget : audit .ResourceTarget (appsBySlug [app ]),
1341
+ UserID : me .ID ,
1342
+ Ip : audit .ParseIP (auditableIP ),
1343
+ StatusCode : int32 (w .StatusCode ), //nolint:gosec
1344
+ }), "audit log 2" )
1345
+ require .Len (t , auditor .AuditLogs (), 2 , "two audit logs, session timed out" )
1346
+
1347
+ // Fourth request, new IP produces new audit log.
1348
+ auditableIP = randomIPv6 (t )
1349
+ rw = httptest .NewRecorder ()
1350
+ r = httptest .NewRequest ("GET" , "/app" , nil )
1351
+ r .Header .Set (codersdk .SessionTokenHeader , client .SessionToken ())
1352
+ r = requestWithAuditorAndRemoteAddr (r , auditor , auditableIP )
1353
+
1354
+ _ , ok = workspaceappsResolveRequest (t , rw , r , workspaceapps.ResolveRequestOptions {
1355
+ Logger : api .Logger ,
1356
+ SignedTokenProvider : api .WorkspaceAppsProvider ,
1357
+ DashboardURL : api .AccessURL ,
1358
+ PathAppBaseURL : api .AccessURL ,
1359
+ AppHostname : api .AppHostname ,
1360
+ AppRequest : req ,
1361
+ })
1362
+ require .True (t , ok )
1363
+ w = rw .Result ()
1364
+ _ = w .Body .Close ()
1365
+ require .True (t , auditor .Contains (t , database.AuditLog {
1366
+ OrganizationID : workspace .OrganizationID ,
1367
+ Action : database .AuditActionOpen ,
1368
+ ResourceType : audit .ResourceType (appsBySlug [app ]),
1369
+ ResourceID : audit .ResourceID (appsBySlug [app ]),
1370
+ ResourceTarget : audit .ResourceTarget (appsBySlug [app ]),
1371
+ UserID : me .ID ,
1372
+ Ip : audit .ParseIP (auditableIP ),
1373
+ StatusCode : int32 (w .StatusCode ), //nolint:gosec
1374
+ }), "audit log 3" )
1375
+ require .Len (t , auditor .AuditLogs (), 3 , "three audit logs, new IP" )
1376
+ }
1377
+ })
1251
1378
}
1252
1379
1253
1380
type auditorKey int
@@ -1281,7 +1408,7 @@ func workspaceappsResolveRequest(t testing.TB, w http.ResponseWriter, r *http.Re
1281
1408
if opts .SignedTokenProvider != nil && auditorValue != nil {
1282
1409
auditor , ok := auditorValue .(audit.Auditor )
1283
1410
require .True (t , ok , "auditor is not an audit.Auditor" )
1284
- opts .SignedTokenProvider = signedTokenProviderWithAuditor (t , opts .SignedTokenProvider , auditor )
1411
+ opts .SignedTokenProvider = signedTokenProviderWithAuditor (t , opts .SignedTokenProvider , auditor , time . Hour )
1285
1412
}
1286
1413
1287
1414
tracing .StatusWriterMiddleware (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
@@ -1291,13 +1418,14 @@ func workspaceappsResolveRequest(t testing.TB, w http.ResponseWriter, r *http.Re
1291
1418
return token , ok
1292
1419
}
1293
1420
1294
- func signedTokenProviderWithAuditor (t testing.TB , provider workspaceapps.SignedTokenProvider , auditor audit.Auditor ) workspaceapps.SignedTokenProvider {
1421
+ func signedTokenProviderWithAuditor (t testing.TB , provider workspaceapps.SignedTokenProvider , auditor audit.Auditor , sessionTimeout time. Duration ) workspaceapps.SignedTokenProvider {
1295
1422
t .Helper ()
1296
1423
p , ok := provider .(* workspaceapps.DBTokenProvider )
1297
1424
require .True (t , ok , "provider is not a DBTokenProvider" )
1298
1425
1299
1426
shallowCopy := * p
1300
1427
shallowCopy .Auditor = & atomic.Pointer [audit.Auditor ]{}
1301
1428
shallowCopy .Auditor .Store (& auditor )
1429
+ shallowCopy .WorkspaceAppAuditSessionTimeout = sessionTimeout
1302
1430
return & shallowCopy
1303
1431
}
0 commit comments