fix(admin): accept IAM service account exports (#2249)

Co-authored-by: houseme <housemecn@gmail.com>
This commit is contained in:
weisd
2026-03-21 13:44:46 +08:00
committed by GitHub
parent 628481be7c
commit 95850c1bcd
2 changed files with 84 additions and 2 deletions
+32 -2
View File
@@ -12,11 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as DeError};
use serde_json::Value;
use serde_json::value::RawValue;
use std::collections::HashMap;
use time::OffsetDateTime;
use time::format_description::well_known::Rfc3339;
use crate::BackendInfo;
@@ -492,6 +493,30 @@ impl<'de> Deserialize<'de> for SRSessionPolicy {
}
}
fn deserialize_vec_or_default<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
where
D: Deserializer<'de>,
{
Ok(Option::<Vec<String>>::deserialize(deserializer)?.unwrap_or_default())
}
fn deserialize_optional_service_account_expiration<'de, D>(deserializer: D) -> Result<Option<OffsetDateTime>, D::Error>
where
D: Deserializer<'de>,
{
let expiration = Option::<String>::deserialize(deserializer)?;
let Some(expiration) = expiration else {
return Ok(None);
};
let expiration = OffsetDateTime::parse(&expiration, &Rfc3339).map_err(D::Error::custom)?;
if expiration.unix_timestamp() == 0 {
return Ok(None);
}
Ok(Some(expiration))
}
/// SRSvcAccCreate - create operation
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SRSvcAccCreate {
@@ -503,6 +528,7 @@ pub struct SRSvcAccCreate {
#[serde(rename = "secretKey")]
pub secret_key: String,
#[serde(default, deserialize_with = "deserialize_vec_or_default")]
pub groups: Vec<String>,
pub claims: HashMap<String, serde_json::Value>,
@@ -516,7 +542,11 @@ pub struct SRSvcAccCreate {
pub description: String,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_optional_service_account_expiration"
)]
pub expiration: Option<OffsetDateTime>,
#[serde(rename = "apiVersion", skip_serializing_if = "Option::is_none")]
+52
View File
@@ -1120,6 +1120,7 @@ mod tests {
};
use rustfs_credentials::{Credentials, IAM_POLICY_CLAIM_NAME_SA};
use rustfs_iam::error::Error as IamError;
use rustfs_madmin::user::SRSvcAccCreate;
use serde_json::Value;
use std::collections::HashMap;
@@ -1205,6 +1206,57 @@ mod tests {
assert!(imported_service_account_status("unknown").is_none());
}
#[test]
fn test_service_account_import_accepts_null_groups_and_epoch_expiration() {
let payload = r#"{
"svcalpha": {
"parent": "useralpha",
"accessKey": "svcalpha",
"secretKey": "svcAlphaSecret123",
"groups": null,
"claims": {
"accessKey": "svcalpha",
"parent": "useralpha",
"sa-policy": "inherited-policy"
},
"sessionPolicy": null,
"status": "on",
"name": "uploaderKey",
"description": "alpha upload key",
"expiration": "1970-01-01T00:00:00Z"
}
}"#;
let svc_accts: HashMap<String, SRSvcAccCreate> = serde_json::from_str(payload).unwrap();
let svc = svc_accts.get("svcalpha").unwrap();
assert!(svc.groups.is_empty());
assert!(svc.expiration.is_none());
}
#[test]
fn test_service_account_import_preserves_non_zero_expiration() {
let payload = r#"{
"svcalpha": {
"parent": "useralpha",
"accessKey": "svcalpha",
"secretKey": "svcAlphaSecret123",
"groups": [],
"claims": {},
"sessionPolicy": null,
"status": "on",
"name": "uploaderKey",
"description": "alpha upload key",
"expiration": "2030-01-02T03:04:05Z"
}
}"#;
let svc_accts: HashMap<String, SRSvcAccCreate> = serde_json::from_str(payload).unwrap();
let svc = svc_accts.get("svcalpha").unwrap();
assert_eq!(svc.expiration.map(|expiration| expiration.unix_timestamp()), Some(1893553445));
}
#[test]
fn test_group_policy_mappings_use_regular_user_type() {
assert_eq!(GROUP_POLICY_MAPPING_USER_TYPE, rustfs_iam::store::UserType::Reg);