Skip to content

Commit 3c71642

Browse files
committed
manually exercise audit logged endpoints not in verify_endpoints list
1 parent 831e013 commit 3c71642

2 files changed

Lines changed: 112 additions & 6 deletions

File tree

nexus/tests/integration_tests/audit_log.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,115 @@ async fn test_audit_log_coverage(ctx: &ControlPlaneTestContext) {
587587
}
588588
}
589589

590+
// Exercise endpoints not in VERIFY_ENDPOINTS. These require special
591+
// handling (unauthenticated, non-JSON bodies, etc.) that prevents them
592+
// from being included in the generic loop above.
593+
//
594+
// For each endpoint we derive the operation_id from the URL via the
595+
// API spec (api_operations.find) rather than hardcoding it, so that a
596+
// URL change or mix-up is caught automatically.
597+
598+
// Make a request and check whether an audit log entry was produced.
599+
// Looks up the operation_id from the URL via the API spec rather than
600+
// hardcoding it, so a URL change or mix-up is caught automatically.
601+
// The builder is wrapped in NexusRequest with UnprivilegedUser auth,
602+
// which is harmless for unauthenticated endpoints (they ignore it).
603+
// Panics if the URL doesn't match any API operation.
604+
let mut check_manual =
605+
async |method: &str, url: &str, builder: RequestBuilder<'_>| {
606+
let before = fetch_log(client, t_start, None).await.items.len();
607+
let _ = NexusRequest::new(
608+
builder.allow_non_dropshot_errors().expect_status(None),
609+
)
610+
.authn_as(AuthnMode::UnprivilegedUser)
611+
.execute()
612+
.await;
613+
let after = fetch_log(client, t_start, None).await.items.len();
614+
let op = api_operations.find(method, url).unwrap_or_else(|| {
615+
panic!("{} {} does not match any API operation", method, url)
616+
});
617+
if let Some(info) = untested_mutating.remove(&op.operation_id) {
618+
if after <= before {
619+
missing_audit.insert(op.operation_id.clone(), info);
620+
}
621+
}
622+
};
623+
624+
// login_local: unauthenticated, JSON body
625+
check_manual(
626+
"POST",
627+
"/v1/login/fake-silo/local",
628+
RequestBuilder::new(
629+
client,
630+
Method::POST,
631+
"/v1/login/fake-silo/local",
632+
)
633+
.body(Some(&serde_json::json!({
634+
"username": "nonexistent",
635+
"password": "doesntmatter"
636+
}))),
637+
)
638+
.await;
639+
640+
// login_saml: unauthenticated, takes UntypedBody (any bytes work)
641+
check_manual(
642+
"POST",
643+
"/login/fake-silo/saml/fake-provider",
644+
RequestBuilder::new(
645+
client,
646+
Method::POST,
647+
"/login/fake-silo/saml/fake-provider",
648+
)
649+
.body(Some(&serde_json::json!({}))),
650+
)
651+
.await;
652+
653+
// logout: session cookie-based, no body needed
654+
check_manual(
655+
"POST",
656+
"/v1/logout",
657+
RequestBuilder::new(client, Method::POST, "/v1/logout"),
658+
)
659+
.await;
660+
661+
// device_auth_request: unauthenticated, URL-encoded body
662+
check_manual(
663+
"POST",
664+
"/device/auth",
665+
RequestBuilder::new(client, Method::POST, "/device/auth")
666+
.body_urlencoded(Some(&device::DeviceAuthRequest {
667+
client_id: uuid::Uuid::nil(),
668+
ttl_seconds: None,
669+
})),
670+
)
671+
.await;
672+
673+
// device_auth_confirm: authenticated, JSON body
674+
check_manual(
675+
"POST",
676+
"/device/confirm",
677+
RequestBuilder::new(client, Method::POST, "/device/confirm")
678+
.body(Some(&device::DeviceAuthVerify {
679+
user_code: "fake-code".to_string(),
680+
})),
681+
)
682+
.await;
683+
684+
// device_access_token: unauthenticated, URL-encoded body
685+
check_manual(
686+
"POST",
687+
"/device/token",
688+
RequestBuilder::new(client, Method::POST, "/device/token")
689+
.body_urlencoded(Some(&device::DeviceAccessTokenRequest {
690+
grant_type:
691+
"urn:ietf:params:oauth:grant-type:device_code"
692+
.to_string(),
693+
device_code: "fake-code".to_string(),
694+
client_id: uuid::Uuid::nil(),
695+
})),
696+
)
697+
.await;
698+
590699
let mut output =
591700
String::from("Mutating endpoints without audit logging:\n");
592701
for (op_id, (method, path)) in &missing_audit {
Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
Mutating endpoints without audit logging:
2+
device_access_token (post "/device/token")
3+
device_auth_request (post "/device/auth")
24
disk_bulk_write_import (post "/v1/disks/{disk}/bulk-write")
5+
logout (post "/v1/logout")
36
system_timeseries_query (post "/v1/system/timeseries/query")
47
timeseries_query (post "/v1/timeseries/query")
58

69
Mutating endpoints not tested (not in VERIFY_ENDPOINTS):
7-
device_access_token (post "/device/token")
8-
device_auth_confirm (post "/device/confirm")
9-
device_auth_request (post "/device/auth")
10-
login_local (post "/v1/login/{silo_name}/local")
11-
login_saml (post "/login/{silo_name}/saml/{provider_name}")
12-
logout (post "/v1/logout")

0 commit comments

Comments
 (0)