Skip to content

Commit 39732aa

Browse files
author
Devdutt Shenoi
committed
feat: adhere to decided format and improve builder pattern impl
1 parent c595f6b commit 39732aa

File tree

3 files changed

+197
-120
lines changed

3 files changed

+197
-120
lines changed

src/audit.rs

Lines changed: 151 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ use std::{
2222
};
2323

2424
use crate::about::current;
25-
use crate::handlers::http::modal::utils::rbac_utils::get_metadata;
2625

2726
use super::option::CONFIG;
2827
use chrono::{DateTime, Utc};
@@ -101,10 +100,22 @@ pub enum AuditLogVersion {
101100
V1 = 1,
102101
}
103102

103+
#[derive(Serialize)]
104+
pub struct AuditDetails {
105+
pub version: AuditLogVersion,
106+
pub id: Ulid,
107+
pub generated_at: DateTime<Utc>,
108+
}
109+
110+
#[derive(Serialize)]
111+
pub struct ServerDetails {
112+
pub version: String,
113+
pub deployment_id: Ulid,
114+
}
115+
104116
// Contains information about the actor (user) who performed the action
105117
#[derive(Serialize, Default)]
106-
#[serde(rename_all = "camelCase")]
107-
pub struct ActorLog {
118+
pub struct ActorDetails {
108119
pub remote_host: String,
109120
pub user_agent: String,
110121
pub username: String,
@@ -113,7 +124,10 @@ pub struct ActorLog {
113124

114125
// Contains details about the HTTP request that was made
115126
#[derive(Serialize, Default)]
116-
pub struct RequestLog {
127+
pub struct RequestDetails {
128+
pub stream: String,
129+
pub start_time: DateTime<Utc>,
130+
pub end_time: DateTime<Utc>,
117131
pub method: String,
118132
pub path: String,
119133
pub protocol: String,
@@ -122,45 +136,34 @@ pub struct RequestLog {
122136

123137
/// Contains information about the response sent back to the client
124138
#[derive(Default, Serialize)]
125-
#[serde(rename_all = "camelCase")]
126-
pub struct ResponseLog {
139+
pub struct ResponseDetails {
127140
pub status_code: u16,
128141
pub error: Option<String>,
129142
}
130143

131144
/// The main audit log structure that combines all audit information
132145
#[derive(Serialize)]
133-
#[serde(rename_all = "camelCase")]
134146
pub struct AuditLog {
135-
pub version: AuditLogVersion,
136-
pub parseable_version: String,
137-
pub deployment_id: Ulid,
138-
pub audit_id: Ulid,
139-
pub start_time: DateTime<Utc>,
140-
pub end_time: DateTime<Utc>,
141-
pub stream: String,
142-
pub actor: ActorLog,
143-
pub request: RequestLog,
144-
pub response: ResponseLog,
147+
pub audit: AuditDetails,
148+
pub parseable_server: ServerDetails,
149+
pub actor: ActorDetails,
150+
pub request: RequestDetails,
151+
pub response: ResponseDetails,
145152
}
146153

147154
/// Builder pattern implementation for constructing audit logs
148155
pub struct AuditLogBuilder {
149156
// Used to ensure that log is only constructed if the logger is enabled
150157
enabled: bool,
151-
start_time: DateTime<Utc>,
152-
stream: String,
153-
pub actor: Option<ActorLog>,
154-
pub request: Option<RequestLog>,
155-
pub response: Option<ResponseLog>,
158+
pub actor: Option<ActorDetails>,
159+
pub request: Option<RequestDetails>,
160+
pub response: Option<ResponseDetails>,
156161
}
157162

158163
impl Default for AuditLogBuilder {
159164
fn default() -> Self {
160165
AuditLogBuilder {
161166
enabled: AUDIT_LOGGER.is_some(),
162-
start_time: Utc::now(),
163-
stream: String::default(),
164167
actor: None,
165168
request: None,
166169
response: None,
@@ -169,91 +172,159 @@ impl Default for AuditLogBuilder {
169172
}
170173

171174
impl AuditLogBuilder {
172-
/// Sets the stream name for the audit log if logger is set
173-
pub fn set_stream_name(mut self, stream: impl Into<String>) -> Self {
174-
if !self.enabled {
175-
return self;
175+
/// Sets the remote host for the audit log
176+
pub fn with_host(mut self, host: impl Into<String>) -> Self {
177+
if self.enabled {
178+
self.actor
179+
.get_or_insert_with(ActorDetails::default)
180+
.remote_host = host.into();
176181
}
177-
self.stream = stream.into();
182+
self
183+
}
178184

185+
/// Sets the username for the audit log
186+
pub fn with_username(mut self, username: impl Into<String>) -> Self {
187+
if self.enabled {
188+
self.actor
189+
.get_or_insert_with(ActorDetails::default)
190+
.username = username.into();
191+
}
179192
self
180193
}
181194

182-
/// Sets the actor details for the audit log if logger is set
183-
pub fn set_actor(
184-
mut self,
185-
host: impl Into<String>,
186-
username: impl Into<String>,
187-
user_agent: impl Into<String>,
188-
auth_method: impl Into<String>,
189-
) -> Self {
190-
if !self.enabled {
191-
return self;
195+
/// Sets the user agent for the audit log
196+
pub fn with_user_agent(mut self, user_agent: impl Into<String>) -> Self {
197+
if self.enabled {
198+
self.actor
199+
.get_or_insert_with(ActorDetails::default)
200+
.user_agent = user_agent.into();
192201
}
193-
self.actor = Some(ActorLog {
194-
remote_host: host.into(),
195-
user_agent: user_agent.into(),
196-
username: username.into(),
197-
authorization_method: auth_method.into(),
198-
});
202+
self
203+
}
199204

205+
/// Sets the authorization method for the audit log
206+
pub fn with_auth_method(mut self, auth_method: impl Into<String>) -> Self {
207+
if self.enabled {
208+
self.actor
209+
.get_or_insert_with(ActorDetails::default)
210+
.authorization_method = auth_method.into();
211+
}
200212
self
201213
}
202214

203-
/// Sets the request details for the audit log if logger is set
204-
pub fn set_request(
205-
mut self,
206-
method: impl Into<String>,
207-
path: impl Into<String>,
208-
protocol: impl Into<String>,
209-
headers: impl IntoIterator<Item = (String, String)>,
210-
) -> Self {
211-
if !self.enabled {
212-
return self;
215+
/// Sets the stream for the request details
216+
pub fn with_stream(mut self, stream: impl Into<String>) -> Self {
217+
if self.enabled {
218+
self.request
219+
.get_or_insert_with(RequestDetails::default)
220+
.stream = stream.into();
213221
}
214-
self.request = Some(RequestLog {
215-
method: method.into(),
216-
path: path.into(),
217-
protocol: protocol.into(),
218-
headers: headers.into_iter().collect(),
219-
});
222+
self
223+
}
220224

225+
/// Sets the request timing details
226+
pub fn with_timing(mut self, start_time: DateTime<Utc>, end_time: DateTime<Utc>) -> Self {
227+
if self.enabled {
228+
let request = self.request.get_or_insert_with(RequestDetails::default);
229+
request.start_time = start_time;
230+
request.end_time = end_time;
231+
}
221232
self
222233
}
223234

224-
/// Sets the response details for the audit log if logger is set
225-
pub fn set_response(mut self, status_code: u16, err: impl Display) -> Self {
226-
if !self.enabled {
227-
return self;
235+
/// Sets the request method details
236+
pub fn with_method(mut self, method: impl Into<String>) -> Self {
237+
if self.enabled {
238+
self.request
239+
.get_or_insert_with(RequestDetails::default)
240+
.method = method.into();
228241
}
229-
let error = err.to_string();
230-
let error = (!error.is_empty()).then_some(error);
231-
self.response = Some(ResponseLog { status_code, error });
242+
self
243+
}
244+
245+
/// Sets the request path
246+
pub fn with_path(mut self, path: impl Into<String>) -> Self {
247+
if self.enabled {
248+
self.request
249+
.get_or_insert_with(RequestDetails::default)
250+
.path = path.into();
251+
}
252+
self
253+
}
254+
255+
/// Sets the request protocol
256+
pub fn with_protocol(mut self, protocol: impl Into<String>) -> Self {
257+
if self.enabled {
258+
self.request
259+
.get_or_insert_with(RequestDetails::default)
260+
.protocol = protocol.into();
261+
}
262+
self
263+
}
264+
265+
/// Sets the request headers
266+
pub fn with_headers(mut self, headers: impl IntoIterator<Item = (String, String)>) -> Self {
267+
if self.enabled {
268+
self.request
269+
.get_or_insert_with(RequestDetails::default)
270+
.headers = headers.into_iter().collect();
271+
}
272+
self
273+
}
232274

275+
/// Sets the response status code
276+
pub fn with_status(mut self, status_code: u16) -> Self {
277+
if self.enabled {
278+
self.response
279+
.get_or_insert_with(ResponseDetails::default)
280+
.status_code = status_code;
281+
}
282+
self
283+
}
284+
285+
/// Sets the response error if any
286+
pub fn with_error(mut self, err: impl Display) -> Self {
287+
if self.enabled {
288+
let error = err.to_string();
289+
if !error.is_empty() {
290+
self.response
291+
.get_or_insert_with(ResponseDetails::default)
292+
.error = Some(error);
293+
}
294+
}
233295
self
234296
}
235297

236298
/// Sends the audit log to the logging server if configured
237299
pub async fn send(self) {
300+
// ensures that we don't progress if logger is not enabled
301+
if !self.enabled {
302+
return;
303+
}
304+
305+
// build the audit log
238306
let AuditLogBuilder {
239-
start_time,
240-
stream,
241307
actor,
242308
request,
243309
response,
244310
..
245311
} = self;
246-
let Some(logger) = AUDIT_LOGGER.as_ref() else {
247-
return;
248-
};
312+
313+
// get the logger
314+
let logger = AUDIT_LOGGER.as_ref().unwrap();
315+
316+
// build the audit log
317+
let now = Utc::now();
249318
let audit_log = AuditLog {
250-
version: AuditLogVersion::V1,
251-
parseable_version: current().released_version.to_string(),
252-
deployment_id: get_metadata().await.unwrap().deployment_id,
253-
audit_id: Ulid::new(),
254-
start_time,
255-
end_time: Utc::now(),
256-
stream,
319+
audit: AuditDetails {
320+
version: AuditLogVersion::V1,
321+
id: Ulid::new(),
322+
generated_at: now,
323+
},
324+
parseable_server: ServerDetails {
325+
version: current().released_version.to_string(),
326+
deployment_id: Ulid::new(),
327+
},
257328
actor: actor.unwrap_or_default(),
258329
request: request.unwrap_or_default(),
259330
response: response.unwrap_or_default(),

0 commit comments

Comments
 (0)