mirror of
https://github.com/rustfs/rustfs.git
synced 2026-01-17 01:30:33 +00:00
add bucketpolicy
This commit is contained in:
@@ -17,7 +17,9 @@ use policy::{
|
||||
arn::ARN,
|
||||
auth::{self, get_claims_from_token_with_secret, is_secret_key_valid, jwt_sign, Credentials, UserIdentity},
|
||||
format::Format,
|
||||
policy::{iam_policy_claim_name_sa, Policy, PolicyDoc, DEFAULT_POLICIES, EMBEDDED_POLICY_TYPE, INHERITED_POLICY_TYPE},
|
||||
policy::{
|
||||
default::DEFAULT_POLICIES, iam_policy_claim_name_sa, Policy, PolicyDoc, EMBEDDED_POLICY_TYPE, INHERITED_POLICY_TYPE,
|
||||
},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
@@ -5,27 +5,22 @@ mod function;
|
||||
mod id;
|
||||
#[allow(clippy::module_inception)]
|
||||
mod policy;
|
||||
mod principal;
|
||||
pub mod resource;
|
||||
pub mod statement;
|
||||
pub(crate) mod utils;
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use action::Action;
|
||||
pub use action::ActionSet;
|
||||
pub use doc::PolicyDoc;
|
||||
|
||||
pub use effect::Effect;
|
||||
pub use function::Functions;
|
||||
pub use id::ID;
|
||||
pub use policy::{default::DEFAULT_POLICIES, Policy};
|
||||
pub use policy::*;
|
||||
pub use principal::Principal;
|
||||
pub use resource::ResourceSet;
|
||||
|
||||
use serde_json::Value;
|
||||
pub use statement::Statement;
|
||||
|
||||
use common::error::Result;
|
||||
|
||||
pub const EMBEDDED_POLICY_TYPE: &str = "embedded-policy";
|
||||
pub const INHERITED_POLICY_TYPE: &str = "inherited-policy";
|
||||
|
||||
@@ -56,73 +51,3 @@ pub enum Error {
|
||||
#[error("invalid resource, type: '{0}', pattern: '{1}'")]
|
||||
InvalidResource(String, String),
|
||||
}
|
||||
|
||||
/// DEFAULT_VERSION is the default version.
|
||||
/// https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_version.html
|
||||
pub const DEFAULT_VERSION: &str = "2012-10-17";
|
||||
|
||||
/// check the data is Validator
|
||||
pub trait Validator {
|
||||
type Error;
|
||||
fn is_valid(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Args<'a> {
|
||||
pub account: &'a str,
|
||||
pub groups: &'a Option<Vec<String>>,
|
||||
pub action: Action,
|
||||
pub bucket: &'a str,
|
||||
pub conditions: &'a HashMap<String, Vec<String>>,
|
||||
pub is_owner: bool,
|
||||
pub object: &'a str,
|
||||
pub claims: &'a HashMap<String, Value>,
|
||||
pub deny_only: bool,
|
||||
}
|
||||
|
||||
impl Args<'_> {
|
||||
pub fn get_role_arn(&self) -> Option<&str> {
|
||||
self.claims.get("roleArn").and_then(|x| x.as_str())
|
||||
}
|
||||
pub fn get_policies(&self, policy_claim_name: &str) -> (HashSet<String>, bool) {
|
||||
get_policies_from_claims(self.claims, policy_claim_name)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_values_from_claims(claims: &HashMap<String, Value>, claim_name: &str) -> (HashSet<String>, bool) {
|
||||
let mut s = HashSet::new();
|
||||
if let Some(pname) = claims.get(claim_name) {
|
||||
if let Some(pnames) = pname.as_array() {
|
||||
for pname in pnames {
|
||||
if let Some(pname_str) = pname.as_str() {
|
||||
for pname in pname_str.split(',') {
|
||||
let pname = pname.trim();
|
||||
if !pname.is_empty() {
|
||||
s.insert(pname.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (s, true);
|
||||
} else if let Some(pname_str) = pname.as_str() {
|
||||
for pname in pname_str.split(',') {
|
||||
let pname = pname.trim();
|
||||
if !pname.is_empty() {
|
||||
s.insert(pname.to_string());
|
||||
}
|
||||
}
|
||||
return (s, true);
|
||||
}
|
||||
}
|
||||
(s, false)
|
||||
}
|
||||
|
||||
fn get_policies_from_claims(claims: &HashMap<String, Value>, policy_claim_name: &str) -> (HashSet<String>, bool) {
|
||||
get_values_from_claims(claims, policy_claim_name)
|
||||
}
|
||||
|
||||
pub fn iam_policy_claim_name_sa() -> String {
|
||||
"sa-policy".to_string()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,42 @@
|
||||
use super::{Args, Effect, Error as IamError, Statement, Validator, DEFAULT_VERSION, ID};
|
||||
use super::{action::Action, statement::BPStatement, Effect, Error as IamError, Statement, ID};
|
||||
use common::error::{Error, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
use serde_json::Value;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
/// DEFAULT_VERSION is the default version.
|
||||
/// https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_version.html
|
||||
pub const DEFAULT_VERSION: &str = "2012-10-17";
|
||||
|
||||
/// check the data is Validator
|
||||
pub trait Validator {
|
||||
type Error;
|
||||
fn is_valid(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Args<'a> {
|
||||
pub account: &'a str,
|
||||
pub groups: &'a Option<Vec<String>>,
|
||||
pub action: Action,
|
||||
pub bucket: &'a str,
|
||||
pub conditions: &'a HashMap<String, Vec<String>>,
|
||||
pub is_owner: bool,
|
||||
pub object: &'a str,
|
||||
pub claims: &'a HashMap<String, Value>,
|
||||
pub deny_only: bool,
|
||||
}
|
||||
|
||||
impl Args<'_> {
|
||||
pub fn get_role_arn(&self) -> Option<&str> {
|
||||
self.claims.get("roleArn").and_then(|x| x.as_str())
|
||||
}
|
||||
pub fn get_policies(&self, policy_claim_name: &str) -> (HashSet<String>, bool) {
|
||||
get_policies_from_claims(self.claims, policy_claim_name)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Default, Debug)]
|
||||
pub struct Policy {
|
||||
@@ -118,6 +153,101 @@ impl Validator for Policy {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct BucketPolicyArgs<'a> {
|
||||
pub account: &'a str,
|
||||
pub groups: &'a Option<Vec<String>>,
|
||||
pub action: Action,
|
||||
pub bucket: &'a str,
|
||||
pub conditions: &'a HashMap<String, Vec<String>>,
|
||||
pub is_owner: bool,
|
||||
pub object: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Default, Debug)]
|
||||
pub struct BucketPolicy {
|
||||
#[serde(default, rename = "ID")]
|
||||
pub id: ID,
|
||||
#[serde(rename = "Version")]
|
||||
pub version: String,
|
||||
#[serde(rename = "Statement")]
|
||||
pub statements: Vec<BPStatement>,
|
||||
}
|
||||
|
||||
impl BucketPolicy {
|
||||
pub fn is_allowed(&self, args: &BucketPolicyArgs) -> bool {
|
||||
for statement in self.statements.iter().filter(|s| matches!(s.effect, Effect::Deny)) {
|
||||
if !statement.is_allowed(args) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if args.is_owner {
|
||||
return true;
|
||||
}
|
||||
|
||||
for statement in self.statements.iter().filter(|s| matches!(s.effect, Effect::Allow)) {
|
||||
if statement.is_allowed(args) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Validator for BucketPolicy {
|
||||
type Error = Error;
|
||||
|
||||
fn is_valid(&self) -> Result<()> {
|
||||
if !self.id.is_empty() && !self.id.eq(DEFAULT_VERSION) {
|
||||
return Err(IamError::InvalidVersion(self.id.0.clone()).into());
|
||||
}
|
||||
|
||||
for statement in self.statements.iter() {
|
||||
statement.is_valid()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn get_values_from_claims(claims: &HashMap<String, Value>, claim_name: &str) -> (HashSet<String>, bool) {
|
||||
let mut s = HashSet::new();
|
||||
if let Some(pname) = claims.get(claim_name) {
|
||||
if let Some(pnames) = pname.as_array() {
|
||||
for pname in pnames {
|
||||
if let Some(pname_str) = pname.as_str() {
|
||||
for pname in pname_str.split(',') {
|
||||
let pname = pname.trim();
|
||||
if !pname.is_empty() {
|
||||
s.insert(pname.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (s, true);
|
||||
} else if let Some(pname_str) = pname.as_str() {
|
||||
for pname in pname_str.split(',') {
|
||||
let pname = pname.trim();
|
||||
if !pname.is_empty() {
|
||||
s.insert(pname.to_string());
|
||||
}
|
||||
}
|
||||
return (s, true);
|
||||
}
|
||||
}
|
||||
(s, false)
|
||||
}
|
||||
|
||||
fn get_policies_from_claims(claims: &HashMap<String, Value>, policy_claim_name: &str) -> (HashSet<String>, bool) {
|
||||
get_values_from_claims(claims, policy_claim_name)
|
||||
}
|
||||
|
||||
pub fn iam_policy_claim_name_sa() -> String {
|
||||
"sa-policy".to_string()
|
||||
}
|
||||
|
||||
pub mod default {
|
||||
use std::{collections::HashSet, sync::LazyLock};
|
||||
|
||||
|
||||
32
policy/src/policy/principal.rs
Normal file
32
policy/src/policy/principal.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
use super::{utils::wildcard, Validator};
|
||||
use common::error::{Error, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq)]
|
||||
#[serde(rename_all = "PascalCase", default)]
|
||||
pub struct Principal {
|
||||
#[serde(rename = "AWS")]
|
||||
aws: HashSet<String>,
|
||||
}
|
||||
|
||||
impl Principal {
|
||||
pub fn is_match(&self, parincipal: &str) -> bool {
|
||||
for pattern in self.aws.iter() {
|
||||
if wildcard::is_simple_match(pattern, parincipal) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Validator for Principal {
|
||||
type Error = Error;
|
||||
fn is_valid(&self) -> Result<()> {
|
||||
if self.aws.is_empty() {
|
||||
return Err(Error::msg("Principal is empty"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
use super::{action::Action, ActionSet, Args, Effect, Error as IamError, Functions, ResourceSet, Validator, ID};
|
||||
use super::{
|
||||
action::Action, ActionSet, Args, BucketPolicyArgs, Effect, Error as IamError, Functions, Principal, ResourceSet, Validator,
|
||||
ID,
|
||||
};
|
||||
use common::error::{Error, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -115,3 +118,86 @@ impl PartialEq for Statement {
|
||||
&& self.conditions == other.conditions
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
||||
#[serde(rename_all = "PascalCase", default)]
|
||||
pub struct BPStatement {
|
||||
#[serde(rename = "Sid", default)]
|
||||
pub sid: ID,
|
||||
#[serde(rename = "Effect")]
|
||||
pub effect: Effect,
|
||||
#[serde(rename = "Principal")]
|
||||
pub principal: Principal,
|
||||
#[serde(rename = "Action")]
|
||||
pub actions: ActionSet,
|
||||
#[serde(rename = "NotAction", default)]
|
||||
pub not_actions: ActionSet,
|
||||
#[serde(rename = "Resource", default)]
|
||||
pub resources: ResourceSet,
|
||||
#[serde(rename = "NotResource", default)]
|
||||
pub not_resources: ResourceSet,
|
||||
#[serde(rename = "Condition", default)]
|
||||
pub conditions: Functions,
|
||||
}
|
||||
|
||||
impl BPStatement {
|
||||
pub fn is_allowed(&self, args: &BucketPolicyArgs) -> bool {
|
||||
let check = 'c: {
|
||||
if !self.principal.is_match(args.account) {
|
||||
break 'c false;
|
||||
}
|
||||
|
||||
if (!self.actions.is_match(&args.action) && !self.actions.is_empty()) || self.not_actions.is_match(&args.action) {
|
||||
break 'c false;
|
||||
}
|
||||
|
||||
let mut resource = String::from(args.bucket);
|
||||
if !args.object.is_empty() {
|
||||
if !args.object.starts_with('/') {
|
||||
resource.push('/');
|
||||
}
|
||||
|
||||
resource.push_str(args.object);
|
||||
} else {
|
||||
resource.push('/');
|
||||
}
|
||||
|
||||
if !self.resources.is_empty() && !self.resources.is_match(&resource, args.conditions) {
|
||||
break 'c false;
|
||||
}
|
||||
|
||||
if !self.not_resources.is_empty() && self.not_resources.is_match(&resource, args.conditions) {
|
||||
break 'c false;
|
||||
}
|
||||
|
||||
self.conditions.evaluate(args.conditions)
|
||||
};
|
||||
|
||||
self.effect.is_allowed(check)
|
||||
}
|
||||
}
|
||||
|
||||
impl Validator for BPStatement {
|
||||
type Error = Error;
|
||||
fn is_valid(&self) -> Result<()> {
|
||||
self.effect.is_valid()?;
|
||||
// check sid
|
||||
self.sid.is_valid()?;
|
||||
|
||||
self.principal.is_valid()?;
|
||||
|
||||
if self.actions.is_empty() && self.not_actions.is_empty() {
|
||||
return Err(IamError::NonAction.into());
|
||||
}
|
||||
|
||||
if self.resources.is_empty() {
|
||||
return Err(IamError::NonResource.into());
|
||||
}
|
||||
|
||||
self.actions.is_valid()?;
|
||||
self.not_actions.is_valid()?;
|
||||
self.resources.is_valid()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user