Skip to main content

rust_web_server/authz/
mod.rs

1//! Authorization (18.14.0, #32): who the caller is ([`Identity`]), and —
2//! from later tranches — what they may do, as relationship-based access
3//! control (Zanzibar-style tuples and a schema).
4//!
5//! Every authenticator in rws hands the authenticated caller to what runs
6//! after it as one [`Identity`]: `JwtLayer`, `OidcAuth`, `SamlSp`,
7//! `BasicAuthLayer`, `ForwardAuthLayer` (with
8//! [`identity_header`](crate::auth::forward::ForwardAuthLayer::identity_header))
9//! and MCP bearer / OAuth tokens. A handler reads it with
10//! [`Identity::current`]:
11//!
12//! ```rust,no_run
13//! use rust_web_server::app::App;
14//! use rust_web_server::authz::Identity;
15//! use rust_web_server::response::{Response, STATUS_CODE_REASON_PHRASE};
16//!
17//! let app = App::with_state(()).get("/me", |_, _, _, _| match Identity::current() {
18//!     Some(me) => Response::text(STATUS_CODE_REASON_PHRASE.n200_ok, me.subject()),
19//!     None => Response::text(STATUS_CODE_REASON_PHRASE.n401_unauthorized, "who are you?"),
20//! });
21//! ```
22//!
23//! The identity is not a request header: the authenticator runs the rest of
24//! the request inside [`with_identity`], so a client can't send one.
25
26pub(crate) mod config;
27pub(crate) mod identity;
28mod layer;
29pub mod rebac;
30#[cfg(any(feature = "model-sqlite", feature = "model-postgres", feature = "model-mysql"))]
31pub mod sql;
32
33pub use identity::{with_identity, Identity};
34pub use layer::{AuthzLayer, ObjectTemplate};
35#[cfg(any(feature = "model-sqlite", feature = "model-postgres", feature = "model-mysql"))]
36pub use sql::SqlStore;
37pub use rebac::{AuthzError, Authorizer, MemoryStore, ObjectRef, Schema, Subject, Tuple, TupleFilter, TupleStore};
38#[cfg(feature = "http2")]
39pub use identity::scope_identity;
40
41#[cfg(test)]
42pub(crate) mod test_support {
43    use crate::application::Application;
44    use crate::request::Request;
45    use crate::response::{Response, STATUS_CODE_REASON_PHRASE};
46    use crate::server::ConnectionInfo;
47
48    /// Answers with the caller: `<subject> via <source>`, or `anonymous`.
49    pub(crate) struct WhoAmI;
50
51    impl Application for WhoAmI {
52        fn execute(&self, _request: &Request, _connection: &ConnectionInfo) -> Result<Response, String> {
53            let who = super::Identity::current().map_or("anonymous".to_string(), |i| format!("{} via {}", i.subject(), i.source()));
54            Ok(Response::text(STATUS_CODE_REASON_PHRASE.n200_ok, &who))
55        }
56    }
57
58    pub(crate) fn body(response: &Response) -> String {
59        String::from_utf8_lossy(&response.content_range_list.first().map(|c| c.body.clone()).unwrap_or_default()).to_string()
60    }
61}
62
63#[cfg(test)]
64mod tests;