Skip to main content

cloudiful_redactor/
service.rs

1use anyhow::{Context, Result};
2
3use crate::{
4    InputKind, RedactionArtifact, RedactionSession, Redactor, RestoreResult,
5    decrypt_session_from_str, encrypt_session_to_string, ensure_restore_valid,
6    inspect_encrypted_session,
7};
8
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct EncryptedRedactionArtifact {
11    pub artifact: RedactionArtifact,
12    pub encrypted_session: String,
13    pub session_summary: crate::SessionSummary,
14}
15
16pub fn redact_text_artifact(
17    redactor: &Redactor,
18    text: &str,
19    input_kind: InputKind,
20) -> Result<RedactionArtifact> {
21    redactor
22        .redact_artifact_with_input_kind(text, input_kind)
23        .map_err(anyhow::Error::new)
24}
25
26pub fn redact_text_artifact_with_source(
27    redactor: &Redactor,
28    text: &str,
29    input_kind: InputKind,
30    source_path: &str,
31) -> Result<RedactionArtifact> {
32    redactor
33        .redact_artifact_with_input_kind_and_source(text, input_kind, Some(source_path))
34        .map_err(anyhow::Error::new)
35}
36
37pub fn redact_text_with_encrypted_session(
38    redactor: &Redactor,
39    text: &str,
40    input_kind: InputKind,
41    passphrase: &str,
42) -> Result<EncryptedRedactionArtifact> {
43    let artifact =
44        redact_text_artifact(redactor, text, input_kind).context("failed to redact text input")?;
45    let encrypted_session = encrypt_session_to_string(&artifact.session, passphrase)
46        .context("failed to encrypt redaction session")?;
47    let session_summary = inspect_encrypted_session(&encrypted_session)
48        .context("failed to inspect encrypted session")?;
49
50    Ok(EncryptedRedactionArtifact {
51        artifact,
52        encrypted_session,
53        session_summary,
54    })
55}
56
57pub fn redact_text_with_encrypted_session_and_source(
58    redactor: &Redactor,
59    text: &str,
60    input_kind: InputKind,
61    source_path: &str,
62    passphrase: &str,
63) -> Result<EncryptedRedactionArtifact> {
64    let artifact = redact_text_artifact_with_source(redactor, text, input_kind, source_path)
65        .context("failed to redact text input")?;
66    let encrypted_session = encrypt_session_to_string(&artifact.session, passphrase)
67        .context("failed to encrypt redaction session")?;
68    let session_summary = inspect_encrypted_session(&encrypted_session)
69        .context("failed to inspect encrypted session")?;
70
71    Ok(EncryptedRedactionArtifact {
72        artifact,
73        encrypted_session,
74        session_summary,
75    })
76}
77
78pub fn decrypt_redaction_session(
79    encrypted_session: &str,
80    passphrase: &str,
81) -> Result<RedactionSession> {
82    decrypt_session_from_str(encrypted_session, passphrase)
83        .context("failed to decrypt provided session")
84}
85
86pub fn restore_text_from_encrypted_session(
87    redactor: &Redactor,
88    text: &str,
89    encrypted_session: &str,
90    passphrase: &str,
91) -> Result<RestoreResult> {
92    let session = decrypt_redaction_session(encrypted_session, passphrase)?;
93    let restored = redactor.restore_text(text, &session);
94    ensure_restore_valid(&restored)?;
95    Ok(restored)
96}
97
98#[cfg(test)]
99mod tests {
100    use super::{
101        redact_text_artifact, redact_text_with_encrypted_session,
102        restore_text_from_encrypted_session,
103    };
104    use crate::{InputKind, RedactionPolicy, RedactorBuilder};
105    use crate::types::FindingKind;
106
107    fn full_redactor() -> crate::Redactor {
108        RedactorBuilder::new()
109            .with_redaction_policy(
110                RedactionPolicy::default()
111                    .with_kind(FindingKind::Domain, true)
112                    .with_kind(FindingKind::Secret, true)
113                    .with_kind(FindingKind::Url, true),
114            )
115            .build()
116    }
117
118    #[test]
119    fn encrypted_redaction_matches_plain_artifact_output() {
120        let redactor = full_redactor();
121        let text = "host=service.example.com secret=EJ2QEVC6AKELW0k2kkVY4NgGKONC";
122        let plain = redact_text_artifact(&redactor, text, InputKind::Text).expect("plain");
123        let encrypted =
124            redact_text_with_encrypted_session(&redactor, text, InputKind::Text, "pass")
125                .expect("encrypted");
126
127        assert_eq!(
128            encrypted.artifact.result.redacted_text,
129            plain.result.redacted_text
130        );
131        assert_eq!(
132            encrypted.artifact.session.redacted_text,
133            plain.session.redacted_text
134        );
135    }
136
137    #[test]
138    fn encrypted_session_restore_round_trips() {
139        let redactor = full_redactor();
140        let text = "host=service.example.com";
141        let encrypted =
142            redact_text_with_encrypted_session(&redactor, text, InputKind::Text, "pass")
143                .expect("encrypted");
144
145        let restored = restore_text_from_encrypted_session(
146            &redactor,
147            &encrypted.artifact.result.redacted_text,
148            &encrypted.encrypted_session,
149            "pass",
150        )
151        .expect("restore");
152
153        assert_eq!(restored.restored_text, text);
154    }
155}