Skip to content

Commit

Permalink
commenting: re-add
Browse files Browse the repository at this point in the history
Just on specific events, not alert groups.

Also adds commenting and history for SQLite.
  • Loading branch information
jasonish committed Jun 4, 2024
1 parent f4982b3 commit fa9ad2c
Show file tree
Hide file tree
Showing 17 changed files with 418 additions and 202 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ bcrypt = "0.15.0"
bytes = "1.5.0"
clap = { version = "4.5.4", features = ["env", "derive", "color"] }

sqlx = { git = "https://github.com/launchbadge/sqlx", default-features = true, features = ["runtime-tokio", "sqlite", "tls-rustls"] }
sqlx = { git = "https://github.com/launchbadge/sqlx", default-features = true, features = ["runtime-tokio", "sqlite", "tls-rustls", "json"] }

filetime = "0.2.23"
glob = "0.3.1"
Expand Down
3 changes: 3 additions & 0 deletions resources/sqlite/migrations/0005_EventComments.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ALTER TABLE events
ADD COLUMN history JSON
default '[]';
81 changes: 15 additions & 66 deletions src/elastic/eventrepo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,12 @@ use super::query_string_query;
use super::Client;
use super::ElasticError;
use super::HistoryEntry;
use super::ACTION_ARCHIVED;
use super::ACTION_COMMENT;
use super::HistoryEntryBuilder;
use super::TAG_ESCALATED;
use crate::datetime;
use crate::datetime::DateTime;
use crate::elastic::importer::ElasticEventSink;
use crate::elastic::request::exists_filter;
use crate::elastic::{
request, ElasticResponse, ACTION_DEESCALATED, ACTION_ESCALATED, TAGS_ARCHIVED, TAGS_ESCALATED,
TAG_ARCHIVED,
};
use crate::elastic::{request, ElasticResponse, TAGS_ARCHIVED, TAGS_ESCALATED, TAG_ARCHIVED};
use crate::eventrepo::{self, DatastoreError};
use crate::queryparser;
use crate::queryparser::QueryElement;
Expand Down Expand Up @@ -292,12 +287,7 @@ impl ElasticEventRepo {
}
}
});
let action = HistoryEntry {
username: "anonymous".to_string(),
timestamp: datetime::DateTime::now().to_elastic(),
action: ACTION_ARCHIVED.to_string(),
comment: None,
};
let action = HistoryEntryBuilder::new_archive().build();
self.add_tag_by_query(query, TAG_ARCHIVED, &action).await
}

Expand All @@ -309,12 +299,7 @@ impl ElasticEventRepo {
}
}
});
let action = HistoryEntry {
username: "anonymous".to_string(),
timestamp: datetime::DateTime::now().to_elastic(),
action: ACTION_ESCALATED.to_string(),
comment: None,
};
let action = HistoryEntryBuilder::new_escalate().build();
self.add_tag_by_query(query, TAG_ESCALATED, &action).await
}

Expand All @@ -326,12 +311,7 @@ impl ElasticEventRepo {
}
}
});
let action = HistoryEntry {
username: "anonymous".to_string(),
timestamp: datetime::DateTime::now().to_elastic(),
action: ACTION_DEESCALATED.to_string(),
comment: None,
};
let action = HistoryEntryBuilder::new_deescalate().build();
self.remove_tag_by_query(query, TAG_ESCALATED, &action)
.await
}
Expand All @@ -340,7 +320,7 @@ impl ElasticEventRepo {
&self,
event_id: &str,
comment: String,
username: &str,
session: Arc<Session>,
) -> Result<(), DatastoreError> {
let query = json!({
"bool": {
Expand All @@ -349,12 +329,10 @@ impl ElasticEventRepo {
}
}
});
let action = HistoryEntry {
username: username.to_string(),
timestamp: datetime::DateTime::now().to_elastic(),
action: ACTION_COMMENT.to_string(),
comment: Some(comment),
};
let action = HistoryEntryBuilder::new_comment()
.username(session.username.clone())
.comment(comment)
.build();
self.add_tags_by_query(query, &[], &action).await
}

