#策略 #授权 #RBAC #ACL

allow-me

基于JSON策略定义的身份验证库

2个版本

0.1.1 2021年1月8日
0.1.0 2021年1月8日

#294 in 身份验证

MIT许可证

74KB
1.5K SLoC

Allow-me

Crates.io Docs.rs MIT License CI

一个基于JSON策略定义的身份验证库。

在简单的Identity(I),Operation(O),Resource(R)模型中定义你的授权规则。评估请求是否符合你的策略规则。

入门指南

[dependencies]
allow-me = "0.1"

示例用法

JSON定义

这是一个包含一个声明并对该策略进行评估的请求的简单示例。

let json = r#"{
        "statements": [
            {
                "effect": "allow",
                "identities": [
                    "actor_a"
                ],
                "operations": [
                    "write"
                ],
                "resources": [
                    "resource_1"
                ]
            }
        ]
    }"#;

// Construct the policy.
let policy = PolicyBuilder::from_json(json).build()?;

// Prepare request (e.g. from user input).
let request = Request::new("actor_a", "write", "resource_1")?;

// Evaluate the request.
match policy.evaluate(&request)? {
    Decision::Allowed => println!("Allowed"),
    Decision::Denied => {
        panic!("Denied!")
    }
};

试试看

cargo run --example json

可变规则

以下示例展示了一个允许任何身份读取/写入其自身资源的规则。

let json = r#"{
    "statements": [
        {
            "effect": "allow",
            "identities": [
                "{{any}}"
            ],
            "operations": [
                "read",
                "write"
            ],
            "resources": [
                "/home/{{identity}}/"
            ]
        }
    ]
}"#;

// Construct the policy.
let policy = PolicyBuilder::from_json(json)
    // use "starts with" matching for resources.
    .with_matcher(matcher::StartsWith)
    .with_default_decision(Decision::Denied)
    .build()?;

// Prepare request (e.g. from user input).
let request = Request::new("johndoe", "write", "/home/johndoe/my.resource")?;

// Evaluate the request.
match policy.evaluate(&request)? {
    Decision::Allowed => println!("Allowed"),
    Decision::Denied => {
        panic!("Denied!")
    }
};

试试看

cargo run --example vars

规则顺序

规则的顺序很重要。在存在冲突规则的情况下,第一个规则生效。在下面的示例中,我们允许actor_a写入resource_1,并拒绝写入其他任何内容。请注意,任何其他请求都将被允许(默认决定)。

let json = r#"{
    "statements": [
        {
            "effect": "allow",
            "identities": [
                "actor_a"
            ],
            "operations": [
                "write"
            ],
            "resources": [
                "resource_1"
            ]
        },
        {
            "effect": "deny",
            "identities": [
                "actor_a"
            ],
            "operations": [
                "write"
            ],
            "resources": [
                "{{any}}"
            ]
        }
    ]
}"#;

// Construct the policy.
let policy = PolicyBuilder::from_json(json)
    // default to Allow all requests.
    .with_default_decision(Decision::Allowed)
    .build()?;

// Prepare request (e.g. from user input).
let request = Request::new("actor_a", "write", "resource_1")?;

// Evaluate specific request.
match policy.evaluate(&request)? {
    Decision::Allowed => println!("allowed write resource_1"),
    Decision::Denied => {
        panic!("Denied!")
    }
};

let request = Request::new("actor_a", "write", "some_other_resource")?;

// Everything else denies.
assert_matches!(policy.evaluate(&request), Ok(Decision::Denied));

试试看

cargo run --example order

自定义

库中有几个扩展点

  • ResourceMatcher特质 - 负责执行资源匹配逻辑。
  • Substituter特质 - 你可以添加自定义变量,这些变量可以被替换。
  • Validator特质 - 验证策略定义。如果你的策略规则需要自定义验证。
  • 请求上下文 - 你可以将自定义数据类型与Request关联。对于自定义SubstituterResourceMatcher,实现自定义变量或匹配逻辑非常有用。

ResourceMatcher

自定义ResourceMatcher,实现"以...开始"匹配。

pub struct StartsWith;

impl ResourceMatcher for StartsWith {
    type Context = ();

    fn do_match(&self, _context: &Request<Self::Context>, input: &str, policy: &str) -> bool {
        input.starts_with(policy)
    }
}

Substituter和自定义请求上下文

自定义Substituter,支持{{any}}{{role}}变量。{{role}}变量用请求上下文中的值替换。

// custom context
struct MyContext {
    role: String
};

// custom substituter
struct RoleSubstituter;

impl Substituter for RoleSubstituter {
    type Context = MyContext;

    fn visit_resource(&self, value: &str, context: &Request<Self::Context>) -> Result<String> {
        match context.context() {
            Some(role_context) => {
                let mut result = value.to_owned();
                for variable in VariableIter::new(value) {
                    result = match variable {
                        "{{any}}" => replace(&result, variable, context.resource()),
                        "{{role}}" => replace(&result, variable, &role_context.role),
                        _ => result,
                    };
                }
                Ok(result)
            }
            None => Ok(value.to_owned()),
        }
    }

    ...
}

试试看

cargo run --example customizations

路线图

  • PolicyBuilder的流畅API
  • 正则表达式支持
  • 基准测试

贡献

欢迎所有贡献和评论!当你发现错误或有改进这个crate的想法时,不要害怕打开问题或PR。

依赖项

~2.8–4.5MB
~85K SLoC