Merge branch 'main' of github.com:rustfs/s3-rustfs into feature/observability-metrics

# Conflicts:
#	.github/workflows/build.yml
#	.github/workflows/ci.yml
#	Cargo.lock
#	Cargo.toml
#	appauth/src/token.rs
#	crates/config/src/config.rs
#	crates/event-notifier/examples/simple.rs
#	crates/event-notifier/src/global.rs
#	crates/event-notifier/src/lib.rs
#	crates/event-notifier/src/notifier.rs
#	crates/event-notifier/src/store.rs
#	crates/filemeta/src/filemeta.rs
#	crates/notify/examples/webhook.rs
#	crates/utils/Cargo.toml
#	ecstore/Cargo.toml
#	ecstore/src/cmd/bucket_replication.rs
#	ecstore/src/config/com.rs
#	ecstore/src/disk/error.rs
#	ecstore/src/disk/mod.rs
#	ecstore/src/set_disk.rs
#	ecstore/src/store_api.rs
#	ecstore/src/store_list_objects.rs
#	iam/Cargo.toml
#	iam/src/manager.rs
#	policy/Cargo.toml
#	rustfs/src/admin/rpc.rs
#	rustfs/src/main.rs
#	rustfs/src/storage/mod.rs
This commit is contained in:
houseme
2025-06-19 13:16:48 +08:00
249 changed files with 25137 additions and 11731 deletions
+311 -52
View File
@@ -1,15 +1,12 @@
use ecstore::disk::error::clone_disk_err;
use ecstore::disk::error::DiskError;
use policy::policy::Error as PolicyError;
pub type Result<T> = core::result::Result<T, Error>;
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error(transparent)]
PolicyError(#[from] PolicyError),
#[error("ecstore error: {0}")]
EcstoreError(common::error::Error),
#[error("{0}")]
StringError(String),
@@ -92,71 +89,333 @@ pub enum Error {
#[error("policy too large")]
PolicyTooLarge,
#[error("config not found")]
ConfigNotFound,
#[error("io error: {0}")]
Io(std::io::Error),
}
impl PartialEq for Error {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Error::StringError(a), Error::StringError(b)) => a == b,
(Error::NoSuchUser(a), Error::NoSuchUser(b)) => a == b,
(Error::NoSuchAccount(a), Error::NoSuchAccount(b)) => a == b,
(Error::NoSuchServiceAccount(a), Error::NoSuchServiceAccount(b)) => a == b,
(Error::NoSuchTempAccount(a), Error::NoSuchTempAccount(b)) => a == b,
(Error::NoSuchGroup(a), Error::NoSuchGroup(b)) => a == b,
(Error::InvalidServiceType(a), Error::InvalidServiceType(b)) => a == b,
(Error::Io(a), Error::Io(b)) => a.kind() == b.kind() && a.to_string() == b.to_string(),
// For complex types like PolicyError, CryptoError, JWTError, compare string representations
(a, b) => std::mem::discriminant(a) == std::mem::discriminant(b) && a.to_string() == b.to_string(),
}
}
}
impl Clone for Error {
fn clone(&self) -> Self {
match self {
Error::PolicyError(e) => Error::StringError(e.to_string()), // Convert to string since PolicyError may not be cloneable
Error::StringError(s) => Error::StringError(s.clone()),
Error::CryptoError(e) => Error::StringError(format!("crypto: {}", e)), // Convert to string
Error::NoSuchUser(s) => Error::NoSuchUser(s.clone()),
Error::NoSuchAccount(s) => Error::NoSuchAccount(s.clone()),
Error::NoSuchServiceAccount(s) => Error::NoSuchServiceAccount(s.clone()),
Error::NoSuchTempAccount(s) => Error::NoSuchTempAccount(s.clone()),
Error::NoSuchGroup(s) => Error::NoSuchGroup(s.clone()),
Error::NoSuchPolicy => Error::NoSuchPolicy,
Error::PolicyInUse => Error::PolicyInUse,
Error::GroupNotEmpty => Error::GroupNotEmpty,
Error::InvalidArgument => Error::InvalidArgument,
Error::IamSysNotInitialized => Error::IamSysNotInitialized,
Error::InvalidServiceType(s) => Error::InvalidServiceType(s.clone()),
Error::ErrCredMalformed => Error::ErrCredMalformed,
Error::CredNotInitialized => Error::CredNotInitialized,
Error::InvalidAccessKeyLength => Error::InvalidAccessKeyLength,
Error::InvalidSecretKeyLength => Error::InvalidSecretKeyLength,
Error::ContainsReservedChars => Error::ContainsReservedChars,
Error::GroupNameContainsReservedChars => Error::GroupNameContainsReservedChars,
Error::JWTError(e) => Error::StringError(format!("jwt err {}", e)), // Convert to string
Error::NoAccessKey => Error::NoAccessKey,
Error::InvalidToken => Error::InvalidToken,
Error::InvalidAccessKey => Error::InvalidAccessKey,
Error::IAMActionNotAllowed => Error::IAMActionNotAllowed,
Error::InvalidExpiration => Error::InvalidExpiration,
Error::NoSecretKeyWithAccessKey => Error::NoSecretKeyWithAccessKey,
Error::NoAccessKeyWithSecretKey => Error::NoAccessKeyWithSecretKey,
Error::PolicyTooLarge => Error::PolicyTooLarge,
Error::ConfigNotFound => Error::ConfigNotFound,
Error::Io(e) => Error::Io(std::io::Error::new(e.kind(), e.to_string())),
}
}
}
impl Error {
pub fn other<E>(error: E) -> Self
where
E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
Error::Io(std::io::Error::other(error))
}
}
impl From<ecstore::error::StorageError> for Error {
fn from(e: ecstore::error::StorageError) -> Self {
match e {
ecstore::error::StorageError::ConfigNotFound => Error::ConfigNotFound,
_ => Error::other(e),
}
}
}
impl From<Error> for ecstore::error::StorageError {
fn from(e: Error) -> Self {
match e {
Error::ConfigNotFound => ecstore::error::StorageError::ConfigNotFound,
_ => ecstore::error::StorageError::other(e),
}
}
}
impl From<policy::error::Error> for Error {
fn from(e: policy::error::Error) -> Self {
match e {
policy::error::Error::PolicyTooLarge => Error::PolicyTooLarge,
policy::error::Error::InvalidArgument => Error::InvalidArgument,
policy::error::Error::InvalidServiceType(s) => Error::InvalidServiceType(s),
policy::error::Error::IAMActionNotAllowed => Error::IAMActionNotAllowed,
policy::error::Error::InvalidExpiration => Error::InvalidExpiration,
policy::error::Error::NoAccessKey => Error::NoAccessKey,
policy::error::Error::InvalidToken => Error::InvalidToken,
policy::error::Error::InvalidAccessKey => Error::InvalidAccessKey,
policy::error::Error::NoSecretKeyWithAccessKey => Error::NoSecretKeyWithAccessKey,
policy::error::Error::NoAccessKeyWithSecretKey => Error::NoAccessKeyWithSecretKey,
policy::error::Error::Io(e) => Error::Io(e),
policy::error::Error::JWTError(e) => Error::JWTError(e),
policy::error::Error::NoSuchUser(s) => Error::NoSuchUser(s),
policy::error::Error::NoSuchAccount(s) => Error::NoSuchAccount(s),
policy::error::Error::NoSuchServiceAccount(s) => Error::NoSuchServiceAccount(s),
policy::error::Error::NoSuchTempAccount(s) => Error::NoSuchTempAccount(s),
policy::error::Error::NoSuchGroup(s) => Error::NoSuchGroup(s),
policy::error::Error::NoSuchPolicy => Error::NoSuchPolicy,
policy::error::Error::PolicyInUse => Error::PolicyInUse,
policy::error::Error::GroupNotEmpty => Error::GroupNotEmpty,
policy::error::Error::InvalidAccessKeyLength => Error::InvalidAccessKeyLength,
policy::error::Error::InvalidSecretKeyLength => Error::InvalidSecretKeyLength,
policy::error::Error::ContainsReservedChars => Error::ContainsReservedChars,
policy::error::Error::GroupNameContainsReservedChars => Error::GroupNameContainsReservedChars,
policy::error::Error::CredNotInitialized => Error::CredNotInitialized,
policy::error::Error::IamSysNotInitialized => Error::IamSysNotInitialized,
policy::error::Error::PolicyError(e) => Error::PolicyError(e),
policy::error::Error::StringError(s) => Error::StringError(s),
policy::error::Error::CryptoError(e) => Error::CryptoError(e),
policy::error::Error::ErrCredMalformed => Error::ErrCredMalformed,
}
}
}
impl From<Error> for std::io::Error {
fn from(e: Error) -> Self {
std::io::Error::other(e)
}
}
impl From<serde_json::Error> for Error {
fn from(e: serde_json::Error) -> Self {
Error::other(e)
}
}
impl From<base64_simd::Error> for Error {
fn from(e: base64_simd::Error) -> Self {
Error::other(e)
}
}
pub fn is_err_config_not_found(err: &Error) -> bool {
matches!(err, Error::ConfigNotFound)
}
// pub fn is_err_no_such_user(e: &Error) -> bool {
// matches!(e, Error::NoSuchUser(_))
// }
pub fn is_err_no_such_policy(err: &common::error::Error) -> bool {
if let Some(e) = err.downcast_ref::<Error>() {
matches!(e, Error::NoSuchPolicy)
} else {
false
}
pub fn is_err_no_such_policy(err: &Error) -> bool {
matches!(err, Error::NoSuchPolicy)
}
pub fn is_err_no_such_user(err: &common::error::Error) -> bool {
if let Some(e) = err.downcast_ref::<Error>() {
matches!(e, Error::NoSuchUser(_))
} else {
false
}
pub fn is_err_no_such_user(err: &Error) -> bool {
matches!(err, Error::NoSuchUser(_))
}
pub fn is_err_no_such_account(err: &common::error::Error) -> bool {
if let Some(e) = err.downcast_ref::<Error>() {
matches!(e, Error::NoSuchAccount(_))
} else {
false
}
pub fn is_err_no_such_account(err: &Error) -> bool {
matches!(err, Error::NoSuchAccount(_))
}
pub fn is_err_no_such_temp_account(err: &common::error::Error) -> bool {
if let Some(e) = err.downcast_ref::<Error>() {
matches!(e, Error::NoSuchTempAccount(_))
} else {
false
}
pub fn is_err_no_such_temp_account(err: &Error) -> bool {
matches!(err, Error::NoSuchTempAccount(_))
}
pub fn is_err_no_such_group(err: &common::error::Error) -> bool {
if let Some(e) = err.downcast_ref::<Error>() {
matches!(e, Error::NoSuchGroup(_))
} else {
false
}
pub fn is_err_no_such_group(err: &Error) -> bool {
matches!(err, Error::NoSuchGroup(_))
}
pub fn is_err_no_such_service_account(err: &common::error::Error) -> bool {
if let Some(e) = err.downcast_ref::<Error>() {
matches!(e, Error::NoSuchServiceAccount(_))
} else {
false
}
pub fn is_err_no_such_service_account(err: &Error) -> bool {
matches!(err, Error::NoSuchServiceAccount(_))
}
pub fn clone_err(e: &common::error::Error) -> common::error::Error {
if let Some(e) = e.downcast_ref::<DiskError>() {
clone_disk_err(e)
} else if let Some(e) = e.downcast_ref::<std::io::Error>() {
if let Some(code) = e.raw_os_error() {
common::error::Error::new(std::io::Error::from_raw_os_error(code))
} else {
common::error::Error::new(std::io::Error::new(e.kind(), e.to_string()))
// pub fn clone_err(e: &Error) -> Error {
// if let Some(e) = e.downcast_ref::<DiskError>() {
// clone_disk_err(e)
// } else if let Some(e) = e.downcast_ref::<std::io::Error>() {
// if let Some(code) = e.raw_os_error() {
// Error::new(std::io::Error::from_raw_os_error(code))
// } else {
// Error::new(std::io::Error::new(e.kind(), e.to_string()))
// }
// } else {
// //TODO: Optimize other types
// Error::msg(e.to_string())
// }
// }
#[cfg(test)]
mod tests {
use super::*;
use std::io::{Error as IoError, ErrorKind};
#[test]
fn test_iam_error_to_io_error_conversion() {
let iam_errors = vec![
Error::NoSuchUser("testuser".to_string()),
Error::NoSuchAccount("testaccount".to_string()),
Error::InvalidArgument,
Error::IAMActionNotAllowed,
Error::PolicyTooLarge,
Error::ConfigNotFound,
];
for iam_error in iam_errors {
let io_error: std::io::Error = iam_error.clone().into();
// Check that conversion creates an io::Error
assert_eq!(io_error.kind(), ErrorKind::Other);
// Check that the error message is preserved
assert!(io_error.to_string().contains(&iam_error.to_string()));
}
}
#[test]
fn test_iam_error_from_storage_error() {
// Test conversion from StorageError
let storage_error = ecstore::error::StorageError::ConfigNotFound;
let iam_error: Error = storage_error.into();
assert_eq!(iam_error, Error::ConfigNotFound);
// Test reverse conversion
let back_to_storage: ecstore::error::StorageError = iam_error.into();
assert_eq!(back_to_storage, ecstore::error::StorageError::ConfigNotFound);
}
#[test]
fn test_iam_error_from_policy_error() {
use policy::error::Error as PolicyError;
let policy_errors = vec![
(PolicyError::NoSuchUser("user1".to_string()), Error::NoSuchUser("user1".to_string())),
(PolicyError::NoSuchPolicy, Error::NoSuchPolicy),
(PolicyError::InvalidArgument, Error::InvalidArgument),
(PolicyError::PolicyTooLarge, Error::PolicyTooLarge),
];
for (policy_error, expected_iam_error) in policy_errors {
let converted_iam_error: Error = policy_error.into();
assert_eq!(converted_iam_error, expected_iam_error);
}
}
#[test]
fn test_iam_error_other_function() {
let custom_error = "Custom IAM error";
let iam_error = Error::other(custom_error);
match iam_error {
Error::Io(io_error) => {
assert!(io_error.to_string().contains(custom_error));
assert_eq!(io_error.kind(), ErrorKind::Other);
}
_ => panic!("Expected Io variant"),
}
}
#[test]
fn test_iam_error_from_serde_json() {
// Test conversion from serde_json::Error
let invalid_json = r#"{"invalid": json}"#;
let json_error = serde_json::from_str::<serde_json::Value>(invalid_json).unwrap_err();
let iam_error: Error = json_error.into();
match iam_error {
Error::Io(io_error) => {
assert_eq!(io_error.kind(), ErrorKind::Other);
}
_ => panic!("Expected Io variant"),
}
}
#[test]
fn test_helper_functions() {
// Test helper functions for error type checking
assert!(is_err_config_not_found(&Error::ConfigNotFound));
assert!(!is_err_config_not_found(&Error::NoSuchPolicy));
assert!(is_err_no_such_policy(&Error::NoSuchPolicy));
assert!(!is_err_no_such_policy(&Error::ConfigNotFound));
assert!(is_err_no_such_user(&Error::NoSuchUser("test".to_string())));
assert!(!is_err_no_such_user(&Error::NoSuchAccount("test".to_string())));
assert!(is_err_no_such_account(&Error::NoSuchAccount("test".to_string())));
assert!(!is_err_no_such_account(&Error::NoSuchUser("test".to_string())));
assert!(is_err_no_such_temp_account(&Error::NoSuchTempAccount("test".to_string())));
assert!(!is_err_no_such_temp_account(&Error::NoSuchAccount("test".to_string())));
assert!(is_err_no_such_group(&Error::NoSuchGroup("test".to_string())));
assert!(!is_err_no_such_group(&Error::NoSuchUser("test".to_string())));
assert!(is_err_no_such_service_account(&Error::NoSuchServiceAccount("test".to_string())));
assert!(!is_err_no_such_service_account(&Error::NoSuchAccount("test".to_string())));
}
#[test]
fn test_iam_error_io_preservation() {
// Test that Io variant preserves original io::Error
let original_io = IoError::new(ErrorKind::PermissionDenied, "access denied");
let iam_error = Error::Io(original_io);
let converted_io: std::io::Error = iam_error.into();
// Note: Our clone implementation creates a new io::Error with the same kind and message
// but it becomes ErrorKind::Other when cloned
assert_eq!(converted_io.kind(), ErrorKind::Other);
assert!(converted_io.to_string().contains("access denied"));
}
#[test]
fn test_error_display_format() {
let test_cases = vec![
(Error::NoSuchUser("testuser".to_string()), "user 'testuser' does not exist"),
(Error::NoSuchAccount("testaccount".to_string()), "account 'testaccount' does not exist"),
(Error::InvalidArgument, "invalid arguments specified"),
(Error::IAMActionNotAllowed, "action not allowed"),
(Error::ConfigNotFound, "config not found"),
];
for (error, expected_message) in test_cases {
assert_eq!(error.to_string(), expected_message);
}
} else {
//TODO: Optimize other types
common::error::Error::msg(e.to_string())
}
}
+2 -6
View File
@@ -1,6 +1,5 @@
use common::error::{Error, Result};
use crate::error::{Error, Result};
use ecstore::store::ECStore;
use error::Error as IamError;
use manager::IamCache;
use policy::auth::Credentials;
use std::sync::{Arc, OnceLock};
@@ -62,8 +61,5 @@ pub async fn init_iam_sys(ecstore: Arc<ECStore>) -> Result<()> {
#[inline]
pub fn get() -> Result<Arc<IamSys<ObjectStore>>> {
IAM_SYS
.get()
.map(Arc::clone)
.ok_or(Error::new(IamError::IamSysNotInitialized))
IAM_SYS.get().map(Arc::clone).ok_or(Error::IamSysNotInitialized)
}
+112 -110
View File
@@ -1,3 +1,4 @@
use crate::error::{is_err_config_not_found, Error, Result};
use crate::{
cache::{Cache, CacheEntity},
error::{is_err_no_such_group, is_err_no_such_policy, is_err_no_such_user, Error as IamError},
@@ -8,9 +9,7 @@ use crate::{
STATUS_DISABLED, STATUS_ENABLED,
},
};
use common::error::{Error, Result};
use ecstore::config::error::is_err_config_not_found;
use ecstore::utils::{crypto::base64_encode, path::path_join_buf};
// use ecstore::utils::crypto::base64_encode;
use madmin::{AccountStatus, AddOrUpdateUserReq, GroupDesc};
use policy::{
arn::ARN,
@@ -20,6 +19,8 @@ use policy::{
default::DEFAULT_POLICIES, iam_policy_claim_name_sa, Policy, PolicyDoc, EMBEDDED_POLICY_TYPE, INHERITED_POLICY_TYPE,
},
};
use rustfs_utils::crypto::base64_encode;
use rustfs_utils::path::path_join_buf;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::{
@@ -75,7 +76,7 @@ where
T: Store,
{
pub(crate) async fn new(api: T) -> Arc<Self> {
let (sender, receiver) = mpsc::channel::<i64>(100);
let (sender, reciver) = mpsc::channel::<i64>(100);
let sys = Arc::new(Self {
api,
@@ -86,46 +87,53 @@ where
last_timestamp: AtomicI64::new(0),
});
sys.clone().init(receiver).await.unwrap();
sys.clone().init(reciver).await.unwrap();
sys
}
async fn init(self: Arc<Self>, receiver: Receiver<i64>) -> Result<()> {
async fn init(self: Arc<Self>, reciver: Receiver<i64>) -> Result<()> {
self.clone().save_iam_formatter().await?;
self.clone().load().await?;
// Background thread starts periodic updates or receives signal updates
tokio::spawn({
let s = Arc::clone(&self);
async move {
let ticker = tokio::time::interval(Duration::from_secs(120));
tokio::pin!(ticker, receiver);
loop {
select! {
_ = ticker.tick() => {
if let Err(err) =s.clone().load().await{
error!("iam load err {:?}", err);
}
},
i = receiver.recv() => {
match i {
Some(t) => {
let last = s.last_timestamp.load(Ordering::Relaxed);
if last <= t {
// 检查环境变量是否设置
let skip_background_task = std::env::var("RUSTFS_SKIP_BACKGROUND_TASK").is_ok();
if let Err(err) =s.clone().load().await{
error!("iam load err {:?}", err);
if !skip_background_task {
// Background thread starts periodic updates or receives signal updates
tokio::spawn({
let s = Arc::clone(&self);
async move {
let ticker = tokio::time::interval(Duration::from_secs(120));
tokio::pin!(ticker, reciver);
loop {
select! {
_ = ticker.tick() => {
warn!("iam load ticker");
if let Err(err) =s.clone().load().await{
error!("iam load err {:?}", err);
}
},
i = reciver.recv() => {
warn!("iam load reciver");
match i {
Some(t) => {
let last = s.last_timestamp.load(Ordering::Relaxed);
if last <= t {
warn!("iam load reciver load");
if let Err(err) =s.clone().load().await{
error!("iam load err {:?}", err);
}
ticker.reset();
}
ticker.reset();
}
},
None => return,
},
None => return,
}
}
}
}
}
}
});
});
}
Ok(())
}
@@ -183,7 +191,7 @@ where
pub async fn get_policy(&self, name: &str) -> Result<Policy> {
if name.is_empty() {
return Err(Error::new(IamError::InvalidArgument));
return Err(Error::InvalidArgument);
}
let policies = MappedPolicy::new(name).to_slice();
@@ -200,13 +208,13 @@ where
.load()
.get(&policy)
.cloned()
.ok_or(Error::new(IamError::NoSuchPolicy))?;
.ok_or(Error::NoSuchPolicy)?;
to_merge.push(v.policy);
}
if to_merge.is_empty() {
return Err(Error::new(IamError::NoSuchPolicy));
return Err(Error::NoSuchPolicy);
}
Ok(Policy::merge_policies(to_merge))
@@ -214,20 +222,15 @@ where
pub async fn get_policy_doc(&self, name: &str) -> Result<PolicyDoc> {
if name.is_empty() {
return Err(Error::new(IamError::InvalidArgument));
return Err(Error::InvalidArgument);
}
self.cache
.policy_docs
.load()
.get(name)
.cloned()
.ok_or(Error::new(IamError::NoSuchPolicy))
self.cache.policy_docs.load().get(name).cloned().ok_or(Error::NoSuchPolicy)
}
pub async fn delete_policy(&self, name: &str, is_from_notify: bool) -> Result<()> {
if name.is_empty() {
return Err(Error::new(IamError::InvalidArgument));
return Err(Error::InvalidArgument);
}
if is_from_notify {
@@ -255,7 +258,7 @@ where
});
if !users.is_empty() || !groups.is_empty() {
return Err(IamError::PolicyInUse.into());
return Err(Error::PolicyInUse);
}
if let Err(err) = self.api.delete_policy_doc(name).await {
@@ -275,7 +278,7 @@ where
pub async fn set_policy(&self, name: &str, policy: Policy) -> Result<OffsetDateTime> {
if name.is_empty() || policy.is_empty() {
return Err(Error::new(IamError::InvalidArgument));
return Err(Error::InvalidArgument);
}
let policy_doc = self
@@ -407,7 +410,7 @@ where
}
if !user_exists {
return Err(Error::new(IamError::NoSuchUser(access_key.to_string())));
return Err(Error::NoSuchUser(access_key.to_string()));
}
Ok(ret)
@@ -453,13 +456,13 @@ where
/// create a service account and update cache
pub async fn add_service_account(&self, cred: Credentials) -> Result<OffsetDateTime> {
if cred.access_key.is_empty() || cred.parent_user.is_empty() {
return Err(Error::new(IamError::InvalidArgument));
return Err(Error::InvalidArgument);
}
let users = self.cache.users.load();
if let Some(x) = users.get(&cred.access_key) {
if x.credentials.is_service_account() {
return Err(Error::new(IamError::IAMActionNotAllowed));
return Err(Error::IAMActionNotAllowed);
}
}
@@ -476,11 +479,11 @@ where
pub async fn update_service_account(&self, name: &str, opts: UpdateServiceAccountOpts) -> Result<OffsetDateTime> {
let Some(ui) = self.cache.users.load().get(name).cloned() else {
return Err(IamError::NoSuchServiceAccount(name.to_string()).into());
return Err(Error::NoSuchServiceAccount(name.to_string()));
};
if !ui.credentials.is_service_account() {
return Err(IamError::NoSuchServiceAccount(name.to_string()).into());
return Err(Error::NoSuchServiceAccount(name.to_string()));
}
let mut cr = ui.credentials.clone();
@@ -488,7 +491,7 @@ where
if let Some(secret) = opts.secret_key {
if !is_secret_key_valid(&secret) {
return Err(IamError::InvalidSecretKeyLength.into());
return Err(Error::InvalidSecretKeyLength);
}
cr.secret_key = secret;
}
@@ -535,7 +538,7 @@ where
if !session_policy.version.is_empty() && !session_policy.statements.is_empty() {
let policy_buf = serde_json::to_vec(&session_policy)?;
if policy_buf.len() > MAX_SVCSESSION_POLICY_SIZE {
return Err(IamError::PolicyTooLarge.into());
return Err(Error::PolicyTooLarge);
}
m.insert(SESSION_POLICY_NAME.to_owned(), serde_json::Value::String(base64_encode(&policy_buf)));
@@ -558,7 +561,7 @@ where
pub async fn policy_db_get(&self, name: &str, groups: &Option<Vec<String>>) -> Result<Vec<String>> {
if name.is_empty() {
return Err(Error::new(IamError::InvalidArgument));
return Err(Error::InvalidArgument);
}
let (mut policies, _) = self.policy_db_get_internal(name, false, false).await?;
@@ -594,7 +597,7 @@ where
Cache::add_or_update(&self.cache.groups, name, p, OffsetDateTime::now_utc());
}
m.get(name).cloned().ok_or(IamError::NoSuchGroup(name.to_string()))?
m.get(name).cloned().ok_or(Error::NoSuchGroup(name.to_string()))?
}
};
@@ -639,7 +642,7 @@ where
Cache::add_or_update(&self.cache.user_policies, name, p, OffsetDateTime::now_utc());
p.clone()
} else {
let mp = match self.cache.sts_policies.load().get(name) {
match self.cache.sts_policies.load().get(name) {
Some(p) => p.clone(),
None => {
let mut m = HashMap::new();
@@ -651,8 +654,7 @@ where
MappedPolicy::default()
}
}
};
mp
}
}
}
};
@@ -696,7 +698,7 @@ where
for group in self
.cache
.user_group_memberships
.user_group_memeberships
.load()
.get(name)
.cloned()
@@ -737,7 +739,7 @@ where
}
pub async fn policy_db_set(&self, name: &str, user_type: UserType, is_group: bool, policy: &str) -> Result<OffsetDateTime> {
if name.is_empty() {
return Err(Error::new(IamError::InvalidArgument));
return Err(Error::InvalidArgument);
}
if policy.is_empty() {
@@ -763,7 +765,7 @@ where
let policy_docs_cache = self.cache.policy_docs.load();
for p in mp.to_slice() {
if !policy_docs_cache.contains_key(&p) {
return Err(Error::new(IamError::NoSuchPolicy));
return Err(Error::NoSuchPolicy);
}
}
@@ -791,14 +793,14 @@ where
cred.is_expired(),
cred.parent_user.is_empty()
);
return Err(Error::new(IamError::InvalidArgument));
return Err(Error::InvalidArgument);
}
if let Some(policy) = policy_name {
let mp = MappedPolicy::new(policy);
let (_, combined_policy_stmt) = filter_policies(&self.cache, &mp.policies, "temp");
if combined_policy_stmt.is_empty() {
return Err(Error::msg(format!("need policy not found {}", IamError::NoSuchPolicy)));
return Err(Error::other(format!("need poliy not found {}", IamError::NoSuchPolicy)));
}
self.api
@@ -821,15 +823,15 @@ where
pub async fn get_user_info(&self, name: &str) -> Result<madmin::UserInfo> {
let users = self.cache.users.load();
let policies = self.cache.user_policies.load();
let group_members = self.cache.user_group_memberships.load();
let group_members = self.cache.user_group_memeberships.load();
let u = match users.get(name) {
Some(u) => u,
None => return Err(Error::new(IamError::NoSuchUser(name.to_string()))),
None => return Err(Error::NoSuchUser(name.to_string())),
};
if u.credentials.is_temp() || u.credentials.is_service_account() {
return Err(Error::new(IamError::IAMActionNotAllowed));
return Err(Error::IAMActionNotAllowed);
}
let mut uinfo = madmin::UserInfo {
@@ -860,7 +862,7 @@ where
let users = self.cache.users.load();
let policies = self.cache.user_policies.load();
let group_members = self.cache.user_group_memberships.load();
let group_members = self.cache.user_group_memeberships.load();
for (k, v) in users.iter() {
if v.credentials.is_temp() || v.credentials.is_service_account() {
@@ -894,7 +896,7 @@ where
pub async fn get_bucket_users(&self, bucket_name: &str) -> Result<HashMap<String, madmin::UserInfo>> {
let users = self.cache.users.load();
let policies_cache = self.cache.user_policies.load();
let group_members = self.cache.user_group_memberships.load();
let group_members = self.cache.user_group_memeberships.load();
let group_policy_cache = self.cache.group_policies.load();
let mut ret = HashMap::new();
@@ -961,7 +963,7 @@ where
if let Some(x) = users.get(access_key) {
warn!("user already exists: {:?}", x);
if x.credentials.is_temp() {
return Err(IamError::IAMActionNotAllowed.into());
return Err(Error::IAMActionNotAllowed);
}
}
@@ -971,7 +973,7 @@ where
_ => auth::ACCOUNT_OFF,
}
};
let user_entity = UserIdentity::from(Credentials {
let user_entiry = UserIdentity::from(Credentials {
access_key: access_key.to_string(),
secret_key: args.secret_key.to_string(),
status: status.to_owned(),
@@ -979,21 +981,21 @@ where
});
self.api
.save_user_identity(access_key, UserType::Reg, user_entity.clone(), None)
.save_user_identity(access_key, UserType::Reg, user_entiry.clone(), None)
.await?;
self.update_user_with_claims(access_key, user_entity)?;
self.update_user_with_claims(access_key, user_entiry)?;
Ok(OffsetDateTime::now_utc())
}
pub async fn delete_user(&self, access_key: &str, utype: UserType) -> Result<()> {
if access_key.is_empty() {
return Err(Error::new(IamError::InvalidArgument));
return Err(Error::InvalidArgument);
}
if utype == UserType::Reg {
if let Some(member_of) = self.cache.user_group_memberships.load().get(access_key) {
if let Some(member_of) = self.cache.user_group_memeberships.load().get(access_key) {
for member in member_of.iter() {
let _ = self
.remove_members_from_group(member, vec![access_key.to_string()], false)
@@ -1041,13 +1043,13 @@ where
pub async fn update_user_secret_key(&self, access_key: &str, secret_key: &str) -> Result<()> {
if access_key.is_empty() || secret_key.is_empty() {
return Err(Error::new(IamError::InvalidArgument));
return Err(Error::InvalidArgument);
}
let users = self.cache.users.load();
let u = match users.get(access_key) {
Some(u) => u,
None => return Err(Error::new(IamError::NoSuchUser(access_key.to_string()))),
None => return Err(Error::NoSuchUser(access_key.to_string())),
};
let mut cred = u.credentials.clone();
@@ -1064,21 +1066,21 @@ where
pub async fn set_user_status(&self, access_key: &str, status: AccountStatus) -> Result<OffsetDateTime> {
if access_key.is_empty() {
return Err(Error::new(IamError::InvalidArgument));
return Err(Error::InvalidArgument);
}
if !access_key.is_empty() && status != AccountStatus::Enabled && status != AccountStatus::Disabled {
return Err(Error::new(IamError::InvalidArgument));
return Err(Error::InvalidArgument);
}
let users = self.cache.users.load();
let u = match users.get(access_key) {
Some(u) => u,
None => return Err(Error::new(IamError::NoSuchUser(access_key.to_string()))),
None => return Err(Error::NoSuchUser(access_key.to_string())),
};
if u.credentials.is_temp() || u.credentials.is_service_account() {
return Err(Error::new(IamError::IAMActionNotAllowed));
return Err(Error::IAMActionNotAllowed);
}
let status = {
@@ -1088,7 +1090,7 @@ where
}
};
let user_entity = UserIdentity::from(Credentials {
let user_entiry = UserIdentity::from(Credentials {
access_key: access_key.to_string(),
secret_key: u.credentials.secret_key.clone(),
status: status.to_owned(),
@@ -1096,10 +1098,10 @@ where
});
self.api
.save_user_identity(access_key, UserType::Reg, user_entity.clone(), None)
.save_user_identity(access_key, UserType::Reg, user_entiry.clone(), None)
.await?;
self.update_user_with_claims(access_key, user_entity)?;
self.update_user_with_claims(access_key, user_entiry)?;
Ok(OffsetDateTime::now_utc())
}
@@ -1123,7 +1125,7 @@ where
let users = self.cache.users.load();
let u = match users.get(access_key) {
Some(u) => u,
None => return Err(Error::new(IamError::NoSuchUser(access_key.to_string()))),
None => return Err(Error::NoSuchUser(access_key.to_string())),
};
if u.credentials.is_temp() {
@@ -1135,7 +1137,7 @@ where
pub async fn add_users_to_group(&self, group: &str, members: Vec<String>) -> Result<OffsetDateTime> {
if group.is_empty() {
return Err(Error::new(IamError::InvalidArgument));
return Err(Error::InvalidArgument);
}
let users_cache = self.cache.users.load();
@@ -1143,10 +1145,10 @@ where
for member in members.iter() {
if let Some(u) = users_cache.get(member) {
if u.credentials.is_temp() || u.credentials.is_service_account() {
return Err(Error::new(IamError::IAMActionNotAllowed));
return Err(Error::IAMActionNotAllowed);
}
} else {
return Err(Error::new(IamError::NoSuchUser(member.to_string())));
return Err(Error::NoSuchUser(member.to_string()));
}
}
@@ -1167,12 +1169,12 @@ where
Cache::add_or_update(&self.cache.groups, group, &gi, OffsetDateTime::now_utc());
let user_group_memberships = self.cache.user_group_memberships.load();
let user_group_memeberships = self.cache.user_group_memeberships.load();
members.iter().for_each(|member| {
if let Some(m) = user_group_memberships.get(member) {
if let Some(m) = user_group_memeberships.get(member) {
let mut m = m.clone();
m.insert(group.to_string());
Cache::add_or_update(&self.cache.user_group_memberships, member, &m, OffsetDateTime::now_utc());
Cache::add_or_update(&self.cache.user_group_memeberships, member, &m, OffsetDateTime::now_utc());
}
});
@@ -1181,13 +1183,13 @@ where
pub async fn set_group_status(&self, name: &str, enable: bool) -> Result<OffsetDateTime> {
if name.is_empty() {
return Err(Error::new(IamError::InvalidArgument));
return Err(Error::InvalidArgument);
}
let groups = self.cache.groups.load();
let mut gi = match groups.get(name) {
Some(gi) => gi.clone(),
None => return Err(Error::new(IamError::NoSuchGroup(name.to_string()))),
None => return Err(Error::NoSuchGroup(name.to_string())),
};
if enable {
@@ -1213,7 +1215,7 @@ where
.load()
.get(name)
.cloned()
.ok_or(Error::new(IamError::NoSuchGroup(name.to_string())))?;
.ok_or(Error::NoSuchGroup(name.to_string()))?;
Ok(GroupDesc {
name: name.to_string(),
@@ -1240,7 +1242,7 @@ where
.load()
.get(name)
.cloned()
.ok_or(Error::new(IamError::NoSuchGroup(name.to_string())))?;
.ok_or(Error::NoSuchGroup(name.to_string()))?;
let s: HashSet<&String> = HashSet::from_iter(gi.members.iter());
let d: HashSet<&String> = HashSet::from_iter(members.iter());
@@ -1252,12 +1254,12 @@ where
Cache::add_or_update(&self.cache.groups, name, &gi, OffsetDateTime::now_utc());
let user_group_memberships = self.cache.user_group_memberships.load();
let user_group_memeberships = self.cache.user_group_memeberships.load();
members.iter().for_each(|member| {
if let Some(m) = user_group_memberships.get(member) {
if let Some(m) = user_group_memeberships.get(member) {
let mut m = m.clone();
m.remove(name);
Cache::add_or_update(&self.cache.user_group_memberships, member, &m, OffsetDateTime::now_utc());
Cache::add_or_update(&self.cache.user_group_memeberships, member, &m, OffsetDateTime::now_utc());
}
});
@@ -1266,7 +1268,7 @@ where
pub async fn remove_users_from_group(&self, group: &str, members: Vec<String>) -> Result<OffsetDateTime> {
if group.is_empty() {
return Err(Error::new(IamError::InvalidArgument));
return Err(Error::InvalidArgument);
}
let users_cache = self.cache.users.load();
@@ -1274,10 +1276,10 @@ where
for member in members.iter() {
if let Some(u) = users_cache.get(member) {
if u.credentials.is_temp() || u.credentials.is_service_account() {
return Err(Error::new(IamError::IAMActionNotAllowed));
return Err(Error::IAMActionNotAllowed);
}
} else {
return Err(Error::new(IamError::NoSuchUser(member.to_string())));
return Err(Error::NoSuchUser(member.to_string()));
}
}
@@ -1287,10 +1289,10 @@ where
.load()
.get(group)
.cloned()
.ok_or(Error::new(IamError::NoSuchGroup(group.to_string())))?;
.ok_or(Error::NoSuchGroup(group.to_string()))?;
if members.is_empty() && !gi.members.is_empty() {
return Err(IamError::GroupNotEmpty.into());
return Err(Error::GroupNotEmpty);
}
if members.is_empty() {
@@ -1308,23 +1310,23 @@ where
}
fn remove_group_from_memberships_map(&self, group: &str) {
let user_group_memberships = self.cache.user_group_memberships.load();
for (k, v) in user_group_memberships.iter() {
let user_group_memeberships = self.cache.user_group_memeberships.load();
for (k, v) in user_group_memeberships.iter() {
if v.contains(group) {
let mut m = v.clone();
m.remove(group);
Cache::add_or_update(&self.cache.user_group_memberships, k, &m, OffsetDateTime::now_utc());
Cache::add_or_update(&self.cache.user_group_memeberships, k, &m, OffsetDateTime::now_utc());
}
}
}
fn update_group_memberships_map(&self, group: &str, gi: &GroupInfo) {
let user_group_memberships = self.cache.user_group_memberships.load();
let user_group_memeberships = self.cache.user_group_memeberships.load();
for member in gi.members.iter() {
if let Some(m) = user_group_memberships.get(member) {
if let Some(m) = user_group_memeberships.get(member) {
let mut m = m.clone();
m.insert(group.to_string());
Cache::add_or_update(&self.cache.user_group_memberships, member, &m, OffsetDateTime::now_utc());
Cache::add_or_update(&self.cache.user_group_memeberships, member, &m, OffsetDateTime::now_utc());
}
}
}
@@ -1442,7 +1444,7 @@ where
Cache::delete(&self.cache.users, name, OffsetDateTime::now_utc());
}
let member_of = self.cache.user_group_memberships.load();
let member_of = self.cache.user_group_memeberships.load();
if let Some(m) = member_of.get(name) {
for group in m.iter() {
if let Err(err) = self.remove_members_from_group(group, vec![name.to_string()], true).await {
@@ -1589,7 +1591,7 @@ pub fn get_token_signing_key() -> Option<String> {
pub fn extract_jwt_claims(u: &UserIdentity) -> Result<HashMap<String, Value>> {
let Some(sys_key) = get_token_signing_key() else {
return Err(Error::msg("global active sk not init"));
return Err(Error::other("global active sk not init"));
};
let keys = vec![&sys_key, &u.credentials.secret_key];
@@ -1599,7 +1601,7 @@ pub fn extract_jwt_claims(u: &UserIdentity) -> Result<HashMap<String, Value>> {
return Ok(claims);
}
}
Err(Error::msg("unable to extract claims"))
Err(Error::other("unable to extract claims"))
}
fn filter_policies(cache: &Cache, policy_name: &str, bucket_name: &str) -> (String, Policy) {
+3 -3
View File
@@ -1,9 +1,9 @@
pub mod object;
use crate::cache::Cache;
use common::error::Result;
use crate::error::Result;
use policy::{auth::UserIdentity, policy::PolicyDoc};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde::{Deserialize, Serialize, de::DeserializeOwned};
use std::collections::{HashMap, HashSet};
use time::OffsetDateTime;
@@ -49,7 +49,7 @@ pub trait Store: Clone + Send + Sync + 'static {
m: &mut HashMap<String, MappedPolicy>,
) -> Result<()>;
async fn load_mapped_policys(&self, user_type: UserType, is_group: bool, m: &mut HashMap<String, MappedPolicy>)
-> Result<()>;
-> Result<()>;
async fn load_all(&self, cache: &Cache) -> Result<()>;
}
+39 -38
View File
@@ -1,26 +1,25 @@
use super::{GroupInfo, MappedPolicy, Store, UserType};
use crate::error::{Error, Result, is_err_config_not_found};
use crate::{
cache::{Cache, CacheEntity},
error::{is_err_no_such_policy, is_err_no_such_user},
get_global_action_cred,
manager::{extract_jwt_claims, get_default_policyes},
};
use common::error::{Error, Result};
use ecstore::{
config::{
com::{delete_config, read_config, read_config_with_metadata, save_config},
error::is_err_config_not_found,
RUSTFS_CONFIG_PREFIX,
com::{delete_config, read_config, read_config_with_metadata, save_config},
},
store::ECStore,
store_api::{ObjectInfo, ObjectOptions},
store_list_objects::{ObjectInfoOrErr, WalkOptions},
utils::path::{path_join_buf, SLASH_SEPARATOR},
};
use futures::future::join_all;
use lazy_static::lazy_static;
use policy::{auth::UserIdentity, policy::PolicyDoc};
use serde::{de::DeserializeOwned, Serialize};
use rustfs_utils::path::{SLASH_SEPARATOR, path_join_buf};
use serde::{Serialize, de::DeserializeOwned};
use std::{collections::HashMap, sync::Arc};
use tokio::sync::broadcast::{self, Receiver as B_Receiver};
use tokio::sync::mpsc::{self, Sender};
@@ -153,7 +152,7 @@ impl ObjectStore {
let _ = sender
.send(StringOrErr {
item: None,
err: Some(err),
err: Some(err.into()),
})
.await;
return;
@@ -207,13 +206,13 @@ impl ObjectStore {
let mut futures = Vec::with_capacity(names.len());
for name in names {
let policy_name = ecstore::utils::path::dir(name);
let policy_name = rustfs_utils::path::dir(name);
futures.push(async move {
match self.load_policy(&policy_name).await {
Ok(p) => Ok(p),
Err(err) => {
if !is_err_no_such_policy(&err) {
Err(Error::msg(std::format!("load policy doc failed: {}", err)))
Err(Error::other(format!("load policy doc failed: {}", err)))
} else {
Ok(PolicyDoc::default())
}
@@ -239,13 +238,13 @@ impl ObjectStore {
let mut futures = Vec::with_capacity(names.len());
for name in names {
let user_name = ecstore::utils::path::dir(name);
let user_name = rustfs_utils::path::dir(name);
futures.push(async move {
match self.load_user_identity(&user_name, user_type).await {
Ok(res) => Ok(res),
Err(err) => {
if !is_err_no_such_user(&err) {
Err(Error::msg(std::format!("load user failed: {}", err)))
Err(Error::other(format!("load user failed: {}", err)))
} else {
Ok(UserIdentity::default())
}
@@ -272,7 +271,7 @@ impl ObjectStore {
.await
.map_err(|err| {
if is_err_config_not_found(&err) {
Error::new(crate::error::Error::NoSuchPolicy)
Error::NoSuchPolicy
} else {
err
}
@@ -296,7 +295,7 @@ impl ObjectStore {
Ok(p) => Ok(p),
Err(err) => {
if !is_err_no_such_policy(&err) {
Err(Error::msg(std::format!("load mapped policy failed: {}", err)))
Err(Error::other(format!("load mapped policy failed: {}", err)))
} else {
Ok(MappedPolicy::default())
}
@@ -369,10 +368,12 @@ impl Store for ObjectStore {
let mut data = serde_json::to_vec(&item)?;
data = Self::encrypt_data(&data)?;
save_config(self.object_api.clone(), path.as_ref(), data).await
save_config(self.object_api.clone(), path.as_ref(), data).await?;
Ok(())
}
async fn delete_iam_config(&self, path: impl AsRef<str> + Send) -> Result<()> {
delete_config(self.object_api.clone(), path.as_ref()).await
delete_config(self.object_api.clone(), path.as_ref()).await?;
Ok(())
}
async fn save_user_identity(
@@ -390,7 +391,7 @@ impl Store for ObjectStore {
.await
.map_err(|err| {
if is_err_config_not_found(&err) {
Error::new(crate::error::Error::NoSuchPolicy)
Error::NoSuchPolicy
} else {
err
}
@@ -403,7 +404,7 @@ impl Store for ObjectStore {
.await
.map_err(|err| {
if is_err_config_not_found(&err) {
Error::new(crate::error::Error::NoSuchUser(name.to_owned()))
Error::NoSuchUser(name.to_owned())
} else {
err
}
@@ -412,7 +413,7 @@ impl Store for ObjectStore {
if u.credentials.is_expired() {
let _ = self.delete_iam_config(get_user_identity_path(name, user_type)).await;
let _ = self.delete_iam_config(get_mapped_policy_path(name, user_type, false)).await;
return Err(Error::new(crate::error::Error::NoSuchUser(name.to_owned())));
return Err(Error::NoSuchUser(name.to_owned()));
}
if u.credentials.access_key.is_empty() {
@@ -430,7 +431,7 @@ impl Store for ObjectStore {
let _ = self.delete_iam_config(get_mapped_policy_path(name, user_type, false)).await;
}
warn!("extract_jwt_claims failed: {}", err);
return Err(Error::new(crate::error::Error::NoSuchUser(name.to_owned())));
return Err(Error::NoSuchUser(name.to_owned()));
}
}
}
@@ -463,7 +464,7 @@ impl Store for ObjectStore {
}
if let Some(item) = v.item {
let name = ecstore::utils::path::dir(&item);
let name = rustfs_utils::path::dir(&item);
self.load_user(&name, user_type, m).await?;
}
}
@@ -476,7 +477,7 @@ impl Store for ObjectStore {
.await
.map_err(|err| {
if is_err_config_not_found(&err) {
Error::new(crate::error::Error::NoSuchUser(name.to_owned()))
Error::NoSuchUser(name.to_owned())
} else {
err
}
@@ -491,7 +492,7 @@ impl Store for ObjectStore {
async fn delete_group_info(&self, name: &str) -> Result<()> {
self.delete_iam_config(get_group_info_path(name)).await.map_err(|err| {
if is_err_config_not_found(&err) {
Error::new(crate::error::Error::NoSuchPolicy)
Error::NoSuchPolicy
} else {
err
}
@@ -501,7 +502,7 @@ impl Store for ObjectStore {
async fn load_group(&self, name: &str, m: &mut HashMap<String, GroupInfo>) -> Result<()> {
let u: GroupInfo = self.load_iam_config(get_group_info_path(name)).await.map_err(|err| {
if is_err_config_not_found(&err) {
Error::new(crate::error::Error::NoSuchPolicy)
Error::NoSuchPolicy
} else {
err
}
@@ -525,7 +526,7 @@ impl Store for ObjectStore {
}
if let Some(item) = v.item {
let name = ecstore::utils::path::dir(&item);
let name = rustfs_utils::path::dir(&item);
self.load_group(&name, m).await?;
}
}
@@ -539,7 +540,7 @@ impl Store for ObjectStore {
async fn delete_policy_doc(&self, name: &str) -> Result<()> {
self.delete_iam_config(get_policy_doc_path(name)).await.map_err(|err| {
if is_err_config_not_found(&err) {
Error::new(crate::error::Error::NoSuchPolicy)
Error::NoSuchPolicy
} else {
err
}
@@ -552,7 +553,7 @@ impl Store for ObjectStore {
.await
.map_err(|err| {
if is_err_config_not_found(&err) {
Error::new(crate::error::Error::NoSuchPolicy)
Error::NoSuchPolicy
} else {
err
}
@@ -589,7 +590,7 @@ impl Store for ObjectStore {
}
if let Some(item) = v.item {
let name = ecstore::utils::path::dir(&item);
let name = rustfs_utils::path::dir(&item);
self.load_policy_doc(&name, m).await?;
}
}
@@ -613,7 +614,7 @@ impl Store for ObjectStore {
.await
.map_err(|err| {
if is_err_config_not_found(&err) {
Error::new(crate::error::Error::NoSuchPolicy)
Error::NoSuchPolicy
} else {
err
}
@@ -689,7 +690,7 @@ impl Store for ObjectStore {
continue;
}
let policy_name = ecstore::utils::path::dir(&policies_list[idx]);
let policy_name = rustfs_utils::path::dir(&policies_list[idx]);
info!("load policy: {}", policy_name);
@@ -705,7 +706,7 @@ impl Store for ObjectStore {
continue;
}
let policy_name = ecstore::utils::path::dir(&policies_list[idx]);
let policy_name = rustfs_utils::path::dir(&policies_list[idx]);
info!("load policy: {}", policy_name);
policy_docs_cache.insert(policy_name, p);
}
@@ -733,7 +734,7 @@ impl Store for ObjectStore {
continue;
}
let name = ecstore::utils::path::dir(&item_name_list[idx]);
let name = rustfs_utils::path::dir(&item_name_list[idx]);
info!("load reg user: {}", name);
user_items_cache.insert(name, p);
}
@@ -747,7 +748,7 @@ impl Store for ObjectStore {
continue;
}
let name = ecstore::utils::path::dir(&item_name_list[idx]);
let name = rustfs_utils::path::dir(&item_name_list[idx]);
info!("load reg user: {}", name);
user_items_cache.insert(name, p);
}
@@ -763,10 +764,10 @@ impl Store for ObjectStore {
let mut items_cache = CacheEntity::default();
for item in item_name_list.iter() {
let name = ecstore::utils::path::dir(item);
let name = rustfs_utils::path::dir(item);
info!("load group: {}", name);
if let Err(err) = self.load_group(&name, &mut items_cache).await {
return Err(Error::msg(std::format!("load group failed: {}", err)));
return Err(Error::other(format!("load group failed: {}", err)));
};
}
@@ -827,7 +828,7 @@ impl Store for ObjectStore {
info!("load group policy: {}", name);
if let Err(err) = self.load_mapped_policy(name, UserType::Reg, true, &mut items_cache).await {
if !is_err_no_such_policy(&err) {
return Err(Error::msg(std::format!("load group policy failed: {}", err)));
return Err(Error::other(format!("load group policy failed: {}", err)));
}
};
}
@@ -842,11 +843,11 @@ impl Store for ObjectStore {
let mut items_cache = HashMap::default();
for item in item_name_list.iter() {
let name = ecstore::utils::path::dir(item);
let name = rustfs_utils::path::dir(item);
info!("load svc user: {}", name);
if let Err(err) = self.load_user(&name, UserType::Svc, &mut items_cache).await {
if !is_err_no_such_user(&err) {
return Err(Error::msg(std::format!("load svc user failed: {}", err)));
return Err(Error::other(format!("load svc user failed: {}", err)));
}
};
}
@@ -860,7 +861,7 @@ impl Store for ObjectStore {
.await
{
if !is_err_no_such_policy(&err) {
return Err(Error::msg(std::format!("load_mapped_policy failed: {}", err)));
return Err(Error::other(format!("load_mapped_policy failed: {}", err)));
}
}
}
@@ -879,7 +880,7 @@ impl Store for ObjectStore {
for item in item_name_list.iter() {
info!("load sts user path: {}", item);
let name = ecstore::utils::path::dir(item);
let name = rustfs_utils::path::dir(item);
info!("load sts user: {}", name);
if let Err(err) = self.load_user(&name, UserType::Sts, &mut sts_items_cache).await {
info!("load sts user failed: {}", err);
+37 -36
View File
@@ -1,35 +1,36 @@
use crate::error::Error as IamError;
use crate::error::is_err_no_such_account;
use crate::error::is_err_no_such_temp_account;
use crate::error::Error as IamError;
use crate::error::{Error, Result};
use crate::get_global_action_cred;
use crate::manager::IamCache;
use crate::manager::extract_jwt_claims;
use crate::manager::get_default_policyes;
use crate::manager::IamCache;
use crate::store::MappedPolicy;
use crate::store::Store;
use crate::store::UserType;
use common::error::{Error, Result};
use ecstore::utils::crypto::base64_decode;
use ecstore::utils::crypto::base64_encode;
// use ecstore::utils::crypto::base64_decode;
// use ecstore::utils::crypto::base64_encode;
use madmin::AddOrUpdateUserReq;
use madmin::GroupDesc;
use policy::arn::ARN;
use policy::auth::ACCOUNT_ON;
use policy::auth::Credentials;
use policy::auth::UserIdentity;
use policy::auth::contains_reserved_chars;
use policy::auth::create_new_credentials_with_metadata;
use policy::auth::generate_credentials;
use policy::auth::is_access_key_valid;
use policy::auth::is_secret_key_valid;
use policy::auth::Credentials;
use policy::auth::UserIdentity;
use policy::auth::ACCOUNT_ON;
use policy::policy::iam_policy_claim_name_sa;
use policy::policy::Args;
use policy::policy::Policy;
use policy::policy::PolicyDoc;
use policy::policy::EMBEDDED_POLICY_TYPE;
use policy::policy::INHERITED_POLICY_TYPE;
use serde_json::json;
use policy::policy::Policy;
use policy::policy::PolicyDoc;
use policy::policy::iam_policy_claim_name_sa;
use rustfs_utils::crypto::{base64_decode, base64_encode};
use serde_json::Value;
use serde_json::json;
use std::collections::HashMap;
use std::sync::Arc;
use time::OffsetDateTime;
@@ -81,7 +82,7 @@ impl<T: Store> IamSys<T> {
pub async fn delete_policy(&self, name: &str, notify: bool) -> Result<()> {
for k in get_default_policyes().keys() {
if k == name {
return Err(Error::msg("system policy can not be deleted"));
return Err(Error::other("system policy can not be deleted"));
}
}
@@ -123,11 +124,11 @@ impl<T: Store> IamSys<T> {
pub async fn get_role_policy(&self, arn_str: &str) -> Result<(ARN, String)> {
let Some(arn) = ARN::parse(arn_str).ok() else {
return Err(Error::msg("Invalid ARN"));
return Err(Error::other("Invalid ARN"));
};
let Some(policy) = self.roles_map.get(&arn) else {
return Err(Error::msg("No such role"));
return Err(Error::other("No such role"));
};
Ok((arn, policy.clone()))
@@ -157,7 +158,7 @@ impl<T: Store> IamSys<T> {
pub async fn is_temp_user(&self, name: &str) -> Result<(bool, String)> {
let Some(u) = self.store.get_user(name).await else {
return Err(IamError::NoSuchUser(name.to_string()).into());
return Err(IamError::NoSuchUser(name.to_string()));
};
if u.credentials.is_temp() {
Ok((true, u.credentials.parent_user))
@@ -167,7 +168,7 @@ impl<T: Store> IamSys<T> {
}
pub async fn is_service_account(&self, name: &str) -> Result<(bool, String)> {
let Some(u) = self.store.get_user(name).await else {
return Err(IamError::NoSuchUser(name.to_string()).into());
return Err(IamError::NoSuchUser(name.to_string()));
};
if u.credentials.is_service_account() {
@@ -193,22 +194,22 @@ impl<T: Store> IamSys<T> {
opts: NewServiceAccountOpts,
) -> Result<(Credentials, OffsetDateTime)> {
if parent_user.is_empty() {
return Err(IamError::InvalidArgument.into());
return Err(IamError::InvalidArgument);
}
if !opts.access_key.is_empty() && opts.secret_key.is_empty() {
return Err(IamError::NoSecretKeyWithAccessKey.into());
return Err(IamError::NoSecretKeyWithAccessKey);
}
if !opts.secret_key.is_empty() && opts.access_key.is_empty() {
return Err(IamError::NoAccessKeyWithSecretKey.into());
return Err(IamError::NoAccessKeyWithSecretKey);
}
if parent_user == opts.access_key {
return Err(IamError::IAMActionNotAllowed.into());
return Err(IamError::IAMActionNotAllowed);
}
if opts.expiration.is_none() {
return Err(IamError::InvalidExpiration.into());
return Err(IamError::InvalidExpiration);
}
// TODO: check allow_site_replicator_account
@@ -217,7 +218,7 @@ impl<T: Store> IamSys<T> {
policy.validate()?;
let buf = serde_json::to_vec(&policy)?;
if buf.len() > MAX_SVCSESSION_POLICY_SIZE {
return Err(IamError::PolicyTooLarge.into());
return Err(IamError::PolicyTooLarge);
}
buf
@@ -304,7 +305,7 @@ impl<T: Store> IamSys<T> {
Ok(res) => res,
Err(err) => {
if is_err_no_such_account(&err) {
return Err(IamError::NoSuchServiceAccount(access_key.to_string()).into());
return Err(IamError::NoSuchServiceAccount(access_key.to_string()));
}
return Err(err);
@@ -312,7 +313,7 @@ impl<T: Store> IamSys<T> {
};
if !sa.credentials.is_service_account() {
return Err(IamError::NoSuchServiceAccount(access_key.to_string()).into());
return Err(IamError::NoSuchServiceAccount(access_key.to_string()));
}
let op_pt = claims.get(&iam_policy_claim_name_sa());
@@ -329,7 +330,7 @@ impl<T: Store> IamSys<T> {
async fn get_account_with_claims(&self, access_key: &str) -> Result<(UserIdentity, HashMap<String, Value>)> {
let Some(acc) = self.store.get_user(access_key).await else {
return Err(IamError::NoSuchAccount(access_key.to_string()).into());
return Err(IamError::NoSuchAccount(access_key.to_string()));
};
let m = extract_jwt_claims(&acc)?;
@@ -363,7 +364,7 @@ impl<T: Store> IamSys<T> {
Ok(res) => res,
Err(err) => {
if is_err_no_such_account(&err) {
return Err(IamError::NoSuchTempAccount(access_key.to_string()).into());
return Err(IamError::NoSuchTempAccount(access_key.to_string()));
}
return Err(err);
@@ -371,7 +372,7 @@ impl<T: Store> IamSys<T> {
};
if !sa.credentials.is_temp() {
return Err(IamError::NoSuchTempAccount(access_key.to_string()).into());
return Err(IamError::NoSuchTempAccount(access_key.to_string()));
}
let op_pt = claims.get(&iam_policy_claim_name_sa());
@@ -388,11 +389,11 @@ impl<T: Store> IamSys<T> {
pub async fn get_claims_for_svc_acc(&self, access_key: &str) -> Result<HashMap<String, Value>> {
let Some(u) = self.store.get_user(access_key).await else {
return Err(IamError::NoSuchServiceAccount(access_key.to_string()).into());
return Err(IamError::NoSuchServiceAccount(access_key.to_string()));
};
if u.credentials.is_service_account() {
return Err(IamError::NoSuchServiceAccount(access_key.to_string()).into());
return Err(IamError::NoSuchServiceAccount(access_key.to_string()));
}
extract_jwt_claims(&u)
@@ -414,15 +415,15 @@ impl<T: Store> IamSys<T> {
pub async fn create_user(&self, access_key: &str, args: &AddOrUpdateUserReq) -> Result<OffsetDateTime> {
if !is_access_key_valid(access_key) {
return Err(IamError::InvalidAccessKeyLength.into());
return Err(IamError::InvalidAccessKeyLength);
}
if contains_reserved_chars(access_key) {
return Err(IamError::ContainsReservedChars.into());
return Err(IamError::ContainsReservedChars);
}
if !is_secret_key_valid(&args.secret_key) {
return Err(IamError::InvalidSecretKeyLength.into());
return Err(IamError::InvalidSecretKeyLength);
}
self.store.add_user(access_key, args).await
@@ -431,11 +432,11 @@ impl<T: Store> IamSys<T> {
pub async fn set_user_secret_key(&self, access_key: &str, secret_key: &str) -> Result<()> {
if !is_access_key_valid(access_key) {
return Err(IamError::InvalidAccessKeyLength.into());
return Err(IamError::InvalidAccessKeyLength);
}
if !is_secret_key_valid(secret_key) {
return Err(IamError::InvalidSecretKeyLength.into());
return Err(IamError::InvalidSecretKeyLength);
}
self.store.update_user_secret_key(access_key, secret_key).await
@@ -467,7 +468,7 @@ impl<T: Store> IamSys<T> {
pub async fn add_users_to_group(&self, group: &str, users: Vec<String>) -> Result<OffsetDateTime> {
if contains_reserved_chars(group) {
return Err(IamError::GroupNameContainsReservedChars.into());
return Err(IamError::GroupNameContainsReservedChars);
}
self.store.add_users_to_group(group, users).await
// TODO: notification
+6 -6
View File
@@ -1,7 +1,7 @@
use common::error::{Error, Result};
use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header};
use rand::{Rng, RngCore};
use serde::{de::DeserializeOwned, Serialize};
use serde::{Serialize, de::DeserializeOwned};
use std::io::{Error, Result};
/// Generates a random access key of the specified length.
///
@@ -24,7 +24,7 @@ pub fn gen_access_key(length: usize) -> Result<String> {
];
if length < 3 {
return Err(Error::msg("access key length is too short"));
return Err(Error::other("access key length is too short"));
}
let mut result = String::with_capacity(length);
@@ -55,7 +55,7 @@ pub fn gen_secret_key(length: usize) -> Result<String> {
use base64_simd::URL_SAFE_NO_PAD;
if length < 8 {
return Err(Error::msg("secret key length is too short"));
return Err(Error::other("secret key length is too short"));
}
let mut rng = rand::rng();
@@ -68,7 +68,7 @@ pub fn gen_secret_key(length: usize) -> Result<String> {
Ok(key_str)
}
pub fn generate_jwt<T: Serialize>(claims: &T, secret: &str) -> Result<String, jsonwebtoken::errors::Error> {
pub fn generate_jwt<T: Serialize>(claims: &T, secret: &str) -> std::result::Result<String, jsonwebtoken::errors::Error> {
let header = Header::new(Algorithm::HS512);
jsonwebtoken::encode(&header, &claims, &EncodingKey::from_secret(secret.as_bytes()))
}
@@ -76,7 +76,7 @@ pub fn generate_jwt<T: Serialize>(claims: &T, secret: &str) -> Result<String, js
pub fn extract_claims<T: DeserializeOwned>(
token: &str,
secret: &str,
) -> Result<jsonwebtoken::TokenData<T>, jsonwebtoken::errors::Error> {
) -> std::result::Result<jsonwebtoken::TokenData<T>, jsonwebtoken::errors::Error> {
jsonwebtoken::decode::<T>(
token,
&DecodingKey::from_secret(secret.as_bytes()),