use std::collections::HashSet; use self::admin::Admin; use crate::models::{client::Client, user::User}; mod admin; /// The action which was attempted on a resource pub enum Action { Create(T), Read(T), Update(T, T), Delete(T), } trait ScopeSuperSet { fn is_superset_of(&self, other: &Self) -> bool; } trait Scope: ToString { /// Parse a scope of the format: `{Scope::NAME}:{modifiers}` fn parse_modifiers(modifiers: &str) -> Result> where Self: Sized; /// Returns `true` if and only if the given `user` is allowed to take the /// given `action` with this scope fn has_user_permission(&self, user: &User, action: &Action) -> bool; // Returns `true` if and only if the given `user` is allowed to take the /// given `action` with this scope fn has_client_permission(&self, user: &User, action: &Action) -> bool; } pub struct ParseScopeError { scope: Box, error: ParseScopeErrorType, } impl ParseScopeError { fn invalid_type(scope: &str, scope_type: &str) -> Self { let scope = scope.into(); let error = ParseScopeErrorType::InvalidType(scope_type.into()); Self { scope, error } } } pub enum ParseScopeErrorType { InvalidType(Box), InvalidModifiers(Box), } fn parse_scope(scope: &str) -> Result, ParseScopeError> { let mut split = scope.split(':'); let scope_type = split.next().unwrap(); let _modifiers: String = split.collect(); match scope_type { "admin" => Ok(Box::new(Admin)), _ => Err(ParseScopeError::invalid_type(scope, scope_type)), } } fn parse_scopes(scopes: &str) -> Result>, ParseScopeError> { scopes .split_whitespace() .map(|scope| parse_scope(scope)) .collect() } fn parse_scopes_errors( results: &[Result, ParseScopeError>], ) -> Vec<&ParseScopeError> { let mut errors = Vec::with_capacity(results.len()); for result in results { if let Err(pse) = result { errors.push(pse) } } errors } /// Returns `true` if and only if all values in `left_scopes` are contained in /// `right_scopes`. pub fn is_subset_of(left_scopes: &str, right_scopes: &str) -> bool { let right_scopes: HashSet<&str> = right_scopes.split_whitespace().collect(); for scope in left_scopes.split_whitespace() { if !right_scopes.contains(scope) { return false; } } true } pub fn has_user_permission( user: User, action: Action, client_scopes: &str, ) -> Result { let scopes = parse_scopes(client_scopes)?; for scope in scopes { if scope.has_user_permission(&user, &action) { return Ok(true); } } Ok(false) } pub fn has_client_permission( user: User, action: Action, client_scopes: &str, ) -> Result { let scopes = parse_scopes(client_scopes)?; for scope in scopes { if scope.has_client_permission(&user, &action) { return Ok(true); } } Ok(false) }