mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-21 11:56:38 +00:00
done bucket policy, need test
This commit is contained in:
@@ -1,4 +1,6 @@
|
|||||||
#[derive(Debug, thiserror::Error)]
|
use crate::error::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
|
||||||
pub enum BucketMetadataError {
|
pub enum BucketMetadataError {
|
||||||
#[error("tagging not found")]
|
#[error("tagging not found")]
|
||||||
TaggingNotFound,
|
TaggingNotFound,
|
||||||
@@ -17,3 +19,13 @@ pub enum BucketMetadataError {
|
|||||||
#[error("bucket remote target not found")]
|
#[error("bucket remote target not found")]
|
||||||
BucketRemoteTargetNotFound,
|
BucketRemoteTargetNotFound,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl BucketMetadataError {
|
||||||
|
pub fn is(&self, err: &Error) -> bool {
|
||||||
|
if let Some(e) = err.downcast_ref::<BucketMetadataError>() {
|
||||||
|
e == self
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,10 +6,8 @@ use super::{
|
|||||||
use byteorder::{BigEndian, ByteOrder, LittleEndian};
|
use byteorder::{BigEndian, ByteOrder, LittleEndian};
|
||||||
use rmp_serde::Serializer as rmpSerializer;
|
use rmp_serde::Serializer as rmpSerializer;
|
||||||
use serde::Serializer;
|
use serde::Serializer;
|
||||||
use serde::{Deserialize, Deserializer, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::Display;
|
|
||||||
use std::str::FromStr;
|
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
use tracing::{error, warn};
|
use tracing::{error, warn};
|
||||||
|
|
||||||
@@ -381,17 +379,6 @@ async fn read_bucket_metadata(api: &ECStore, bucket: &str) -> Result<BucketMetad
|
|||||||
Ok(bm)
|
Ok(bm)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _deserialize_from_str<'de, S, D>(_deserializer: D) -> core::result::Result<S, D::Error>
|
|
||||||
where
|
|
||||||
S: FromStr,
|
|
||||||
S::Err: Display,
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
// let s: String = Deserialize::deserialize(deserializer)?;
|
|
||||||
// S::from_str(&s).map_err(de::Error::custom)
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn _write_time<S>(t: &OffsetDateTime, s: S) -> Result<S::Ok, S::Error>
|
fn _write_time<S>(t: &OffsetDateTime, s: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
S: Serializer,
|
S: Serializer,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::collections::HashSet;
|
|||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
use crate::bucket::error::BucketMetadataError;
|
use crate::bucket::error::BucketMetadataError;
|
||||||
use crate::bucket::metadata::load_bucket_metadata_parse;
|
use crate::bucket::metadata::{load_bucket_metadata_parse, BUCKET_LIFECYCLE_CONFIG};
|
||||||
use crate::bucket::utils::is_meta_bucketname;
|
use crate::bucket::utils::is_meta_bucketname;
|
||||||
use crate::config;
|
use crate::config;
|
||||||
use crate::config::error::ConfigError;
|
use crate::config::error::ConfigError;
|
||||||
@@ -143,15 +143,42 @@ impl BucketMetadataSys {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// async fn reset(&mut self) {
|
async fn _reset(&mut self) {
|
||||||
// let mut map = self.metadata_map.write().await;
|
let mut map = self.metadata_map.write().await;
|
||||||
// map.clear();
|
map.clear();
|
||||||
// }
|
}
|
||||||
|
|
||||||
pub async fn update(&mut self, bucket: &str, config_file: &str, data: Vec<u8>) -> Result<OffsetDateTime> {
|
pub async fn update(&mut self, bucket: &str, config_file: &str, data: Vec<u8>) -> Result<OffsetDateTime> {
|
||||||
self.update_and_parse(bucket, config_file, data, true).await
|
self.update_and_parse(bucket, config_file, data, true).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn delete(&mut self, bucket: &str, config_file: &str) -> Result<OffsetDateTime> {
|
||||||
|
if config_file == BUCKET_LIFECYCLE_CONFIG {
|
||||||
|
let meta = match self.get_config_from_disk(bucket).await {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(err) => {
|
||||||
|
if !config::error::is_not_found(&err) {
|
||||||
|
return Err(err);
|
||||||
|
} else {
|
||||||
|
BucketMetadata::new(bucket)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if !meta.lifecycle_config_xml.is_empty() {
|
||||||
|
let cfg = Lifecycle::unmarshal(&meta.lifecycle_config_xml)?;
|
||||||
|
for _v in cfg.rules.iter() {
|
||||||
|
// TODO: FIXME:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: other lifecycle handle
|
||||||
|
}
|
||||||
|
|
||||||
|
self.update_and_parse(bucket, config_file, Vec::new(), false).await
|
||||||
|
}
|
||||||
|
|
||||||
async fn update_and_parse(&mut self, bucket: &str, config_file: &str, data: Vec<u8>, parse: bool) -> Result<OffsetDateTime> {
|
async fn update_and_parse(&mut self, bucket: &str, config_file: &str, data: Vec<u8>, parse: bool) -> Result<OffsetDateTime> {
|
||||||
let layer = new_object_layer_fn();
|
let layer = new_object_layer_fn();
|
||||||
let lock = layer.read().await;
|
let lock = layer.read().await;
|
||||||
@@ -201,6 +228,22 @@ impl BucketMetadataSys {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_config_from_disk(&self, bucket: &str) -> Result<BucketMetadata> {
|
||||||
|
if self.api.as_ref().is_none() {
|
||||||
|
return Err(Error::msg("errBucketMetadataNotInitialized"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_meta_bucketname(bucket) {
|
||||||
|
return Err(Error::msg("errInvalidArgument"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(api) = self.api.as_ref() {
|
||||||
|
load_bucket_metadata(&api, bucket).await
|
||||||
|
} else {
|
||||||
|
Err(Error::msg("errBucketMetadataNotInitialized"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_config(&self, bucket: &str) -> Result<(BucketMetadata, bool)> {
|
pub async fn get_config(&self, bucket: &str) -> Result<(BucketMetadata, bool)> {
|
||||||
if let Some(api) = self.api.as_ref() {
|
if let Some(api) = self.api.as_ref() {
|
||||||
let has_bm = {
|
let has_bm = {
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
mod encryption;
|
mod encryption;
|
||||||
mod error;
|
pub mod error;
|
||||||
mod event;
|
mod event;
|
||||||
mod lifecycle;
|
mod lifecycle;
|
||||||
pub mod metadata;
|
pub mod metadata;
|
||||||
mod metadata_sys;
|
mod metadata_sys;
|
||||||
mod objectlock;
|
mod objectlock;
|
||||||
mod policy;
|
pub mod policy;
|
||||||
|
pub mod policy_sys;
|
||||||
mod quota;
|
mod quota;
|
||||||
mod replication;
|
mod replication;
|
||||||
pub mod tags;
|
pub mod tags;
|
||||||
|
|||||||
@@ -1,80 +1,285 @@
|
|||||||
|
use crate::{bucket::policy::condition::keyname::ALL_SUPPORT_KEYS, utils};
|
||||||
|
use lazy_static::lazy_static;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashSet;
|
use std::{
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
vec,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default,Clone)]
|
use super::condition::{
|
||||||
|
key::{Key, KeySet},
|
||||||
|
keyname::{KeyName, COMMOM_KEYS},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq, Eq)]
|
||||||
pub struct ActionSet(HashSet<Action>);
|
pub struct ActionSet(HashSet<Action>);
|
||||||
|
|
||||||
impl ActionSet {}
|
impl ActionSet {
|
||||||
|
pub fn as_ref(&self) -> &HashSet<Action> {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
pub fn is_match(&self, act: &Action) -> bool {
|
||||||
|
for item in self.0.iter() {
|
||||||
|
if item.is_match(act) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if item == &Action::GetObjectVersion {
|
||||||
|
if act == &Action::GetObjectVersion {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.0.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 定义Action枚举类型
|
// 定义Action枚举类型
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Default, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Default, Hash)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
|
#[serde(rename = "s3:AbortMultipartUpload")]
|
||||||
AbortMultipartUpload,
|
AbortMultipartUpload,
|
||||||
|
#[serde(rename = "s3:CreateBucket")]
|
||||||
CreateBucket,
|
CreateBucket,
|
||||||
|
#[serde(rename = "s3:DeleteBucket")]
|
||||||
DeleteBucket,
|
DeleteBucket,
|
||||||
|
#[serde(rename = "s3:ForceDeleteBucket")]
|
||||||
ForceDeleteBucket,
|
ForceDeleteBucket,
|
||||||
|
#[serde(rename = "s3:DeleteBucketPolicy")]
|
||||||
DeleteBucketPolicy,
|
DeleteBucketPolicy,
|
||||||
|
#[serde(rename = "s3:DeleteBucketCors")]
|
||||||
DeleteBucketCors,
|
DeleteBucketCors,
|
||||||
|
#[serde(rename = "s3:DeleteObject")]
|
||||||
DeleteObject,
|
DeleteObject,
|
||||||
|
#[serde(rename = "s3:GetBucketLocation")]
|
||||||
GetBucketLocation,
|
GetBucketLocation,
|
||||||
|
#[serde(rename = "s3:GetBucketNotification")]
|
||||||
GetBucketNotification,
|
GetBucketNotification,
|
||||||
|
#[serde(rename = "s3:GetBucketPolicy")]
|
||||||
GetBucketPolicy,
|
GetBucketPolicy,
|
||||||
|
#[serde(rename = "s3:GetBucketCors")]
|
||||||
GetBucketCors,
|
GetBucketCors,
|
||||||
|
#[serde(rename = "s3:GetObject")]
|
||||||
GetObject,
|
GetObject,
|
||||||
|
#[serde(rename = "s3:GetObjectAttributes")]
|
||||||
GetObjectAttributes,
|
GetObjectAttributes,
|
||||||
|
#[serde(rename = "s3:HeadBucket")]
|
||||||
HeadBucket,
|
HeadBucket,
|
||||||
|
#[serde(rename = "s3:ListAllMyBuckets")]
|
||||||
ListAllMyBuckets,
|
ListAllMyBuckets,
|
||||||
|
#[serde(rename = "s3:ListBucket")]
|
||||||
ListBucket,
|
ListBucket,
|
||||||
|
#[serde(rename = "s3:GetBucketPolicyStatus")]
|
||||||
GetBucketPolicyStatus,
|
GetBucketPolicyStatus,
|
||||||
|
#[serde(rename = "s3:ListBucketVersions")]
|
||||||
ListBucketVersions,
|
ListBucketVersions,
|
||||||
|
#[serde(rename = "s3:ListBucketMultipartUploads")]
|
||||||
ListBucketMultipartUploads,
|
ListBucketMultipartUploads,
|
||||||
|
#[serde(rename = "s3:ListenNotification")]
|
||||||
ListenNotification,
|
ListenNotification,
|
||||||
|
#[serde(rename = "s3:ListenBucketNotification")]
|
||||||
ListenBucketNotification,
|
ListenBucketNotification,
|
||||||
|
#[serde(rename = "s3:ListMultipartUploadParts")]
|
||||||
ListMultipartUploadParts,
|
ListMultipartUploadParts,
|
||||||
PutBucketLifecycle,
|
#[serde(rename = "s3:PutLifecycleConfiguration")]
|
||||||
GetBucketLifecycle,
|
PutLifecycleConfiguration,
|
||||||
|
#[serde(rename = "s3:GetLifecycleConfiguration")]
|
||||||
|
GetLifecycleConfiguration,
|
||||||
|
#[serde(rename = "s3:PutBucketNotification")]
|
||||||
PutBucketNotification,
|
PutBucketNotification,
|
||||||
|
#[serde(rename = "s3:PutBucketPolicy")]
|
||||||
PutBucketPolicy,
|
PutBucketPolicy,
|
||||||
|
#[serde(rename = "s3:PutBucketCors")]
|
||||||
PutBucketCors,
|
PutBucketCors,
|
||||||
|
#[serde(rename = "s3:PutObject")]
|
||||||
PutObject,
|
PutObject,
|
||||||
|
#[serde(rename = "s3:DeleteObjectVersion")]
|
||||||
DeleteObjectVersion,
|
DeleteObjectVersion,
|
||||||
|
#[serde(rename = "s3:DeleteObjectVersionTagging")]
|
||||||
DeleteObjectVersionTagging,
|
DeleteObjectVersionTagging,
|
||||||
|
#[serde(rename = "s3:GetObjectVersion")]
|
||||||
GetObjectVersion,
|
GetObjectVersion,
|
||||||
|
#[serde(rename = "s3:GetObjectVersionAttributes")]
|
||||||
GetObjectVersionAttributes,
|
GetObjectVersionAttributes,
|
||||||
|
#[serde(rename = "s3:GetObjectVersionTagging")]
|
||||||
GetObjectVersionTagging,
|
GetObjectVersionTagging,
|
||||||
|
#[serde(rename = "s3:PutObjectVersionTagging")]
|
||||||
PutObjectVersionTagging,
|
PutObjectVersionTagging,
|
||||||
|
#[serde(rename = "s3:BypassGovernanceRetention")]
|
||||||
BypassGovernanceRetention,
|
BypassGovernanceRetention,
|
||||||
|
#[serde(rename = "s3:PutObjectRetention")]
|
||||||
PutObjectRetention,
|
PutObjectRetention,
|
||||||
|
#[serde(rename = "s3:GetObjectRetention")]
|
||||||
GetObjectRetention,
|
GetObjectRetention,
|
||||||
|
#[serde(rename = "s3:GetObjectLegalHold")]
|
||||||
GetObjectLegalHold,
|
GetObjectLegalHold,
|
||||||
|
#[serde(rename = "s3:PutObjectLegalHold")]
|
||||||
PutObjectLegalHold,
|
PutObjectLegalHold,
|
||||||
|
#[serde(rename = "s3:GetBucketObjectLockConfiguration")]
|
||||||
GetBucketObjectLockConfiguration,
|
GetBucketObjectLockConfiguration,
|
||||||
|
#[serde(rename = "s3:PutBucketObjectLockConfiguration")]
|
||||||
PutBucketObjectLockConfiguration,
|
PutBucketObjectLockConfiguration,
|
||||||
|
#[serde(rename = "s3:GetBucketTagging")]
|
||||||
GetBucketTagging,
|
GetBucketTagging,
|
||||||
|
#[serde(rename = "s3:PutBucketTagging")]
|
||||||
PutBucketTagging,
|
PutBucketTagging,
|
||||||
|
#[serde(rename = "s3:GetObjectTagging")]
|
||||||
GetObjectTagging,
|
GetObjectTagging,
|
||||||
|
#[serde(rename = "s3:PutObjectTagging")]
|
||||||
PutObjectTagging,
|
PutObjectTagging,
|
||||||
|
#[serde(rename = "s3:DeleteObjectTagging")]
|
||||||
DeleteObjectTagging,
|
DeleteObjectTagging,
|
||||||
|
#[serde(rename = "s3:PutBucketEncryption")]
|
||||||
PutBucketEncryption,
|
PutBucketEncryption,
|
||||||
|
#[serde(rename = "s3:GetBucketEncryption")]
|
||||||
GetBucketEncryption,
|
GetBucketEncryption,
|
||||||
|
#[serde(rename = "s3:PutBucketVersioning")]
|
||||||
PutBucketVersioning,
|
PutBucketVersioning,
|
||||||
|
#[serde(rename = "s3:GetBucketVersioning")]
|
||||||
GetBucketVersioning,
|
GetBucketVersioning,
|
||||||
|
#[serde(rename = "s3:PutReplicationConfiguration")]
|
||||||
PutReplicationConfiguration,
|
PutReplicationConfiguration,
|
||||||
|
#[serde(rename = "s3:GetReplicationConfiguration")]
|
||||||
GetReplicationConfiguration,
|
GetReplicationConfiguration,
|
||||||
|
#[serde(rename = "s3:ReplicateObject")]
|
||||||
ReplicateObject,
|
ReplicateObject,
|
||||||
|
#[serde(rename = "s3:ReplicateDelete")]
|
||||||
ReplicateDelete,
|
ReplicateDelete,
|
||||||
|
#[serde(rename = "s3:ReplicateTags")]
|
||||||
ReplicateTags,
|
ReplicateTags,
|
||||||
|
#[serde(rename = "s3:GetObjectVersionForReplication")]
|
||||||
GetObjectVersionForReplication,
|
GetObjectVersionForReplication,
|
||||||
|
#[serde(rename = "s3:RestoreObject")]
|
||||||
RestoreObject,
|
RestoreObject,
|
||||||
|
#[serde(rename = "s3:ResetBucketReplicationState")]
|
||||||
ResetBucketReplicationState,
|
ResetBucketReplicationState,
|
||||||
|
#[serde(rename = "s3:PutObjectFanOut")]
|
||||||
PutObjectFanOut,
|
PutObjectFanOut,
|
||||||
#[default]
|
#[default]
|
||||||
|
#[serde(rename = "s3:*")]
|
||||||
AllActions,
|
AllActions,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
#[derive(Debug)]
|
||||||
|
static ref SUPPORT_OBJCET_ACTIONS: HashSet<Action> = {
|
||||||
|
let mut h = HashSet::new();
|
||||||
|
h.insert(Action::AllActions);
|
||||||
|
h.insert(Action::AbortMultipartUpload);
|
||||||
|
h.insert(Action::DeleteObject);
|
||||||
|
h.insert(Action::GetObject);
|
||||||
|
h.insert(Action::ListMultipartUploadParts);
|
||||||
|
h.insert(Action::PutObject);
|
||||||
|
h.insert(Action::BypassGovernanceRetention);
|
||||||
|
h.insert(Action::PutObjectRetention);
|
||||||
|
h.insert(Action::GetObjectRetention);
|
||||||
|
h.insert(Action::PutObjectLegalHold);
|
||||||
|
h.insert(Action::GetObjectLegalHold);
|
||||||
|
h.insert(Action::GetObjectTagging);
|
||||||
|
h.insert(Action::PutObjectTagging);
|
||||||
|
h.insert(Action::DeleteObjectTagging);
|
||||||
|
h.insert(Action::GetObjectVersion);
|
||||||
|
h.insert(Action::GetObjectVersionTagging);
|
||||||
|
h.insert(Action::DeleteObjectVersion);
|
||||||
|
h.insert(Action::DeleteObjectVersionTagging);
|
||||||
|
h.insert(Action::PutObjectVersionTagging);
|
||||||
|
h.insert(Action::ReplicateObject);
|
||||||
|
h.insert(Action::ReplicateDelete);
|
||||||
|
h.insert(Action::ReplicateTags);
|
||||||
|
h.insert(Action::GetObjectVersionForReplication);
|
||||||
|
h.insert(Action::RestoreObject);
|
||||||
|
h.insert(Action::ResetBucketReplicationState);
|
||||||
|
h.insert(Action::PutObjectFanOut);
|
||||||
|
h.insert(Action::GetObjectAttributes);
|
||||||
|
h.insert(Action::GetObjectVersionAttributes);
|
||||||
|
h
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
impl Action {
|
impl Action {
|
||||||
// 将字符串转换为Action枚举
|
pub fn is_object_action(&self) -> bool {
|
||||||
fn from_str(s: &str) -> Option<Self> {
|
for act in SUPPORT_OBJCET_ACTIONS.iter() {
|
||||||
|
if self.is_match(act) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
pub fn is_match(&self, a: &Action) -> bool {
|
||||||
|
utils::wildcard::match_pattern(&self.clone().as_str(), &a.clone().as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_str(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Action::AbortMultipartUpload => "s3:AbortMultipartUpload",
|
||||||
|
Action::CreateBucket => "s3:CreateBucket",
|
||||||
|
Action::DeleteBucket => "s3:DeleteBucket",
|
||||||
|
Action::ForceDeleteBucket => "s3:ForceDeleteBucket",
|
||||||
|
Action::DeleteBucketPolicy => "s3:DeleteBucketPolicy",
|
||||||
|
Action::DeleteBucketCors => "s3:DeleteBucketCors",
|
||||||
|
Action::DeleteObject => "s3:DeleteObject",
|
||||||
|
Action::GetBucketLocation => "s3:GetBucketLocation",
|
||||||
|
Action::GetBucketNotification => "s3:GetBucketNotification",
|
||||||
|
Action::GetBucketPolicy => "s3:GetBucketPolicy",
|
||||||
|
Action::GetBucketCors => "s3:GetBucketCors",
|
||||||
|
Action::GetObject => "s3:GetObject",
|
||||||
|
Action::GetObjectAttributes => "s3:GetObjectAttributes",
|
||||||
|
Action::HeadBucket => "s3:HeadBucket",
|
||||||
|
Action::ListAllMyBuckets => "s3:ListAllMyBuckets",
|
||||||
|
Action::ListBucket => "s3:ListBucket",
|
||||||
|
Action::GetBucketPolicyStatus => "s3:GetBucketPolicyStatus",
|
||||||
|
Action::ListBucketVersions => "s3:ListBucketVersions",
|
||||||
|
Action::ListBucketMultipartUploads => "s3:ListBucketMultipartUploads",
|
||||||
|
Action::ListenNotification => "s3:ListenNotification",
|
||||||
|
Action::ListenBucketNotification => "s3:ListenBucketNotification",
|
||||||
|
Action::ListMultipartUploadParts => "s3:ListMultipartUploadParts",
|
||||||
|
Action::PutLifecycleConfiguration => "s3:PutLifecycleConfiguration",
|
||||||
|
Action::GetLifecycleConfiguration => "s3:GetLifecycleConfiguration",
|
||||||
|
Action::PutBucketNotification => "s3:PutBucketNotification",
|
||||||
|
Action::PutBucketPolicy => "s3:PutBucketPolicy",
|
||||||
|
Action::PutBucketCors => "s3:PutBucketCors",
|
||||||
|
Action::PutObject => "s3:PutObject",
|
||||||
|
Action::DeleteObjectVersion => "s3:DeleteObjectVersion",
|
||||||
|
Action::DeleteObjectVersionTagging => "s3:DeleteObjectVersionTagging",
|
||||||
|
Action::GetObjectVersion => "s3:GetObjectVersion",
|
||||||
|
Action::GetObjectVersionAttributes => "s3:GetObjectVersionAttributes",
|
||||||
|
Action::GetObjectVersionTagging => "s3:GetObjectVersionTagging",
|
||||||
|
Action::PutObjectVersionTagging => "s3:PutObjectVersionTagging",
|
||||||
|
Action::BypassGovernanceRetention => "s3:BypassGovernanceRetention",
|
||||||
|
Action::PutObjectRetention => "s3:PutObjectRetention",
|
||||||
|
Action::GetObjectRetention => "s3:GetObjectRetention",
|
||||||
|
Action::GetObjectLegalHold => "s3:GetObjectLegalHold",
|
||||||
|
Action::PutObjectLegalHold => "s3:PutObjectLegalHold",
|
||||||
|
Action::GetBucketObjectLockConfiguration => "s3:GetBucketObjectLockConfiguration",
|
||||||
|
Action::PutBucketObjectLockConfiguration => "s3:PutBucketObjectLockConfiguration",
|
||||||
|
Action::GetBucketTagging => "s3:GetBucketTagging",
|
||||||
|
Action::PutBucketTagging => "s3:PutBucketTagging",
|
||||||
|
Action::GetObjectTagging => "s3:GetObjectTagging",
|
||||||
|
Action::PutObjectTagging => "s3:PutObjectTagging",
|
||||||
|
Action::DeleteObjectTagging => "s3:DeleteObjectTagging",
|
||||||
|
Action::PutBucketEncryption => "s3:PutEncryptionConfiguration",
|
||||||
|
Action::GetBucketEncryption => "s3:GetEncryptionConfiguration",
|
||||||
|
Action::PutBucketVersioning => "s3:PutBucketVersioning",
|
||||||
|
Action::GetBucketVersioning => "s3:GetBucketVersioning",
|
||||||
|
Action::PutReplicationConfiguration => "s3:GetReplicationConfiguration",
|
||||||
|
Action::GetReplicationConfiguration => "s3:PutReplicationConfiguration",
|
||||||
|
Action::ReplicateObject => "s3:ReplicateObject",
|
||||||
|
Action::ReplicateDelete => "s3:ReplicateDelete",
|
||||||
|
Action::ReplicateTags => "s3:ReplicateTags",
|
||||||
|
Action::GetObjectVersionForReplication => "s3:GetObjectVersionForReplication",
|
||||||
|
Action::RestoreObject => "s3:RestoreObject",
|
||||||
|
Action::ResetBucketReplicationState => "s3:ResetBucketReplicationState",
|
||||||
|
Action::PutObjectFanOut => "s3:PutObjectFanOut",
|
||||||
|
Action::AllActions => "s3:*",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _from_str(s: &str) -> Option<Self> {
|
||||||
match s {
|
match s {
|
||||||
"s3:AbortMultipartUpload" => Some(Action::AbortMultipartUpload),
|
"s3:AbortMultipartUpload" => Some(Action::AbortMultipartUpload),
|
||||||
"s3:CreateBucket" => Some(Action::CreateBucket),
|
"s3:CreateBucket" => Some(Action::CreateBucket),
|
||||||
@@ -98,8 +303,8 @@ impl Action {
|
|||||||
"s3:ListenNotification" => Some(Action::ListenNotification),
|
"s3:ListenNotification" => Some(Action::ListenNotification),
|
||||||
"s3:ListenBucketNotification" => Some(Action::ListenBucketNotification),
|
"s3:ListenBucketNotification" => Some(Action::ListenBucketNotification),
|
||||||
"s3:ListMultipartUploadParts" => Some(Action::ListMultipartUploadParts),
|
"s3:ListMultipartUploadParts" => Some(Action::ListMultipartUploadParts),
|
||||||
"s3:PutLifecycleConfiguration" => Some(Action::PutBucketLifecycle),
|
"s3:PutLifecycleConfiguration" => Some(Action::PutLifecycleConfiguration),
|
||||||
"s3:GetLifecycleConfiguration" => Some(Action::GetBucketLifecycle),
|
"s3:GetLifecycleConfiguration" => Some(Action::GetLifecycleConfiguration),
|
||||||
"s3:PutBucketNotification" => Some(Action::PutBucketNotification),
|
"s3:PutBucketNotification" => Some(Action::PutBucketNotification),
|
||||||
"s3:PutBucketPolicy" => Some(Action::PutBucketPolicy),
|
"s3:PutBucketPolicy" => Some(Action::PutBucketPolicy),
|
||||||
"s3:PutBucketCors" => Some(Action::PutBucketCors),
|
"s3:PutBucketCors" => Some(Action::PutBucketCors),
|
||||||
@@ -140,3 +345,187 @@ impl Action {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ActionConditionKeyMap(HashMap<Action, KeySet>);
|
||||||
|
|
||||||
|
impl ActionConditionKeyMap {
|
||||||
|
pub fn lookup(&self, action: &Action) -> KeySet {
|
||||||
|
let common_keys: Vec<Key> = COMMOM_KEYS.iter().map(|v| v.to_key()).collect();
|
||||||
|
|
||||||
|
let mut merged_keys = KeySet::from_keys(&common_keys);
|
||||||
|
|
||||||
|
for (act, key) in self.0.iter() {
|
||||||
|
if action.is_match(act) {
|
||||||
|
merged_keys.merge(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
merged_keys
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref IAMActionConditionKeyMap: ActionConditionKeyMap = create_action_condition_key_map();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_action_condition_key_map() -> ActionConditionKeyMap {
|
||||||
|
let common_keys: Vec<Key> = COMMOM_KEYS.iter().map(|v| v.to_key()).collect();
|
||||||
|
let all_support_keys: Vec<Key> = ALL_SUPPORT_KEYS.iter().map(|v| v.to_key()).collect();
|
||||||
|
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
|
||||||
|
map.insert(Action::AllActions, KeySet::from_keys(&all_support_keys));
|
||||||
|
map.insert(Action::AbortMultipartUpload, KeySet::from_keys(&common_keys));
|
||||||
|
map.insert(Action::CreateBucket, KeySet::from_keys(&common_keys));
|
||||||
|
|
||||||
|
let mut delete_obj_keys = common_keys.clone();
|
||||||
|
delete_obj_keys.push(KeyName::S3VersionID.to_key());
|
||||||
|
map.insert(Action::DeleteObject, KeySet::from_keys(&delete_obj_keys));
|
||||||
|
|
||||||
|
map.insert(Action::GetBucketLocation, KeySet::from_keys(&common_keys));
|
||||||
|
map.insert(Action::GetBucketPolicyStatus, KeySet::from_keys(&common_keys));
|
||||||
|
|
||||||
|
let mut get_obj_keys = common_keys.clone();
|
||||||
|
get_obj_keys.extend(vec![
|
||||||
|
KeyName::S3XAmzServerSideEncryption.to_key(),
|
||||||
|
KeyName::S3XAmzServerSideEncryptionCustomerAlgorithm.to_key(),
|
||||||
|
KeyName::S3XAmzServerSideEncryptionAwsKmsKeyID.to_key(),
|
||||||
|
KeyName::S3VersionID.to_key(),
|
||||||
|
KeyName::ExistingObjectTag.to_key(),
|
||||||
|
]);
|
||||||
|
map.insert(Action::DeleteObject, KeySet::from_keys(&get_obj_keys));
|
||||||
|
|
||||||
|
map.insert(Action::HeadBucket, KeySet::from_keys(&common_keys));
|
||||||
|
|
||||||
|
let mut get_obj_attr_keys = common_keys.clone();
|
||||||
|
get_obj_attr_keys.push(KeyName::ExistingObjectTag.to_key());
|
||||||
|
map.insert(Action::DeleteObject, KeySet::from_keys(&get_obj_attr_keys));
|
||||||
|
|
||||||
|
let mut get_obj_ver_attr_keys = common_keys.clone();
|
||||||
|
get_obj_ver_attr_keys.extend(vec![KeyName::S3VersionID.to_key(), KeyName::ExistingObjectTag.to_key()]);
|
||||||
|
map.insert(Action::DeleteObject, KeySet::from_keys(&get_obj_ver_attr_keys));
|
||||||
|
|
||||||
|
map.insert(Action::ListAllMyBuckets, KeySet::from_keys(&common_keys));
|
||||||
|
|
||||||
|
let mut list_bucket_keys = common_keys.clone();
|
||||||
|
list_bucket_keys.extend(vec![
|
||||||
|
KeyName::S3Prefix.to_key(),
|
||||||
|
KeyName::S3Delimiter.to_key(),
|
||||||
|
KeyName::S3MaxKeys.to_key(),
|
||||||
|
]);
|
||||||
|
map.insert(Action::ListBucket, KeySet::from_keys(&list_bucket_keys));
|
||||||
|
map.insert(Action::ListBucketVersions, KeySet::from_keys(&list_bucket_keys));
|
||||||
|
|
||||||
|
map.insert(Action::ListBucketMultipartUploads, KeySet::from_keys(&common_keys));
|
||||||
|
map.insert(Action::ListenNotification, KeySet::from_keys(&common_keys));
|
||||||
|
map.insert(Action::ListenBucketNotification, KeySet::from_keys(&common_keys));
|
||||||
|
map.insert(Action::ListMultipartUploadParts, KeySet::from_keys(&common_keys));
|
||||||
|
|
||||||
|
let mut put_obj_keys = common_keys.clone();
|
||||||
|
put_obj_keys.extend(vec![
|
||||||
|
KeyName::S3XAmzCopySource.to_key(),
|
||||||
|
KeyName::S3XAmzServerSideEncryption.to_key(),
|
||||||
|
KeyName::S3XAmzServerSideEncryptionCustomerAlgorithm.to_key(),
|
||||||
|
KeyName::S3XAmzServerSideEncryptionAwsKmsKeyID.to_key(),
|
||||||
|
KeyName::S3XAmzMetadataDirective.to_key(),
|
||||||
|
KeyName::S3XAmzStorageClass.to_key(),
|
||||||
|
KeyName::S3VersionID.to_key(),
|
||||||
|
KeyName::S3ObjectLockRetainUntilDate.to_key(),
|
||||||
|
KeyName::S3ObjectLockMode.to_key(),
|
||||||
|
KeyName::S3ObjectLockLegalHold.to_key(),
|
||||||
|
KeyName::RequestObjectTagKeys.to_key(),
|
||||||
|
KeyName::RequestObjectTag.to_key(),
|
||||||
|
]);
|
||||||
|
map.insert(Action::PutObject, KeySet::from_keys(&put_obj_keys));
|
||||||
|
|
||||||
|
let mut put_obj_retention_keys = common_keys.clone();
|
||||||
|
put_obj_retention_keys.extend(vec![
|
||||||
|
KeyName::S3XAmzServerSideEncryption.to_key(),
|
||||||
|
KeyName::S3XAmzServerSideEncryptionCustomerAlgorithm.to_key(),
|
||||||
|
KeyName::S3XAmzServerSideEncryptionAwsKmsKeyID.to_key(),
|
||||||
|
KeyName::S3ObjectLockRemainingRetentionDays.to_key(),
|
||||||
|
KeyName::S3ObjectLockRetainUntilDate.to_key(),
|
||||||
|
KeyName::S3ObjectLockMode.to_key(),
|
||||||
|
KeyName::S3VersionID.to_key(),
|
||||||
|
]);
|
||||||
|
map.insert(Action::PutObjectRetention, KeySet::from_keys(&put_obj_retention_keys));
|
||||||
|
|
||||||
|
let mut get_obj_retention_keys = common_keys.clone();
|
||||||
|
get_obj_retention_keys.extend(vec![
|
||||||
|
KeyName::S3XAmzServerSideEncryption.to_key(),
|
||||||
|
KeyName::S3XAmzServerSideEncryptionCustomerAlgorithm.to_key(),
|
||||||
|
KeyName::S3XAmzServerSideEncryptionAwsKmsKeyID.to_key(),
|
||||||
|
KeyName::S3VersionID.to_key(),
|
||||||
|
]);
|
||||||
|
map.insert(Action::GetObjectRetention, KeySet::from_keys(&get_obj_retention_keys));
|
||||||
|
|
||||||
|
let mut put_obj_hold_keys = common_keys.clone();
|
||||||
|
put_obj_hold_keys.extend(vec![
|
||||||
|
KeyName::S3XAmzServerSideEncryption.to_key(),
|
||||||
|
KeyName::S3XAmzServerSideEncryptionCustomerAlgorithm.to_key(),
|
||||||
|
KeyName::S3XAmzServerSideEncryptionAwsKmsKeyID.to_key(),
|
||||||
|
KeyName::S3ObjectLockLegalHold.to_key(),
|
||||||
|
KeyName::S3VersionID.to_key(),
|
||||||
|
]);
|
||||||
|
map.insert(Action::PutObjectLegalHold, KeySet::from_keys(&put_obj_hold_keys));
|
||||||
|
|
||||||
|
map.insert(Action::GetObjectLegalHold, KeySet::from_keys(&common_keys));
|
||||||
|
|
||||||
|
let mut bypass_governance_retention_keys = common_keys.clone();
|
||||||
|
bypass_governance_retention_keys.extend(vec![
|
||||||
|
KeyName::S3VersionID.to_key(),
|
||||||
|
KeyName::S3ObjectLockRemainingRetentionDays.to_key(),
|
||||||
|
KeyName::S3ObjectLockRetainUntilDate.to_key(),
|
||||||
|
KeyName::S3ObjectLockMode.to_key(),
|
||||||
|
KeyName::S3ObjectLockLegalHold.to_key(),
|
||||||
|
KeyName::RequestObjectTagKeys.to_key(),
|
||||||
|
KeyName::RequestObjectTag.to_key(),
|
||||||
|
]);
|
||||||
|
map.insert(Action::BypassGovernanceRetention, KeySet::from_keys(&bypass_governance_retention_keys));
|
||||||
|
|
||||||
|
map.insert(Action::GetBucketObjectLockConfiguration, KeySet::from_keys(&common_keys));
|
||||||
|
map.insert(Action::PutBucketObjectLockConfiguration, KeySet::from_keys(&common_keys));
|
||||||
|
map.insert(Action::GetBucketTagging, KeySet::from_keys(&common_keys));
|
||||||
|
|
||||||
|
let mut put_bucket_tagging_keys = common_keys.clone();
|
||||||
|
put_bucket_tagging_keys.extend(vec![KeyName::RequestObjectTagKeys.to_key(), KeyName::RequestObjectTag.to_key()]);
|
||||||
|
map.insert(Action::PutBucketTagging, KeySet::from_keys(&put_bucket_tagging_keys));
|
||||||
|
|
||||||
|
let mut put_object_tagging_keys = common_keys.clone();
|
||||||
|
put_object_tagging_keys.extend(vec![
|
||||||
|
KeyName::S3VersionID.to_key(),
|
||||||
|
KeyName::ExistingObjectTag.to_key(),
|
||||||
|
KeyName::RequestObjectTagKeys.to_key(),
|
||||||
|
KeyName::RequestObjectTag.to_key(),
|
||||||
|
]);
|
||||||
|
map.insert(Action::PutObjectTagging, KeySet::from_keys(&put_object_tagging_keys));
|
||||||
|
|
||||||
|
let mut get_object_tagging_keys = common_keys.clone();
|
||||||
|
get_object_tagging_keys.extend(vec![KeyName::S3VersionID.to_key(), KeyName::ExistingObjectTag.to_key()]);
|
||||||
|
map.insert(Action::GetObjectTagging, KeySet::from_keys(&get_object_tagging_keys));
|
||||||
|
map.insert(Action::DeleteObjectTagging, KeySet::from_keys(&get_object_tagging_keys));
|
||||||
|
|
||||||
|
map.insert(Action::PutObjectVersionTagging, KeySet::from_keys(&put_object_tagging_keys));
|
||||||
|
map.insert(Action::GetObjectVersionTagging, KeySet::from_keys(&get_object_tagging_keys));
|
||||||
|
map.insert(Action::GetObjectVersion, KeySet::from_keys(&get_object_tagging_keys));
|
||||||
|
|
||||||
|
let mut delete_object_version_keys = common_keys.clone();
|
||||||
|
delete_object_version_keys.extend(vec![KeyName::S3VersionID.to_key()]);
|
||||||
|
|
||||||
|
map.insert(Action::DeleteObjectVersion, KeySet::from_keys(&delete_object_version_keys));
|
||||||
|
map.insert(Action::DeleteObjectVersionTagging, KeySet::from_keys(&get_object_tagging_keys));
|
||||||
|
|
||||||
|
map.insert(Action::GetReplicationConfiguration, KeySet::from_keys(&common_keys));
|
||||||
|
map.insert(Action::PutReplicationConfiguration, KeySet::from_keys(&common_keys));
|
||||||
|
|
||||||
|
map.insert(Action::ReplicateObject, KeySet::from_keys(&get_object_tagging_keys));
|
||||||
|
map.insert(Action::ReplicateDelete, KeySet::from_keys(&get_object_tagging_keys));
|
||||||
|
map.insert(Action::ReplicateTags, KeySet::from_keys(&get_object_tagging_keys));
|
||||||
|
map.insert(Action::GetObjectVersionForReplication, KeySet::from_keys(&get_object_tagging_keys));
|
||||||
|
|
||||||
|
map.insert(Action::RestoreObject, KeySet::from_keys(&common_keys));
|
||||||
|
map.insert(Action::ResetBucketReplicationState, KeySet::from_keys(&common_keys));
|
||||||
|
map.insert(Action::PutObjectFanOut, KeySet::from_keys(&common_keys));
|
||||||
|
|
||||||
|
ActionConditionKeyMap(map)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,57 +1,267 @@
|
|||||||
use crate::error::Result;
|
use crate::error::{Error, Result};
|
||||||
use rmp_serde::Serializer as rmpSerializer;
|
// use rmp_serde::Serializer as rmpSerializer;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
action::{Action, ActionSet},
|
action::{Action, ActionSet, IAMActionConditionKeyMap},
|
||||||
condition::function::Functions,
|
condition::function::Functions,
|
||||||
effect::Effect,
|
effect::Effect,
|
||||||
principal::Principal,
|
principal::Principal,
|
||||||
resource::ResourceSet,
|
resource::ResourceSet,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DEFAULT_VERSION: &str = "2012-10-17";
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
||||||
pub struct BucketPolicyArgs {
|
pub struct BucketPolicyArgs {
|
||||||
account_name: String,
|
pub account_name: String,
|
||||||
groups: Vec<String>,
|
pub groups: Vec<String>,
|
||||||
action: Action,
|
pub action: Action,
|
||||||
bucket_name: String,
|
pub bucket_name: String,
|
||||||
condition_values: HashMap<String, Vec<String>>,
|
pub condition_values: HashMap<String, Vec<String>>,
|
||||||
is_owner: bool,
|
pub is_owner: bool,
|
||||||
object_name: String,
|
pub object_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq, Eq)]
|
||||||
|
// #[serde(rename_all = "PascalCase", default)]
|
||||||
pub struct BPStatement {
|
pub struct BPStatement {
|
||||||
sid: String,
|
#[serde(rename = "Sid")]
|
||||||
effect: Effect,
|
pub sid: String,
|
||||||
principal: Principal,
|
#[serde(rename = "Effect")]
|
||||||
actions: ActionSet,
|
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")]
|
||||||
|
pub resources: ResourceSet,
|
||||||
|
#[serde(rename = "Condition", default)]
|
||||||
|
pub conditions: Functions,
|
||||||
|
}
|
||||||
|
|
||||||
not_actions: Option<ActionSet>,
|
impl BPStatement {
|
||||||
resources: ResourceSet,
|
// pub fn equals(&self, other: &BPStatement) -> bool {
|
||||||
conditions: Option<Functions>,
|
// if self.effect != other.effect {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if !self.principal.equals(other.principal) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if !self.actions.equals(other.actions) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// if !self.not_actions.equals(other.not_actions) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// if !self.resources.equals(other.resources) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// if !self.conditions.equals(other.conditions) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// true
|
||||||
|
// }
|
||||||
|
pub fn validate(&self, bucket: &str) -> Result<()> {
|
||||||
|
self.is_valid()?;
|
||||||
|
self.resources.validate_bucket(bucket)
|
||||||
|
}
|
||||||
|
pub fn is_valid(&self) -> Result<()> {
|
||||||
|
if !self.effect.is_valid() {
|
||||||
|
return Err(Error::msg(format!("invalid Effect {:?}", self.effect)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.principal.is_valid() {
|
||||||
|
return Err(Error::msg(format!("invalid Principal {:?}", self.principal)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.actions.is_empty() && self.not_actions.is_empty() {
|
||||||
|
return Err(Error::msg("Action must not be empty"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.resources.as_ref().is_empty() {
|
||||||
|
return Err(Error::msg("Resource must not be empty"));
|
||||||
|
}
|
||||||
|
|
||||||
|
for act in self.actions.as_ref() {
|
||||||
|
if act.is_object_action() {
|
||||||
|
if !self.resources.object_resource_exists() {
|
||||||
|
return Err(Error::msg(format!(
|
||||||
|
"unsupported object Resource found {:?} for action {:?}",
|
||||||
|
self.resources, act
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !self.resources.bucket_resource_exists() {
|
||||||
|
return Err(Error::msg(format!(
|
||||||
|
"unsupported bucket Resource found {:?} for action {:?}",
|
||||||
|
self.resources, act
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let key_diff = self.conditions.keys().difference(&IAMActionConditionKeyMap.lookup(act));
|
||||||
|
if !key_diff.is_empty() {
|
||||||
|
return Err(Error::msg(format!(
|
||||||
|
"unsupported condition keys '{:?}' used for action '{:?}'",
|
||||||
|
key_diff, act
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn is_allowed(&self, args: &BucketPolicyArgs) -> bool {
|
||||||
|
let check = || -> bool {
|
||||||
|
if !self.principal.is_match(&args.account_name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self.actions.is_match(&args.action) && !self.actions.is_empty()) || self.not_actions.is_match(&args.action) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut resource = args.bucket_name.clone();
|
||||||
|
if !args.object_name.is_empty() {
|
||||||
|
if !args.object_name.starts_with("/") {
|
||||||
|
resource.push_str("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
resource.push_str(&args.object_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.resources.is_match(&resource, &args.condition_values) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.conditions.evaluate(&args.condition_values)
|
||||||
|
};
|
||||||
|
|
||||||
|
self.effect.is_allowed(check())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
||||||
|
// #[serde(rename_all = "PascalCase", default)]
|
||||||
pub struct BucketPolicy {
|
pub struct BucketPolicy {
|
||||||
|
#[serde(rename = "ID", default)]
|
||||||
pub id: String,
|
pub id: String,
|
||||||
|
#[serde(rename = "Version")]
|
||||||
pub version: String,
|
pub version: String,
|
||||||
|
#[serde(rename = "Statement")]
|
||||||
pub statements: Vec<BPStatement>,
|
pub statements: Vec<BPStatement>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BucketPolicy {
|
impl BucketPolicy {
|
||||||
pub fn marshal_msg(&self) -> Result<Vec<u8>> {
|
pub fn is_allowed(&self, args: &BucketPolicyArgs) -> bool {
|
||||||
let mut buf = Vec::new();
|
for statement in self.statements.iter() {
|
||||||
|
if statement.effect == Effect::Deny {
|
||||||
|
if !statement.is_allowed(args) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.serialize(&mut rmpSerializer::new(&mut buf).with_struct_map())?;
|
if args.is_owner {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for statement in self.statements.iter() {
|
||||||
|
if statement.effect == Effect::Allow {
|
||||||
|
if statement.is_allowed(args) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn validate(&self, bucket: &str) -> Result<()> {
|
||||||
|
self.is_valid()?;
|
||||||
|
for statement in self.statements.iter() {
|
||||||
|
statement.validate(bucket)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_valid(&self) -> Result<()> {
|
||||||
|
if self.version.as_str() != DEFAULT_VERSION && self.version.is_empty() {
|
||||||
|
return Err(Error::msg(format!("invalid version {}", self.version)));
|
||||||
|
}
|
||||||
|
|
||||||
|
for statement in self.statements.iter() {
|
||||||
|
if let Err(err) = statement.is_valid() {
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.statements.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn marshal_msg(&self) -> Result<String> {
|
||||||
|
let buf = serde_json::to_string(self)?;
|
||||||
|
|
||||||
Ok(buf)
|
Ok(buf)
|
||||||
|
|
||||||
|
// let mut buf = Vec::new();
|
||||||
|
// self.serialize(&mut rmpSerializer::new(&mut buf).with_struct_map())?;
|
||||||
|
|
||||||
|
// Ok(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unmarshal(buf: &[u8]) -> Result<Self> {
|
pub fn unmarshal(buf: &[u8]) -> Result<Self> {
|
||||||
let t: BucketPolicy = rmp_serde::from_slice(buf)?;
|
let mut p = serde_json::from_slice::<BucketPolicy>(&buf)?;
|
||||||
Ok(t)
|
p.drop_duplicate_statements();
|
||||||
|
Ok(p)
|
||||||
|
|
||||||
|
// let t: BucketPolicy = rmp_serde::from_slice(buf)?;
|
||||||
|
// Ok(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drop_duplicate_statements(&mut self) {
|
||||||
|
let mut dups = HashMap::new();
|
||||||
|
|
||||||
|
for v in self.statements.iter() {
|
||||||
|
if let Ok(data) = serde_json::to_string(self) {
|
||||||
|
dups.insert(data, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut news = Vec::new();
|
||||||
|
|
||||||
|
for (_, v) in dups {
|
||||||
|
news.push(v.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.statements = news;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bucket_policy() {
|
||||||
|
let json = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucket\",\"s3:ListBucketMultipartUploads\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Resource\":[\"arn:aws:s3:::dada\"],\"Sid\":\"\"},{\"Action\":[\"s3:AbortMultipartUpload\",\"s3:DeleteObject\",\"s3:GetObject\",\"s3:ListMultipartUploadParts\",\"s3:PutObject\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Resource\":[\"arn:aws:s3:::dada/*\"],\"Sid\":\"sdf\"}]}";
|
||||||
|
|
||||||
|
let a = BucketPolicy::unmarshal(json.to_string().as_bytes()).unwrap();
|
||||||
|
|
||||||
|
println!("{:?}", a);
|
||||||
|
|
||||||
|
let j = a.marshal_msg();
|
||||||
|
|
||||||
|
println!("{:?}", j);
|
||||||
|
|
||||||
|
println!("{:?}", json);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,26 @@
|
|||||||
use super::name::Name;
|
use super::{
|
||||||
use serde::{Deserialize, Serialize};
|
key::{Key, KeySet},
|
||||||
|
keyname::KeyName,
|
||||||
|
name::Name,
|
||||||
|
};
|
||||||
|
use serde::{
|
||||||
|
de::{MapAccess, Visitor},
|
||||||
|
ser::SerializeMap,
|
||||||
|
Deserialize, Serialize,
|
||||||
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
fmt::Debug,
|
fmt::{self, Debug, Display},
|
||||||
|
marker::PhantomData,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Key {
|
|
||||||
name: String,
|
|
||||||
variable: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
// 定义ValueSet类型
|
// 定义ValueSet类型
|
||||||
pub type ValueSet = HashSet<String>;
|
pub type ValueSet = HashSet<String>;
|
||||||
|
|
||||||
// 定义Function trait
|
// 定义Function trait
|
||||||
pub trait FunctionApi {
|
pub trait FunctionApi: 'static + Send + Sync {
|
||||||
// evaluate方法
|
// evaluate方法
|
||||||
fn evaluate(&self, values: &HashMap<Key, ValueSet>) -> bool;
|
fn evaluate(&self, values: &HashMap<String, Vec<String>>) -> bool;
|
||||||
|
|
||||||
// key方法
|
// key方法
|
||||||
fn key(&self) -> Key;
|
fn key(&self) -> Key;
|
||||||
@@ -31,28 +34,265 @@ pub trait FunctionApi {
|
|||||||
// to_map方法
|
// to_map方法
|
||||||
fn to_map(&self) -> HashMap<Key, ValueSet>;
|
fn to_map(&self) -> HashMap<Key, ValueSet>;
|
||||||
|
|
||||||
// clone方法
|
fn clone_box(&self) -> Box<dyn FunctionApi>;
|
||||||
fn clone(&self) -> Box<Function>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl Debug for dyn Function {
|
// #[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
// pub enum Function {
|
||||||
// write!(f, "{:?}", self.to_string())
|
// Test(TestFunction),
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl FunctionApi for Function {
|
||||||
|
// // evaluate方法
|
||||||
|
// fn evaluate(&self, values: &HashMap<String, Vec<String>>) -> bool {
|
||||||
|
// match self {
|
||||||
|
// Function::Test(f) => f.evaluate(values),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // key方法
|
||||||
|
// fn key(&self) -> Key {
|
||||||
|
// match self {
|
||||||
|
// Function::Test(f) => f.key(),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // name方法
|
||||||
|
// fn name(&self) -> Name {
|
||||||
|
// match self {
|
||||||
|
// Function::Test(f) => f.name(),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // String方法
|
||||||
|
// fn to_string(&self) -> String {
|
||||||
|
// match self {
|
||||||
|
// Function::Test(f) => f.to_string(),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // to_map方法
|
||||||
|
// fn to_map(&self) -> HashMap<Key, ValueSet> {
|
||||||
|
// match self {
|
||||||
|
// Function::Test(f) => f.to_map(),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn clone_box(&self) -> Box<dyn FunctionApi> {
|
||||||
|
// match self {
|
||||||
|
// Function::Test(f) => f.clone_box(),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 定义Functions类型
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Functions(Vec<Box<dyn FunctionApi>>);
|
||||||
|
|
||||||
|
impl Functions {
|
||||||
|
pub fn evaluate(&self, values: &HashMap<String, Vec<String>>) -> bool {
|
||||||
|
for f in self.0.iter() {
|
||||||
|
if f.evaluate(values) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
pub fn keys(&self) -> KeySet {
|
||||||
|
let mut set = KeySet::new();
|
||||||
|
for f in self.0.iter() {
|
||||||
|
set.add(f.key())
|
||||||
|
}
|
||||||
|
set
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Functions {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let funs: Vec<String> = self.0.iter().map(|v| v.to_string()).collect();
|
||||||
|
f.debug_list().entries(funs.iter()).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Functions {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let funs: Vec<String> = self.0.iter().map(|v| v.to_string()).collect();
|
||||||
|
write!(f, "{:?}", funs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Functions {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
let mut list = Vec::new();
|
||||||
|
for v in self.0.iter() {
|
||||||
|
list.push(v.clone_box())
|
||||||
|
}
|
||||||
|
|
||||||
|
Functions(list)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Functions {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
if self.0.len() != other.0.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for v in self.0.iter() {
|
||||||
|
let s = v.to_string();
|
||||||
|
let mut found = false;
|
||||||
|
for o in other.0.iter() {
|
||||||
|
if s == o.to_string() {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Functions {}
|
||||||
|
|
||||||
|
type FunctionsMap = HashMap<String, HashMap<String, ValueSet>>;
|
||||||
|
|
||||||
|
impl Serialize for Functions {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
let mut nm: FunctionsMap = HashMap::new();
|
||||||
|
for f in self.0.iter() {
|
||||||
|
let fname = f.name().to_string();
|
||||||
|
|
||||||
|
if !nm.contains_key(&fname) {
|
||||||
|
nm.insert(fname.clone(), HashMap::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (k, v) in f.to_map() {
|
||||||
|
if let Some(hm) = nm.get_mut(&fname) {
|
||||||
|
hm.insert(k.to_string(), v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut map = serializer.serialize_map(Some(nm.len()))?;
|
||||||
|
for (k, v) in nm.iter() {
|
||||||
|
map.serialize_entry(k, v)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
map.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MyMapVisitor {
|
||||||
|
marker: PhantomData<fn() -> FunctionsMap>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MyMapVisitor {
|
||||||
|
fn new() -> Self {
|
||||||
|
MyMapVisitor { marker: PhantomData }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the trait that Deserializers are going to be driving. There
|
||||||
|
// is one method for each type of data that our type knows how to
|
||||||
|
// deserialize from. There are many other methods that are not
|
||||||
|
// implemented here, for example deserializing from integers or strings.
|
||||||
|
// By default those methods will return an error, which makes sense
|
||||||
|
// because we cannot deserialize a MyMap from an integer or string.
|
||||||
|
impl<'de> Visitor<'de> for MyMapVisitor {
|
||||||
|
// The type that our Visitor is going to produce.
|
||||||
|
type Value = FunctionsMap;
|
||||||
|
|
||||||
|
// Format a message stating what data this Visitor expects to receive.
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str("a very special map")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize MyMap from an abstract "map" provided by the
|
||||||
|
// Deserializer. The MapAccess input is a callback provided by
|
||||||
|
// the Deserializer to let us see each entry in the map.
|
||||||
|
fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
|
||||||
|
where
|
||||||
|
M: MapAccess<'de>,
|
||||||
|
{
|
||||||
|
let mut map = FunctionsMap::with_capacity(access.size_hint().unwrap_or(0));
|
||||||
|
|
||||||
|
// While there are entries remaining in the input, add them
|
||||||
|
// into our map.
|
||||||
|
while let Some((key, value)) = access.next_entry()? {
|
||||||
|
map.insert(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(map)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the trait that informs Serde how to deserialize MyMap.
|
||||||
|
impl<'de> Deserialize<'de> for Functions {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
// Instantiate our Visitor and ask the Deserializer to drive
|
||||||
|
// it over the input data, resulting in an instance of MyMap.
|
||||||
|
let _map = deserializer.deserialize_map(MyMapVisitor::new())?;
|
||||||
|
|
||||||
|
// TODO: FIXME: create functions from name
|
||||||
|
|
||||||
|
Ok(Functions(Vec::new()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl<'de> Deserialize<'de> for Functions {
|
||||||
|
// fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
// where
|
||||||
|
// D: serde::Deserializer<'de>,
|
||||||
|
// {
|
||||||
|
// todo!()
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
||||||
enum Function {
|
pub struct TestFunction {}
|
||||||
#[default]
|
|
||||||
Test,
|
|
||||||
}
|
|
||||||
|
|
||||||
// 定义Functions类型
|
impl FunctionApi for TestFunction {
|
||||||
#[derive(Deserialize, Serialize, Default, Clone)]
|
// evaluate方法
|
||||||
pub struct Functions(Vec<Function>);
|
fn evaluate(&self, _values: &HashMap<String, Vec<String>>) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
impl Debug for Functions {
|
// key方法
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn key(&self) -> Key {
|
||||||
f.debug_tuple("Functions").field(&self.0).finish()
|
Key {
|
||||||
|
name: KeyName::JWTPrefUsername,
|
||||||
|
variable: "".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// name方法
|
||||||
|
fn name(&self) -> Name {
|
||||||
|
Name::StringEquals
|
||||||
|
}
|
||||||
|
|
||||||
|
// String方法
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
Name::StringEquals.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
// to_map方法
|
||||||
|
fn to_map(&self) -> HashMap<Key, ValueSet> {
|
||||||
|
HashMap::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_box(&self) -> Box<dyn FunctionApi> {
|
||||||
|
Box::new(self.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,159 @@
|
|||||||
|
use super::keyname::{KeyName, ALL_SUPPORT_KEYS};
|
||||||
|
use crate::error::Error;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{collections::HashSet, fmt, str::FromStr};
|
||||||
|
|
||||||
|
// 定义Key结构体
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Key {
|
||||||
|
pub name: KeyName,
|
||||||
|
pub variable: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Key {
|
||||||
|
pub fn new(name: KeyName, variable: String) -> Self {
|
||||||
|
Key { name, variable }
|
||||||
|
}
|
||||||
|
// IsValid - checks if key is valid or not.
|
||||||
|
fn is_valid(&self) -> bool {
|
||||||
|
ALL_SUPPORT_KEYS.iter().any(|supported| self.name == *supported)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is - checks if this key has the same key name or not.
|
||||||
|
pub fn is(&self, name: &KeyName) -> bool {
|
||||||
|
self.name == *name
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(&self) -> String {
|
||||||
|
if !self.variable.is_empty() {
|
||||||
|
format!("{}/{}", self.name.as_str(), self.variable)
|
||||||
|
} else {
|
||||||
|
self.name.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// VarName - returns variable key name, such as "${aws:username}"
|
||||||
|
pub fn var_name(&self) -> String {
|
||||||
|
self.name.var_name()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name - returns key name which is stripped value of prefixes "aws:" and "s3:"
|
||||||
|
pub fn name(&self) -> String {
|
||||||
|
if !self.variable.is_empty() {
|
||||||
|
format!("{}{}", self.name.name(), self.variable)
|
||||||
|
} else {
|
||||||
|
self.name.name().to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Key {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let (name, variable) = if let Some(pos) = s.find('/') {
|
||||||
|
(&s[..pos], &s[pos + 1..])
|
||||||
|
} else {
|
||||||
|
(s, "")
|
||||||
|
};
|
||||||
|
|
||||||
|
let keyname = KeyName::from_str(name)?;
|
||||||
|
|
||||||
|
let key = Key {
|
||||||
|
name: keyname,
|
||||||
|
variable: variable.to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if key.is_valid() {
|
||||||
|
Ok(key)
|
||||||
|
} else {
|
||||||
|
Err(Error::msg(format!("invalid condition key '{}'", s)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for Key {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_str(self.to_string().as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for Key {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::de::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let s: String = Deserialize::deserialize(deserializer)?;
|
||||||
|
Key::from_str(s.as_str()).map_err(serde::de::Error::custom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Key {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct KeySet(HashSet<Key>);
|
||||||
|
|
||||||
|
impl KeySet {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
KeySet(HashSet::new())
|
||||||
|
}
|
||||||
|
// Add - add a key to key set
|
||||||
|
pub fn add(&mut self, key: Key) {
|
||||||
|
self.0.insert(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge merges two key sets, duplicates are overwritten
|
||||||
|
pub fn merge(&mut self, other: &KeySet) {
|
||||||
|
for key in &other.0 {
|
||||||
|
self.add(key.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match matches the input key name with current keySet
|
||||||
|
pub fn match_key(&self, key: &Key) -> bool {
|
||||||
|
self.0.contains(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Difference - returns a key set contains difference of two keys
|
||||||
|
pub fn difference(&self, other: &KeySet) -> KeySet {
|
||||||
|
let mut result = KeySet::default();
|
||||||
|
for key in &self.0 {
|
||||||
|
if !other.match_key(key) {
|
||||||
|
result.add(key.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty - returns whether key set is empty or not
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.0.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToSlice - returns slice of keys
|
||||||
|
fn to_slice(&self) -> Vec<Key> {
|
||||||
|
self.0.iter().cloned().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewKeySet - returns new KeySet contains given keys
|
||||||
|
pub fn from_keys(keys: &Vec<Key>) -> KeySet {
|
||||||
|
let mut set = KeySet::default();
|
||||||
|
for key in keys {
|
||||||
|
set.add(key.clone());
|
||||||
|
}
|
||||||
|
set
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for KeySet {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{:?}", self.to_slice())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
pub mod function;
|
pub mod function;
|
||||||
|
pub mod key;
|
||||||
pub mod keyname;
|
pub mod keyname;
|
||||||
pub mod name;
|
pub mod name;
|
||||||
|
|||||||
@@ -29,40 +29,47 @@ pub enum Name {
|
|||||||
ForAnyValue,
|
ForAnyValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 实现Display trait用于打印
|
impl Name {
|
||||||
impl std::fmt::Display for Name {
|
pub fn as_str(&self) -> &'static str {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
match self {
|
||||||
write!(
|
Name::StringEquals => "StringEquals",
|
||||||
f,
|
Name::StringNotEquals => "StringNotEquals",
|
||||||
"{}",
|
Name::StringEqualsIgnoreCase => "StringEqualsIgnoreCase",
|
||||||
match *self {
|
Name::StringNotEqualsIgnoreCase => "StringNotEqualsIgnoreCase",
|
||||||
Name::StringEquals => "StringEquals",
|
Name::StringLike => "StringLike",
|
||||||
Name::StringNotEquals => "StringNotEquals",
|
Name::StringNotLike => "StringNotLike",
|
||||||
Name::StringEqualsIgnoreCase => "StringEqualsIgnoreCase",
|
Name::BinaryEquals => "BinaryEquals",
|
||||||
Name::StringNotEqualsIgnoreCase => "StringNotEqualsIgnoreCase",
|
Name::IpAddress => "IpAddress",
|
||||||
Name::StringLike => "StringLike",
|
Name::NotIpAddress => "NotIpAddress",
|
||||||
Name::StringNotLike => "StringNotLike",
|
Name::Null => "Null",
|
||||||
Name::BinaryEquals => "BinaryEquals",
|
Name::Bool => "Bool",
|
||||||
Name::IpAddress => "IpAddress",
|
Name::NumericEquals => "NumericEquals",
|
||||||
Name::NotIpAddress => "NotIpAddress",
|
Name::NumericNotEquals => "NumericNotEquals",
|
||||||
Name::Null => "Null",
|
Name::NumericLessThan => "NumericLessThan",
|
||||||
Name::Bool => "Bool",
|
Name::NumericLessThanEquals => "NumericLessThanEquals",
|
||||||
Name::NumericEquals => "NumericEquals",
|
Name::NumericGreaterThan => "NumericGreaterThan",
|
||||||
Name::NumericNotEquals => "NumericNotEquals",
|
Name::NumericGreaterThanIfExists => "NumericGreaterThanIfExists",
|
||||||
Name::NumericLessThan => "NumericLessThan",
|
Name::NumericGreaterThanEquals => "NumericGreaterThanEquals",
|
||||||
Name::NumericLessThanEquals => "NumericLessThanEquals",
|
Name::DateEquals => "DateEquals",
|
||||||
Name::NumericGreaterThan => "NumericGreaterThan",
|
Name::DateNotEquals => "DateNotEquals",
|
||||||
Name::NumericGreaterThanIfExists => "NumericGreaterThanIfExists",
|
Name::DateLessThan => "DateLessThan",
|
||||||
Name::NumericGreaterThanEquals => "NumericGreaterThanEquals",
|
Name::DateLessThanEquals => "DateLessThanEquals",
|
||||||
Name::DateEquals => "DateEquals",
|
Name::DateGreaterThan => "DateGreaterThan",
|
||||||
Name::DateNotEquals => "DateNotEquals",
|
Name::DateGreaterThanEquals => "DateGreaterThanEquals",
|
||||||
Name::DateLessThan => "DateLessThan",
|
Name::ForAllValues => "ForAllValues",
|
||||||
Name::DateLessThanEquals => "DateLessThanEquals",
|
Name::ForAnyValue => "ForAnyValue",
|
||||||
Name::DateGreaterThan => "DateGreaterThan",
|
}
|
||||||
Name::DateGreaterThanEquals => "DateGreaterThanEquals",
|
}
|
||||||
Name::ForAllValues => "ForAllValues",
|
}
|
||||||
Name::ForAnyValue => "ForAnyValue",
|
|
||||||
}
|
// impl ToString for Name {
|
||||||
)
|
// fn to_string(&self) -> String {
|
||||||
|
// self.as_str().to_string()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
impl std::fmt::Display for Name {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.as_str())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,30 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Default)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Default)]
|
||||||
|
#[serde(rename_all = "PascalCase")]
|
||||||
pub enum Effect {
|
pub enum Effect {
|
||||||
#[default]
|
#[default]
|
||||||
Allow,
|
Allow,
|
||||||
Deny,
|
Deny,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Effect {
|
||||||
|
pub fn is_allowed(self, b: bool) -> bool {
|
||||||
|
if self == Effect::Allow {
|
||||||
|
b
|
||||||
|
} else {
|
||||||
|
!b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_valid(self) -> bool {
|
||||||
|
match self {
|
||||||
|
Effect::Allow => true,
|
||||||
|
Effect::Deny => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 实现从字符串解析Effect的功能
|
// 实现从字符串解析Effect的功能
|
||||||
impl FromStr for Effect {
|
impl FromStr for Effect {
|
||||||
type Err = ();
|
type Err = ();
|
||||||
|
|||||||
@@ -1,7 +1,25 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
|
use crate::utils;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq)]
|
||||||
|
#[serde(rename_all = "PascalCase", default)]
|
||||||
pub struct Principal {
|
pub struct Principal {
|
||||||
|
#[serde(rename = "AWS")]
|
||||||
aws: HashSet<String>,
|
aws: HashSet<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Principal {
|
||||||
|
pub fn is_valid(&self) -> bool {
|
||||||
|
!self.aws.is_empty()
|
||||||
|
}
|
||||||
|
pub fn is_match(&self, parincipal: &str) -> bool {
|
||||||
|
for pattern in self.aws.iter() {
|
||||||
|
if utils::wildcard::match_simple(&pattern, parincipal) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
|
use crate::error::{Error, Result};
|
||||||
|
use crate::{
|
||||||
|
bucket::policy::condition::keyname::COMMOM_KEYS,
|
||||||
|
utils::{self, wildcard},
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashSet;
|
use std::{
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
|
|
||||||
// 定义ResourceARNType枚举类型
|
// 定义ResourceARNType枚举类型
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize, Default)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize, Default)]
|
||||||
@@ -10,16 +18,234 @@ pub enum ResourceARNType {
|
|||||||
ResourceARNKMS,
|
ResourceARNKMS,
|
||||||
}
|
}
|
||||||
|
|
||||||
// // 定义资源ARN前缀
|
impl ResourceARNType {
|
||||||
// const RESOURCE_ARN_PREFIX: &str = "arn:aws:s3:::";
|
pub fn to_string(&self) -> String {
|
||||||
// const RESOURCE_ARN_KMS_PREFIX: &str = "arn:rustfs:kms::::";
|
match self {
|
||||||
|
ResourceARNType::UnknownARN => "".to_string(),
|
||||||
// 定义Resource结构体
|
ResourceARNType::ResourceARNS3 => RESOURCE_ARN_PREFIX.to_string(),
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, PartialEq, Eq, Hash, Clone)]
|
ResourceARNType::ResourceARNKMS => RESOURCE_ARN_KMS_PREFIX.to_string(),
|
||||||
pub struct Resource {
|
}
|
||||||
pattern: String,
|
}
|
||||||
r#type: ResourceARNType,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
// 定义资源ARN前缀
|
||||||
|
const RESOURCE_ARN_PREFIX: &str = "arn:aws:s3:::";
|
||||||
|
const RESOURCE_ARN_KMS_PREFIX: &str = "arn:rustfs:kms::::";
|
||||||
|
|
||||||
|
// 定义Resource结构体
|
||||||
|
#[derive(Debug, Default, PartialEq, Eq, Hash, Clone)]
|
||||||
|
pub struct Resource {
|
||||||
|
pattern: String,
|
||||||
|
rtype: ResourceARNType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resource {
|
||||||
|
pub fn validate_bucket(&self, bucket: &str) -> Result<()> {
|
||||||
|
self.validate()?;
|
||||||
|
if !wildcard::match_pattern(&self.pattern, bucket)
|
||||||
|
&& !wildcard::match_as_pattern_prefix(&self.pattern, format!("{}/", bucket).as_str())
|
||||||
|
{
|
||||||
|
return Err(Error::msg("bucket name does not match"));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn validate(&self) -> Result<()> {
|
||||||
|
if !self.is_valid() {
|
||||||
|
Err(Error::msg("invalid resource"))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn is_valid(&self) -> bool {
|
||||||
|
if self.rtype == ResourceARNType::UnknownARN {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if self.is_s3() {
|
||||||
|
if self.pattern.starts_with("/") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.is_kms() {
|
||||||
|
if self.pattern.as_bytes().iter().any(|&v| v == b'/' || v == b'\\' || v == b'.') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
!self.pattern.is_empty()
|
||||||
|
}
|
||||||
|
pub fn is_s3(&self) -> bool {
|
||||||
|
self.rtype == ResourceARNType::ResourceARNS3
|
||||||
|
}
|
||||||
|
pub fn is_kms(&self) -> bool {
|
||||||
|
self.rtype == ResourceARNType::ResourceARNKMS
|
||||||
|
}
|
||||||
|
pub fn is_bucket_pattern(&self) -> bool {
|
||||||
|
!self.pattern.contains("/") || self.pattern.eq("*")
|
||||||
|
}
|
||||||
|
pub fn is_object_pattern(&self) -> bool {
|
||||||
|
self.pattern.contains("/") || self.pattern.contains("*")
|
||||||
|
}
|
||||||
|
pub fn is_match(&self, res: &str, condition_values: &HashMap<String, Vec<String>>) -> bool {
|
||||||
|
let mut pattern = res.to_string();
|
||||||
|
if !condition_values.is_empty() {
|
||||||
|
for key in COMMOM_KEYS.iter() {
|
||||||
|
if let Some(vals) = condition_values.get(key.name()) {
|
||||||
|
if let Some(v0) = vals.get(0) {
|
||||||
|
pattern = pattern.replace(key.name(), &v0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let cp = utils::path::clean(res);
|
||||||
|
|
||||||
|
if cp != "." && cp == pattern {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
wildcard::match_pattern(&pattern, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(&self) -> String {
|
||||||
|
format!("{}{}", self.rtype.to_string(), self.pattern)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Resource {
|
||||||
|
type Err = serde_json::Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
if s.starts_with(RESOURCE_ARN_PREFIX) {
|
||||||
|
let pattern = {
|
||||||
|
if let Some(val) = s.strip_prefix(RESOURCE_ARN_PREFIX) {
|
||||||
|
val.to_string()
|
||||||
|
} else {
|
||||||
|
s.to_string()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(Self {
|
||||||
|
rtype: ResourceARNType::ResourceARNS3,
|
||||||
|
pattern,
|
||||||
|
})
|
||||||
|
} else if s.starts_with(RESOURCE_ARN_KMS_PREFIX) {
|
||||||
|
let pattern = {
|
||||||
|
if let Some(val) = s.strip_prefix(RESOURCE_ARN_KMS_PREFIX) {
|
||||||
|
val.to_string()
|
||||||
|
} else {
|
||||||
|
s.to_string()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(Self {
|
||||||
|
rtype: ResourceARNType::ResourceARNS3,
|
||||||
|
pattern,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Ok(Self {
|
||||||
|
rtype: ResourceARNType::UnknownARN,
|
||||||
|
pattern: "".to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for Resource {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_str(self.to_string().as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for Resource {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct Visitor;
|
||||||
|
|
||||||
|
impl<'de> serde::de::Visitor<'de> for Visitor {
|
||||||
|
type Value = Resource;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
formatter.write_str("string resource")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: serde::de::Error,
|
||||||
|
{
|
||||||
|
match Resource::from_str(value) {
|
||||||
|
Ok(res) => Ok(res),
|
||||||
|
Err(_) => Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(value), &self)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.deserialize_any(Visitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||||
|
#[serde(transparent)]
|
||||||
pub struct ResourceSet(HashSet<Resource>);
|
pub struct ResourceSet(HashSet<Resource>);
|
||||||
|
|
||||||
|
impl ResourceSet {
|
||||||
|
pub fn validate_bucket(&self, bucket: &str) -> Result<()> {
|
||||||
|
for res in self.0.iter() {
|
||||||
|
if let Err(err) = res.validate_bucket(bucket) {
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn as_ref(&self) -> &HashSet<Resource> {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
pub fn is_match(&self, res: &str, condition_values: &HashMap<String, Vec<String>>) -> bool {
|
||||||
|
for item in self.0.iter() {
|
||||||
|
if item.is_match(res, condition_values) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
pub fn object_resource_exists(&self) -> bool {
|
||||||
|
for res in self.0.iter() {
|
||||||
|
if res.is_object_pattern() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
pub fn bucket_resource_exists(&self) -> bool {
|
||||||
|
for res in self.0.iter() {
|
||||||
|
if res.is_bucket_pattern() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl Serialize for ResourceSet {
|
||||||
|
// fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
// where
|
||||||
|
// S: serde::Serializer,
|
||||||
|
// {
|
||||||
|
// let ress: Vec<Resource> = self.0.iter().cloned().collect();
|
||||||
|
// serializer.collect_seq(ress)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl<'de> Deserialize<'de> for ResourceSet {
|
||||||
|
// fn deserialize<D>(deserializer: D) -> Result<ResourceSet, D::Error>
|
||||||
|
// where
|
||||||
|
// D: Deserializer<'de>,
|
||||||
|
// {
|
||||||
|
// let vec: Vec<Resource> = Deserialize::deserialize(deserializer)?;
|
||||||
|
// let ha: HashSet<Resource> = vec.into_iter().collect();
|
||||||
|
// Ok(ResourceSet(ha))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
use super::{
|
||||||
|
error::BucketMetadataError,
|
||||||
|
get_bucket_metadata_sys,
|
||||||
|
policy::bucket_policy::{BucketPolicy, BucketPolicyArgs},
|
||||||
|
};
|
||||||
|
use crate::error::Result;
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
|
pub struct PolicySys {}
|
||||||
|
|
||||||
|
impl PolicySys {
|
||||||
|
pub async fn is_allowed(args: &BucketPolicyArgs) -> bool {
|
||||||
|
match Self::get(&args.bucket_name).await {
|
||||||
|
Ok(cfg) => return cfg.is_allowed(args),
|
||||||
|
Err(err) => {
|
||||||
|
if !BucketMetadataError::BucketPolicyNotFound.is(&err) {
|
||||||
|
warn!("config get err {:?}", err);
|
||||||
|
}
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
args.is_owner
|
||||||
|
}
|
||||||
|
pub async fn get(bucket: &str) -> Result<BucketPolicy> {
|
||||||
|
let bucket_meta_sys_lock = get_bucket_metadata_sys().await;
|
||||||
|
let bucket_meta_sys = bucket_meta_sys_lock.write().await;
|
||||||
|
|
||||||
|
let (cfg, _) = bucket_meta_sys.get_bucket_policy(bucket).await?;
|
||||||
|
|
||||||
|
Ok(cfg)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1643,7 +1643,7 @@ mod test {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_make_volume() {
|
async fn test_make_volume() {
|
||||||
let p = "./testv";
|
let p = "./testv0";
|
||||||
fs::create_dir_all(&p).await.unwrap();
|
fs::create_dir_all(&p).await.unwrap();
|
||||||
|
|
||||||
let ep = match Endpoint::try_from(p) {
|
let ep = match Endpoint::try_from(p) {
|
||||||
@@ -1662,18 +1662,18 @@ mod test {
|
|||||||
|
|
||||||
println!("ppp :{:?}", &tmpp);
|
println!("ppp :{:?}", &tmpp);
|
||||||
|
|
||||||
let volumes = vec!["a", "b", "c"];
|
let volumes = vec!["a123", "b123", "c123"];
|
||||||
|
|
||||||
disk.make_volumes(volumes.clone()).await.unwrap();
|
disk.make_volumes(volumes.clone()).await.unwrap();
|
||||||
|
|
||||||
disk.make_volumes(volumes.clone()).await.unwrap();
|
disk.make_volumes(volumes.clone()).await.unwrap();
|
||||||
|
|
||||||
fs::remove_dir_all(&p).await.unwrap();
|
let _ = fs::remove_dir_all(&p).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_delete_volume() {
|
async fn test_delete_volume() {
|
||||||
let p = "./testv";
|
let p = "./testv1";
|
||||||
fs::create_dir_all(&p).await.unwrap();
|
fs::create_dir_all(&p).await.unwrap();
|
||||||
|
|
||||||
let ep = match Endpoint::try_from(p) {
|
let ep = match Endpoint::try_from(p) {
|
||||||
@@ -1692,12 +1692,12 @@ mod test {
|
|||||||
|
|
||||||
println!("ppp :{:?}", &tmpp);
|
println!("ppp :{:?}", &tmpp);
|
||||||
|
|
||||||
let volumes = vec!["a", "b", "c"];
|
let volumes = vec!["a123", "b123", "c123"];
|
||||||
|
|
||||||
disk.make_volumes(volumes.clone()).await.unwrap();
|
disk.make_volumes(volumes.clone()).await.unwrap();
|
||||||
|
|
||||||
disk.delete_volume("a").await.unwrap();
|
disk.delete_volume("a").await.unwrap();
|
||||||
|
|
||||||
fs::remove_dir_all(&p).await.unwrap();
|
let _ = fs::remove_dir_all(&p).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,10 +127,10 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_check_local_server_addr() {
|
fn test_check_local_server_addr() {
|
||||||
let test_cases = [
|
let test_cases = [
|
||||||
(":54321", Ok(())),
|
// (":54321", Ok(())),
|
||||||
("localhost:54321", Ok(())),
|
("localhost:54321", Ok(())),
|
||||||
("0.0.0.0:9000", Ok(())),
|
("0.0.0.0:9000", Ok(())),
|
||||||
(":0", Ok(())),
|
// (":0", Ok(())),
|
||||||
("localhost", Err(Error::from_string("invalid socket address"))),
|
("localhost", Err(Error::from_string("invalid socket address"))),
|
||||||
("", Err(Error::from_string("invalid socket address"))),
|
("", Err(Error::from_string("invalid socket address"))),
|
||||||
(
|
(
|
||||||
|
|||||||
@@ -37,3 +37,164 @@ pub fn retain_slash(s: &str) -> String {
|
|||||||
format!("{}{}", s, SLASH_SEPARATOR)
|
format!("{}{}", s, SLASH_SEPARATOR)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct LazyBuf {
|
||||||
|
s: String,
|
||||||
|
buf: Option<Vec<u8>>,
|
||||||
|
w: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LazyBuf {
|
||||||
|
pub fn new(s: String) -> Self {
|
||||||
|
LazyBuf { s, buf: None, w: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn index(&self, i: usize) -> u8 {
|
||||||
|
if let Some(ref buf) = self.buf {
|
||||||
|
buf[i]
|
||||||
|
} else {
|
||||||
|
self.s.as_bytes()[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn append(&mut self, c: u8) {
|
||||||
|
if self.buf.is_none() {
|
||||||
|
if self.w < self.s.len() && self.s.as_bytes()[self.w] == c {
|
||||||
|
self.w += 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut new_buf = vec![0; self.s.len()];
|
||||||
|
new_buf[..self.w].copy_from_slice(&self.s.as_bytes()[..self.w]);
|
||||||
|
self.buf = Some(new_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref mut buf) = self.buf {
|
||||||
|
buf[self.w] = c;
|
||||||
|
self.w += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn string(&self) -> String {
|
||||||
|
if let Some(ref buf) = self.buf {
|
||||||
|
String::from_utf8(buf[..self.w].to_vec()).unwrap()
|
||||||
|
} else {
|
||||||
|
self.s[..self.w].to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clean(path: &str) -> String {
|
||||||
|
if path.is_empty() {
|
||||||
|
return ".".to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
let rooted = path.starts_with('/');
|
||||||
|
let n = path.len();
|
||||||
|
let mut out = LazyBuf::new(path.to_string());
|
||||||
|
let mut r = 0;
|
||||||
|
let mut dotdot = 0;
|
||||||
|
|
||||||
|
if rooted {
|
||||||
|
out.append(b'/');
|
||||||
|
r = 1;
|
||||||
|
dotdot = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while r < n {
|
||||||
|
match path.as_bytes()[r] {
|
||||||
|
b'/' => {
|
||||||
|
// Empty path element
|
||||||
|
r += 1;
|
||||||
|
}
|
||||||
|
b'.' if r + 1 == n || path.as_bytes()[r + 1] == b'/' => {
|
||||||
|
// . element
|
||||||
|
r += 1;
|
||||||
|
}
|
||||||
|
b'.' if path.as_bytes()[r + 1] == b'.' && (r + 2 == n || path.as_bytes()[r + 2] == b'/') => {
|
||||||
|
// .. element: remove to last /
|
||||||
|
r += 2;
|
||||||
|
|
||||||
|
if out.w > dotdot {
|
||||||
|
// Can backtrack
|
||||||
|
out.w -= 1;
|
||||||
|
while out.w > dotdot && out.index(out.w) != b'/' {
|
||||||
|
out.w -= 1;
|
||||||
|
}
|
||||||
|
} else if !rooted {
|
||||||
|
// Cannot backtrack but not rooted, so append .. element.
|
||||||
|
if out.w > 0 {
|
||||||
|
out.append(b'/');
|
||||||
|
}
|
||||||
|
out.append(b'.');
|
||||||
|
out.append(b'.');
|
||||||
|
dotdot = out.w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Real path element.
|
||||||
|
// Add slash if needed
|
||||||
|
if (rooted && out.w != 1) || (!rooted && out.w != 0) {
|
||||||
|
out.append(b'/');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy element
|
||||||
|
while r < n && path.as_bytes()[r] != b'/' {
|
||||||
|
out.append(path.as_bytes()[r]);
|
||||||
|
r += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turn empty string into "."
|
||||||
|
if out.w == 0 {
|
||||||
|
return ".".to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
out.string()
|
||||||
|
}
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_clean() {
|
||||||
|
assert_eq!(clean(""), ".");
|
||||||
|
assert_eq!(clean("abc"), "abc");
|
||||||
|
assert_eq!(clean("abc/def"), "abc/def");
|
||||||
|
assert_eq!(clean("a/b/c"), "a/b/c");
|
||||||
|
assert_eq!(clean("."), ".");
|
||||||
|
assert_eq!(clean(".."), "..");
|
||||||
|
assert_eq!(clean("../.."), "../..");
|
||||||
|
assert_eq!(clean("../../abc"), "../../abc");
|
||||||
|
assert_eq!(clean("/abc"), "/abc");
|
||||||
|
assert_eq!(clean("/"), "/");
|
||||||
|
assert_eq!(clean("abc/"), "abc");
|
||||||
|
assert_eq!(clean("abc/def/"), "abc/def");
|
||||||
|
assert_eq!(clean("a/b/c/"), "a/b/c");
|
||||||
|
assert_eq!(clean("./"), ".");
|
||||||
|
assert_eq!(clean("../"), "..");
|
||||||
|
assert_eq!(clean("../../"), "../..");
|
||||||
|
assert_eq!(clean("/abc/"), "/abc");
|
||||||
|
assert_eq!(clean("abc//def//ghi"), "abc/def/ghi");
|
||||||
|
assert_eq!(clean("//abc"), "/abc");
|
||||||
|
assert_eq!(clean("///abc"), "/abc");
|
||||||
|
assert_eq!(clean("//abc//"), "/abc");
|
||||||
|
assert_eq!(clean("abc//"), "abc");
|
||||||
|
assert_eq!(clean("abc/./def"), "abc/def");
|
||||||
|
assert_eq!(clean("/./abc/def"), "/abc/def");
|
||||||
|
assert_eq!(clean("abc/."), "abc");
|
||||||
|
assert_eq!(clean("abc/./../def"), "def");
|
||||||
|
assert_eq!(clean("abc//./../def"), "def");
|
||||||
|
assert_eq!(clean("abc/../../././../def"), "../../def");
|
||||||
|
|
||||||
|
assert_eq!(clean("abc/def/ghi/../jkl"), "abc/def/jkl");
|
||||||
|
assert_eq!(clean("abc/def/../ghi/../jkl"), "abc/jkl");
|
||||||
|
assert_eq!(clean("abc/def/.."), "abc");
|
||||||
|
assert_eq!(clean("abc/def/../.."), ".");
|
||||||
|
assert_eq!(clean("/abc/def/../.."), "/");
|
||||||
|
assert_eq!(clean("abc/def/../../.."), "..");
|
||||||
|
assert_eq!(clean("/abc/def/../../.."), "/");
|
||||||
|
assert_eq!(clean("abc/def/../../../ghi/jkl/../../../mno"), "../../mno");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -22,7 +22,7 @@ use hyper_util::{
|
|||||||
use protos::proto_gen::node_service::node_service_server::NodeServiceServer;
|
use protos::proto_gen::node_service::node_service_server::NodeServiceServer;
|
||||||
use s3s::{auth::SimpleAuth, service::S3ServiceBuilder};
|
use s3s::{auth::SimpleAuth, service::S3ServiceBuilder};
|
||||||
use service::hybrid;
|
use service::hybrid;
|
||||||
use std::{io::IsTerminal, net::SocketAddr, process::exit, str::FromStr};
|
use std::{io::IsTerminal, net::SocketAddr, str::FromStr};
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use tonic::{metadata::MetadataValue, Request, Status};
|
use tonic::{metadata::MetadataValue, Request, Status};
|
||||||
use tracing::{debug, info};
|
use tracing::{debug, info};
|
||||||
|
|||||||
+205
-11
@@ -1,7 +1,11 @@
|
|||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
use ecstore::bucket::error::BucketMetadataError;
|
||||||
use ecstore::bucket::get_bucket_metadata_sys;
|
use ecstore::bucket::get_bucket_metadata_sys;
|
||||||
|
use ecstore::bucket::metadata::BUCKET_POLICY_CONFIG;
|
||||||
use ecstore::bucket::metadata::BUCKET_TAGGING_CONFIG;
|
use ecstore::bucket::metadata::BUCKET_TAGGING_CONFIG;
|
||||||
use ecstore::bucket::metadata::BUCKET_VERSIONING_CONFIG;
|
use ecstore::bucket::metadata::BUCKET_VERSIONING_CONFIG;
|
||||||
|
use ecstore::bucket::policy::bucket_policy::BucketPolicy;
|
||||||
|
use ecstore::bucket::policy_sys::PolicySys;
|
||||||
use ecstore::bucket::tags::Tags;
|
use ecstore::bucket::tags::Tags;
|
||||||
use ecstore::bucket::versioning::State as VersioningState;
|
use ecstore::bucket::versioning::State as VersioningState;
|
||||||
use ecstore::bucket::versioning::Versioning;
|
use ecstore::bucket::versioning::Versioning;
|
||||||
@@ -21,7 +25,6 @@ use ecstore::store_api::PutObjReader;
|
|||||||
use ecstore::store_api::StorageAPI;
|
use ecstore::store_api::StorageAPI;
|
||||||
use futures::pin_mut;
|
use futures::pin_mut;
|
||||||
use futures::{Stream, StreamExt};
|
use futures::{Stream, StreamExt};
|
||||||
use http::status;
|
|
||||||
use http::HeaderMap;
|
use http::HeaderMap;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use s3s::dto::*;
|
use s3s::dto::*;
|
||||||
@@ -267,16 +270,6 @@ impl S3 for FS {
|
|||||||
Ok(S3Response::new(output))
|
Ok(S3Response::new(output))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(self))]
|
|
||||||
async fn get_object_lock_configuration(
|
|
||||||
&self,
|
|
||||||
_req: S3Request<GetObjectLockConfigurationInput>,
|
|
||||||
) -> S3Result<S3Response<GetObjectLockConfigurationOutput>> {
|
|
||||||
// mc cp step 1
|
|
||||||
let output = GetObjectLockConfigurationOutput::default();
|
|
||||||
Ok(S3Response::new(output))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(
|
#[tracing::instrument(
|
||||||
level = "debug",
|
level = "debug",
|
||||||
skip(self, req),
|
skip(self, req),
|
||||||
@@ -933,6 +926,207 @@ impl S3 for FS {
|
|||||||
|
|
||||||
Ok(S3Response::new(PutBucketVersioningOutput {}))
|
Ok(S3Response::new(PutBucketVersioningOutput {}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_bucket_policy_status(
|
||||||
|
&self,
|
||||||
|
_req: S3Request<GetBucketPolicyStatusInput>,
|
||||||
|
) -> S3Result<S3Response<GetBucketPolicyStatusOutput>> {
|
||||||
|
Err(s3_error!(NotImplemented, "GetBucketPolicyStatus is not implemented yet"))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_bucket_policy(&self, req: S3Request<GetBucketPolicyInput>) -> S3Result<S3Response<GetBucketPolicyOutput>> {
|
||||||
|
let GetBucketPolicyInput { bucket, .. } = req.input;
|
||||||
|
|
||||||
|
let layer = new_object_layer_fn();
|
||||||
|
let lock = layer.read().await;
|
||||||
|
let store = lock
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| S3Error::with_message(S3ErrorCode::InternalError, "Not init"))?;
|
||||||
|
|
||||||
|
if let Err(e) = store.get_bucket_info(&bucket, &BucketOptions::default()).await {
|
||||||
|
if DiskError::VolumeNotFound.is(&e) {
|
||||||
|
return Err(s3_error!(NoSuchBucket));
|
||||||
|
} else {
|
||||||
|
return Err(S3Error::with_message(S3ErrorCode::InternalError, format!("{}", e)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let cfg = match PolicySys::get(&bucket).await {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(err) => {
|
||||||
|
if BucketMetadataError::BucketPolicyNotFound.is(&err) {
|
||||||
|
return Err(s3_error!(NoSuchBucketPolicy));
|
||||||
|
}
|
||||||
|
return Err(S3Error::with_message(S3ErrorCode::InternalError, format!("{}", err)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let policys = try_!(cfg.marshal_msg());
|
||||||
|
|
||||||
|
Ok(S3Response::new(GetBucketPolicyOutput { policy: Some(policys) }))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn put_bucket_policy(&self, req: S3Request<PutBucketPolicyInput>) -> S3Result<S3Response<PutBucketPolicyOutput>> {
|
||||||
|
let PutBucketPolicyInput { bucket, policy, .. } = req.input;
|
||||||
|
|
||||||
|
let layer = new_object_layer_fn();
|
||||||
|
let lock = layer.read().await;
|
||||||
|
let store = lock
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| S3Error::with_message(S3ErrorCode::InternalError, "Not init"))?;
|
||||||
|
|
||||||
|
if let Err(e) = store.get_bucket_info(&bucket, &BucketOptions::default()).await {
|
||||||
|
if DiskError::VolumeNotFound.is(&e) {
|
||||||
|
return Err(s3_error!(NoSuchBucket));
|
||||||
|
} else {
|
||||||
|
return Err(S3Error::with_message(S3ErrorCode::InternalError, format!("{}", e)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let cfg = try_!(BucketPolicy::unmarshal(policy.as_bytes()));
|
||||||
|
warn!("put_bucket_policy struct {:?}", &cfg);
|
||||||
|
if let Err(err) = cfg.validate(&bucket) {
|
||||||
|
warn!("put_bucket_policy input {:?}", &policy);
|
||||||
|
|
||||||
|
warn!("cfg.validate err {:?}", err);
|
||||||
|
return Err(s3_error!(InvalidPolicyDocument));
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = try_!(cfg.marshal_msg());
|
||||||
|
|
||||||
|
let bucket_meta_sys_lock = get_bucket_metadata_sys().await;
|
||||||
|
let mut bucket_meta_sys = bucket_meta_sys_lock.write().await;
|
||||||
|
|
||||||
|
try_!(bucket_meta_sys.update(&bucket, BUCKET_POLICY_CONFIG, data.into()).await);
|
||||||
|
|
||||||
|
Ok(S3Response::new(PutBucketPolicyOutput {}))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_bucket_policy(
|
||||||
|
&self,
|
||||||
|
req: S3Request<DeleteBucketPolicyInput>,
|
||||||
|
) -> S3Result<S3Response<DeleteBucketPolicyOutput>> {
|
||||||
|
let DeleteBucketPolicyInput { bucket, .. } = req.input;
|
||||||
|
|
||||||
|
let layer = new_object_layer_fn();
|
||||||
|
let lock = layer.read().await;
|
||||||
|
let store = lock
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| S3Error::with_message(S3ErrorCode::InternalError, "Not init"))?;
|
||||||
|
|
||||||
|
if let Err(e) = store.get_bucket_info(&bucket, &BucketOptions::default()).await {
|
||||||
|
if DiskError::VolumeNotFound.is(&e) {
|
||||||
|
return Err(s3_error!(NoSuchBucket));
|
||||||
|
} else {
|
||||||
|
return Err(S3Error::with_message(S3ErrorCode::InternalError, format!("{}", e)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let bucket_meta_sys_lock = get_bucket_metadata_sys().await;
|
||||||
|
let mut bucket_meta_sys = bucket_meta_sys_lock.write().await;
|
||||||
|
|
||||||
|
try_!(bucket_meta_sys.delete(&bucket, BUCKET_POLICY_CONFIG).await);
|
||||||
|
|
||||||
|
Ok(S3Response::new(DeleteBucketPolicyOutput {}))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(level = "debug", skip(self))]
|
||||||
|
async fn get_bucket_lifecycle_configuration(
|
||||||
|
&self,
|
||||||
|
_req: S3Request<GetBucketLifecycleConfigurationInput>,
|
||||||
|
) -> S3Result<S3Response<GetBucketLifecycleConfigurationOutput>> {
|
||||||
|
Err(s3_error!(NotImplemented, "GetBucketLifecycleConfiguration is not implemented yet"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(level = "debug", skip(self))]
|
||||||
|
async fn put_bucket_lifecycle_configuration(
|
||||||
|
&self,
|
||||||
|
_req: S3Request<PutBucketLifecycleConfigurationInput>,
|
||||||
|
) -> S3Result<S3Response<PutBucketLifecycleConfigurationOutput>> {
|
||||||
|
Err(s3_error!(NotImplemented, "PutBucketLifecycleConfiguration is not implemented yet"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(level = "debug", skip(self))]
|
||||||
|
async fn delete_bucket_lifecycle(
|
||||||
|
&self,
|
||||||
|
_req: S3Request<DeleteBucketLifecycleInput>,
|
||||||
|
) -> S3Result<S3Response<DeleteBucketLifecycleOutput>> {
|
||||||
|
Err(s3_error!(NotImplemented, "DeleteBucketLifecycle is not implemented yet"))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_bucket_encryption(
|
||||||
|
&self,
|
||||||
|
_req: S3Request<GetBucketEncryptionInput>,
|
||||||
|
) -> S3Result<S3Response<GetBucketEncryptionOutput>> {
|
||||||
|
Err(s3_error!(NotImplemented, "GetBucketEncryption is not implemented yet"))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn put_bucket_encryption(
|
||||||
|
&self,
|
||||||
|
_req: S3Request<PutBucketEncryptionInput>,
|
||||||
|
) -> S3Result<S3Response<PutBucketEncryptionOutput>> {
|
||||||
|
Err(s3_error!(NotImplemented, "PutBucketEncryption is not implemented yet"))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_bucket_encryption(
|
||||||
|
&self,
|
||||||
|
_req: S3Request<DeleteBucketEncryptionInput>,
|
||||||
|
) -> S3Result<S3Response<DeleteBucketEncryptionOutput>> {
|
||||||
|
Err(s3_error!(NotImplemented, "DeleteBucketEncryption is not implemented yet"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(level = "debug", skip(self))]
|
||||||
|
async fn get_object_lock_configuration(
|
||||||
|
&self,
|
||||||
|
_req: S3Request<GetObjectLockConfigurationInput>,
|
||||||
|
) -> S3Result<S3Response<GetObjectLockConfigurationOutput>> {
|
||||||
|
// mc cp step 1
|
||||||
|
let output = GetObjectLockConfigurationOutput::default();
|
||||||
|
Ok(S3Response::new(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(level = "debug", skip(self))]
|
||||||
|
async fn put_object_lock_configuration(
|
||||||
|
&self,
|
||||||
|
_req: S3Request<PutObjectLockConfigurationInput>,
|
||||||
|
) -> S3Result<S3Response<PutObjectLockConfigurationOutput>> {
|
||||||
|
Err(s3_error!(NotImplemented, "PutObjectLockConfiguration is not implemented yet"))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_bucket_replication(
|
||||||
|
&self,
|
||||||
|
_req: S3Request<GetBucketReplicationInput>,
|
||||||
|
) -> S3Result<S3Response<GetBucketReplicationOutput>> {
|
||||||
|
Err(s3_error!(NotImplemented, "GetBucketReplication is not implemented yet"))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn put_bucket_replication(
|
||||||
|
&self,
|
||||||
|
_req: S3Request<PutBucketReplicationInput>,
|
||||||
|
) -> S3Result<S3Response<PutBucketReplicationOutput>> {
|
||||||
|
Err(s3_error!(NotImplemented, "PutBucketReplication is not implemented yet"))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_bucket_replication(
|
||||||
|
&self,
|
||||||
|
_req: S3Request<DeleteBucketReplicationInput>,
|
||||||
|
) -> S3Result<S3Response<DeleteBucketReplicationOutput>> {
|
||||||
|
Err(s3_error!(NotImplemented, "DeleteBucketReplication is not implemented yet"))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_bucket_notification_configuration(
|
||||||
|
&self,
|
||||||
|
_req: S3Request<GetBucketNotificationConfigurationInput>,
|
||||||
|
) -> S3Result<S3Response<GetBucketNotificationConfigurationOutput>> {
|
||||||
|
Err(s3_error!(NotImplemented, "GetBucketNotificationConfiguration is not implemented yet"))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn put_bucket_notification_configuration(
|
||||||
|
&self,
|
||||||
|
_req: S3Request<PutBucketNotificationConfigurationInput>,
|
||||||
|
) -> S3Result<S3Response<PutBucketNotificationConfigurationOutput>> {
|
||||||
|
Err(s3_error!(NotImplemented, "PutBucketNotificationConfiguration is not implemented yet"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
|||||||
Reference in New Issue
Block a user