add iam system

add iam store

feat: add crypto crate

introduce decrypt_data and encrypt_data functions

Signed-off-by: bestgopher <84328409@qq.com>
This commit is contained in:
bestgopher
2024-10-14 23:11:49 +08:00
parent 8519a596ec
commit bb2f765e5d
61 changed files with 5319 additions and 64 deletions
+146
View File
@@ -0,0 +1,146 @@
use super::func::InnerFunc;
use ipnetwork::IpNetwork;
use serde::{de::Visitor, Deserialize, Serialize};
use std::{borrow::Cow, collections::HashMap, net::IpAddr};
pub type AddrFunc = InnerFunc<AddrFuncValue>;
impl AddrFunc {
pub(crate) fn evaluate(&self, values: &HashMap<String, Vec<String>>) -> bool {
let rvalues = values.get(self.key.name().as_str()).map(|t| t.iter()).unwrap_or_default();
for r in rvalues {
let Ok(ip) = r.parse::<IpAddr>() else {
return false;
};
for ip_net in self.values.0.iter() {
if ip_net.contains(ip) {
return true;
}
}
}
false
}
}
#[derive(Serialize, Clone)]
#[serde(transparent)]
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
pub struct AddrFuncValue(Vec<IpNetwork>);
impl<'de> Deserialize<'de> for AddrFuncValue {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct AddrFuncValueVisitor;
impl<'d> Visitor<'d> for AddrFuncValueVisitor {
type Value = AddrFuncValue;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("cidr string")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(AddrFuncValue(vec![Self::to_cidr::<E>(v)?]))
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'d>,
{
Ok(AddrFuncValue({
let mut data = Vec::with_capacity(seq.size_hint().unwrap_or_default());
while let Some(v) = seq.next_element::<&str>()? {
data.push(Self::to_cidr::<A::Error>(v)?)
}
data
}))
}
}
impl AddrFuncValueVisitor {
fn to_cidr<E: serde::de::Error>(v: &str) -> Result<IpNetwork, E> {
let mut cidr_str = Cow::from(v);
if v.find('/').is_none() {
cidr_str.to_mut().push_str("/32");
}
Ok(cidr_str
.parse::<IpNetwork>()
.map_err(|_| E::custom(format!("{v} can not be parsed to CIDR")))?)
}
}
deserializer.deserialize_any(AddrFuncValueVisitor)
}
}
#[cfg(test)]
mod tests {
use super::{AddrFunc, AddrFuncValue};
use crate::policy::function::{
key::Key,
key_name::AwsKeyName::*,
key_name::KeyName::{self, *},
};
use test_case::test_case;
fn new_func(name: KeyName, variable: Option<String>, value: Vec<&str>) -> AddrFunc {
AddrFunc {
key: Key { name, variable },
values: AddrFuncValue(value.into_iter().map(|x| x.parse().unwrap()).collect()),
}
}
#[test_case(r#"{"aws:SourceIp": "203.0.113.0/24"}"#, new_func(Aws(AWSSourceIP), None, vec!["203.0.113.0/24"]); "1")]
#[test_case(r#"{"aws:SourceIp": "203.0.113.0"}"#, new_func(Aws(AWSSourceIP), None, vec!["203.0.113.0/32"]); "2")]
#[test_case(r#"{"aws:SourceIp": "2001:DB8:1234:5678::/64"}"#, new_func(Aws(AWSSourceIP),None, vec!["2001:DB8:1234:5678::/64"]); "3")]
#[test_case(r#"{"aws:SourceIp": "2001:DB8:1234:5678::"}"#, new_func(Aws(AWSSourceIP), None, vec!["2001:DB8:1234:5678::/32"]); "4")]
#[test_case(r#"{"aws:SourceIp": ["203.0.113.0/24","203.0.113.0"]}"#, new_func(Aws(AWSSourceIP), None, vec!["203.0.113.0/24", "203.0.113.0/32"]); "5")]
#[test_case(r#"{"aws:SourceIp": ["2001:DB8:1234:5678::/64","203.0.113.0/24"]}"#, new_func(Aws(AWSSourceIP), None, vec!["2001:DB8:1234:5678::/64", "203.0.113.0/24"]); "6")]
#[test_case(r#"{"aws:SourceIp": ["2001:DB8:1234:5678::/64", "2001:DB8:1234:5678::"]}"#, new_func(Aws(AWSSourceIP),None, vec!["2001:DB8:1234:5678::/64", "2001:DB8:1234:5678::/32"]); "7")]
#[test_case(r#"{"aws:SourceIp": ["2001:DB8:1234:5678::", "203.0.113.0"]}"#, new_func(Aws(AWSSourceIP), None, vec!["2001:DB8:1234:5678::/32", "203.0.113.0/32"]); "8")]
#[test_case(r#"{"aws:SourceIp/a": "203.0.113.0/24"}"#, new_func(Aws(AWSSourceIP), Some("a".into()), vec!["203.0.113.0/24"]); "9")]
#[test_case(r#"{"aws:SourceIp/a": "203.0.113.0/24"}"#, new_func(Aws(AWSSourceIP), Some("a".into()), vec!["203.0.113.0/24"]); "10")]
#[test_case(r#"{"aws:SourceIp/a": "203.0.113.0"}"#, new_func(Aws(AWSSourceIP), Some("a".into()), vec!["203.0.113.0/32"]); "11")]
#[test_case(r#"{"aws:SourceIp/a": "2001:DB8:1234:5678::/64"}"#, new_func(Aws(AWSSourceIP),Some("a".into()), vec!["2001:DB8:1234:5678::/64"]); "12")]
#[test_case(r#"{"aws:SourceIp/a": "2001:DB8:1234:5678::"}"#, new_func(Aws(AWSSourceIP), Some("a".into()), vec!["2001:DB8:1234:5678::/32"]); "13")]
#[test_case(r#"{"aws:SourceIp/a": ["203.0.113.0/24", "203.0.113.0"]}"#, new_func(Aws(AWSSourceIP), Some("a".into()), vec!["203.0.113.0/24", "203.0.113.0/32"]); "14")]
#[test_case(r#"{"aws:SourceIp/a": ["2001:DB8:1234:5678::/64", "203.0.113.0/24"]}"#, new_func(Aws(AWSSourceIP), Some("a".into()), vec!["2001:DB8:1234:5678::/64", "203.0.113.0/24"]); "15")]
#[test_case(r#"{"aws:SourceIp/a": ["2001:DB8:1234:5678::/64", "2001:DB8:1234:5678::"]}"#, new_func(Aws(AWSSourceIP),Some("a".into()), vec!["2001:DB8:1234:5678::/64", "2001:DB8:1234:5678::/32"]); "16")]
#[test_case(r#"{"aws:SourceIp/a": ["2001:DB8:1234:5678::", "203.0.113.0"]}"#, new_func(Aws(AWSSourceIP), Some("a".into()), vec!["2001:DB8:1234:5678::/32", "203.0.113.0/32"]); "17")]
fn test_deser(input: &str, expect: AddrFunc) -> Result<(), serde_json::Error> {
let v: AddrFunc = serde_json::from_str(input)?;
assert_eq!(v, expect);
Ok(())
}
#[test_case(r#"{"aws:SourceIp":["203.0.113.0/24"]}"#, new_func(Aws(AWSSourceIP), None, vec!["203.0.113.0/24"]); "1")]
#[test_case(r#"{"aws:SourceIp":["203.0.113.0/32"]}"#, new_func(Aws(AWSSourceIP), None, vec!["203.0.113.0/32"]); "2")]
#[test_case(r#"{"aws:SourceIp":["2001:db8:1234:5678::/64"]}"#, new_func(Aws(AWSSourceIP),None, vec!["2001:DB8:1234:5678::/64"]); "3")]
#[test_case(r#"{"aws:SourceIp":["2001:db8:1234:5678::/32"]}"#, new_func(Aws(AWSSourceIP), None, vec!["2001:DB8:1234:5678::/32"]); "4")]
#[test_case(r#"{"aws:SourceIp":["203.0.113.0/24","203.0.113.0/32"]}"#, new_func(Aws(AWSSourceIP), None, vec!["203.0.113.0/24", "203.0.113.0/32"]); "5")]
#[test_case(r#"{"aws:SourceIp":["2001:db8:1234:5678::/64","203.0.113.0/24"]}"#, new_func(Aws(AWSSourceIP), None, vec!["2001:DB8:1234:5678::/64", "203.0.113.0/24"]); "6")]
#[test_case(r#"{"aws:SourceIp":["2001:db8:1234:5678::/64","2001:db8:1234:5678::/32"]}"#, new_func(Aws(AWSSourceIP),None, vec!["2001:DB8:1234:5678::/64", "2001:DB8:1234:5678::/32"]); "7")]
#[test_case(r#"{"aws:SourceIp":["2001:db8:1234:5678::/32","203.0.113.0/32"]}"#, new_func(Aws(AWSSourceIP), None, vec!["2001:DB8:1234:5678::/32", "203.0.113.0/32"]); "8")]
#[test_case(r#"{"aws:SourceIp/a":["203.0.113.0/24"]}"#, new_func(Aws(AWSSourceIP), Some("a".into()), vec!["203.0.113.0/24"]); "9")]
#[test_case(r#"{"aws:SourceIp/a":["203.0.113.0/24"]}"#, new_func(Aws(AWSSourceIP), Some("a".into()), vec!["203.0.113.0/24"]); "10")]
#[test_case(r#"{"aws:SourceIp/a":["203.0.113.0/32"]}"#, new_func(Aws(AWSSourceIP), Some("a".into()), vec!["203.0.113.0/32"]); "11")]
#[test_case(r#"{"aws:SourceIp/a":["2001:db8:1234:5678::/64"]}"#, new_func(Aws(AWSSourceIP),Some("a".into()), vec!["2001:DB8:1234:5678::/64"]); "12")]
#[test_case(r#"{"aws:SourceIp/a":["2001:db8:1234:5678::/32"]}"#, new_func(Aws(AWSSourceIP), Some("a".into()), vec!["2001:DB8:1234:5678::/32"]); "13")]
#[test_case(r#"{"aws:SourceIp/a":["203.0.113.0/24","203.0.113.0/32"]}"#, new_func(Aws(AWSSourceIP), Some("a".into()), vec!["203.0.113.0/24", "203.0.113.0/32"]); "14")]
#[test_case(r#"{"aws:SourceIp/a":["2001:db8:1234:5678::/64","203.0.113.0/24"]}"#, new_func(Aws(AWSSourceIP), Some("a".into()), vec!["2001:DB8:1234:5678::/64", "203.0.113.0/24"]); "15")]
#[test_case(r#"{"aws:SourceIp/a":["2001:db8:1234:5678::/64","2001:db8:1234:5678::/32"]}"#, new_func(Aws(AWSSourceIP),Some("a".into()), vec!["2001:DB8:1234:5678::/64", "2001:DB8:1234:5678::/32"]); "16")]
#[test_case(r#"{"aws:SourceIp/a":["2001:db8:1234:5678::/32","203.0.113.0/32"]}"#, new_func(Aws(AWSSourceIP), Some("a".into()), vec!["2001:DB8:1234:5678::/32", "203.0.113.0/32"]); "17")]
fn test_ser(expect: &str, input: AddrFunc) -> Result<(), serde_json::Error> {
let v = serde_json::to_string(&input)?;
assert_eq!(v, expect);
Ok(())
}
}
+10
View File
@@ -0,0 +1,10 @@
use serde::{Deserialize, Serialize};
use super::func::InnerFunc;
pub type BinaryFunc = InnerFunc<BinaryFuncValue>;
// todo implement it
#[derive(Serialize, Deserialize, Clone)]
#[serde(transparent)]
pub struct BinaryFuncValue(String);
+119
View File
@@ -0,0 +1,119 @@
use super::func::InnerFunc;
use serde::{de, Deserialize, Deserializer, Serialize};
use std::{collections::HashMap, fmt};
pub type BoolFunc = InnerFunc<BoolFuncValue>;
impl BoolFunc {
pub fn evaluate_bool(&self, values: &HashMap<String, Vec<String>>) -> bool {
match values.get(self.key.name().as_str()).and_then(|x| x.get(0)) {
Some(x) => self.values.0.to_string().as_str() == x,
None => false,
}
}
pub fn evaluate_null(&self, values: &HashMap<String, Vec<String>>) -> bool {
let len = values.get(self.key.name().as_str()).map(Vec::len).unwrap_or(0);
if self.values.0 {
return len == 0;
}
len != 0
}
}
#[derive(Clone)]
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
pub struct BoolFuncValue(bool);
impl Serialize for BoolFuncValue {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.0.to_string())
}
}
impl<'de> Deserialize<'de> for BoolFuncValue {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct BoolOrStringVisitor;
impl<'de> de::Visitor<'de> for BoolOrStringVisitor {
type Value = BoolFuncValue;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a boolean or a string representing 'true' or 'false'")
}
fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(BoolFuncValue(value))
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(BoolFuncValue(value.parse::<bool>().map_err(|e| E::custom(format!("{e:?}")))?))
}
}
deserializer.deserialize_any(BoolOrStringVisitor)
}
}
#[cfg(test)]
mod tests {
use super::{BoolFunc, BoolFuncValue};
use crate::policy::function::{
key::Key,
key_name::AwsKeyName::*,
key_name::KeyName::{self, *},
};
use test_case::test_case;
fn new_func(name: KeyName, variable: Option<String>, value: bool) -> BoolFunc {
BoolFunc {
key: Key { name, variable },
values: BoolFuncValue(value),
}
}
#[test_case(r#"{"aws:SecureTransport": "true"}"#, new_func(Aws(AWSSecureTransport), None, true); "1")]
#[test_case(r#"{"aws:SecureTransport": "false"}"#, new_func(Aws(AWSSecureTransport), None, false); "2")]
#[test_case(r#"{"aws:SecureTransport": true}"#, new_func(Aws(AWSSecureTransport), None, true); "3")]
#[test_case(r#"{"aws:SecureTransport": false}"#, new_func(Aws(AWSSecureTransport), None, false); "4")]
#[test_case(r#"{"aws:SecureTransport/a": "true"}"#, new_func(Aws(AWSSecureTransport), Some("a".into()), true); "9")]
#[test_case(r#"{"aws:SecureTransport/a": "false"}"#, new_func(Aws(AWSSecureTransport), Some("a".into()), false); "10")]
#[test_case(r#"{"aws:SecureTransport/a": true}"#, new_func(Aws(AWSSecureTransport), Some("a".into()), true); "11")]
#[test_case(r#"{"aws:SecureTransport/a": false}"#, new_func(Aws(AWSSecureTransport), Some("a".into()), false); "12")]
fn test_deser(input: &str, expect: BoolFunc) -> Result<(), serde_json::Error> {
let v: BoolFunc = serde_json::from_str(input)?;
assert_eq!(v, expect);
Ok(())
}
#[test_case(r#"{"aws:usernamea":"johndoe"}"#)]
#[test_case(r#"{"aws:username":[]}"#)] // 空
#[test_case(r#"{"aws:usernamea/value":"johndoe"}"#)]
#[test_case(r#"{"aws:usernamea/value":["johndoe", "aaa"]}"#)]
#[test_case(r#""aaa""#)]
fn test_deser_failed(input: &str) {
assert!(serde_json::from_str::<BoolFunc>(input).is_err());
}
#[test_case(r#"{"aws:SecureTransport":"true"}"#, new_func(Aws(AWSSecureTransport), None, true); "1")]
#[test_case(r#"{"aws:SecureTransport":"false"}"#, new_func(Aws(AWSSecureTransport), None, false);"2")]
#[test_case(r#"{"aws:SecureTransport/aa":"true"}"#, new_func(Aws(AWSSecureTransport),Some("aa".into()), true);"3")]
#[test_case(r#"{"aws:SecureTransport/aa":"false"}"#, new_func(Aws(AWSSecureTransport), Some("aa".into()), false);"4")]
fn test_ser(expect: &str, input: BoolFunc) -> Result<(), serde_json::Error> {
let v = serde_json::to_string(&input)?;
assert_eq!(v.as_str(), expect);
Ok(())
}
}
+78
View File
@@ -0,0 +1,78 @@
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use super::{addr::AddrFunc, binary::BinaryFunc, bool_null::BoolFunc, date::DateFunc, number::NumberFunc, string::StringFunc};
#[derive(Clone, Serialize, Deserialize)]
pub enum Condition {
StringEquals(StringFunc),
StringNotEquals(StringFunc),
StringEqualsIgnoreCase(StringFunc),
StringNotEqualsIgnoreCase(StringFunc),
StringLike(StringFunc),
StringNotLike(StringFunc),
BinaryEquals(BinaryFunc),
IpAddress(AddrFunc),
NotIpAddress(AddrFunc),
Null(BoolFunc),
Bool(BoolFunc),
NumericEquals(NumberFunc),
NumericNotEquals(NumberFunc),
NumericLessThan(NumberFunc),
NumericLessThanEquals(NumberFunc),
NumericGreaterThan(NumberFunc),
NumericGreaterThanIfExists(NumberFunc),
NumericGreaterThanEquals(NumberFunc),
DateEquals(DateFunc),
DateNotEquals(DateFunc),
DateLessThan(DateFunc),
DateLessThanEquals(DateFunc),
DateGreaterThan(DateFunc),
DateGreaterThanEquals(DateFunc),
}
impl Condition {
pub fn evaluate(&self, for_all: bool, values: &HashMap<String, Vec<String>>) -> bool {
use Condition::*;
let r = match self {
StringEquals(s) => s.evaluate(for_all, false, false, values),
StringNotEquals(s) => s.evaluate(for_all, false, false, values),
StringEqualsIgnoreCase(s) => s.evaluate(for_all, true, false, values),
StringNotEqualsIgnoreCase(s) => s.evaluate(for_all, true, false, values),
StringLike(s) => s.evaluate(for_all, false, true, values),
StringNotLike(s) => s.evaluate(for_all, false, true, values),
BinaryEquals(s) => todo!(),
IpAddress(s) => s.evaluate(values),
NotIpAddress(s) => s.evaluate(values),
Null(s) => s.evaluate_null(values),
Bool(s) => s.evaluate_bool(values),
NumericEquals(s) => s.evaluate(i64::eq, false, values),
NumericNotEquals(s) => s.evaluate(i64::ne, false, values),
NumericLessThan(s) => s.evaluate(i64::lt, false, values),
NumericLessThanEquals(s) => s.evaluate(i64::le, false, values),
NumericGreaterThan(s) => s.evaluate(i64::gt, false, values),
NumericGreaterThanIfExists(s) => s.evaluate(i64::ge, true, values),
NumericGreaterThanEquals(s) => s.evaluate(i64::ge, false, values),
DateEquals(s) => s.evaluate(OffsetDateTime::eq, values),
DateNotEquals(s) => s.evaluate(OffsetDateTime::ne, values),
DateLessThan(s) => s.evaluate(OffsetDateTime::lt, values),
DateLessThanEquals(s) => s.evaluate(OffsetDateTime::le, values),
DateGreaterThan(s) => s.evaluate(OffsetDateTime::gt, values),
DateGreaterThanEquals(s) => s.evaluate(OffsetDateTime::ge, values),
};
if self.is_negate() {
!r
} else {
r
}
}
pub fn is_negate(&self) -> bool {
use Condition::*;
matches!(self, StringNotEquals(_) | StringNotEqualsIgnoreCase(_) | NotIpAddress(_))
}
}
+107
View File
@@ -0,0 +1,107 @@
use super::func::InnerFunc;
use serde::{de, Deserialize, Deserializer, Serialize};
use std::{collections::HashMap, fmt};
use time::{format_description::well_known::Rfc3339, OffsetDateTime};
pub type DateFunc = InnerFunc<DateFuncValue>;
impl DateFunc {
pub fn evaluate(
&self,
op: impl FnOnce(&OffsetDateTime, &OffsetDateTime) -> bool,
values: &HashMap<String, Vec<String>>,
) -> bool {
let v = match values.get(self.key.name().as_str()).and_then(|x| x.get(0)) {
Some(x) => x,
None => return false,
};
let Ok(rv) = OffsetDateTime::parse(v, &Rfc3339) else {
return false;
};
op(&self.values.0, &rv)
}
}
#[derive(Clone)]
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
pub struct DateFuncValue(OffsetDateTime);
impl Serialize for DateFuncValue {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::Error;
serializer.serialize_str(
&self
.0
.format(&Rfc3339)
.map_err(|e| S::Error::custom(format!("format datetime failed: {e:?}")))?,
)
}
}
impl<'de> Deserialize<'de> for DateFuncValue {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct DateVisitor;
impl<'de> de::Visitor<'de> for DateVisitor {
type Value = DateFuncValue;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a data string that is representable in RFC 3339 format.")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(DateFuncValue(
OffsetDateTime::parse(value, &Rfc3339).map_err(|e| E::custom(format!("{e:?}")))?,
))
}
}
deserializer.deserialize_str(DateVisitor)
}
}
#[cfg(test)]
mod tests {
use super::{DateFunc, DateFuncValue};
use crate::policy::function::{
key::Key,
key_name::KeyName::{self, *},
key_name::S3KeyName::*,
};
use test_case::test_case;
use time::{format_description::well_known::Rfc3339, OffsetDateTime};
fn new_func(name: KeyName, variable: Option<String>, value: &str) -> DateFunc {
DateFunc {
key: Key { name, variable },
values: DateFuncValue(OffsetDateTime::parse(value, &Rfc3339).unwrap()),
}
}
#[test_case(r#"{"s3:object-lock-retain-until-date": "2009-11-10T15:00:00Z"}"#, new_func(S3(S3ObjectLockRetainUntilDate), None, "2009-11-10T15:00:00Z"); "1")]
#[test_case(r#"{"s3:object-lock-retain-until-date/a": "2009-11-10T15:00:00Z"}"#, new_func(S3(S3ObjectLockRetainUntilDate), Some("a".into()), "2009-11-10T15:00:00Z"); "2")]
fn test_deser(input: &str, expect: DateFunc) -> Result<(), serde_json::Error> {
let v: DateFunc = serde_json::from_str(input)?;
assert_eq!(v, expect);
Ok(())
}
#[test_case(r#"{"s3:object-lock-retain-until-date":"2009-11-10T15:00:00Z"}"#, new_func(S3(S3ObjectLockRetainUntilDate), None, "2009-11-10T15:00:00Z"); "1")]
#[test_case(r#"{"s3:object-lock-retain-until-date/a":"2009-11-10T15:00:00Z"}"#, new_func(S3(S3ObjectLockRetainUntilDate), Some("a".into()), "2009-11-10T15:00:00Z"); "2")]
fn test_ser(expect: &str, input: DateFunc) -> Result<(), serde_json::Error> {
let v = serde_json::to_string(&input)?;
assert_eq!(v, expect);
Ok(())
}
}
+91
View File
@@ -0,0 +1,91 @@
use std::{collections::HashMap, marker::PhantomData};
use serde::{
de::{self, Visitor},
Deserialize, Deserializer, Serialize,
};
use super::{condition::Condition, key::Key};
#[derive(Clone, Serialize, Deserialize)]
pub enum Func {
ForAnyValues(Vec<Condition>),
ForAllValues(Vec<Condition>),
ForNormal(Vec<Condition>),
}
impl Func {
pub fn evaluate(&self, values: &HashMap<String, Vec<String>>) -> bool {
match self {
Self::ForAnyValues(conditions) => conditions.iter().all(|x| x.evaluate(true, values)),
Self::ForAllValues(conditions) => conditions.iter().all(|x| x.evaluate(false, values)),
Self::ForNormal(conditions) => conditions.iter().all(|x| x.evaluate(false, values)),
}
}
}
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
pub struct InnerFunc<T> {
pub key: Key,
pub values: T,
}
impl<T: Clone> Clone for InnerFunc<T> {
fn clone(&self) -> Self {
Self {
key: self.key.clone(),
values: self.values.clone(),
}
}
}
impl<T: Serialize> Serialize for InnerFunc<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeMap;
let mut map = serializer.serialize_map(Some(1))?;
map.serialize_key(&self.key)?;
map.serialize_value(&self.values)?;
map.end()
}
}
impl<'de, T> Deserialize<'de> for InnerFunc<T>
where
T: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct FuncVisitor<T>(PhantomData<T>);
impl<'v, T> Visitor<'v> for FuncVisitor<T>
where
T: Deserialize<'v>,
{
type Value = InnerFunc<T>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("struct StringFunc")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: de::MapAccess<'v>,
{
use serde::de::Error;
let Some((key, values)) = map.next_entry::<Key, T>()? else {
return Err(A::Error::custom("no k-v pair"));
};
Ok(InnerFunc { key, values })
}
}
deserializer.deserialize_map(FuncVisitor::<T>(PhantomData))
}
}
+110
View File
@@ -0,0 +1,110 @@
use serde::{Deserialize, Serialize};
use super::key_name::KeyName;
use crate::policy::{Error, Validator};
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(test, derive(PartialEq, Eq))]
#[serde(into = "String")]
#[serde(try_from = "&str")]
pub struct Key {
pub name: KeyName,
pub variable: Option<String>,
}
impl Validator for Key {}
impl Key {
pub fn is(&self, other: &KeyName) -> bool {
self.name.eq(other)
}
pub fn val_name(&self) -> String {
self.name.val_name()
}
pub fn name(&self) -> String {
if let Some(ref x) = self.variable {
format!("{}/{}", self.name.name(), x)
} else {
self.name.name().to_owned()
}
}
}
impl From<Key> for String {
fn from(value: Key) -> Self {
value.name()
}
}
impl TryFrom<&str> for Key {
type Error = Error;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let mut iter = value.splitn(2, '/');
let name = iter.next().ok_or_else(|| Error::InvalidKey(value.to_string()))?;
let variable = iter.next().map(Into::into);
Ok(Self {
name: KeyName::try_from(name)?,
variable,
})
}
}
#[cfg(test)]
mod tests {
use super::Key;
use test_case::test_case;
fn new_key(name: &str, value: Option<&str>) -> Key {
Key {
name: name.try_into().unwrap(),
variable: value.map(ToString::to_string),
}
}
#[test_case(new_key("s3:x-amz-copy-source", Some("aaa")), r#""s3:x-amz-copy-source/aaa""#)]
#[test_case(new_key("s3:x-amz-copy-source", None), r#""s3:x-amz-copy-source""#)]
#[test_case(new_key("aws:Referer", Some("bbb")), r#""aws:Referer/bbb""#)]
#[test_case(new_key("aws:Referer", None), r#""aws:Referer""#)]
#[test_case(new_key("jwt:website", None), r#""jwt:website""#)]
#[test_case(new_key("jwt:website", Some("aaa")), r#""jwt:website/aaa""#)]
#[test_case(new_key("svc:DurationSeconds", None), r#""svc:DurationSeconds""#)]
#[test_case(new_key("svc:DurationSeconds", Some("aaa")), r#""svc:DurationSeconds/aaa""#)]
fn test_serialize_successful(key: Key, except: &str) -> Result<(), serde_json::Error> {
let val = serde_json::to_string(&key)?;
assert_eq!(val.as_str(), except);
Ok(())
}
#[test_case("s3:x-amz-copy-source1/aaa")]
#[test_case("s33:x-amz-copy-source")]
#[test_case("aw2s:Referer/bbb")]
#[test_case("aws:Referera")]
#[test_case("jwdt:website")]
#[test_case("jwt:dwebsite/aaa")]
#[test_case("sfvc:DuratdionSeconds")]
#[test_case("svc:DursationSeconds/aaa")]
fn test_deserialize_falied(key: &str) {
let val = serde_json::from_str::<Key>(key);
assert!(val.is_err());
}
#[test_case(new_key("s3:x-amz-copy-source", Some("aaa")), r#""s3:x-amz-copy-source/aaa""#)]
#[test_case(new_key("s3:x-amz-copy-source", None), r#""s3:x-amz-copy-source""#)]
#[test_case(new_key("aws:Referer", Some("bbb")), r#""aws:Referer/bbb""#)]
#[test_case(new_key("aws:Referer", None), r#""aws:Referer""#)]
#[test_case(new_key("jwt:website", None), r#""jwt:website""#)]
#[test_case(new_key("jwt:website", Some("aaa")), r#""jwt:website/aaa""#)]
#[test_case(new_key("svc:DurationSeconds", None), r#""svc:DurationSeconds""#)]
#[test_case(new_key("svc:DurationSeconds", Some("aaa")), r#""svc:DurationSeconds/aaa""#)]
fn test_deserialize(except: Key, input: &str) -> Result<(), serde_json::Error> {
let v = serde_json::from_str::<Key>(input)?;
assert_eq!(v.name, except.name);
assert_eq!(v.variable, except.variable);
Ok(())
}
}
+333
View File
@@ -0,0 +1,333 @@
use crate::policy::Error::{self, InvalidKeyName};
use serde::{Deserialize, Serialize};
use strum::{EnumString, IntoStaticStr};
#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
#[serde(try_from = "&str", untagged)]
pub enum KeyName {
Aws(AwsKeyName),
Jwt(JwtKeyName),
Ldap(LdapKeyName),
Sts(StsKeyName),
Svc(SvcKeyName),
S3(S3KeyName),
}
impl TryFrom<&str> for KeyName {
type Error = Error;
fn try_from(value: &str) -> Result<Self, Self::Error> {
Ok(if value.starts_with("s3:") {
Self::S3(S3KeyName::try_from(value).map_err(|_| InvalidKeyName(value.into()))?)
} else if value.starts_with("aws:") {
Self::Aws(AwsKeyName::try_from(value).map_err(|_| InvalidKeyName(value.into()))?)
} else if value.starts_with("ldap:") {
Self::Ldap(LdapKeyName::try_from(value).map_err(|_| InvalidKeyName(value.into()))?)
} else if value.starts_with("sts:") {
Self::Sts(StsKeyName::try_from(value).map_err(|_| InvalidKeyName(value.into()))?)
} else if value.starts_with("jwt:") {
Self::Jwt(JwtKeyName::try_from(value).map_err(|_| InvalidKeyName(value.into()))?)
} else if value.starts_with("svc:") {
Self::Svc(SvcKeyName::try_from(value).map_err(|_| InvalidKeyName(value.into()))?)
} else {
Err(InvalidKeyName(value.into()))?
})
}
}
impl KeyName {
pub const COMMON_KEYS: &[KeyName] = &[
// s3
KeyName::S3(S3KeyName::S3SignatureVersion),
KeyName::S3(S3KeyName::S3AuthType),
KeyName::S3(S3KeyName::S3SignatureAge),
KeyName::S3(S3KeyName::S3XAmzContentSha256),
KeyName::S3(S3KeyName::S3LocationConstraint),
//aws
KeyName::Aws(AwsKeyName::AWSReferer),
KeyName::Aws(AwsKeyName::AWSSourceIP),
KeyName::Aws(AwsKeyName::AWSUserAgent),
KeyName::Aws(AwsKeyName::AWSSecureTransport),
KeyName::Aws(AwsKeyName::AWSCurrentTime),
KeyName::Aws(AwsKeyName::AWSEpochTime),
KeyName::Aws(AwsKeyName::AWSPrincipalType),
KeyName::Aws(AwsKeyName::AWSUserID),
KeyName::Aws(AwsKeyName::AWSUsername),
KeyName::Aws(AwsKeyName::AWSGroups),
// ldap
KeyName::Ldap(LdapKeyName::LDAPUser),
KeyName::Ldap(LdapKeyName::LDAPUsername),
KeyName::Ldap(LdapKeyName::LDAPGroups),
// jwt
KeyName::Jwt(JwtKeyName::JWTSub),
KeyName::Jwt(JwtKeyName::JWTIss),
KeyName::Jwt(JwtKeyName::JWTAud),
KeyName::Jwt(JwtKeyName::JWTJti),
KeyName::Jwt(JwtKeyName::JWTName),
KeyName::Jwt(JwtKeyName::JWTUpn),
KeyName::Jwt(JwtKeyName::JWTGroups),
KeyName::Jwt(JwtKeyName::JWTGivenName),
KeyName::Jwt(JwtKeyName::JWTFamilyName),
KeyName::Jwt(JwtKeyName::JWTMiddleName),
KeyName::Jwt(JwtKeyName::JWTNickName),
KeyName::Jwt(JwtKeyName::JWTPrefUsername),
KeyName::Jwt(JwtKeyName::JWTProfile),
KeyName::Jwt(JwtKeyName::JWTPicture),
KeyName::Jwt(JwtKeyName::JWTWebsite),
KeyName::Jwt(JwtKeyName::JWTEmail),
KeyName::Jwt(JwtKeyName::JWTGender),
KeyName::Jwt(JwtKeyName::JWTBirthdate),
KeyName::Jwt(JwtKeyName::JWTPhoneNumber),
KeyName::Jwt(JwtKeyName::JWTAddress),
KeyName::Jwt(JwtKeyName::JWTScope),
KeyName::Jwt(JwtKeyName::JWTClientID),
];
pub fn name(&self) -> &str {
match self {
KeyName::Aws(aws) => aws.into(),
KeyName::Jwt(jwt) => jwt.into(),
KeyName::Ldap(ldap) => ldap.into(),
KeyName::Sts(sts) => sts.into(),
KeyName::Svc(svc) => svc.into(),
KeyName::S3(s3) => s3.into(),
}
}
pub fn val_name(&self) -> String {
match self {
KeyName::Aws(aws) => Into::<&str>::into(aws).to_owned(),
KeyName::Jwt(jwt) => Into::<&str>::into(jwt).to_owned(),
KeyName::Ldap(ldap) => Into::<&str>::into(ldap).to_owned(),
KeyName::Sts(sts) => Into::<&str>::into(sts).to_owned(),
KeyName::Svc(svc) => Into::<&str>::into(svc).to_owned(),
KeyName::S3(s3) => Into::<&str>::into(s3).to_owned(),
}
}
}
#[derive(Clone, EnumString, Debug, IntoStaticStr, Eq, PartialEq, Serialize, Deserialize)]
#[serde(try_from = "&str", into = "&str")]
pub enum S3KeyName {
#[strum(serialize = "s3:x-amz-copy-source")]
S3XAmzCopySource,
#[strum(serialize = "s3:x-amz-server-side-encryption")]
S3XAmzServerSideEncryption,
#[strum(serialize = "s3:x-amz-server-side-encryption-customer-algorithm")]
S3XAmzServerSideEncryptionCustomerAlgorithm,
#[strum(serialize = "s3:signatureversion")]
S3SignatureVersion,
#[strum(serialize = "s3:authType")]
S3AuthType,
#[strum(serialize = "s3:signatureAge")]
S3SignatureAge,
#[strum(serialize = "s3:x-amz-content-sha256")]
S3XAmzContentSha256,
#[strum(serialize = "s3:LocationConstraint")]
S3LocationConstraint,
#[strum(serialize = "s3:object-lock-retain-until-date")]
S3ObjectLockRetainUntilDate,
#[strum(serialize = "s3:max-keys")]
S3MaxKeys,
}
#[derive(Clone, EnumString, Debug, IntoStaticStr, Eq, PartialEq, Serialize, Deserialize)]
#[serde(try_from = "&str", into = "&str")]
pub enum JwtKeyName {
#[strum(serialize = "jwt:sub")]
JWTSub,
#[strum(serialize = "jwt:iss")]
JWTIss,
#[strum(serialize = "jwt:aud")]
JWTAud,
#[strum(serialize = "jwt:jti")]
JWTJti,
#[strum(serialize = "jwt:name")]
JWTName,
#[strum(serialize = "jwt:upn")]
JWTUpn,
#[strum(serialize = "jwt:groups")]
JWTGroups,
#[strum(serialize = "jwt:given_name")]
JWTGivenName,
#[strum(serialize = "jwt:family_name")]
JWTFamilyName,
#[strum(serialize = "jwt:middle_name")]
JWTMiddleName,
#[strum(serialize = "jwt:nickname")]
JWTNickName,
#[strum(serialize = "jwt:preferred_username")]
JWTPrefUsername,
#[strum(serialize = "jwt:profile")]
JWTProfile,
#[strum(serialize = "jwt:picture")]
JWTPicture,
#[strum(serialize = "jwt:website")]
JWTWebsite,
#[strum(serialize = "jwt:email")]
JWTEmail,
#[strum(serialize = "jwt:gender")]
JWTGender,
#[strum(serialize = "jwt:birthdate")]
JWTBirthdate,
#[strum(serialize = "jwt:phone_number")]
JWTPhoneNumber,
#[strum(serialize = "jwt:address")]
JWTAddress,
#[strum(serialize = "jwt:scope")]
JWTScope,
#[strum(serialize = "jwt:client_id")]
JWTClientID,
}
#[derive(Clone, EnumString, Debug, IntoStaticStr, Eq, PartialEq, Serialize, Deserialize)]
#[serde(try_from = "&str", into = "&str")]
pub enum SvcKeyName {
#[strum(serialize = "svc:DurationSeconds")]
SVCDurationSeconds,
}
#[derive(Clone, EnumString, Debug, IntoStaticStr, Eq, PartialEq, Serialize, Deserialize)]
#[serde(try_from = "&str", into = "&str")]
pub enum LdapKeyName {
#[strum(serialize = "ldap:user")]
LDAPUser,
#[strum(serialize = "ldap:username")]
LDAPUsername,
#[strum(serialize = "ldap:groups")]
LDAPGroups,
}
#[derive(Clone, EnumString, Debug, IntoStaticStr, Eq, PartialEq, Serialize, Deserialize)]
#[serde(try_from = "&str", into = "&str")]
pub enum StsKeyName {
#[strum(serialize = "sts:DurationSeconds")]
STSDurationSeconds,
}
#[derive(Clone, EnumString, Debug, IntoStaticStr, Eq, PartialEq, Serialize, Deserialize)]
#[serde(try_from = "&str", into = "&str")]
pub enum AwsKeyName {
#[strum(serialize = "aws:Referer")]
AWSReferer,
#[strum(serialize = "aws:SourceIp")]
AWSSourceIP,
#[strum(serialize = "aws:UserAgent")]
AWSUserAgent,
#[strum(serialize = "aws:SecureTransport")]
AWSSecureTransport,
#[strum(serialize = "aws:CurrentTime")]
AWSCurrentTime,
#[strum(serialize = "aws:EpochTime")]
AWSEpochTime,
#[strum(serialize = "aws:principaltype")]
AWSPrincipalType,
#[strum(serialize = "aws:userid")]
AWSUserID,
#[strum(serialize = "aws:username")]
AWSUsername,
#[strum(serialize = "aws:groups")]
AWSGroups,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::policy::Error;
use serde::Deserialize;
use test_case::test_case;
#[test_case("s3:x-amz-copy-source", KeyName::S3(S3KeyName::S3XAmzCopySource))]
#[test_case("aws:SecureTransport", KeyName::Aws(AwsKeyName::AWSSecureTransport))]
#[test_case("jwt:sub", KeyName::Jwt(JwtKeyName::JWTSub))]
#[test_case("ldap:user", KeyName::Ldap(LdapKeyName::LDAPUser))]
#[test_case("sts:DurationSeconds", KeyName::Sts(StsKeyName::STSDurationSeconds))]
#[test_case("svc:DurationSeconds", KeyName::Svc(SvcKeyName::SVCDurationSeconds))]
fn key_name_from_str_successful(val: &str, except: KeyName) {
let key_name = KeyName::try_from(val);
assert_eq!(key_name, Ok(except));
}
#[test_case("S3:x-amz-copy-source")]
#[test_case("aWs:SecureTransport")]
#[test_case("jwt:suB")]
#[test_case("ldap:us")]
#[test_case("DurationSeconds")]
fn key_name_from_str_failed(val: &str) {
assert_eq!(KeyName::try_from(val), Err(Error::InvalidKeyName(val.to_string())));
}
#[test_case("s3:x-amz-copy-source", KeyName::S3(S3KeyName::S3XAmzCopySource))]
#[test_case("aws:SecureTransport", KeyName::Aws(AwsKeyName::AWSSecureTransport))]
#[test_case("jwt:sub", KeyName::Jwt(JwtKeyName::JWTSub))]
#[test_case("ldap:user", KeyName::Ldap(LdapKeyName::LDAPUser))]
#[test_case("sts:DurationSeconds", KeyName::Sts(StsKeyName::STSDurationSeconds))]
#[test_case("svc:DurationSeconds", KeyName::Svc(SvcKeyName::SVCDurationSeconds))]
fn key_name_deserialize(val: &str, except: KeyName) {
#[derive(Deserialize)]
struct TestCase {
data: KeyName,
}
let data = format!("{{\"data\":\"{val}\"}}");
let data: TestCase = serde_json::from_str(data.as_str()).expect("unmarshal failed");
assert_eq!(data.data, except);
}
#[test_case("s3:x-amz-copy-source", KeyName::S3(S3KeyName::S3XAmzCopySource))]
#[test_case("aws:SecureTransport", KeyName::Aws(AwsKeyName::AWSSecureTransport))]
#[test_case("jwt:sub", KeyName::Jwt(JwtKeyName::JWTSub))]
#[test_case("ldap:user", KeyName::Ldap(LdapKeyName::LDAPUser))]
#[test_case("sts:DurationSeconds", KeyName::Sts(StsKeyName::STSDurationSeconds))]
#[test_case("svc:DurationSeconds", KeyName::Svc(SvcKeyName::SVCDurationSeconds))]
fn key_name_serialize(except: &str, value: KeyName) {
#[derive(Serialize)]
struct TestCase {
data: KeyName,
}
let except = format!("{{\"data\":\"{except}\"}}");
let data = serde_json::to_string(&TestCase { data: value }).expect("marshal failed");
assert_eq!(data, except);
}
}
+113
View File
@@ -0,0 +1,113 @@
use std::collections::HashMap;
use super::func::InnerFunc;
use serde::{
de::{Error, Visitor},
Deserialize, Deserializer, Serialize,
};
pub type NumberFunc = InnerFunc<NumberFuncValue>;
#[derive(Clone)]
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
pub struct NumberFuncValue(i64);
impl NumberFunc {
pub fn evaluate(&self, op: impl FnOnce(&i64, &i64) -> bool, if_exists: bool, values: &HashMap<String, Vec<String>>) -> bool {
let v = match values.get(self.key.name().as_str()).and_then(|x| x.get(0)) {
Some(x) => x,
None => return if_exists,
};
let Ok(rv) = v.parse::<i64>() else {
return false;
};
op(&rv, &self.values.0)
}
}
impl Serialize for NumberFuncValue {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.0.to_string().as_str())
}
}
impl<'de> Deserialize<'de> for NumberFuncValue {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct NumberVisitor;
impl<'de> Visitor<'de> for NumberVisitor {
type Value = NumberFuncValue;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a number or a string that can be represented as a number.")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: Error,
{
Ok(NumberFuncValue(value.parse().map_err(|e| E::custom(format!("{e:?}")))?))
}
fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
where
E: Error,
{
Ok(NumberFuncValue(value))
}
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
where
E: Error,
{
Ok(NumberFuncValue(value as i64))
}
}
deserializer.deserialize_any(NumberVisitor)
}
}
#[cfg(test)]
mod tests {
use super::{NumberFunc, NumberFuncValue};
use crate::policy::function::{
key::Key,
key_name::KeyName::{self, *},
key_name::S3KeyName::*,
};
use test_case::test_case;
fn new_func(name: KeyName, variable: Option<String>, value: i64) -> NumberFunc {
NumberFunc {
key: Key { name, variable },
values: NumberFuncValue(value),
}
}
#[test_case(r#"{"s3:max-keys": 1}"#, new_func(S3(S3MaxKeys), None, 1); "1")]
#[test_case(r#"{"s3:max-keys/a": 1}"#, new_func(S3(S3MaxKeys), Some("a".into()), 1); "2")]
#[test_case(r#"{"s3:max-keys": "1"}"#, new_func(S3(S3MaxKeys), None, 1); "3")]
#[test_case(r#"{"s3:max-keys/a": "1"}"#, new_func(S3(S3MaxKeys), Some("a".into()), 1); "4")]
fn test_deser(input: &str, expect: NumberFunc) -> Result<(), serde_json::Error> {
let v: NumberFunc = serde_json::from_str(input)?;
assert_eq!(v, expect);
Ok(())
}
#[test_case(r#"{"s3:max-keys":"1"}"#, new_func(S3(S3MaxKeys), None, 1); "1")]
#[test_case(r#"{"s3:max-keys/a":"1"}"#, new_func(S3(S3MaxKeys), Some("a".into()), 1); "2")]
fn test_ser(expect: &str, input: NumberFunc) -> Result<(), serde_json::Error> {
let v = serde_json::to_string(&input)?;
assert_eq!(v, expect);
Ok(())
}
}
+220
View File
@@ -0,0 +1,220 @@
#[cfg(test)]
use std::collections::BTreeSet as Set;
#[cfg(not(test))]
use std::collections::HashSet as Set;
use std::fmt;
use std::{borrow::Cow, collections::HashMap};
use serde::{de, ser::SerializeSeq, Deserialize, Deserializer, Serialize};
use crate::policy::utils::wildcard;
use super::{func::InnerFunc, key_name::KeyName};
pub type StringFunc = InnerFunc<StringFuncValue>;
impl StringFunc {
fn eval(&self, for_all: bool, ignore_case: bool, values: &HashMap<String, Vec<String>>) -> bool {
let rvalues = values
.get(self.key.name().as_str())
.map(|t| {
t.iter()
.map(|x| {
if ignore_case {
Cow::Owned(x.to_lowercase())
} else {
Cow::from(x)
}
})
.collect::<Set<_>>()
})
.unwrap_or_default();
let fvalues = self
.values
.0
.iter()
.map(|c| {
let mut c = Cow::from(c);
for key in KeyName::COMMON_KEYS {
match values.get(key.name()).and_then(|x| x.get(0)) {
Some(v) if !v.is_empty() => return Cow::Owned(c.to_mut().replace(key.name(), v)),
_ => continue,
};
}
c
})
.map(|x| if ignore_case { Cow::Owned(x.to_lowercase()) } else { x })
.collect::<Set<_>>();
let ivalues = rvalues.intersection(&fvalues);
if for_all {
rvalues.is_empty() || rvalues.len() == ivalues.count()
} else {
ivalues.count() > 0
}
}
fn eval_like(&self, for_all: bool, values: &HashMap<String, Vec<String>>) -> bool {
if let Some(rvalues) = values.get(self.key.name().as_str()) {
for v in rvalues.iter() {
let matched = self
.values
.0
.iter()
.map(|c| {
let mut c = Cow::from(c);
for key in KeyName::COMMON_KEYS {
match values.get(key.name()).and_then(|x| x.get(0)) {
Some(v) if !v.is_empty() => return Cow::Owned(c.to_mut().replace(key.name(), v)),
_ => continue,
};
}
c
})
.any(|x| wildcard::is_match(x, v));
if for_all {
if !matched {
return false;
}
} else if matched {
return true;
}
}
}
for_all
}
pub(crate) fn evaluate(&self, for_all: bool, ignore_case: bool, like: bool, values: &HashMap<String, Vec<String>>) -> bool {
if like {
self.eval_like(for_all, values)
} else {
self.eval(for_all, ignore_case, values)
}
}
}
/// 解析values字段
#[derive(Clone)]
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
pub struct StringFuncValue(Set<String>);
impl Serialize for StringFuncValue {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if self.0.len() == 1 {
serializer.serialize_some(&self.0.iter().next())
} else {
let mut seq = serializer.serialize_seq(Some(self.0.len()))?;
for element in &self.0 {
seq.serialize_element(element)?;
}
seq.end()
}
}
}
impl<'d> Deserialize<'d> for StringFuncValue {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'d>,
{
struct StringOrVecVisitor;
impl<'de> de::Visitor<'de> for StringOrVecVisitor {
type Value = StringFuncValue;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a string or an array of strings")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok({
let mut hash = Set::new();
hash.insert(value.to_string());
StringFuncValue(hash)
})
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
#[cfg(test)]
let mut values = Set::new();
#[cfg(not(test))]
let mut values = Set::with_capacity(seq.size_hint().unwrap_or(0));
while let Some(value) = seq.next_element::<String>()? {
values.insert(value);
}
Ok(StringFuncValue(values))
}
}
let result = deserializer.deserialize_any(StringOrVecVisitor)?;
if result.0.is_empty() {
use serde::de::Error;
return Err(D::Error::custom("empty"));
}
Ok(result)
}
}
#[cfg(test)]
mod tests {
use super::{StringFunc, StringFuncValue};
use crate::policy::function::{
key::Key,
key_name::AwsKeyName::*,
key_name::KeyName::{self, *},
};
use test_case::test_case;
fn new_func(name: KeyName, variable: Option<String>, values: Vec<&str>) -> StringFunc {
StringFunc {
key: Key { name, variable },
values: StringFuncValue(values.into_iter().map(|x| x.to_owned()).collect()),
}
}
#[test_case(r#"{"aws:username": "johndoe"}"#, new_func(Aws(AWSUsername), None, vec!["johndoe"]))]
#[test_case(r#"{"aws:username": ["johndoe", "aaa"]}"#, new_func(Aws(AWSUsername), None, vec!["johndoe", "aaa"]))]
#[test_case(r#"{"aws:username/value": "johndoe"}"#, new_func(Aws(AWSUsername), Some("value".into()), vec!["johndoe"]))]
#[test_case(r#"{"aws:username/value": ["johndoe", "aaa"]}"#, new_func(Aws(AWSUsername), Some("value".into()), vec!["johndoe", "aaa"]))]
fn test_deser(input: &str, expect: StringFunc) -> Result<(), serde_json::Error> {
let v: StringFunc = serde_json::from_str(input)?;
assert_eq!(v, expect);
Ok(())
}
#[test_case(r#"{"aws:usernamea":"johndoe"}"#)]
#[test_case(r#"{"aws:username":[]}"#)] // 空
#[test_case(r#"{"aws:usernamea/value":"johndoe"}"#)]
#[test_case(r#"{"aws:usernamea/value":["johndoe", "aaa"]}"#)]
#[test_case(r#""aaa""#)]
fn test_deser_failed(input: &str) {
assert!(serde_json::from_str::<StringFunc>(input).is_err());
}
#[test_case(r#"{"aws:username":"johndoe"}"#, new_func(Aws(AWSUsername), None, vec!["johndoe"]))]
#[test_case(r#"{"aws:username":["aaa","johndoe"]}"#, new_func(Aws(AWSUsername), None, vec!["johndoe", "aaa"]))]
#[test_case(r#"{"aws:username/value":"johndoe"}"#, new_func(Aws(AWSUsername), Some("value".into()), vec!["johndoe"]))]
#[test_case(r#"{"aws:username/value":["aaa","johndoe"]}"#, new_func(Aws(AWSUsername), Some("value".into()), vec!["johndoe", "aaa"]))]
fn test_ser(expect: &str, input: StringFunc) -> Result<(), serde_json::Error> {
let v = serde_json::to_string(&input)?;
assert_eq!(v.as_str(), expect);
Ok(())
}
}