Expand Down Expand Up @@ -487,12 +465,7 @@ impl ElasticEventRepo {
&self,
alert_group: api::AlertGroupSpec,
) -> Result<(), DatastoreError> {
let action = HistoryEntry {
username: "anonymous".to_string(),
timestamp: DateTime::now().to_elastic(),
action: ACTION_ARCHIVED.to_string(),
comment: None,
};
let action = HistoryEntryBuilder::new_archive().build();
self.add_tags_by_alert_group(alert_group, &TAGS_ARCHIVED, &action)
.await
}
Expand All @@ -502,13 +475,9 @@ impl ElasticEventRepo {
alert_group: api::AlertGroupSpec,
session: Arc<Session>,
) -> Result<(), DatastoreError> {
let action = HistoryEntry {
username: session.username().to_string(),
//username: "anonymous".to_string(),
timestamp: DateTime::now().to_elastic(),
action: ACTION_ESCALATED.to_string(),
comment: None,
};
let action = HistoryEntryBuilder::new_escalate()
.username(session.username.clone())
.build();
self.add_tags_by_alert_group(alert_group, &TAGS_ESCALATED, &action)
.await
}
Expand All @@ -517,12 +486,7 @@ impl ElasticEventRepo {
&self,
alert_group: api::AlertGroupSpec,
) -> Result<(), DatastoreError> {
let action = HistoryEntry {
username: "anonymous".to_string(),
timestamp: DateTime::now().to_elastic(),
action: ACTION_DEESCALATED.to_string(),
comment: None,
};
let action = HistoryEntryBuilder::new_deescalate().build();
self.remove_tags_by_alert_group(alert_group, &TAGS_ESCALATED, &action)
.await
}
Expand Down Expand Up @@ -629,21 +593,6 @@ impl ElasticEventRepo {
Ok(response)
}

pub async fn comment_by_alert_group(
&self,
alert_group: api::AlertGroupSpec,
comment: String,
username: &str,
) -> Result<(), DatastoreError> {
let entry = HistoryEntry {
username: username.to_string(),
timestamp: DateTime::now().to_elastic(),
action: ACTION_COMMENT.to_string(),
comment: Some(comment),
};
self.add_tags_by_alert_group(alert_group, &[], &entry).await
}

