mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-28 07:57:01 +00:00
rewrite iam
This commit is contained in:
+26
-15
@@ -1,11 +1,13 @@
|
||||
use std::{collections::HashSet, ops::Deref};
|
||||
|
||||
use ecstore::error::{Error, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::HashSet, ops::Deref};
|
||||
use strum::{EnumString, IntoStaticStr};
|
||||
|
||||
use super::{utils::wildcard, Error, Validator};
|
||||
use crate::sys::Validator;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Default)]
|
||||
use super::{utils::wildcard, Error as IamError};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Default, Debug)]
|
||||
pub struct ActionSet(pub HashSet<Action>);
|
||||
|
||||
impl ActionSet {
|
||||
@@ -35,12 +37,19 @@ impl Deref for ActionSet {
|
||||
}
|
||||
|
||||
impl Validator for ActionSet {
|
||||
fn is_valid(&self) -> Result<(), super::Error> {
|
||||
type Error = Error;
|
||||
fn is_valid(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Hash, PartialEq, Eq, Clone)]
|
||||
impl PartialEq for ActionSet {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.len() == other.len() && self.0.iter().all(|x| other.0.contains(x))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Hash, PartialEq, Eq, Clone, Debug)]
|
||||
#[serde(try_from = "&str", untagged)]
|
||||
pub enum Action {
|
||||
S3Action(S3Action),
|
||||
@@ -77,26 +86,28 @@ impl TryFrom<&str> for Action {
|
||||
type Error = Error;
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
if value.starts_with(Self::S3_PREFIX) {
|
||||
Ok(Self::S3Action(S3Action::try_from(value).map_err(|_| Error::InvalidAction(value.into()))?))
|
||||
Ok(Self::S3Action(
|
||||
S3Action::try_from(value).map_err(|_| IamError::InvalidAction(value.into()))?,
|
||||
))
|
||||
} else if value.starts_with(Self::ADMIN_PREFIX) {
|
||||
Ok(Self::AdminAction(
|
||||
AdminAction::try_from(value).map_err(|_| Error::InvalidAction(value.into()))?,
|
||||
AdminAction::try_from(value).map_err(|_| IamError::InvalidAction(value.into()))?,
|
||||
))
|
||||
} else if value.starts_with(Self::STS_PREFIX) {
|
||||
Ok(Self::StsAction(
|
||||
StsAction::try_from(value).map_err(|_| Error::InvalidAction(value.into()))?,
|
||||
StsAction::try_from(value).map_err(|_| IamError::InvalidAction(value.into()))?,
|
||||
))
|
||||
} else if value.starts_with(Self::KMS_PREFIX) {
|
||||
Ok(Self::KmsAction(
|
||||
KmsAction::try_from(value).map_err(|_| Error::InvalidAction(value.into()))?,
|
||||
KmsAction::try_from(value).map_err(|_| IamError::InvalidAction(value.into()))?,
|
||||
))
|
||||
} else {
|
||||
Err(Error::InvalidAction(value.into()))
|
||||
Err(IamError::InvalidAction(value.into()).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Hash, PartialEq, Eq, Clone, EnumString, IntoStaticStr)]
|
||||
#[derive(Serialize, Deserialize, Hash, PartialEq, Eq, Clone, EnumString, IntoStaticStr, Debug)]
|
||||
#[cfg_attr(test, derive(Default))]
|
||||
#[serde(try_from = "&str", into = "&str")]
|
||||
pub enum S3Action {
|
||||
@@ -113,7 +124,7 @@ pub enum S3Action {
|
||||
GetObjectVersionAction,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Hash, PartialEq, Eq, Clone, EnumString, IntoStaticStr)]
|
||||
#[derive(Serialize, Deserialize, Hash, PartialEq, Eq, Clone, EnumString, IntoStaticStr, Debug)]
|
||||
#[serde(try_from = "&str", into = "&str")]
|
||||
pub enum AdminAction {
|
||||
#[strum(serialize = "admin:*")]
|
||||
@@ -144,11 +155,11 @@ pub enum AdminAction {
|
||||
CreateServiceAccountAdminAction,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Hash, PartialEq, Eq, Clone, EnumString, IntoStaticStr)]
|
||||
#[derive(Serialize, Deserialize, Hash, PartialEq, Eq, Clone, EnumString, IntoStaticStr, Debug)]
|
||||
#[serde(try_from = "&str", into = "&str")]
|
||||
pub enum StsAction {}
|
||||
|
||||
#[derive(Serialize, Deserialize, Hash, PartialEq, Eq, Clone, EnumString, IntoStaticStr)]
|
||||
#[derive(Serialize, Deserialize, Hash, PartialEq, Eq, Clone, EnumString, IntoStaticStr, Debug)]
|
||||
#[serde(try_from = "&str", into = "&str")]
|
||||
pub enum KmsAction {
|
||||
#[strum(serialize = "kms:*")]
|
||||
|
||||
@@ -10,3 +10,50 @@ pub struct PolicyDoc {
|
||||
pub create_date: Option<OffsetDateTime>,
|
||||
pub update_date: Option<OffsetDateTime>,
|
||||
}
|
||||
|
||||
impl PolicyDoc {
|
||||
pub fn new(policy: Policy) -> Self {
|
||||
Self {
|
||||
version: 1,
|
||||
policy,
|
||||
create_date: Some(OffsetDateTime::now_utc()),
|
||||
update_date: Some(OffsetDateTime::now_utc()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, policy: Policy) {
|
||||
self.version += 1;
|
||||
self.policy = policy;
|
||||
self.update_date = Some(OffsetDateTime::now_utc());
|
||||
|
||||
if self.create_date.is_none() {
|
||||
self.create_date = self.update_date;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_policy(policy: Policy) -> Self {
|
||||
Self {
|
||||
version: 1,
|
||||
policy,
|
||||
create_date: None,
|
||||
update_date: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<u8>> for PolicyDoc {
|
||||
type Error = serde_json::Error;
|
||||
|
||||
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
match serde_json::from_slice::<PolicyDoc>(&value) {
|
||||
Ok(res) => Ok(res),
|
||||
Err(err) => match serde_json::from_slice::<Policy>(&value) {
|
||||
Ok(res2) => Ok(Self {
|
||||
policy: res2,
|
||||
..Default::default()
|
||||
}),
|
||||
Err(_) => Err(err),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use ecstore::error::{Error, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum::{EnumString, IntoStaticStr};
|
||||
|
||||
use super::{Error, Validator};
|
||||
use crate::sys::Validator;
|
||||
|
||||
#[derive(Serialize, Clone, Deserialize, EnumString, IntoStaticStr, Default)]
|
||||
#[derive(Serialize, Clone, Deserialize, EnumString, IntoStaticStr, Default, Debug, PartialEq)]
|
||||
#[serde(try_from = "&str", into = "&str")]
|
||||
pub enum Effect {
|
||||
#[default]
|
||||
@@ -24,7 +25,8 @@ impl Effect {
|
||||
}
|
||||
|
||||
impl Validator for Effect {
|
||||
fn is_valid(&self) -> Result<(), Error> {
|
||||
type Error = Error;
|
||||
fn is_valid(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ pub mod key_name;
|
||||
pub mod number;
|
||||
pub mod string;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct Functions {
|
||||
for_any_value: Vec<Condition>,
|
||||
for_all_values: Vec<Condition>,
|
||||
@@ -143,6 +143,21 @@ impl<'de> Deserialize<'de> for Functions {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Functions {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
if !(self.for_all_values.len() == other.for_all_values.len()
|
||||
&& self.for_any_value.len() == other.for_any_value.len()
|
||||
&& self.for_normal.len() == other.for_normal.len())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
self.for_any_value.iter().all(|x| other.for_any_value.contains(x))
|
||||
&& self.for_all_values.iter().all(|x| other.for_all_values.contains(x))
|
||||
&& self.for_normal.iter().all(|x| other.for_normal.contains(x))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct Value;
|
||||
|
||||
@@ -151,7 +166,6 @@ mod tests {
|
||||
use crate::policy::function::condition::Condition::*;
|
||||
use crate::policy::function::func::FuncKeyValue;
|
||||
use crate::policy::function::key::Key;
|
||||
use crate::policy::function::key_name::KeyName;
|
||||
use crate::policy::function::string::StringFunc;
|
||||
use crate::policy::function::string::StringFuncValue;
|
||||
use crate::policy::Functions;
|
||||
@@ -369,7 +383,6 @@ mod tests {
|
||||
values: StringFuncValue(vec!["us-east-1"].into_iter().map(ToOwned::to_owned).collect()),
|
||||
}],
|
||||
})],
|
||||
..Default::default()
|
||||
},
|
||||
r#"{"ForAllValues:StringNotLike":{"s3:LocationConstraint":"us-east-1"},"ForAnyValue:StringNotLike":{"s3:LocationConstraint":["us-east-1","us-east-2"]},"StringNotLike":{"s3:LocationConstraint":"us-east-1"}}"#;
|
||||
"3"
|
||||
|
||||
@@ -27,9 +27,8 @@ impl AddrFunc {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
#[derive(Serialize, Clone, PartialEq, Eq, Debug)]
|
||||
#[serde(transparent)]
|
||||
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
|
||||
pub struct AddrFuncValue(Vec<IpNetwork>);
|
||||
|
||||
impl<'de> Deserialize<'de> for AddrFuncValue {
|
||||
@@ -73,9 +72,9 @@ impl<'de> Deserialize<'de> for AddrFuncValue {
|
||||
cidr_str.to_mut().push_str("/32");
|
||||
}
|
||||
|
||||
Ok(cidr_str
|
||||
cidr_str
|
||||
.parse::<IpNetwork>()
|
||||
.map_err(|_| E::custom(format!("{v} can not be parsed to CIDR")))?)
|
||||
.map_err(|_| E::custom(format!("{v} can not be parsed to CIDR")))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::func::InnerFunc;
|
||||
@@ -5,6 +7,12 @@ use super::func::InnerFunc;
|
||||
pub type BinaryFunc = InnerFunc<BinaryFuncValue>;
|
||||
|
||||
// todo implement it
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
|
||||
#[serde(transparent)]
|
||||
pub struct BinaryFuncValue(String);
|
||||
|
||||
impl BinaryFunc {
|
||||
pub fn evaluate(&self, _values: &HashMap<String, Vec<String>>) -> bool {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ pub type BoolFunc = InnerFunc<BoolFuncValue>;
|
||||
impl BoolFunc {
|
||||
pub fn evaluate_bool(&self, values: &HashMap<String, Vec<String>>) -> bool {
|
||||
for inner in self.0.iter() {
|
||||
if !match values.get(inner.key.name().as_str()).and_then(|x| x.get(0)) {
|
||||
if !match values.get(inner.key.name().as_str()).and_then(|x| x.first()) {
|
||||
Some(x) => inner.values.0.to_string().as_str() == x,
|
||||
None => false,
|
||||
} {
|
||||
@@ -32,8 +32,7 @@ impl BoolFunc {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct BoolFuncValue(bool);
|
||||
|
||||
impl Serialize for BoolFuncValue {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use serde::de::{Error, MapAccess};
|
||||
use serde::ser::SerializeMap;
|
||||
use serde::{Deserialize, Serialize, Serializer};
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
use super::{addr::AddrFunc, binary::BinaryFunc, bool_null::BoolFunc, date::DateFunc, number::NumberFunc, string::StringFunc};
|
||||
|
||||
#[derive(Clone, Deserialize)]
|
||||
#[derive(Clone, Deserialize, Debug)]
|
||||
pub enum Condition {
|
||||
StringEquals(StringFunc),
|
||||
StringNotEquals(StringFunc),
|
||||
@@ -102,7 +102,7 @@ impl Condition {
|
||||
StringNotEqualsIgnoreCase(s) => s.evaluate(for_all, true, false, true, values),
|
||||
StringLike(s) => s.evaluate(for_all, false, true, false, values),
|
||||
StringNotLike(s) => s.evaluate(for_all, false, true, true, values),
|
||||
BinaryEquals(s) => todo!(),
|
||||
BinaryEquals(s) => s.evaluate(values),
|
||||
IpAddress(s) => s.evaluate(values),
|
||||
NotIpAddress(s) => s.evaluate(values),
|
||||
Null(s) => s.evaluate_null(values),
|
||||
@@ -164,3 +164,35 @@ impl Condition {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Condition {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(Self::StringEquals(l0), Self::StringEquals(r0)) => l0 == r0,
|
||||
(Self::StringNotEquals(l0), Self::StringNotEquals(r0)) => l0 == r0,
|
||||
(Self::StringEqualsIgnoreCase(l0), Self::StringEqualsIgnoreCase(r0)) => l0 == r0,
|
||||
(Self::StringNotEqualsIgnoreCase(l0), Self::StringNotEqualsIgnoreCase(r0)) => l0 == r0,
|
||||
(Self::StringLike(l0), Self::StringLike(r0)) => l0 == r0,
|
||||
(Self::StringNotLike(l0), Self::StringNotLike(r0)) => l0 == r0,
|
||||
(Self::BinaryEquals(l0), Self::BinaryEquals(r0)) => l0 == r0,
|
||||
(Self::IpAddress(l0), Self::IpAddress(r0)) => l0 == r0,
|
||||
(Self::NotIpAddress(l0), Self::NotIpAddress(r0)) => l0 == r0,
|
||||
(Self::Null(l0), Self::Null(r0)) => l0 == r0,
|
||||
(Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
|
||||
(Self::NumericEquals(l0), Self::NumericEquals(r0)) => l0 == r0,
|
||||
(Self::NumericNotEquals(l0), Self::NumericNotEquals(r0)) => l0 == r0,
|
||||
(Self::NumericLessThan(l0), Self::NumericLessThan(r0)) => l0 == r0,
|
||||
(Self::NumericLessThanEquals(l0), Self::NumericLessThanEquals(r0)) => l0 == r0,
|
||||
(Self::NumericGreaterThan(l0), Self::NumericGreaterThan(r0)) => l0 == r0,
|
||||
(Self::NumericGreaterThanIfExists(l0), Self::NumericGreaterThanIfExists(r0)) => l0 == r0,
|
||||
(Self::NumericGreaterThanEquals(l0), Self::NumericGreaterThanEquals(r0)) => l0 == r0,
|
||||
(Self::DateEquals(l0), Self::DateEquals(r0)) => l0 == r0,
|
||||
(Self::DateNotEquals(l0), Self::DateNotEquals(r0)) => l0 == r0,
|
||||
(Self::DateLessThan(l0), Self::DateLessThan(r0)) => l0 == r0,
|
||||
(Self::DateLessThanEquals(l0), Self::DateLessThanEquals(r0)) => l0 == r0,
|
||||
(Self::DateGreaterThan(l0), Self::DateGreaterThan(r0)) => l0 == r0,
|
||||
(Self::DateGreaterThanEquals(l0), Self::DateGreaterThanEquals(r0)) => l0 == r0,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ pub type DateFunc = InnerFunc<DateFuncValue>;
|
||||
impl DateFunc {
|
||||
pub fn evaluate(&self, op: impl Fn(&OffsetDateTime, &OffsetDateTime) -> bool, values: &HashMap<String, Vec<String>>) -> bool {
|
||||
for inner in self.0.iter() {
|
||||
let v = match values.get(inner.key.name().as_str()).and_then(|x| x.get(0)) {
|
||||
let v = match values.get(inner.key.name().as_str()).and_then(|x| x.first()) {
|
||||
Some(x) => x,
|
||||
None => return false,
|
||||
};
|
||||
@@ -26,8 +26,7 @@ impl DateFunc {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct DateFuncValue(OffsetDateTime);
|
||||
|
||||
impl Serialize for DateFuncValue {
|
||||
@@ -52,7 +51,7 @@ impl<'de> Deserialize<'de> for DateFuncValue {
|
||||
{
|
||||
struct DateVisitor;
|
||||
|
||||
impl<'de> de::Visitor<'de> for DateVisitor {
|
||||
impl de::Visitor<'_> for DateVisitor {
|
||||
type Value = DateFuncValue;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
|
||||
@@ -7,10 +7,10 @@ use serde::{
|
||||
|
||||
use super::key::Key;
|
||||
|
||||
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
pub struct InnerFunc<T>(pub(crate) Vec<FuncKeyValue<T>>);
|
||||
|
||||
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
pub struct FuncKeyValue<T> {
|
||||
pub key: Key,
|
||||
pub values: T,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use super::key_name::KeyName;
|
||||
use crate::policy::{Error, Validator};
|
||||
use crate::{policy::Error as PolicyError, sys::Validator};
|
||||
use ecstore::error::Error;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(into = "String")]
|
||||
#[serde(try_from = "&str")]
|
||||
pub struct Key {
|
||||
@@ -11,7 +11,9 @@ pub struct Key {
|
||||
pub variable: Option<String>,
|
||||
}
|
||||
|
||||
impl Validator for Key {}
|
||||
impl Validator for Key {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl Key {
|
||||
pub fn is(&self, other: &KeyName) -> bool {
|
||||
@@ -36,7 +38,7 @@ impl From<Key> for String {
|
||||
let mut data = String::from(Into::<&str>::into(&value.name));
|
||||
if let Some(x) = value.variable.as_ref() {
|
||||
data.push('/');
|
||||
data.push_str(&x);
|
||||
data.push_str(x);
|
||||
}
|
||||
data
|
||||
}
|
||||
@@ -47,7 +49,7 @@ impl TryFrom<&str> for Key {
|
||||
|
||||
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 name = iter.next().ok_or_else(|| PolicyError::InvalidKey(value.to_string()))?;
|
||||
let variable = iter.next().map(Into::into);
|
||||
|
||||
Ok(Self {
|
||||
|
||||
@@ -54,9 +54,9 @@ impl KeyName {
|
||||
KeyName::Aws(AwsKeyName::AWSUsername),
|
||||
KeyName::Aws(AwsKeyName::AWSGroups),
|
||||
// ldap
|
||||
KeyName::Ldap(LdapKeyName::LDAPUser),
|
||||
KeyName::Ldap(LdapKeyName::LDAPUsername),
|
||||
KeyName::Ldap(LdapKeyName::LDAPGroups),
|
||||
KeyName::Ldap(LdapKeyName::User),
|
||||
KeyName::Ldap(LdapKeyName::Username),
|
||||
KeyName::Ldap(LdapKeyName::Groups),
|
||||
// jwt
|
||||
KeyName::Jwt(JwtKeyName::JWTSub),
|
||||
KeyName::Jwt(JwtKeyName::JWTIss),
|
||||
@@ -252,13 +252,13 @@ pub enum SvcKeyName {
|
||||
#[serde(try_from = "&str", into = "&str")]
|
||||
pub enum LdapKeyName {
|
||||
#[strum(serialize = "ldap:user")]
|
||||
LDAPUser,
|
||||
User,
|
||||
|
||||
#[strum(serialize = "ldap:username")]
|
||||
LDAPUsername,
|
||||
Username,
|
||||
|
||||
#[strum(serialize = "ldap:groups")]
|
||||
LDAPGroups,
|
||||
Groups,
|
||||
}
|
||||
|
||||
#[derive(Clone, EnumString, Debug, IntoStaticStr, Eq, PartialEq, Serialize, Deserialize)]
|
||||
@@ -312,7 +312,7 @@ mod tests {
|
||||
#[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("ldap:user", KeyName::Ldap(LdapKeyName::User))]
|
||||
#[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) {
|
||||
@@ -332,7 +332,7 @@ mod tests {
|
||||
#[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("ldap:user", KeyName::Ldap(LdapKeyName::User))]
|
||||
#[test_case("sts:DurationSeconds", KeyName::Sts(StsKeyName::STSDurationSeconds))]
|
||||
#[test_case("svc:DurationSeconds", KeyName::Svc(SvcKeyName::SVCDurationSeconds))]
|
||||
fn key_name_deserialize(val: &str, except: KeyName) {
|
||||
@@ -349,7 +349,7 @@ mod tests {
|
||||
#[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("ldap:user", KeyName::Ldap(LdapKeyName::User))]
|
||||
#[test_case("sts:DurationSeconds", KeyName::Sts(StsKeyName::STSDurationSeconds))]
|
||||
#[test_case("svc:DurationSeconds", KeyName::Svc(SvcKeyName::SVCDurationSeconds))]
|
||||
fn key_name_serialize(except: &str, value: KeyName) {
|
||||
|
||||
@@ -8,14 +8,13 @@ use serde::{
|
||||
|
||||
pub type NumberFunc = InnerFunc<NumberFuncValue>;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct NumberFuncValue(i64);
|
||||
|
||||
impl NumberFunc {
|
||||
pub fn evaluate(&self, op: impl Fn(&i64, &i64) -> bool, if_exists: bool, values: &HashMap<String, Vec<String>>) -> bool {
|
||||
for inner in self.0.iter() {
|
||||
let v = match values.get(inner.key.name().as_str()).and_then(|x| x.get(0)) {
|
||||
let v = match values.get(inner.key.name().as_str()).and_then(|x| x.first()) {
|
||||
Some(x) => x,
|
||||
None => return if_exists,
|
||||
};
|
||||
@@ -49,7 +48,7 @@ impl<'de> Deserialize<'de> for NumberFuncValue {
|
||||
{
|
||||
struct NumberVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for NumberVisitor {
|
||||
impl Visitor<'_> for NumberVisitor {
|
||||
type Value = NumberFuncValue;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
|
||||
@@ -63,7 +63,7 @@ impl FuncKeyValue<StringFuncValue> {
|
||||
.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)) {
|
||||
match values.get(key.name()).and_then(|x| x.first()) {
|
||||
Some(v) if !v.is_empty() => return Cow::Owned(c.to_mut().replace(&key.var_name(), v)),
|
||||
_ => continue,
|
||||
};
|
||||
@@ -93,7 +93,7 @@ impl FuncKeyValue<StringFuncValue> {
|
||||
.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)) {
|
||||
match values.get(key.name()).and_then(|x| x.first()) {
|
||||
Some(v) if !v.is_empty() => return Cow::Owned(c.to_mut().replace(&key.var_name(), v)),
|
||||
_ => continue,
|
||||
};
|
||||
@@ -118,8 +118,8 @@ impl FuncKeyValue<StringFuncValue> {
|
||||
}
|
||||
|
||||
/// 解析values字段
|
||||
#[derive(Clone)]
|
||||
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
|
||||
pub struct StringFuncValue(pub Set<String>);
|
||||
|
||||
impl Serialize for StringFuncValue {
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
use ecstore::error::{Error, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::ops::Deref;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::sys::Validator;
|
||||
|
||||
use super::{Error, Validator};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Default)]
|
||||
#[derive(Serialize, Deserialize, Clone, Default, Debug)]
|
||||
pub struct ID(pub String);
|
||||
|
||||
impl Validator for ID {
|
||||
type Error = Error;
|
||||
/// if id is a valid utf string, then it is valid.
|
||||
fn is_valid(&self) -> Result<(), Error> {
|
||||
fn is_valid(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
+85
-10
@@ -1,8 +1,10 @@
|
||||
use super::{Effect, Error as IamError, Statement, ID};
|
||||
use crate::sys::{Args, Validator, DEFAULT_VERSION};
|
||||
use ecstore::error::{Error, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
|
||||
use super::{Args, Effect, Error, Statement, Validator, DEFAULT_VERSION, ID};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Default)]
|
||||
#[derive(Serialize, Deserialize, Clone, Default, Debug)]
|
||||
pub struct Policy {
|
||||
pub id: ID,
|
||||
pub version: String,
|
||||
@@ -29,12 +31,81 @@ impl Policy {
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn match_resource(&self, resource: &str) -> bool {
|
||||
for statement in self.statements.iter() {
|
||||
if statement.resources.match_resource(resource) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn drop_duplicate_statements(&mut self) {
|
||||
let mut dups = HashSet::new();
|
||||
for i in 0..self.statements.len() {
|
||||
if dups.contains(&i) {
|
||||
// i is already a duplicate of some statement, so we do not need to
|
||||
// compare with it.
|
||||
continue;
|
||||
}
|
||||
for j in (i + 1)..self.statements.len() {
|
||||
if !self.statements[i].eq(&self.statements[j]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// save duplicate statement index for removal.
|
||||
dups.insert(j);
|
||||
}
|
||||
}
|
||||
|
||||
// remove duplicate items from the slice.
|
||||
let mut c = 0;
|
||||
for i in 0..self.statements.len() {
|
||||
if dups.contains(&i) {
|
||||
continue;
|
||||
}
|
||||
self.statements[c] = self.statements[i].clone();
|
||||
c += 1;
|
||||
}
|
||||
self.statements.truncate(c);
|
||||
}
|
||||
pub fn merge_policies(inputs: Vec<Policy>) -> Policy {
|
||||
let mut merged = Policy::default();
|
||||
|
||||
for p in inputs {
|
||||
if merged.version.is_empty() {
|
||||
merged.version = p.version.clone();
|
||||
}
|
||||
for st in p.statements {
|
||||
merged.statements.push(st.clone());
|
||||
}
|
||||
}
|
||||
merged.drop_duplicate_statements();
|
||||
merged
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.statements.is_empty()
|
||||
}
|
||||
|
||||
pub fn validate(&self) -> Result<()> {
|
||||
self.is_valid()
|
||||
}
|
||||
|
||||
pub fn parse_config(data: &[u8]) -> Result<Policy> {
|
||||
let policy: Policy = serde_json::from_slice(data)?;
|
||||
policy.validate()?;
|
||||
Ok(policy)
|
||||
}
|
||||
}
|
||||
|
||||
impl Validator for Policy {
|
||||
fn is_valid(&self) -> Result<(), Error> {
|
||||
type Error = Error;
|
||||
|
||||
fn is_valid(&self) -> Result<()> {
|
||||
if !self.id.is_empty() && !self.id.eq(DEFAULT_VERSION) {
|
||||
return Err(Error::InvalidVersion(self.id.0.clone()));
|
||||
return Err(IamError::InvalidVersion(self.id.0.clone()).into());
|
||||
}
|
||||
|
||||
for statement in self.statements.iter() {
|
||||
@@ -48,15 +119,19 @@ impl Validator for Policy {
|
||||
pub mod default {
|
||||
use std::{collections::HashSet, sync::LazyLock};
|
||||
|
||||
use crate::policy::{
|
||||
action::{Action, AdminAction, KmsAction, S3Action},
|
||||
resource::Resource,
|
||||
ActionSet, Effect, Functions, ResourceSet, Statement, DEFAULT_VERSION,
|
||||
use crate::{
|
||||
policy::{
|
||||
action::{Action, AdminAction, KmsAction, S3Action},
|
||||
resource::Resource,
|
||||
ActionSet, Effect, Functions, ResourceSet, Statement,
|
||||
},
|
||||
sys::DEFAULT_VERSION,
|
||||
};
|
||||
|
||||
use super::Policy;
|
||||
|
||||
pub const DEFAULT_POLICIES: LazyLock<[(&'static str, Policy); 6]> = LazyLock::new(|| {
|
||||
#[allow(clippy::incompatible_msrv)]
|
||||
pub static DEFAULT_POLICIES: LazyLock<[(&'static str, Policy); 6]> = LazyLock::new(|| {
|
||||
[
|
||||
(
|
||||
"readwrite",
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
use ecstore::error::{Error, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
hash::Hash,
|
||||
ops::Deref,
|
||||
};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::sys::Validator;
|
||||
|
||||
use super::{
|
||||
function::key_name::KeyName,
|
||||
utils::{path, wildcard},
|
||||
Error, Validator,
|
||||
Error as IamError,
|
||||
};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Default)]
|
||||
#[derive(Serialize, Deserialize, Clone, Default, Debug)]
|
||||
pub struct ResourceSet(pub HashSet<Resource>);
|
||||
|
||||
impl ResourceSet {
|
||||
@@ -25,6 +27,16 @@ impl ResourceSet {
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn match_resource(&self, resource: &str) -> bool {
|
||||
for re in self.0.iter() {
|
||||
if re.match_resource(resource) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ResourceSet {
|
||||
@@ -36,7 +48,8 @@ impl Deref for ResourceSet {
|
||||
}
|
||||
|
||||
impl Validator for ResourceSet {
|
||||
fn is_valid(&self) -> Result<(), Error> {
|
||||
type Error = Error;
|
||||
fn is_valid(&self) -> Result<()> {
|
||||
for resource in self.0.iter() {
|
||||
resource.is_valid()?;
|
||||
}
|
||||
@@ -45,7 +58,13 @@ impl Validator for ResourceSet {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Hash, Eq, PartialEq, Serialize, Deserialize, Clone)]
|
||||
impl PartialEq for ResourceSet {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.len() == other.len() && self.0.iter().all(|x| other.0.contains(x))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Hash, Eq, PartialEq, Serialize, Deserialize, Clone, Debug)]
|
||||
pub enum Resource {
|
||||
S3(String),
|
||||
Kms(String),
|
||||
@@ -76,15 +95,19 @@ impl Resource {
|
||||
|
||||
wildcard::is_match(pattern, resource)
|
||||
}
|
||||
|
||||
pub fn match_resource(&self, resource: &str) -> bool {
|
||||
self.is_match(resource, &HashMap::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for Resource {
|
||||
type Error = Error;
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
let resource = if value.starts_with(Self::S3_PREFIX) {
|
||||
Resource::S3(value[Self::S3_PREFIX.len()..].into())
|
||||
Resource::S3(value.strip_prefix(Self::S3_PREFIX).unwrap().into())
|
||||
} else {
|
||||
return Err(Error::InvalidResource("unknown".into(), value.into()));
|
||||
return Err(IamError::InvalidResource("unknown".into(), value.into()).into());
|
||||
};
|
||||
|
||||
resource.is_valid()?;
|
||||
@@ -93,11 +116,12 @@ impl TryFrom<&str> for Resource {
|
||||
}
|
||||
|
||||
impl Validator for Resource {
|
||||
type Error = Error;
|
||||
fn is_valid(&self) -> Result<(), Error> {
|
||||
match self {
|
||||
Self::S3(pattern) => {
|
||||
if pattern.is_empty() || pattern.starts_with('/') {
|
||||
return Err(Error::InvalidResource("s3".into(), pattern.into()));
|
||||
return Err(IamError::InvalidResource("s3".into(), pattern.into()).into());
|
||||
}
|
||||
}
|
||||
Self::Kms(pattern) => {
|
||||
@@ -108,7 +132,7 @@ impl Validator for Resource {
|
||||
.map(|(i, _)| i)
|
||||
.is_some()
|
||||
{
|
||||
return Err(Error::InvalidResource("kms".into(), pattern.into()));
|
||||
return Err(IamError::InvalidResource("kms".into(), pattern.into()).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+23
-12
@@ -1,8 +1,10 @@
|
||||
use crate::sys::{Args, Validator};
|
||||
|
||||
use super::{action::Action, ActionSet, Effect, Error as IamError, Functions, ResourceSet, ID};
|
||||
use ecstore::error::{Error, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{action::Action, ActionSet, Args, Effect, Error, Functions, ResourceSet, Validator, ID};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Default)]
|
||||
#[derive(Serialize, Deserialize, Clone, Default, Debug)]
|
||||
pub struct Statement {
|
||||
pub sid: ID,
|
||||
pub effect: Effect,
|
||||
@@ -60,17 +62,15 @@ impl Statement {
|
||||
resource.push('/');
|
||||
}
|
||||
|
||||
if self.is_kms() {
|
||||
if resource == "/" || self.resources.is_empty() {
|
||||
break 'c self.conditions.evaluate(&args.conditions);
|
||||
}
|
||||
if self.is_kms() && (resource == "/" || self.resources.is_empty()) {
|
||||
break 'c self.conditions.evaluate(args.conditions);
|
||||
}
|
||||
|
||||
if !self.resources.is_match(&resource, &args.conditions) && !self.is_admin() && !self.is_sts() {
|
||||
if !self.resources.is_match(&resource, args.conditions) && !self.is_admin() && !self.is_sts() {
|
||||
break 'c false;
|
||||
}
|
||||
|
||||
self.conditions.evaluate(&args.conditions)
|
||||
self.conditions.evaluate(args.conditions)
|
||||
};
|
||||
|
||||
self.effect.is_allowed(check)
|
||||
@@ -78,17 +78,18 @@ impl Statement {
|
||||
}
|
||||
|
||||
impl Validator for Statement {
|
||||
fn is_valid(&self) -> Result<(), Error> {
|
||||
type Error = Error;
|
||||
fn is_valid(&self) -> Result<()> {
|
||||
self.effect.is_valid()?;
|
||||
// check sid
|
||||
self.sid.is_valid()?;
|
||||
|
||||
if self.actions.is_empty() || self.not_actions.is_empty() {
|
||||
return Err(Error::NonAction);
|
||||
return Err(IamError::NonAction.into());
|
||||
}
|
||||
|
||||
if self.resources.is_empty() {
|
||||
return Err(Error::NonResource);
|
||||
return Err(IamError::NonResource.into());
|
||||
}
|
||||
|
||||
self.actions.is_valid()?;
|
||||
@@ -98,3 +99,13 @@ impl Validator for Statement {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Statement {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.effect == other.effect
|
||||
&& self.actions == other.actions
|
||||
&& self.not_actions == other.not_actions
|
||||
&& self.resources == other.resources
|
||||
&& self.conditions == other.conditions
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use serde_json::Value;
|
||||
pub mod path;
|
||||
pub mod wildcard;
|
||||
|
||||
pub fn get_values_from_claims(claim: &HashMap<String, Value>, chaim_name: &str) -> (Vec<String>, bool) {
|
||||
pub fn _get_values_from_claims(claim: &HashMap<String, Value>, chaim_name: &str) -> (Vec<String>, bool) {
|
||||
let mut result = vec![];
|
||||
let Some(pname) = claim.get(chaim_name) else {
|
||||
return (result, false);
|
||||
@@ -39,7 +39,7 @@ pub fn get_values_from_claims(claim: &HashMap<String, Value>, chaim_name: &str)
|
||||
(result, true)
|
||||
}
|
||||
|
||||
pub fn split_path(path: &str, second_index: bool) -> (&str, &str) {
|
||||
pub fn _split_path(path: &str, second_index: bool) -> (&str, &str) {
|
||||
let index = if second_index {
|
||||
let Some(first) = path.find('/') else {
|
||||
return (path, "");
|
||||
@@ -63,7 +63,7 @@ pub fn split_path(path: &str, second_index: bool) -> (&str, &str) {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::split_path;
|
||||
use super::_split_path;
|
||||
|
||||
#[test_case::test_case("format.json", false => ("format.json", ""))]
|
||||
#[test_case::test_case("users/tester.json", false => ("users/", "tester.json"))]
|
||||
@@ -82,6 +82,6 @@ mod tests {
|
||||
("policydb/groups/", "cn=project/d,ou=groups,ou=swengg,dc=min,dc=io.json"))
|
||||
]
|
||||
fn test_split_path(path: &str, second_index: bool) -> (&str, &str) {
|
||||
split_path(path, second_index)
|
||||
_split_path(path, second_index)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user