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_with_encrypted_session(
27    redactor: &Redactor,
28    text: &str,
29    input_kind: InputKind,
30    passphrase: &str,
31) -> Result<EncryptedRedactionArtifact> {
32    let artifact =
33        redact_text_artifact(redactor, text, input_kind).context("failed to redact text input")?;
34    let encrypted_session = encrypt_session_to_string(&artifact.session, passphrase)
35        .context("failed to encrypt redaction session")?;
36    let session_summary = inspect_encrypted_session(&encrypted_session)
37        .context("failed to inspect encrypted session")?;
38
39    Ok(EncryptedRedactionArtifact {
40        artifact,
41        encrypted_session,
42        session_summary,
43    })
44}
45
46pub fn decrypt_redaction_session(
47    encrypted_session: &str,
48    passphrase: &str,
49) -> Result<RedactionSession> {
50    decrypt_session_from_str(encrypted_session, passphrase)
51        .context("failed to decrypt provided session")
52}
53
54pub fn restore_text_from_encrypted_session(
55    redactor: &Redactor,
56    text: &str,
57    encrypted_session: &str,
58    passphrase: &str,
59) -> Result<RestoreResult> {
60    let session = decrypt_redaction_session(encrypted_session, passphrase)?;
61    let restored = redactor.restore_text(text, &session);
62    ensure_restore_valid(&restored)?;
63    Ok(restored)
64}
65
66#[cfg(test)]
67mod tests {
68    use super::{
69        redact_text_artifact, redact_text_with_encrypted_session,
70        restore_text_from_encrypted_session,
71    };
72    use crate::{InputKind, RedactorBuilder};
73
74    #[test]
75    fn encrypted_redaction_matches_plain_artifact_output() {
76        let redactor = RedactorBuilder::new().build();
77        let text = "host=service.example.com secret=EJ2QEVC6AKELW0k2kkVY4NgGKONC";
78        let plain = redact_text_artifact(&redactor, text, InputKind::Text).expect("plain");
79        let encrypted =
80            redact_text_with_encrypted_session(&redactor, text, InputKind::Text, "pass")
81                .expect("encrypted");
82
83        assert_eq!(
84            encrypted.artifact.result.redacted_text,
85            plain.result.redacted_text
86        );
87        assert_eq!(
88            encrypted.artifact.session.redacted_text,
89            plain.session.redacted_text
90        );
91    }
92
93    #[test]
94    fn encrypted_session_restore_round_trips() {
95        let redactor = RedactorBuilder::new().build();
96        let text = "host=service.example.com";
97        let encrypted =
98            redact_text_with_encrypted_session(&redactor, text, InputKind::Text, "pass")
99                .expect("encrypted");
100
101        let restored = restore_text_from_encrypted_session(
102            &redactor,
103            &encrypted.artifact.result.redacted_text,
104            &encrypted.encrypted_session,
105            "pass",
106        )
107        .expect("restore");
108
109        assert_eq!(restored.restored_text, text);
110    }
111}