diff options
Diffstat (limited to 'src/api/access.rs')
| -rw-r--r-- | src/api/access.rs | 44 |
1 files changed, 43 insertions, 1 deletions
diff --git a/src/api/access.rs b/src/api/access.rs index 63aa87b..29d796a 100644 --- a/src/api/access.rs +++ b/src/api/access.rs @@ -47,6 +47,33 @@ impl Access { } } + /// As capabilities: `Caps` as they are; in a swarm without a registry, what every + /// user may do there (the swarm's data, protocol data to read, and to write for + /// `--protocol-writers`), to narrow by a token's limit. + pub(crate) fn caps(&self, state: &AppState, user: &str) -> Caps { + use crate::access::Capability; + match self { + Self::Caps(caps) => caps.clone(), + Self::Everything => { + let protocol: &[&str] = if state.protocol_writers.contains(user) { + &["read", "write"] + } else { + &["read"] + }; + let cap = |path: Vec<String>, rights: &[&str]| Capability { + path, + rights: rights.iter().map(|right| (*right).to_owned()).collect(), + by: String::new(), + since: 0, + }; + Caps::new(vec![ + cap(Vec::new(), &["read", "write"]), + cap(meta::path(&[]), protocol), + ]) + } + } + } + pub(crate) fn visible(&self, path: &[String]) -> Visible { match self { Self::Everything => Visible::All, @@ -209,6 +236,8 @@ enum Need { Store, /// Looking at this node (its figures, peers, dashboard): any capability. Observe, + /// Logging in, refreshing, asking who one is: any registered user. + Auth, /// A right on protocol data. Protocol(Right, Vec<String>), } @@ -224,6 +253,8 @@ fn needed(method: &Method, path: &str) -> Need { let under = |prefix: &str| path == prefix || path.starts_with(&format!("{prefix}/")); if under("/store") || under("/stream") { Need::Store + } else if under("/auth") { + Need::Auth } else if (path == "/peers" && looking) || path == "/admin/stats" || under("/admin/dashboard") { Need::Observe } else if under("/peers") { @@ -288,7 +319,7 @@ pub(crate) async fn authorize( } }; let allowed = match needed(request.method(), request.uri().path()) { - Need::Store => true, + Need::Store | Need::Auth => true, Need::Observe => match &access { Access::Everything => true, Access::Caps(caps) => !caps.is_empty(), @@ -303,6 +334,15 @@ pub(crate) async fn authorize( None, ); } + // A token that says less: only what it allows too. + let access = match request + .extensions() + .get::<super::login::Token>() + .and_then(|token| token.claims.limit.clone()) + { + None => access, + Some(limit) => Access::Caps(access.caps(&state, &user).within(limit)), + }; request.extensions_mut().insert(access); next.run(request).await } @@ -338,6 +378,8 @@ mod tests { let read = |parts: &[&str]| Need::Protocol(Right::Read, meta::path(parts)); let write = |parts: &[&str]| Need::Protocol(Right::Write, meta::path(parts)); assert_eq!(needed(&Method::GET, "/store"), Need::Store); + assert_eq!(needed(&Method::POST, "/auth/login"), Need::Auth); + assert_eq!(needed(&Method::GET, "/auth/whoami"), Need::Auth); assert_eq!(needed(&Method::PUT, "/store/a/b"), Need::Store); assert_eq!(needed(&Method::GET, "/stream/a"), Need::Store); assert_eq!(needed(&Method::GET, "/storehouse"), read(&[])); |
