use crate::SecurityContext;
use crate::pre_authorize::SecurityExpression;
use std::future::Future;
use std::pin::Pin;
pub trait PostAuthorize<T> {
fn check_post_authorize(
&self,
context: &SecurityContext,
return_value: &T,
) -> Pin<Box<dyn Future<Output = bool> + Send>>;
}
#[derive(Debug, Clone)]
pub struct PostAuthorizeOptions {
pub expressions: Vec<SecurityExpression>,
pub require_all: bool,
pub return_object_filter: Option<String>,
}
impl PostAuthorizeOptions {
pub fn new() -> Self {
Self {
expressions: Vec::new(),
require_all: true,
return_object_filter: None,
}
}
pub fn add_expression(mut self, expr: SecurityExpression) -> Self {
self.expressions.push(expr);
self
}
pub fn add_expression_string(mut self, expr: impl Into<String>) -> Self {
let parsed = SecurityExpression::parse(&expr.into());
self.expressions.extend(parsed);
self
}
pub fn return_object_filter(mut self, filter: impl Into<String>) -> Self {
self.return_object_filter = Some(filter.into());
self
}
pub fn require_all(mut self, require_all: bool) -> Self {
self.require_all = require_all;
self
}
pub async fn evaluate(&self, context: &SecurityContext) -> bool {
if self.expressions.is_empty() && self.return_object_filter.is_none() {
return true;
}
if self.require_all {
for expr in &self.expressions {
if !expr.evaluate(context).await {
return false;
}
}
true
} else {
for expr in &self.expressions {
if expr.evaluate(context).await {
return true;
}
}
false
}
}
}
impl Default for PostAuthorizeOptions {
fn default() -> Self {
Self::new()
}
}
pub async fn check_post_authorize(
context: &SecurityContext,
expression: &str,
) -> Result<bool, crate::SecurityError> {
let options = PostAuthorizeOptions::new().add_expression_string(expression);
Ok(options.evaluate(context).await)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_post_authorize_options_builder() {
let opts = PostAuthorizeOptions::new()
.add_expression_string("hasRole('ADMIN')")
.return_object_filter("returnObject.owner == 'admin'");
assert_eq!(opts.expressions.len(), 1);
assert!(opts.return_object_filter.is_some());
}
#[test]
fn test_post_authorize_options_default() {
let opts = PostAuthorizeOptions::default();
assert!(opts.expressions.is_empty());
assert!(opts.require_all);
assert!(opts.return_object_filter.is_none());
}
#[tokio::test]
async fn test_post_authorize_empty_expressions() {
let context = SecurityContext::new();
let opts = PostAuthorizeOptions::new();
assert!(opts.evaluate(&context).await);
}
#[tokio::test]
async fn test_post_authorize_check() {
let context = SecurityContext::new();
let result = check_post_authorize(&context, "hasRole('ADMIN')").await;
assert!(result.is_ok());
assert!(!result.unwrap());
}
}