Skip to content

Commit b6bf45b

Browse files
committed
convert audit_log auth_method column to an enum
1 parent cfb7836 commit b6bf45b

21 files changed

Lines changed: 29823 additions & 26 deletions

File tree

nexus/db-model/src/audit_log.rs

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ pub struct AuditLogEntryInitParams {
3838
pub source_ip: IpAddr,
3939
pub user_agent: Option<String>,
4040
pub actor: AuditLogActor,
41-
pub auth_method: Option<String>,
41+
pub auth_method: Option<AuditLogAuthMethod>,
4242
}
4343

4444
impl_enum_type!(
@@ -86,6 +86,54 @@ impl_enum_type!(
8686
Timeout => b"timeout"
8787
);
8888

89+
impl_enum_type!(
90+
AuditLogAuthMethodEnum:
91+
92+
#[derive(
93+
Clone,
94+
Copy,
95+
Debug,
96+
AsExpression,
97+
FromSqlRow,
98+
Serialize,
99+
Deserialize,
100+
PartialEq,
101+
Eq,
102+
)]
103+
pub enum AuditLogAuthMethod;
104+
105+
// Enum values
106+
SessionCookie => b"session_cookie"
107+
AccessToken => b"access_token"
108+
ScimToken => b"scim_token"
109+
Spoof => b"spoof"
110+
);
111+
112+
impl From<AuditLogAuthMethod> for views::AuthMethod {
113+
fn from(m: AuditLogAuthMethod) -> Self {
114+
match m {
115+
AuditLogAuthMethod::SessionCookie => {
116+
views::AuthMethod::SessionCookie
117+
}
118+
AuditLogAuthMethod::AccessToken => views::AuthMethod::AccessToken,
119+
AuditLogAuthMethod::ScimToken => views::AuthMethod::ScimToken,
120+
AuditLogAuthMethod::Spoof => views::AuthMethod::Spoof,
121+
}
122+
}
123+
}
124+
125+
impl From<&nexus_types::authn::SchemeName> for AuditLogAuthMethod {
126+
fn from(s: &nexus_types::authn::SchemeName) -> Self {
127+
use nexus_types::authn::SchemeName;
128+
match s {
129+
SchemeName::SessionCookie => AuditLogAuthMethod::SessionCookie,
130+
SchemeName::AccessToken => AuditLogAuthMethod::AccessToken,
131+
SchemeName::ScimToken => AuditLogAuthMethod::ScimToken,
132+
SchemeName::Spoof => AuditLogAuthMethod::Spoof,
133+
}
134+
}
135+
}
136+
89137
#[derive(Queryable, Insertable, Selectable, Clone, Debug)]
90138
#[diesel(table_name = audit_log)]
91139
pub struct AuditLogEntryInit {
@@ -115,7 +163,7 @@ pub struct AuditLogEntryInit {
115163

116164
/// API token or session cookie. Optional because it will not be defined
117165
/// on unauthenticated requests like login attempts.
118-
pub auth_method: Option<String>,
166+
pub auth_method: Option<AuditLogAuthMethod>,
119167
}
120168

121169
impl From<AuditLogEntryInitParams> for AuditLogEntryInit {
@@ -182,20 +230,20 @@ pub struct AuditLogEntry {
182230
/// Actor kind indicating builtin user, silo user, or unauthenticated
183231
pub actor_kind: AuditLogActorKind,
184232

185-
/// The name of the authn scheme used. None if unauthenticated.
186-
pub auth_method: Option<String>,
187-
188233
// Fields that are not present on init
189234
/// Time log entry was completed with info about result of operation
190235
pub time_completed: DateTime<Utc>,
191-
/// Result kind indicating success, error, or timeout
192-
pub result_kind: AuditLogResultKind,
193236
/// Optional because not present for timeout result
194237
pub http_status_code: Option<SqlU16>,
195238
/// Optional even if result is an error
196239
pub error_code: Option<String>,
197240
/// Always present if result is an error
198241
pub error_message: Option<String>,
242+
/// Result kind indicating success, error, or timeout
243+
pub result_kind: AuditLogResultKind,
244+
245+
/// The authn scheme used. None if unauthenticated.
246+
pub auth_method: Option<AuditLogAuthMethod>,
199247
}
200248

201249
/// Struct that we can use as a kind of constructor arg for our actual audit
@@ -320,7 +368,7 @@ impl TryFrom<AuditLogEntry> for views::AuditLogEntry {
320368
views::AuditLogEntryActor::Unauthenticated
321369
}
322370
},
323-
auth_method: entry.auth_method,
371+
auth_method: entry.auth_method.map(Into::into),
324372
time_completed: entry.time_completed,
325373
result: match entry.result_kind {
326374
AuditLogResultKind::Success => {

nexus/db-model/src/schema_versions.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use std::{collections::BTreeMap, sync::LazyLock};
1616
///
1717
/// This must be updated when you change the database schema. Refer to
1818
/// schema/crdb/README.adoc in the root of this repository for details.
19-
pub const SCHEMA_VERSION: Version = Version::new(220, 0, 0);
19+
pub const SCHEMA_VERSION: Version = Version::new(221, 0, 0);
2020

2121
/// List of all past database schema versions, in *reverse* order
2222
///
@@ -28,6 +28,7 @@ static KNOWN_VERSIONS: LazyLock<Vec<KnownVersion>> = LazyLock::new(|| {
2828
// | leaving the first copy as an example for the next person.
2929
// v
3030
// KnownVersion::new(next_int, "unique-dirname-with-the-sql-files"),
31+
KnownVersion::new(221, "audit-log-auth-method-enum"),
3132
KnownVersion::new(220, "multicast-implicit-lifecycle"),
3233
KnownVersion::new(219, "blueprint-sled-last-used-ip"),
3334
KnownVersion::new(218, "measurements"),

nexus/db-schema/src/enums.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ define_enums! {
2424
AffinityPolicyEnum => "affinity_policy",
2525
AlertClassEnum => "alert_class",
2626
AuditLogActorKindEnum => "audit_log_actor_kind",
27+
AuditLogAuthMethodEnum => "audit_log_auth_method",
2728
AuditLogResultKindEnum => "audit_log_result_kind",
2829
AlertDeliveryTriggerEnum => "alert_delivery_trigger",
2930
AlertDeliveryStateEnum => "alert_delivery_state",

nexus/db-schema/src/schema.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2885,12 +2885,12 @@ table! {
28852885
actor_id -> Nullable<Uuid>,
28862886
actor_silo_id -> Nullable<Uuid>,
28872887
actor_kind -> crate::enums::AuditLogActorKindEnum,
2888-
auth_method -> Nullable<Text>,
28892888
time_completed -> Nullable<Timestamptz>,
28902889
http_status_code -> Nullable<Int4>, // SqlU16
28912890
error_code -> Nullable<Text>,
28922891
error_message -> Nullable<Text>,
28932892
result_kind -> Nullable<crate::enums::AuditLogResultKindEnum>,
2893+
auth_method -> Nullable<crate::enums::AuditLogAuthMethodEnum>,
28942894
}
28952895
}
28962896

@@ -2906,12 +2906,12 @@ table! {
29062906
actor_id -> Nullable<Uuid>,
29072907
actor_silo_id -> Nullable<Uuid>,
29082908
actor_kind -> crate::enums::AuditLogActorKindEnum,
2909-
auth_method -> Nullable<Text>,
29102909
time_completed -> Timestamptz,
29112910
http_status_code -> Nullable<Int4>, // SqlU16
29122911
error_code -> Nullable<Text>,
29132912
error_message -> Nullable<Text>,
29142913
result_kind -> crate::enums::AuditLogResultKindEnum,
2914+
auth_method -> Nullable<crate::enums::AuditLogAuthMethodEnum>,
29152915
}
29162916
}
29172917

nexus/external-api/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ api_versions!([
7070
// | date-based version should be at the top of the list.
7171
// v
7272
// (next_yyyymmddnn, IDENT),
73+
(2026011500, AUDIT_LOG_AUTH_METHOD_ENUM),
7374
(2026011300, DOC_LINT_SUMMARY_TRAILING_PERIOD),
7475
(2026011100, MULTICAST_JOIN_LEAVE_DOCS),
7576
(2026010800, MULTICAST_IMPLICIT_LIFECYCLE_UPDATES),

nexus/src/app/audit_log.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ impl super::Nexus {
126126
AuditLogActor::UserBuiltin { .. }
127127
| AuditLogActor::SiloUser { .. }
128128
| AuditLogActor::Scim { .. } => {
129-
opctx.authn.scheme_used().map(|s| s.to_string())
129+
opctx.authn.scheme_used().map(Into::into)
130130
}
131131
// if we tried to pull it off the opctx this would be None anyway,
132132
// but it's better to be explicit

nexus/tests/integration_tests/audit_log.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ async fn test_audit_log_list(ctx: &ControlPlaneTestContext) {
101101
assert_eq!(e1.operation_id, "project_create");
102102
assert_eq!(e1.source_ip.to_string(), "127.0.0.1");
103103
assert_eq!(e1.user_agent, None); // no user agent passed by default
104-
assert_eq!(e1.auth_method, Some("spoof".to_string()));
104+
assert_eq!(e1.auth_method, Some(views::AuthMethod::Spoof));
105105
assert!(e1.time_started >= t1 && e1.time_started <= t2);
106106
assert!(e1.time_completed > e1.time_started);
107107
assert_eq!(
@@ -145,7 +145,7 @@ async fn test_audit_log_list(ctx: &ControlPlaneTestContext) {
145145
assert_eq!(e3.operation_id, "project_create");
146146
assert_eq!(e3.source_ip.to_string(), "127.0.0.1");
147147
assert_eq!(e3.user_agent.clone().unwrap(), "A".repeat(256));
148-
assert_eq!(e3.auth_method, Some("session_cookie".to_string()));
148+
assert_eq!(e3.auth_method, Some(views::AuthMethod::SessionCookie));
149149
assert!(e3.time_started >= t3 && e3.time_started <= t4);
150150
assert!(e3.time_completed > e3.time_started);
151151
assert_eq!(
@@ -396,6 +396,6 @@ fn verify_entry(
396396
}
397397
);
398398
assert_eq!(entry.source_ip.to_string(), "127.0.0.1");
399-
assert_eq!(entry.auth_method, Some("spoof".to_string()));
399+
assert_eq!(entry.auth_method, Some(views::AuthMethod::Spoof));
400400
assert!(entry.time_completed > entry.time_started);
401401
}

nexus/types/src/external_api/views.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1799,6 +1799,23 @@ pub enum AuditLogEntryActor {
17991799
Unauthenticated,
18001800
}
18011801

1802+
/// Authentication method used for a request
1803+
#[derive(
1804+
Debug, Clone, Copy, Deserialize, Serialize, JsonSchema, PartialEq, Eq,
1805+
)]
1806+
#[serde(rename_all = "snake_case")]
1807+
pub enum AuthMethod {
1808+
/// Console session cookie
1809+
SessionCookie,
1810+
/// Device access token (OAuth 2.0 device authorization flow)
1811+
AccessToken,
1812+
/// SCIM client bearer token
1813+
ScimToken,
1814+
/// Spoof authentication (test only)
1815+
#[schemars(skip)]
1816+
Spoof,
1817+
}
1818+
18021819
/// Result of an audit log entry
18031820
#[derive(Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq)]
18041821
#[serde(tag = "kind", rename_all = "snake_case")]
@@ -1851,10 +1868,9 @@ pub struct AuditLogEntry {
18511868

18521869
pub actor: AuditLogEntryActor,
18531870

1854-
/// How the user authenticated the request. Possible values are
1855-
/// "session_cookie" and "access_token". Optional because it will not be
1856-
/// defined on unauthenticated requests like login attempts.
1857-
pub auth_method: Option<String>,
1871+
/// How the user authenticated the request (access token, session, or SCIM
1872+
/// token). Null for unauthenticated requests like login attempts.
1873+
pub auth_method: Option<AuthMethod>,
18581874

18591875
// Fields that are optional because they get filled in after the action completes
18601876
/// Time operation completed

0 commit comments

Comments
 (0)