async fn get_earliest_timestamp(
&self,
) -> Result<Option<crate::datetime::DateTime>, DatastoreError> {
Expand Down
4 changes: 2 additions & 2 deletions src/elastic/eventrepo/alerts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: MIT

use axum::{response::IntoResponse, Json};
use tracing::{error, info, warn};
use tracing::{debug, error, warn};

use crate::{
elastic::{AlertQueryOptions, ElasticResponse},
Expand Down Expand Up @@ -163,7 +163,7 @@ impl ElasticEventRepo {
return Err(DatastoreError::ElasticSearchError(error.first_reason()));
}

info!(
debug!(
"Elasticsearch alert query took {:?}, es-time: {}, response-size: {}",
start.elapsed(),
response.took,
Expand Down
91 changes: 82 additions & 9 deletions src/elastic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,28 @@ pub mod eventrepo;
pub mod importer;
pub mod request;

pub const ACTION_ARCHIVED: &str = "archived";
pub const ACTION_ESCALATED: &str = "escalated";
pub const ACTION_DEESCALATED: &str = "de-escalated";
pub const ACTION_COMMENT: &str = "comment";
pub(crate) const TAG_ESCALATED: &str = "evebox.escalated";
pub(crate) const TAGS_ESCALATED: [&str; 1] = [TAG_ESCALATED];
pub(crate) const TAG_ARCHIVED: &str = "evebox.archived";
pub(crate) const TAGS_ARCHIVED: [&str; 1] = [TAG_ARCHIVED];

pub(crate) enum HistoryType {
Archived,
Escalated,
Deescalated,
Comment,
}

pub const TAG_ESCALATED: &str = "evebox.escalated";
pub const TAGS_ESCALATED: [&str; 1] = [TAG_ESCALATED];
pub const TAG_ARCHIVED: &str = "evebox.archived";
pub const TAGS_ARCHIVED: [&str; 1] = [TAG_ARCHIVED];
impl std::fmt::Display for HistoryType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
HistoryType::Archived => write!(f, "archived"),
HistoryType::Escalated => write!(f, "escalated"),
HistoryType::Deescalated => write!(f, "de-escalated"),
HistoryType::Comment => write!(f, "comment"),
}
}
}

#[derive(Debug, Error)]
pub enum ElasticError {
Expand All @@ -50,13 +63,73 @@ pub(crate) struct AlertQueryOptions {

#[derive(Serialize)]
pub(crate) struct HistoryEntry {
pub username: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub username: Option<String>,
pub timestamp: String,
pub action: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>,
}

impl HistoryEntry {
pub(crate) fn to_json(&self) -> String {
serde_json::to_string(self).unwrap()
}
}

pub(crate) struct HistoryEntryBuilder {
timestamp: DateTime,
action: String,
username: Option<String>,
comment: Option<String>,
}

impl HistoryEntryBuilder {
fn new(action: HistoryType) -> Self {
Self {
action: action.to_string(),
timestamp: DateTime::now(),
username: None,
comment: None,
}
}

pub fn new_archive() -> Self {
Self::new(HistoryType::Archived)
}

pub fn new_escalate() -> Self {
Self::new(HistoryType::Escalated)
}

pub fn new_deescalate() -> Self {
Self::new(HistoryType::Deescalated)
}

pub fn new_comment() -> Self {
Self::new(HistoryType::Comment)
}

pub fn username(mut self, username: Option<impl Into<String>>) -> Self {
self.username = username.map(|u| u.into());
self
}

pub fn comment(mut self, comment: impl Into<String>) -> Self {
self.comment = Some(comment.into());
self
}

pub fn build(self) -> HistoryEntry {
HistoryEntry {
username: self.username,
timestamp: self.timestamp.to_rfc3339_utc(),
action: self.action,
comment: self.comment,
}
}
}

pub fn query_string_query(query_string: &str) -> serde_json::Value {
let escaped = query_string
.replace('\\', "\\\\")
Expand Down
11 changes: 10 additions & 1 deletion src/eve/eve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ impl Eve for serde_json::Value {
}
}

pub fn add_evebox_metadata(event: &mut serde_json::Value, filename: Option<String>) {
pub(crate) fn add_evebox_metadata(event: &mut serde_json::Value, filename: Option<String>) {
if let serde_json::Value::Null = event["evebox"] {
event["evebox"] = serde_json::json!({});
}
Expand All @@ -31,3 +31,12 @@ pub fn add_evebox_metadata(event: &mut serde_json::Value, filename: Option<Strin
// Add a tags object.
event["tags"] = serde_json::json!([]);
}

pub(crate) fn ensure_has_history(event: &mut serde_json::Value) {
if let serde_json::Value::Null = &event["evebox"] {
event["evebox"] = json!({});
}
if let serde_json::Value::Null = &event["evebox"]["history"] {
event["evebox"]["history"] = json!([]);
}
}
28 changes: 8 additions & 20 deletions src/eventrepo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ pub enum DatastoreError {
SerdeJsonError(#[from] serde_json::Error),
#[error("time parser error: {0}")]
DateTimeParse(#[from] crate::datetime::ParseError),

#[error("failed to parse number: {0}")]
ParseIntError(#[from] std::num::ParseIntError),
#[error("sqlx: {0}")]
SqlxError(#[from] sqlx::Error),

Expand Down Expand Up @@ -139,33 +140,19 @@ impl EventRepo {
) -> Result<(), DatastoreError> {
match self {
EventRepo::Elastic(ds) => ds.escalate_by_alert_group(alert_group, session).await,
EventRepo::SQLite(ds) => ds.escalate_by_alert_group(alert_group).await,
EventRepo::SQLite(ds) => ds.escalate_by_alert_group(session, alert_group).await,
_ => Err(DatastoreError::Unimplemented),
}
}

pub async fn deescalate_by_alert_group(
&self,
session: Arc<Session>,
alert_group: api::AlertGroupSpec,
) -> Result<(), DatastoreError> {
match self {
EventRepo::Elastic(ds) => ds.deescalate_by_alert_group(alert_group).await,
EventRepo::SQLite(ds) => ds.deescalate_by_alert_group(alert_group).await,
_ => Err(DatastoreError::Unimplemented),
}
}

pub async fn comment_by_alert_group(
&self,
alert_group: api::AlertGroupSpec,
comment: String,
username: &str,
) -> Result<(), DatastoreError> {
match self {
EventRepo::Elastic(ds) => {
ds.comment_by_alert_group(alert_group, comment, username)
.await
}
EventRepo::SQLite(ds) => ds.deescalate_by_alert_group(session, alert_group).await,
_ => Err(DatastoreError::Unimplemented),
}
}
Expand All @@ -185,10 +172,11 @@ impl EventRepo {
&self,
event_id: &str,
comment: String,
username: &str,
session: Arc<Session>,
) -> Result<(), DatastoreError> {
match self {
EventRepo::Elastic(ds) => ds.comment_event_by_id(event_id, comment, username).await,
EventRepo::Elastic(ds) => ds.comment_event_by_id(event_id, comment, session).await,
EventRepo::SQLite(ds) => ds.comment_event_by_id(event_id, comment, session).await,
_ => Err(DatastoreError::Unimplemented),
}
}
Expand Down
Loading

0 comments on commit fa9ad2c

Please sign in to comment.