bucketwarden-server 0.1.0

BucketWarden storage server runtime.
Documentation
use super::*;

impl BucketWarden {
    pub fn console_api_audit_events(
        &mut self,
        access_key_id: &str,
        query: ConsoleApiAuditQuery,
    ) -> Result<ConsoleApiAuditEvents, RuntimeError> {
        let principal = self.console_api_principal(access_key_id)?;
        self.require_operator_action(&principal, OperatorAction::ReadAudit, "*", "ui:GetAudit")?;
        let outcome = query
            .outcome
            .as_deref()
            .map(parse_audit_outcome)
            .transpose()?;
        let filter = bucketwarden_audit::AuditFilter {
            subject: query.subject.as_deref(),
            action: query.action.as_deref(),
            resource: query.resource.as_deref(),
            outcome,
            min_sequence: query.min_sequence,
            max_sequence: query.max_sequence,
        };
        let mut events = self
            .audit
            .filter_events(&filter)
            .into_iter()
            .cloned()
            .collect::<Vec<_>>();
        events.retain(|event| {
            matches_text(
                &[
                    &event.subject,
                    &event.action,
                    &event.resource,
                    &format!("{:?}", event.outcome),
                    event.detail.as_deref().unwrap_or_default(),
                ],
                &query.q,
            )
        });
        match query.sort.as_deref().unwrap_or("-sequence") {
            "sequence" => events.sort_by_key(|event| event.sequence),
            "-sequence" => events.sort_by(|left, right| right.sequence.cmp(&left.sequence)),
            "subject" => events.sort_by(|left, right| left.subject.cmp(&right.subject)),
            "-subject" => events.sort_by(|left, right| right.subject.cmp(&left.subject)),
            "action" => events.sort_by(|left, right| left.action.cmp(&right.action)),
            "-action" => events.sort_by(|left, right| right.action.cmp(&left.action)),
            "resource" => events.sort_by(|left, right| left.resource.cmp(&right.resource)),
            "-resource" => events.sort_by(|left, right| right.resource.cmp(&left.resource)),
            value => {
                return Err(RuntimeError::InvalidListParameter {
                    name: "sort".to_string(),
                    value: value.to_string(),
                })
            }
        }
        let list_query = ConsoleApiListQuery {
            limit: query.limit,
            offset: query.offset,
            prefix: None,
            q: query.q,
            sort: query.sort,
        };
        let (page, events) = page_vec(events, &list_query, "-sequence")?;
        Ok(ConsoleApiAuditEvents { page, events })
    }

    pub fn console_api_evidence_list(
        &mut self,
        access_key_id: &str,
        query: ConsoleApiListQuery,
    ) -> Result<ConsoleApiEvidenceList, RuntimeError> {
        let principal = self.console_api_principal(access_key_id)?;
        let report = self.ops_evidence_export_report(&principal, None)?;
        let mut evidence = evidence_items(&report);
        match query.sort.as_deref().unwrap_or("name") {
            "name" => evidence.sort_by(|left, right| left.name.cmp(&right.name)),
            "-name" => evidence.sort_by(|left, right| right.name.cmp(&left.name)),
            "bytes" => evidence.sort_by_key(|item| item.bytes),
            "-bytes" => evidence.sort_by(|left, right| right.bytes.cmp(&left.bytes)),
            "record_count" => evidence.sort_by_key(|item| item.record_count),
            "-record_count" => {
                evidence.sort_by(|left, right| right.record_count.cmp(&left.record_count))
            }
            value => {
                return Err(RuntimeError::InvalidListParameter {
                    name: "sort".to_string(),
                    value: value.to_string(),
                })
            }
        }
        evidence.retain(|item| matches_text(&[&item.name, &item.content_type], &query.q));
        let (page, evidence) = page_vec(evidence, &query, "name")?;
        Ok(ConsoleApiEvidenceList { page, evidence })
    }

    pub fn console_api_evidence_export(
        &mut self,
        access_key_id: &str,
    ) -> Result<ConsoleApiEvidenceExport, RuntimeError> {
        let principal = self.console_api_principal(access_key_id)?;
        let report = self.ops_evidence_export_report(&principal, None)?;
        Ok(ConsoleApiEvidenceExport {
            filename: format!(
                "bucketwarden-evidence-{}.json",
                report.generated_at_epoch_seconds
            ),
            content_type: "application/json".to_string(),
            report,
        })
    }
}

fn parse_audit_outcome(value: &str) -> Result<AuditOutcome, RuntimeError> {
    match value {
        "Allowed" | "allowed" => Ok(AuditOutcome::Allowed),
        "Denied" | "denied" => Ok(AuditOutcome::Denied),
        "Failed" | "failed" => Ok(AuditOutcome::Failed),
        _ => Err(RuntimeError::InvalidListParameter {
            name: "outcome".to_string(),
            value: value.to_string(),
        }),
    }
}

fn evidence_items(report: &OpsEvidenceExportReport) -> Vec<ConsoleApiEvidenceItem> {
    vec![
        item("snapshot_json", &report.snapshot_json, 1),
        item(
            "audit_json_lines",
            &report.audit_json_lines,
            report.audit_event_count,
        ),
        item(
            "notification_json_lines",
            &report.notification_json_lines,
            report.notification_event_count,
        ),
        item(
            "replication_json_lines",
            &report.replication_json_lines,
            report.replication_record_count,
        ),
        item(
            "storage_backend_evidence_json",
            &report.storage_backend_evidence_json,
            1,
        ),
        item(
            "replication_strategy_evidence_json",
            &report.replication_strategy_evidence_json,
            1,
        ),
        item(
            "metadata_architecture_evidence_json",
            &report.metadata_architecture_evidence_json,
            1,
        ),
    ]
}

fn item(name: &str, body: &str, record_count: usize) -> ConsoleApiEvidenceItem {
    ConsoleApiEvidenceItem {
        name: name.to_string(),
        content_type: if name.ends_with("_lines") {
            "application/jsonl".to_string()
        } else {
            "application/json".to_string()
        },
        bytes: body.len(),
        record_count,
    }
}