Skip to main content

bulwark_security/server/
mod.rs

1use crate::request::context::RequestContext;
2use crate::security::decision::DecisionEngine;
3use crate::{BulwarkError, BulwarkResult, Decision};
4
5/// Server
6///
7/// Executor terakhir dalam pipeline Bulwark.
8///
9/// Server **TIDAK** memiliki logika security.
10/// Server **HANYA** menjalankan hasil keputusan dari `DecisionEngine`.
11///
12/// Alur:
13/// RequestContext -> DecisionEngine -> Server
14pub struct Server {
15    decision_engine: DecisionEngine,
16}
17
18impl Server {
19    /// Membuat server baru dengan `DecisionEngine`.
20    pub fn new(decision_engine: DecisionEngine) -> Self {
21        Self { decision_engine }
22    }
23
24    /// Menangani satu request berdasarkan hasil DecisionEngine.
25    ///
26    /// ## Kontrak API (STABLE sejak v0.3.0)
27    ///
28    /// - Jika request **diizinkan** (`Allow` atau `Log`),
29    ///   fungsi mengembalikan `Ok(())`.
30    ///
31    /// - Jika request **diblokir**,
32    ///   fungsi **SELALU** mengembalikan `Err(BulwarkError::Blocked)`.
33    ///
34    /// - Isi pesan error **TIDAK dijamin stabil**
35    ///   dan **BUKAN bagian dari API publik**.
36    pub fn handle(&self, ctx: &RequestContext) -> BulwarkResult<()> {
37        match self.decision_engine.decide(ctx) {
38            Ok(Decision::Allow) => Ok(()),
39            Ok(Decision::Log) => Ok(()),
40
41            // Secara desain seharusnya tidak terjadi
42            // karena DecisionEngine sudah memetakan Block ke Err,
43            // tetapi tetap ditangani untuk exhaustiveness.
44            Ok(Decision::Block) => Err(BulwarkError::blocked("request blocked by decision")),
45
46            Err(BulwarkError::Blocked { .. }) => Err(BulwarkError::blocked(
47                // ⚠️ Message is NOT part of the public API
48                "request blocked by bulwark",
49            )),
50
51            Err(err) => Err(err),
52        }
53    }
54}