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}