mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-28 00:58:59 +00:00
fix(iam): reject cross-identity access key collisions (#5085)
fix(iam): reject service account access key collisions
This commit is contained in:
@@ -783,14 +783,10 @@ where
|
||||
}
|
||||
|
||||
let cache = self.cache.snapshot();
|
||||
let users = Arc::clone(&cache.users);
|
||||
if let Some(x) = users.get(&cred.access_key)
|
||||
&& x.credentials.is_service_account()
|
||||
{
|
||||
if cache.users.contains_key(&cred.access_key) || cache.sts_accounts.contains_key(&cred.access_key) {
|
||||
return Err(Error::AccessKeyAlreadyExists);
|
||||
}
|
||||
drop(cache);
|
||||
drop(users);
|
||||
|
||||
let u = UserIdentity::new(cred);
|
||||
|
||||
|
||||
+82
-1
@@ -1429,6 +1429,7 @@ mod tests {
|
||||
/// When true, parent user has no groups and no mapped policies (empty `policy_db_get`).
|
||||
empty_policies: bool,
|
||||
saved_sts_users: Arc<Mutex<HashMap<String, UserIdentity>>>,
|
||||
saved_service_account_count: Arc<Mutex<usize>>,
|
||||
}
|
||||
|
||||
impl StsTestMockStore {
|
||||
@@ -1436,8 +1437,16 @@ mod tests {
|
||||
Self {
|
||||
empty_policies,
|
||||
saved_sts_users: Arc::new(Mutex::new(HashMap::new())),
|
||||
saved_service_account_count: Arc::new(Mutex::new(0)),
|
||||
}
|
||||
}
|
||||
|
||||
fn saved_service_account_count(&self) -> usize {
|
||||
*self
|
||||
.saved_service_account_count
|
||||
.lock()
|
||||
.expect("saved_service_account_count mutex poisoned")
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
@@ -1461,10 +1470,16 @@ mod tests {
|
||||
async fn save_user_identity(
|
||||
&self,
|
||||
name: &str,
|
||||
_user_type: UserType,
|
||||
user_type: UserType,
|
||||
item: UserIdentity,
|
||||
_ttl: Option<usize>,
|
||||
) -> Result<()> {
|
||||
if user_type == UserType::Svc {
|
||||
*self
|
||||
.saved_service_account_count
|
||||
.lock()
|
||||
.expect("saved_service_account_count mutex poisoned") += 1;
|
||||
}
|
||||
self.saved_sts_users
|
||||
.lock()
|
||||
.expect("saved_sts_users mutex poisoned")
|
||||
@@ -1735,6 +1750,72 @@ mod tests {
|
||||
assert_eq!(err, Error::AccessKeyAlreadyExists);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn service_account_creation_rejects_regular_user_access_key() {
|
||||
ensure_test_global_credentials();
|
||||
|
||||
let iam_sys = test_iam_sys().await;
|
||||
let access_key = "REGULARUSERACCESSKEY1";
|
||||
let user = AddOrUpdateUserReq {
|
||||
secret_key: "regularUserSecret123".to_string(),
|
||||
policy: None,
|
||||
status: rustfs_madmin::AccountStatus::Enabled,
|
||||
};
|
||||
|
||||
iam_sys
|
||||
.store
|
||||
.add_user(access_key, &user)
|
||||
.await
|
||||
.expect("regular user should be created");
|
||||
|
||||
let err = iam_sys
|
||||
.new_service_account("svc-parent-user", None, service_account_opts(access_key, "serviceAccountSecret123"))
|
||||
.await
|
||||
.expect_err("service-account creation must reject a regular-user access key");
|
||||
|
||||
assert_eq!(err, Error::AccessKeyAlreadyExists);
|
||||
assert_eq!(iam_sys.store.api.saved_service_account_count(), 0);
|
||||
let existing = iam_sys.get_user(access_key).await.expect("regular user should remain cached");
|
||||
assert!(!existing.credentials.is_service_account());
|
||||
assert_eq!(existing.credentials.secret_key, user.secret_key);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn service_account_creation_rejects_sts_access_key() {
|
||||
ensure_test_global_credentials();
|
||||
|
||||
let iam_sys = test_iam_sys().await;
|
||||
let access_key = "TEMPORARYSERVICEKEY12";
|
||||
let temp_cred = Credentials {
|
||||
access_key: access_key.to_string(),
|
||||
secret_key: "temporarySecretKey123".to_string(),
|
||||
session_token: "temporary-session-token".to_string(),
|
||||
expiration: Some(OffsetDateTime::now_utc() + time::Duration::hours(1)),
|
||||
status: ACCOUNT_ON.to_string(),
|
||||
parent_user: "sts-parent-user".to_string(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
iam_sys
|
||||
.set_temp_user(access_key, &temp_cred, None)
|
||||
.await
|
||||
.expect("temporary credentials should be created");
|
||||
|
||||
let err = iam_sys
|
||||
.new_service_account("svc-parent-user", None, service_account_opts(access_key, "serviceAccountSecret123"))
|
||||
.await
|
||||
.expect_err("service-account creation must reject a temporary access key");
|
||||
|
||||
assert_eq!(err, Error::AccessKeyAlreadyExists);
|
||||
assert_eq!(iam_sys.store.api.saved_service_account_count(), 0);
|
||||
let existing = iam_sys
|
||||
.get_user(access_key)
|
||||
.await
|
||||
.expect("temporary account should remain cached");
|
||||
assert!(existing.credentials.is_temp());
|
||||
assert_eq!(existing.credentials.secret_key, temp_cred.secret_key);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn user_creation_rejects_service_account_access_key() {
|
||||
ensure_test_global_credentials();
|
||||
|
||||
@@ -71,6 +71,7 @@ mod tests {
|
||||
let s3_error = iam_error_to_s3_error(IamError::AccessKeyAlreadyExists);
|
||||
|
||||
assert_eq!(s3_error.code(), &S3ErrorCode::InvalidArgument);
|
||||
assert_eq!(s3_error.status_code(), Some(http::StatusCode::BAD_REQUEST));
|
||||
assert_eq!(s3_error.message(), Some("access key is already in use"));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user