use std::collections::HashMap;
pub const USER_ID_KEY: &str = "x-user-id";
pub const ROLE_KEY: &str = "x-roles";
#[derive(Debug, Clone, Default)]
pub struct Session {
variables: HashMap<String, String>,
}
impl Session {
pub fn new() -> Self {
Self::default()
}
pub fn from_map(variables: HashMap<String, String>) -> Self {
Self { variables }
}
pub fn user_id(&self) -> Option<&str> {
self.get(USER_ID_KEY)
}
pub fn roles(&self) -> Vec<&str> {
let Some(raw) = self.get(ROLE_KEY).or_else(|| self.get("X-Roles")) else {
return Vec::new();
};
raw.split(',')
.map(str::trim)
.filter(|part| !part.is_empty())
.collect()
}
pub fn has_role(&self, role: &str) -> bool {
self.roles().contains(&role)
}
pub fn role(&self) -> Option<&str> {
let raw = self.get(ROLE_KEY).or_else(|| self.get("X-Roles"))?;
let mut parts = raw
.split(',')
.map(str::trim)
.filter(|part| !part.is_empty());
let first = parts.next()?;
if parts.next().is_some() {
None
} else {
Some(first)
}
}
pub fn get(&self, key: &str) -> Option<&str> {
self.variables.get(key).map(|v| v.as_str())
}
pub fn set(&mut self, key: impl Into<String>, value: impl Into<String>) {
self.variables.insert(key.into(), value.into());
}
pub fn has(&self, key: &str) -> bool {
self.variables.contains_key(key)
}
pub fn variables(&self) -> &HashMap<String, String> {
&self.variables
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_session() {
let session = Session::new();
assert_eq!(session.user_id(), None);
assert_eq!(session.role(), None);
assert!(session.roles().is_empty());
assert!(!session.has("anything"));
}
#[test]
fn convenience_identity_keys() {
let mut vars = HashMap::new();
vars.insert(USER_ID_KEY.to_string(), "user-42".to_string());
vars.insert(ROLE_KEY.to_string(), "customer".to_string());
let session = Session::from_map(vars);
assert_eq!(session.user_id(), Some("user-42"));
assert_eq!(session.roles(), vec!["customer"]);
assert_eq!(session.role(), Some("customer"));
assert!(session.has_role("customer"));
assert!(session.has(USER_ID_KEY));
assert!(!session.has("x-admin-secret"));
}
#[test]
fn multi_role_set_is_not_a_singleton() {
let mut session = Session::new();
session.set(ROLE_KEY, "admin,user");
assert_eq!(session.roles(), vec!["admin", "user"]);
assert!(session.has_role("admin"));
assert!(session.has_role("user"));
assert_eq!(session.role(), None);
}
#[test]
fn arbitrary_gateway_keys_via_get() {
let mut session = Session::new();
session.set("x-hasura-user-id", "hasura-user");
assert_eq!(session.get("x-hasura-user-id"), Some("hasura-user"));
assert_eq!(session.user_id(), None); }
#[test]
fn set_and_get() {
let mut session = Session::new();
session.set(ROLE_KEY, "admin");
assert_eq!(session.get(ROLE_KEY), Some("admin"));
assert_eq!(session.roles(), vec!["admin"]);
assert_eq!(session.role(), Some("admin"));
}
}