rewrite service_account handler

This commit is contained in:
weisd
2025-01-21 16:17:28 +08:00
parent 296791b56b
commit 4f1cbf72c6
13 changed files with 707 additions and 456 deletions
+17 -2
View File
@@ -1,6 +1,6 @@
use crate::error::Error as IamError; use crate::error::Error as IamError;
use crate::policy::Policy; use crate::policy::Policy;
use crate::sys::Validator; use crate::sys::{iam_policy_claim_name_sa, Validator, INHERITED_POLICY_TYPE};
use crate::utils; use crate::utils;
use crate::utils::extract_claims; use crate::utils::extract_claims;
use ecstore::error::{Error, Result}; use ecstore::error::{Error, Result};
@@ -158,6 +158,21 @@ impl Credentials {
.unwrap_or_default() .unwrap_or_default()
} }
pub fn is_implied_policy(&self) -> bool {
if self.is_service_account() {
return self
.claims
.as_ref()
.map(|x| {
x.get(&iam_policy_claim_name_sa())
.map_or(false, |v| v == INHERITED_POLICY_TYPE)
})
.unwrap_or_default();
}
false
}
pub fn is_valid(&self) -> bool { pub fn is_valid(&self) -> bool {
if self.status == "off" { if self.status == "off" {
return false; return false;
@@ -200,7 +215,7 @@ pub fn create_new_credentials_with_metadata(
let expiration = { let expiration = {
if let Some(v) = claims.get("exp") { if let Some(v) = claims.get("exp") {
if let Some(expiry) = v.as_i64() { if let Some(expiry) = v.as_i64() {
Some(OffsetDateTime::from_unix_timestamp(expiry)?.to_offset(OffsetDateTime::now_local()?.offset())) Some(OffsetDateTime::from_unix_timestamp(expiry)?.to_offset(OffsetDateTime::now_utc().offset()))
} else { } else {
None None
} }
+8
View File
@@ -132,3 +132,11 @@ pub fn is_err_no_such_group(err: &ecstore::error::Error) -> bool {
false false
} }
} }
pub fn is_err_no_such_service_account(err: &ecstore::error::Error) -> bool {
if let Some(e) = err.downcast_ref::<Error>() {
matches!(e, Error::NoSuchServiceAccount(_))
} else {
false
}
}
+16 -15
View File
@@ -17,7 +17,7 @@ use ecstore::{
error::{Error, Result}, error::{Error, Result},
}; };
use log::{debug, warn}; use log::{debug, warn};
use madmin::{AccountStatus, GroupDesc}; use madmin::{AccountStatus, AddOrUpdateUserReq, GroupDesc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::Value; use serde_json::Value;
use std::{ use std::{
@@ -483,12 +483,12 @@ where
let mut cr = ui.credentials.clone(); let mut cr = ui.credentials.clone();
let current_secret_key = cr.secret_key.clone(); let current_secret_key = cr.secret_key.clone();
if !opts.secret_key.is_empty() { if let Some(secret) = opts.secret_key {
if !is_secret_key_valid(&opts.secret_key) { if !is_secret_key_valid(&secret) {
return Err(IamError::InvalidSecretKeyLength.into()); return Err(IamError::InvalidSecretKeyLength.into());
} }
cr.secret_key = opts.secret_key; cr.secret_key = secret;
} }
if opts.name.is_some() { if opts.name.is_some() {
@@ -554,7 +554,7 @@ where
Ok(OffsetDateTime::now_utc()) Ok(OffsetDateTime::now_utc())
} }
pub async fn policy_db_get(&self, name: &str, groups: &[String]) -> Result<Vec<String>> { pub async fn policy_db_get(&self, name: &str, groups: &Option<Vec<String>>) -> Result<Vec<String>> {
if name.is_empty() { if name.is_empty() {
return Err(Error::new(IamError::InvalidArgument)); return Err(Error::new(IamError::InvalidArgument));
} }
@@ -562,11 +562,13 @@ where
let (mut policies, _) = self.policy_db_get_internal(name, false, false).await?; let (mut policies, _) = self.policy_db_get_internal(name, false, false).await?;
let present = !policies.is_empty(); let present = !policies.is_empty();
for group in groups.iter() { if let Some(groups) = groups {
let (gp, _) = self.policy_db_get_internal(group, true, present).await?; for group in groups.iter() {
gp.iter().for_each(|v| { let (gp, _) = self.policy_db_get_internal(group, true, present).await?;
policies.push(v.clone()); gp.iter().for_each(|v| {
}); policies.push(v.clone());
});
}
} }
Ok(policies) Ok(policies)
@@ -946,7 +948,7 @@ where
m m
} }
pub async fn add_user(&self, access_key: &str, secret_key: &str, status: &str) -> Result<OffsetDateTime> { pub async fn add_user(&self, access_key: &str, args: &AddOrUpdateUserReq) -> Result<OffsetDateTime> {
let users = self.cache.users.load(); let users = self.cache.users.load();
if let Some(x) = users.get(access_key) { if let Some(x) = users.get(access_key) {
warn!("user already exists: {:?}", x); warn!("user already exists: {:?}", x);
@@ -956,15 +958,14 @@ where
} }
let status = { let status = {
match status { match &args.status {
val if val == AccountStatus::Enabled.as_ref() => auth::ACCOUNT_ON, AccountStatus::Enabled => auth::ACCOUNT_ON,
auth::ACCOUNT_ON => auth::ACCOUNT_ON,
_ => auth::ACCOUNT_OFF, _ => auth::ACCOUNT_OFF,
} }
}; };
let user_entiry = UserIdentity::from(Credentials { let user_entiry = UserIdentity::from(Credentials {
access_key: access_key.to_string(), access_key: access_key.to_string(),
secret_key: secret_key.to_string(), secret_key: args.secret_key.to_string(),
status: status.to_owned(), status: status.to_owned(),
..Default::default() ..Default::default()
}); });
+13 -11
View File
@@ -27,6 +27,7 @@ use crate::store::UserType;
use ecstore::error::{Error, Result}; use ecstore::error::{Error, Result};
use ecstore::utils::crypto::base64_decode; use ecstore::utils::crypto::base64_decode;
use ecstore::utils::crypto::base64_encode; use ecstore::utils::crypto::base64_encode;
use madmin::AddOrUpdateUserReq;
use madmin::GroupDesc; use madmin::GroupDesc;
use serde_json::json; use serde_json::json;
use serde_json::Value; use serde_json::Value;
@@ -178,9 +179,9 @@ impl<T: Store> IamSys<T> {
pub async fn new_service_account( pub async fn new_service_account(
&self, &self,
parent_user: &str, parent_user: &str,
groups: Vec<String>, groups: Option<Vec<String>>,
opts: NewServiceAccountOpts, opts: NewServiceAccountOpts,
) -> Result<OffsetDateTime> { ) -> Result<(Credentials, OffsetDateTime)> {
if parent_user.is_empty() { if parent_user.is_empty() {
return Err(IamError::InvalidArgument.into()); return Err(IamError::InvalidArgument.into());
} }
@@ -236,14 +237,15 @@ impl<T: Store> IamSys<T> {
let mut cred = create_new_credentials_with_metadata(&access_key, &secret_key, &m, &secret_key)?; let mut cred = create_new_credentials_with_metadata(&access_key, &secret_key, &m, &secret_key)?;
cred.parent_user = parent_user.to_owned(); cred.parent_user = parent_user.to_owned();
cred.groups = Some(groups); cred.groups = groups;
cred.status = ACCOUNT_ON.to_owned(); cred.status = ACCOUNT_ON.to_owned();
cred.name = opts.name; cred.name = opts.name;
cred.description = opts.description; cred.description = opts.description;
cred.expiration = opts.expiration; cred.expiration = opts.expiration;
self.store.add_service_account(cred).await let create_at = self.store.add_service_account(cred.clone()).await?;
Ok((cred, create_at))
// TODO: notification // TODO: notification
} }
@@ -387,7 +389,7 @@ impl<T: Store> IamSys<T> {
// TODO: notification // TODO: notification
} }
pub async fn create_user(&self, access_key: &str, secret_key: &str, status: &str) -> Result<OffsetDateTime> { pub async fn create_user(&self, access_key: &str, args: &AddOrUpdateUserReq) -> Result<OffsetDateTime> {
if !is_access_key_valid(access_key) { if !is_access_key_valid(access_key) {
return Err(IamError::InvalidAccessKeyLength.into()); return Err(IamError::InvalidAccessKeyLength.into());
} }
@@ -396,11 +398,11 @@ impl<T: Store> IamSys<T> {
return Err(IamError::ContainsReservedChars.into()); return Err(IamError::ContainsReservedChars.into());
} }
if !is_secret_key_valid(secret_key) { if !is_secret_key_valid(&args.secret_key) {
return Err(IamError::InvalidSecretKeyLength.into()); return Err(IamError::InvalidSecretKeyLength.into());
} }
self.store.add_user(access_key, secret_key, status).await self.store.add_user(access_key, args).await
// TODO: notification // TODO: notification
} }
@@ -470,7 +472,7 @@ impl<T: Store> IamSys<T> {
// TODO: notification // TODO: notification
} }
pub async fn policy_db_get(&self, name: &str, groups: &[String]) -> Result<Vec<String>> { pub async fn policy_db_get(&self, name: &str, groups: &Option<Vec<String>>) -> Result<Vec<String>> {
self.store.policy_db_get(name, groups).await self.store.policy_db_get(name, groups).await
} }
@@ -580,7 +582,7 @@ impl<T: Store> IamSys<T> {
is_owner || combined_policy.is_allowed(&parent_args) is_owner || combined_policy.is_allowed(&parent_args)
} }
async fn get_combined_policy(&self, policies: &[String]) -> Policy { pub async fn get_combined_policy(&self, policies: &[String]) -> Policy {
self.store.merge_policies(&policies.join(",")).await.1 self.store.merge_policies(&policies.join(",")).await.1
} }
@@ -676,7 +678,7 @@ pub struct NewServiceAccountOpts {
pub struct UpdateServiceAccountOpts { pub struct UpdateServiceAccountOpts {
pub session_policy: Option<Policy>, pub session_policy: Option<Policy>,
pub secret_key: String, pub secret_key: Option<String>,
pub name: Option<String>, pub name: Option<String>,
pub description: Option<String>, pub description: Option<String>,
pub expiration: Option<OffsetDateTime>, pub expiration: Option<OffsetDateTime>,
@@ -702,7 +704,7 @@ pub trait Validator {
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct Args<'a> { pub struct Args<'a> {
pub account: &'a str, pub account: &'a str,
pub groups: &'a [String], pub groups: &'a Option<Vec<String>>,
pub action: Action, pub action: Action,
pub bucket: &'a str, pub bucket: &'a str,
pub conditions: &'a HashMap<String, Vec<String>>, pub conditions: &'a HashMap<String, Vec<String>>,
+7 -1
View File
@@ -605,7 +605,13 @@ struct ArgsBuilder {
fn policy_is_allowed(policy: Policy, args: ArgsBuilder) -> bool { fn policy_is_allowed(policy: Policy, args: ArgsBuilder) -> bool {
policy.is_allowed(&Args { policy.is_allowed(&Args {
account: &args.account, account: &args.account,
groups: &args.groups, groups: &{
if args.groups.is_empty() {
None
} else {
Some(args.groups.clone())
}
},
action: args.action.as_str().try_into().unwrap(), action: args.action.as_str().try_into().unwrap(),
bucket: &args.bucket, bucket: &args.bucket,
conditions: &args.conditions, conditions: &args.conditions,
+84 -1
View File
@@ -129,7 +129,7 @@ pub struct AddServiceAccountReq {
pub secret_key: String, pub secret_key: String,
#[serde(rename = "name")] #[serde(rename = "name")]
pub name: String, pub name: Option<String>,
#[serde(rename = "description", skip_serializing_if = "Option::is_none")] #[serde(rename = "description", skip_serializing_if = "Option::is_none")]
pub description: Option<String>, pub description: Option<String>,
@@ -137,3 +137,86 @@ pub struct AddServiceAccountReq {
#[serde(rename = "expiration", skip_serializing_if = "Option::is_none")] #[serde(rename = "expiration", skip_serializing_if = "Option::is_none")]
pub expiration: Option<OffsetDateTime>, pub expiration: Option<OffsetDateTime>,
} }
impl AddServiceAccountReq {
pub fn validate(&self) -> Result<(), String> {
if self.access_key.is_empty() {
return Err("accessKey is empty".to_string());
}
if self.secret_key.is_empty() {
return Err("secretKey is empty".to_string());
}
if self.name.is_none() {
return Err("name is empty".to_string());
}
// TODO: validate
Ok(())
}
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Credentials<'a> {
pub access_key: &'a str,
pub secret_key: &'a str,
#[serde(skip_serializing_if = "Option::is_none")]
pub session_token: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(with = "time::serde::rfc3339::option")]
pub expiration: Option<OffsetDateTime>,
}
#[derive(Serialize)]
pub struct AddServiceAccountResp<'a> {
pub credentials: Credentials<'a>,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InfoServiceAccountResp {
pub parent_user: String,
pub account_status: String,
pub implied_policy: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub policy: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(with = "time::serde::rfc3339::option")]
pub expiration: Option<OffsetDateTime>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct UpdateServiceAccountReq {
#[serde(rename = "newPolicy", skip_serializing_if = "Option::is_none")]
pub new_policy: Option<String>,
#[serde(rename = "newSecretKey", skip_serializing_if = "Option::is_none")]
pub new_secret_key: Option<String>,
#[serde(rename = "newStatus", skip_serializing_if = "Option::is_none")]
pub new_status: Option<String>,
#[serde(rename = "newName", skip_serializing_if = "Option::is_none")]
pub new_name: Option<String>,
#[serde(rename = "newDescription", skip_serializing_if = "Option::is_none")]
pub new_description: Option<String>,
#[serde(rename = "newExpiration", skip_serializing_if = "Option::is_none")]
pub new_expiration: Option<OffsetDateTime>,
}
impl UpdateServiceAccountReq {
pub fn validate(&self) -> Result<(), String> {
// TODO: validate
Ok(())
}
}
+1 -4
View File
@@ -270,10 +270,7 @@ impl Operation for AssumeRoleHandle {
let Ok(iam_store) = iam::get() else { return Err(s3_error!(InvalidRequest, "iam not init")) }; let Ok(iam_store) = iam::get() else { return Err(s3_error!(InvalidRequest, "iam not init")) };
if let Err(_err) = iam_store if let Err(_err) = iam_store.policy_db_get(&cred.access_key, &cred.groups).await {
.policy_db_get(&cred.access_key, &cred.groups.unwrap_or_default())
.await
{
return Err(s3_error!(InvalidArgument, "invalid policy arg")); return Err(s3_error!(InvalidArgument, "invalid policy arg"));
} }
+370 -210
View File
@@ -1,139 +1,246 @@
use std::collections::HashMap; use crate::admin::{handlers::check_key_valid, utils::has_space_be};
use crate::admin::{handlers::get_session_token, router::Operation};
use http::HeaderMap; use http::HeaderMap;
use hyper::StatusCode; use hyper::StatusCode;
use iam::{ use iam::{
auth::CredentialsBuilder, error::is_err_no_such_service_account,
policy::action::{Action, AdminAction::ListServiceAccountsAdminAction}, get_global_action_cred,
policy::Policy,
sys::{NewServiceAccountOpts, UpdateServiceAccountOpts},
};
use madmin::{
AddServiceAccountReq, AddServiceAccountResp, Credentials, InfoServiceAccountResp, ListServiceAccountsResp,
ServiceAccountInfo, UpdateServiceAccountReq,
}; };
use madmin::{AddServiceAccountReq, ListServiceAccountsResp, ServiceAccountInfo};
use matchit::Params; use matchit::Params;
use s3s::S3ErrorCode::InvalidRequest;
use s3s::{header::CONTENT_TYPE, s3_error, Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result}; use s3s::{header::CONTENT_TYPE, s3_error, Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result};
use serde::Deserialize;
use serde_urlencoded::from_bytes; use serde_urlencoded::from_bytes;
use std::collections::HashMap;
use tracing::{debug, warn}; use tracing::{debug, warn};
use crate::admin::router::Operation;
use crate::admin::{
handlers::check_key_valid,
models::service_account::{AddServiceAccountResp, Credentials, InfoServiceAccountResp},
};
pub struct AddServiceAccount {} pub struct AddServiceAccount {}
#[async_trait::async_trait] #[async_trait::async_trait]
impl Operation for AddServiceAccount { impl Operation for AddServiceAccount {
async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> { async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
warn!("handle AddServiceAccount "); warn!("handle AddServiceAccount ");
let Some(req_cred) = req.credentials else {
return Err(s3_error!(InvalidRequest, "get cred failed"));
};
unimplemented!() let (cred, _owner) = check_key_valid(get_session_token(&req.headers), &req_cred.access_key).await?;
// let Some(input_cred) = req.credentials else { let mut input = req.input;
// return Err(s3_error!(InvalidRequest, "get cred failed")); let body = match input.store_all_unlimited().await {
// }; Ok(b) => b,
// let _is_owner = true; // 先按true处理,后期根据请求决定。 Err(e) => {
warn!("get body failed, e: {:?}", e);
return Err(s3_error!(InvalidRequest, "get body failed"));
}
};
// let mut input = req.input; let mut create_req: AddServiceAccountReq =
// let body = match input.store_all_unlimited().await { serde_json::from_slice(&body[..]).map_err(|e| s3_error!(InvalidRequest, "unmarshal body failed, e: {:?}", e))?;
// Ok(b) => b, create_req.expiration = create_req.expiration.and_then(|expire| expire.replace_millisecond(0).ok());
// Err(e) => {
// warn!("get body failed, e: {:?}", e);
// return Err(s3_error!(InvalidRequest, "get body failed"));
// }
// };
// let mut create_req: AddServiceAccountReq = if has_space_be(&create_req.access_key) {
// serde_json::from_slice(&body[..]).map_err(|e| s3_error!(InvalidRequest, "unmarshal body failed, e: {:?}", e))?; return Err(s3_error!(InvalidRequest, "access key has spaces"));
}
// create_req.expiration = create_req.expiration.and_then(|expire| expire.replace_millisecond(0).ok()); create_req
.validate()
.map_err(|e| S3Error::with_message(InvalidRequest, e.to_string()))?;
// if create_req.access_key.trim().len() != create_req.access_key.len() { let Some(sys_cred) = get_global_action_cred() else {
// return Err(s3_error!(InvalidRequest, "access key has spaces")); return Err(s3_error!(InvalidRequest, "get sys cred failed"));
// } };
// let (cred, _) = check_key_valid(None, &input_cred.access_key).await.map_err(|e| { if sys_cred.access_key == create_req.access_key {
// debug!("check key failed: {e:?}"); return Err(s3_error!(InvalidArgument, "can't create user with system access key"));
// s3_error!(InternalError, "check key failed") }
// })?;
// // TODO check create_req validity let mut target_user = if let Some(u) = create_req.target_user {
u
} else {
cred.access_key.clone()
};
// // 校验合法性, Name, Expiration, Description let req_user = cred.access_key.clone();
// let target_user = if let Some(u) = create_req.target_user { let mut req_parent_user = cred.access_key.clone();
// u let req_groups = cred.groups.clone();
// } else { let mut req_is_derived_cred = false;
// cred.access_key
// };
// let _deny_only = true;
// // todo 校验权限 if cred.is_owner() || cred.is_service_account() {
req_parent_user = cred.parent_user.clone();
req_is_derived_cred = true;
}
// // if !iam::is_allowed(Args { let Ok(iam_store) = iam::get() else { return Err(s3_error!(InvalidRequest, "iam not init")) };
// // account: &cred.access_key,
// // groups: &[],
// // action: Action::AdminAction(AdminAction::CreateServiceAccountAdminAction),
// // bucket: "",
// // conditions: &HashMap::new(),
// // is_owner,
// // object: "",
// // claims: &HashMap::new(),
// // deny_only,
// // })
// // .await
// // .unwrap_or(false)
// // {
// // return Err(s3_error!(AccessDenied));
// // }
// //
// let cred = CredentialsBuilder::new() if target_user != cred.access_key {
// .parent_user(target_user) let has_user = iam_store.get_user(&target_user).await;
// .access_key(create_req.access_key) if has_user.is_none() && target_user != sys_cred.access_key {
// .secret_key(create_req.secret_key) return Err(s3_error!(InvalidRequest, "target user not exist"));
// .description(create_req.description.unwrap_or_default()) }
// .expiration(create_req.expiration) }
// .session_policy({
// match create_req.policy {
// Some(p) if !p.is_empty() => {
// Some(serde_json::from_slice(p.as_bytes()).map_err(|_| s3_error!(InvalidRequest, "invalid policy"))?)
// }
// _ => None,
// }
// })
// .name(create_req.name)
// .try_build()
// .map_err(|e| s3_error!(InvalidRequest, "build cred failed, err: {:?}", e))?;
// let resp = serde_json::to_vec(&AddServiceAccountResp { let is_svc_acc = target_user == req_user || target_user == req_parent_user;
// credentials: Credentials {
// access_key: &cred.access_key,
// secret_key: &cred.secret_key,
// session_token: None,
// expiration: cred.expiration,
// },
// })
// .unwrap()
// .into();
// iam::add_service_account(cred).await.map_err(|e| { let mut taget_groups = None;
// debug!("add cred failed: {e:?}"); let mut opts = NewServiceAccountOpts {
// s3_error!(InternalError, "add cred failed") access_key: create_req.access_key,
// })?; secret_key: create_req.secret_key,
name: create_req.name,
description: create_req.description,
expiration: create_req.expiration,
session_policy: create_req.policy.and_then(|p| Policy::parse_config(p.as_bytes()).ok()),
..Default::default()
};
// Ok(S3Response::new((StatusCode::OK, resp))) if is_svc_acc {
if req_is_derived_cred {
if req_parent_user.is_empty() {
return Err(s3_error!(AccessDenied, "only derived cred can create service account"));
}
target_user = req_parent_user;
}
taget_groups = req_groups;
if let Some(claims) = cred.claims {
if opts.claims.is_none() {
opts.claims = Some(HashMap::new());
}
for (k, v) in claims.iter() {
if claims.contains_key("exp") {
continue;
}
opts.claims.as_mut().unwrap().insert(k.clone(), v.clone());
}
}
}
let (new_cred, _) = iam_store
.new_service_account(&target_user, taget_groups, opts)
.await
.map_err(|e| {
debug!("create service account failed, e: {:?}", e);
s3_error!(InternalError, "create service account failed")
})?;
let resp = AddServiceAccountResp {
credentials: Credentials {
access_key: &new_cred.access_key,
secret_key: &new_cred.secret_key,
session_token: None,
expiration: new_cred.expiration,
},
};
let body = serde_json::to_vec(&resp).map_err(|e| s3_error!(InternalError, "marshal body failed, e: {:?}", e))?;
let mut header = HeaderMap::new();
header.insert(CONTENT_TYPE, "application/json".parse().unwrap());
Ok(S3Response::with_headers((StatusCode::OK, Body::from(body)), header))
} }
} }
#[derive(Debug, Default, Deserialize)]
struct AccessKeyQuery {
#[serde(rename = "accessKey")]
pub access_key: String,
}
pub struct UpdateServiceAccount {} pub struct UpdateServiceAccount {}
#[async_trait::async_trait] #[async_trait::async_trait]
impl Operation for UpdateServiceAccount { impl Operation for UpdateServiceAccount {
async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> { async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
warn!("handle UpdateServiceAccount"); warn!("handle UpdateServiceAccount");
let Some(_cred) = req.credentials else { return Err(s3_error!(InvalidRequest, "get cred failed")) }; // let Some(req_cred) = req.credentials else {
// return Err(s3_error!(InvalidRequest, "get cred failed"));
// };
// return Err(s3_error!(NotImplemented)); // let (cred, _owner) = check_key_valid(get_session_token(&req.headers), &req_cred.access_key).await?;
//
todo!() let query = {
if let Some(query) = req.uri.query() {
let input: AccessKeyQuery =
from_bytes(query.as_bytes()).map_err(|_e| s3_error!(InvalidArgument, "get body failed1"))?;
input
} else {
AccessKeyQuery::default()
}
};
if query.access_key.is_empty() {
return Err(s3_error!(InvalidArgument, "access key is empty"));
}
let access_key = query.access_key;
let Ok(iam_store) = iam::get() else { return Err(s3_error!(InvalidRequest, "iam not init")) };
// let svc_account = iam_store.get_service_account(&access_key).await.map_err(|e| {
// debug!("get service account failed, e: {:?}", e);
// s3_error!(InternalError, "get service account failed")
// })?;
let mut input = req.input;
let body = match input.store_all_unlimited().await {
Ok(b) => b,
Err(e) => {
warn!("get body failed, e: {:?}", e);
return Err(s3_error!(InvalidRequest, "get body failed"));
}
};
let update_req: UpdateServiceAccountReq =
serde_json::from_slice(&body[..]).map_err(|e| s3_error!(InvalidRequest, "unmarshal body failed, e: {:?}", e))?;
update_req
.validate()
.map_err(|e| S3Error::with_message(InvalidRequest, e.to_string()))?;
// TODO: is_allowed
let sp = {
if let Some(policy) = update_req.new_policy {
let sp = Policy::parse_config(policy.as_bytes()).map_err(|e| {
debug!("parse policy failed, e: {:?}", e);
s3_error!(InvalidArgument, "parse policy failed")
})?;
if sp.version.is_empty() && sp.statements.is_empty() {
None
} else {
Some(sp)
}
} else {
None
}
};
let opts = UpdateServiceAccountOpts {
secret_key: update_req.new_secret_key,
status: update_req.new_status,
name: update_req.new_name,
description: update_req.new_description,
expiration: update_req.new_expiration,
session_policy: sp,
};
let _ = iam_store.update_service_account(&access_key, opts).await.map_err(|e| {
debug!("update service account failed, e: {:?}", e);
s3_error!(InternalError, "update service account failed")
})?;
let mut header = HeaderMap::new();
header.insert(CONTENT_TYPE, "application/json".parse().unwrap());
Ok(S3Response::with_headers((StatusCode::OK, Body::empty()), header))
} }
} }
@@ -143,76 +250,80 @@ impl Operation for InfoServiceAccount {
async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> { async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
warn!("handle InfoServiceAccount"); warn!("handle InfoServiceAccount");
// let Some(cred) = req.credentials else { return Err(s3_error!(InvalidRequest, "get cred failed")) }; let query = {
if let Some(query) = req.uri.query() {
let input: AccessKeyQuery =
from_bytes(query.as_bytes()).map_err(|_e| s3_error!(InvalidArgument, "get body failed1"))?;
input
} else {
AccessKeyQuery::default()
}
};
// //accessKey if query.access_key.is_empty() {
// let Some(ak) = req.uri.query().and_then(|x| { return Err(s3_error!(InvalidArgument, "access key is empty"));
// for mut x in x.split('&').map(|x| x.split('=')) { }
// let Some(key) = x.next() else {
// continue;
// };
// if key != "accessKey" { let access_key = query.access_key;
// continue;
// }
// let Some(value) = x.next() else { let Ok(iam_store) = iam::get() else { return Err(s3_error!(InvalidRequest, "iam not init")) };
// continue;
// };
// return Some(value); let (svc_account, session_policy) = iam_store.get_service_account(&access_key).await.map_err(|e| {
// } debug!("get service account failed, e: {:?}", e);
s3_error!(InternalError, "get service account failed")
})?;
// None // TODO: is_allowed
// }) else {
// return Err(s3_error!(InvalidRequest, "access key is not exist"));
// };
unimplemented!() let implied_policy = if let Some(policy) = session_policy.as_ref() {
policy.version.is_empty() && policy.statements.is_empty()
} else {
true
};
// let (sa, _sp) = iam::get_service_account(ak).await.map_err(|e| { let svc_account_policy = {
// debug!("get service account failed, err: {e:?}"); if !implied_policy {
// s3_error!(InternalError) session_policy
// })?; } else {
let policies = iam_store
.policy_db_get(&svc_account.parent_user, &svc_account.groups)
.await
.map_err(|e| {
debug!("get service account policy failed, e: {:?}", e);
s3_error!(InternalError, "get service account policy failed")
})?;
// if !iam::is_allowed(Args { Some(iam_store.get_combined_policy(&policies).await)
// account: &sa.access_key, }
// groups: &sa.groups.unwrap_or_default()[..], };
// action: Action::AdminAction(ListServiceAccountsAdminAction),
// bucket: "",
// conditions: &HashMap::new(),
// is_owner: true,
// object: "",
// claims: &HashMap::new(),
// deny_only: false,
// })
// .await
// .map_err(|_| s3_error!(InternalError))?
// {
// let req_user = &cred.access_key;
// if req_user != &sa.parent_user {
// return Err(s3_error!(AccessDenied));
// }
// }
// let body = serde_json::to_vec(&InfoServiceAccountResp { let policy = {
// parent_user: sa.parent_user, if let Some(policy) = svc_account_policy {
// account_status: sa.status, Some(serde_json::to_string(&policy).map_err(|e| {
// implied_policy: true, debug!("marshal policy failed, e: {:?}", e);
// // policy: serde_json::to_string_pretty(&sva).map_err(|_| s3_error!(InternalError, "json marshal failed"))?, s3_error!(InternalError, "marshal policy failed")
// policy: "".into(), })?)
// name: sa.name.unwrap_or_default(), } else {
// description: sa.description.unwrap_or_default(), None
// expiration: sa.expiration, }
// }) };
// .map_err(|_| s3_error!(InternalError, "json marshal failed"))?;
// Ok(S3Response::new(( let resp = InfoServiceAccountResp {
// StatusCode::OK, parent_user: svc_account.parent_user,
// crypto::encrypt_data(cred.access_key.as_bytes(), &body[..]) account_status: svc_account.status,
// .map_err(|_| s3_error!(InternalError, "encrypt data failed"))? implied_policy,
// .into(), name: svc_account.name,
// ))) description: svc_account.description,
expiration: svc_account.expiration,
policy,
};
let body = serde_json::to_vec(&resp).map_err(|e| s3_error!(InternalError, "marshal body failed, e: {:?}", e))?;
let mut header = HeaderMap::new();
header.insert(CONTENT_TYPE, "application/json".parse().unwrap());
Ok(S3Response::with_headers((StatusCode::OK, Body::from(body)), header))
} }
} }
@@ -226,76 +337,125 @@ pub struct ListServiceAccount {}
impl Operation for ListServiceAccount { impl Operation for ListServiceAccount {
async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> { async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
warn!("handle ListServiceAccount"); warn!("handle ListServiceAccount");
unimplemented!()
// let query = {
// if let Some(query) = req.uri.query() {
// let input: ListServiceAccountQuery =
// from_bytes(query.as_bytes()).map_err(|_e| s3_error!(InvalidArgument, "get body failed"))?;
// input
// } else {
// ListServiceAccountQuery::default()
// }
// };
// let target_account = if let Some(user) = query.user { let query = {
// user if let Some(query) = req.uri.query() {
// } else { let input: ListServiceAccountQuery =
// let Some(input_cred) = req.credentials else { from_bytes(query.as_bytes()).map_err(|_e| s3_error!(InvalidArgument, "get body failed"))?;
// return Err(s3_error!(InvalidRequest, "get cred failed")); input
// }; } else {
ListServiceAccountQuery::default()
}
};
// let (cred, _owner) = check_key_valid(None, &input_cred.access_key).await.map_err(|e| { let Some(input_cred) = req.credentials else {
// debug!("check key failed: {e:?}"); return Err(s3_error!(InvalidRequest, "get cred failed"));
// s3_error!(InternalError, "check key failed") };
// })?;
// if cred.parent_user.is_empty() { let (cred, _owner) = check_key_valid(get_session_token(&req.headers), &input_cred.access_key)
// input_cred.access_key .await
// } else { .map_err(|e| {
// cred.parent_user debug!("check key failed: {e:?}");
// } s3_error!(InternalError, "check key failed")
// }; })?;
// let service_accounts = iam::list_service_accounts(&target_account).await.map_err(|e| { let target_account = if let Some(user) = query.user {
// debug!("list service account failed: {e:?}"); if user != input_cred.access_key {
// s3_error!(InternalError, "list service account failed") user
// })?; } else if cred.parent_user.is_empty() {
input_cred.access_key
} else {
cred.parent_user
}
} else if cred.parent_user.is_empty() {
input_cred.access_key
} else {
cred.parent_user
};
// let accounts: Vec<ServiceAccountInfo> = service_accounts let Ok(iam_store) = iam::get() else { return Err(s3_error!(InvalidRequest, "iam not init")) };
// .into_iter()
// .map(|sa| ServiceAccountInfo {
// parent_user: sa.parent_user,
// account_status: sa.status,
// implied_policy: true, // or set according to your logic
// access_key: sa.access_key,
// name: sa.name,
// description: sa.description,
// expiration: sa.expiration,
// })
// .collect();
// let data = serde_json::to_vec(&ListServiceAccountsResp { accounts }) let service_accounts = iam_store.list_service_accounts(&target_account).await.map_err(|e| {
// .map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, format!("marshal users err {}", e)))?; debug!("list service account failed: {e:?}");
s3_error!(InternalError, "list service account failed")
})?;
// let mut header = HeaderMap::new(); let accounts: Vec<ServiceAccountInfo> = service_accounts
// header.insert(CONTENT_TYPE, "application/json".parse().unwrap()); .into_iter()
.map(|sa| ServiceAccountInfo {
parent_user: sa.parent_user.clone(),
account_status: sa.status.clone(),
implied_policy: sa.is_implied_policy(), // or set according to your logic
access_key: sa.access_key,
name: sa.name,
description: sa.description,
expiration: sa.expiration,
})
.collect();
// Ok(S3Response::with_headers((StatusCode::OK, Body::from(data)), header)) let data = serde_json::to_vec(&ListServiceAccountsResp { accounts })
.map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, format!("marshal users err {}", e)))?;
let mut header = HeaderMap::new();
header.insert(CONTENT_TYPE, "application/json".parse().unwrap());
Ok(S3Response::with_headers((StatusCode::OK, Body::from(data)), header))
} }
} }
pub struct DeleteServiceAccount {} pub struct DeleteServiceAccount {}
#[async_trait::async_trait] #[async_trait::async_trait]
impl Operation for DeleteServiceAccount { impl Operation for DeleteServiceAccount {
async fn call(&self, req: S3Request<Body>, params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> { async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
warn!("handle DeleteServiceAccount"); warn!("handle DeleteServiceAccount");
let Some(input_cred) = req.credentials else {
let Some(_cred) = req.credentials else { return Err(s3_error!(InvalidRequest, "get cred failed")) }; return Err(s3_error!(InvalidRequest, "get cred failed"));
let Some(_service_account) = params.get("accessKey") else {
return Err(s3_error!(InvalidRequest, "Invalid arguments specified."));
}; };
todo!() let (_cred, _owner) = check_key_valid(get_session_token(&req.headers), &input_cred.access_key)
.await
.map_err(|e| {
debug!("check key failed: {e:?}");
s3_error!(InternalError, "check key failed")
})?;
let query = {
if let Some(query) = req.uri.query() {
let input: AccessKeyQuery =
from_bytes(query.as_bytes()).map_err(|_e| s3_error!(InvalidArgument, "get body failed"))?;
input
} else {
AccessKeyQuery::default()
}
};
if query.access_key.is_empty() {
return Err(s3_error!(InvalidArgument, "access key is empty"));
}
let Ok(iam_store) = iam::get() else { return Err(s3_error!(InvalidRequest, "iam not init")) };
let _svc_account = match iam_store.get_service_account(&query.access_key).await {
Ok((res, _)) => Some(res),
Err(err) => {
if is_err_no_such_service_account(&err) {
return Err(s3_error!(InvalidRequest, "service account not exist"));
}
None
}
};
// TODO: is_allowed
iam_store.delete_service_account(&query.access_key).await.map_err(|e| {
debug!("delete service account failed, e: {:?}", e);
s3_error!(InternalError, "delete service account failed")
})?;
let mut header = HeaderMap::new();
header.insert(CONTENT_TYPE, "application/json".parse().unwrap());
Ok(S3Response::with_headers((StatusCode::OK, Body::empty()), header))
} }
} }
+187 -159
View File
@@ -1,3 +1,5 @@
use std::str::from_utf8;
use http::{HeaderMap, StatusCode}; use http::{HeaderMap, StatusCode};
use iam::get_global_action_cred; use iam::get_global_action_cred;
use madmin::{AccountStatus, AddOrUpdateUserReq}; use madmin::{AccountStatus, AddOrUpdateUserReq};
@@ -10,6 +12,7 @@ use tracing::warn;
use crate::admin::{ use crate::admin::{
handlers::{check_key_valid, get_session_token}, handlers::{check_key_valid, get_session_token},
router::Operation, router::Operation,
utils::has_space_be,
}; };
#[derive(Debug, Deserialize, Default)] #[derive(Debug, Deserialize, Default)]
@@ -23,80 +26,86 @@ pub struct AddUser {}
#[async_trait::async_trait] #[async_trait::async_trait]
impl Operation for AddUser { impl Operation for AddUser {
async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> { async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
warn!("handle AddUser"); let query = {
unimplemented!() if let Some(query) = req.uri.query() {
// let query = { let input: AddUserQuery =
// if let Some(query) = req.uri.query() { from_bytes(query.as_bytes()).map_err(|_e| s3_error!(InvalidArgument, "get body failed1"))?;
// let input: AddUserQuery = input
// from_bytes(query.as_bytes()).map_err(|_e| s3_error!(InvalidArgument, "get body failed1"))?; } else {
// input AddUserQuery::default()
// } else { }
// AddUserQuery::default() };
// }
let Some(input_cred) = req.credentials else {
return Err(s3_error!(InvalidRequest, "get cred failed"));
};
let (cred, _owner) = check_key_valid(get_session_token(&req.headers), &input_cred.access_key).await?;
let ak = query.access_key.as_deref().unwrap_or_default();
if ak.is_empty() {
return Err(s3_error!(InvalidArgument, "access key is empty"));
}
let mut input = req.input;
let body = match input.store_all_unlimited().await {
Ok(b) => b,
Err(e) => {
warn!("get body failed, e: {:?}", e);
return Err(s3_error!(InvalidRequest, "get body failed"));
}
};
// let body_bytes = decrypt_data(input_cred.secret_key.expose().as_bytes(), &body)
// .map_err(|e| S3Error::with_message(S3ErrorCode::InvalidArgument, format!("decrypt_data err {}", e)))?;
let args: AddOrUpdateUserReq = serde_json::from_slice(&body)
.map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, format!("unmarshal body err {}", e)))?;
warn!("add user args {:?}", args);
if args.secret_key.is_empty() {
return Err(s3_error!(InvalidArgument, "access key is empty"));
}
if let Some(sys_cred) = get_global_action_cred() {
if sys_cred.access_key == ak {
return Err(s3_error!(InvalidArgument, "can't create user with system access key"));
}
}
let Ok(iam_store) = iam::get() else { return Err(s3_error!(InvalidRequest, "iam not init")) };
if let Some(user) = iam_store.get_user(ak).await {
if (user.credentials.is_temp() || user.credentials.is_service_account()) && cred.parent_user == ak {
return Err(s3_error!(InvalidArgument, "can't create user with service account access key"));
}
} else if has_space_be(ak) {
return Err(s3_error!(InvalidArgument, "access key has space"));
}
if from_utf8(ak.as_bytes()).is_err() {
return Err(s3_error!(InvalidArgument, "access key is not utf8"));
}
// let check_deny_only = if ak == cred.access_key {
// true
// } else {
// false
// }; // };
// let Some(input_cred) = req.credentials else { // TODO: is_allowed
// return Err(s3_error!(InvalidRequest, "get cred failed"));
// };
// let ak = query.access_key.as_deref().unwrap_or_default(); iam_store
.create_user(ak, &args)
.await
.map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, format!("create_user err {}", e)))?;
// if ak.is_empty() { let mut header = HeaderMap::new();
// return Err(s3_error!(InvalidArgument, "access key is empty")); header.insert(CONTENT_TYPE, "application/json".parse().unwrap());
// }
// let mut input = req.input; Ok(S3Response::with_headers((StatusCode::OK, Body::empty()), header))
// let body = match input.store_all_unlimited().await {
// Ok(b) => b,
// Err(e) => {
// warn!("get body failed, e: {:?}", e);
// return Err(s3_error!(InvalidRequest, "get body failed"));
// }
// };
// // let body_bytes = decrypt_data(input_cred.secret_key.expose().as_bytes(), &body)
// // .map_err(|e| S3Error::with_message(S3ErrorCode::InvalidArgument, format!("decrypt_data err {}", e)))?;
// let args: AddOrUpdateUserReq = serde_json::from_slice(&body)
// .map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, format!("unmarshal body err {}", e)))?;
// warn!("add user args {:?}", args);
// if args.secret_key.is_empty() {
// return Err(s3_error!(InvalidArgument, "access key is empty"));
// }
// if let Some(sys_cred) = get_global_action_cred() {
// if sys_cred.access_key == ak {
// return Err(s3_error!(InvalidArgument, "can't create user with system access key"));
// }
// }
// if let (Some(user), true) = iam::get_user(ak)
// .await
// .map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, format!("marshal users err {}", e)))?
// {
// if user.credentials.is_temp() || user.credentials.is_service_account() {
// return Err(s3_error!(InvalidArgument, "can't create user with service account access key"));
// }
// }
// let token = get_session_token(&req.headers);
// let (cred, _) = check_key_valid(token, &input_cred.access_key).await?;
// if (cred.is_temp() || cred.is_service_account()) && cred.parent_user == input_cred.access_key {
// return Err(s3_error!(InvalidArgument, "can't create user with service account access key"));
// }
// iam::create_user(ak, &args.secret_key, "enabled")
// .await
// .map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, format!("create_user err {}", e)))?;
// let mut header = HeaderMap::new();
// header.insert(CONTENT_TYPE, "application/json".parse().unwrap());
// Ok(S3Response::with_headers((StatusCode::OK, Body::empty()), header))
} }
} }
@@ -106,43 +115,44 @@ impl Operation for SetUserStatus {
async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> { async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
warn!("handle SetUserStatus"); warn!("handle SetUserStatus");
unimplemented!() let query = {
if let Some(query) = req.uri.query() {
let input: AddUserQuery =
from_bytes(query.as_bytes()).map_err(|_e| s3_error!(InvalidArgument, "get body failed"))?;
input
} else {
AddUserQuery::default()
}
};
// let query = { let ak = query.access_key.as_deref().unwrap_or_default();
// if let Some(query) = req.uri.query() {
// let input: AddUserQuery =
// from_bytes(query.as_bytes()).map_err(|_e| s3_error!(InvalidArgument, "get body failed"))?;
// input
// } else {
// AddUserQuery::default()
// }
// };
// let ak = query.access_key.as_deref().unwrap_or_default(); if ak.is_empty() {
return Err(s3_error!(InvalidArgument, "access key is empty"));
}
// if ak.is_empty() { let Some(input_cred) = req.credentials else {
// return Err(s3_error!(InvalidArgument, "access key is empty")); return Err(s3_error!(InvalidRequest, "get cred failed"));
// } };
// let Some(input_cred) = req.credentials else { if input_cred.access_key == ak {
// return Err(s3_error!(InvalidRequest, "get cred failed")); return Err(s3_error!(InvalidArgument, "can't change status of self"));
// }; }
// if input_cred.access_key == ak { let status = AccountStatus::try_from(query.status.as_deref().unwrap_or_default())
// return Err(s3_error!(InvalidArgument, "can't change status of self")); .map_err(|e| S3Error::with_message(S3ErrorCode::InvalidArgument, e))?;
// }
// let status = AccountStatus::try_from(query.status.as_deref().unwrap_or_default()) let Ok(iam_store) = iam::get() else { return Err(s3_error!(InvalidRequest, "iam not init")) };
// .map_err(|e| S3Error::with_message(S3ErrorCode::InvalidArgument, e))?;
// iam::set_user_status(ak, status) iam_store
// .await .set_user_status(ak, status)
// .map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, format!("set_user_status err {}", e)))?; .await
.map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, format!("set_user_status err {}", e)))?;
// let mut header = HeaderMap::new(); let mut header = HeaderMap::new();
// header.insert(CONTENT_TYPE, "application/json".parse().unwrap()); header.insert(CONTENT_TYPE, "application/json".parse().unwrap());
// Ok(S3Response::with_headers((StatusCode::OK, Body::empty()), header)) Ok(S3Response::with_headers((StatusCode::OK, Body::empty()), header))
} }
} }
@@ -151,25 +161,27 @@ pub struct ListUsers {}
impl Operation for ListUsers { impl Operation for ListUsers {
async fn call(&self, _req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> { async fn call(&self, _req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
warn!("handle ListUsers"); warn!("handle ListUsers");
unimplemented!() let Ok(iam_store) = iam::get() else { return Err(s3_error!(InvalidRequest, "iam not init")) };
// let users = iam::list_users()
// .await
// .map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, e.to_string()))?;
// let data = serde_json::to_vec(&users) let users = iam_store
// .map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, format!("marshal users err {}", e)))?; .list_users()
.await
.map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, e.to_string()))?;
// // let Some(input_cred) = req.credentials else { let data = serde_json::to_vec(&users)
// // return Err(s3_error!(InvalidRequest, "get cred failed")); .map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, format!("marshal users err {}", e)))?;
// // };
// // let body = encrypt_data(input_cred.secret_key.expose().as_bytes(), &data) // let Some(input_cred) = req.credentials else {
// // .map_err(|e| S3Error::with_message(S3ErrorCode::InvalidArgument, format!("encrypt_data err {}", e)))?; // return Err(s3_error!(InvalidRequest, "get cred failed"));
// };
// let mut header = HeaderMap::new(); // let body = encrypt_data(input_cred.secret_key.expose().as_bytes(), &data)
// header.insert(CONTENT_TYPE, "application/json".parse().unwrap()); // .map_err(|e| S3Error::with_message(S3ErrorCode::InvalidArgument, format!("encrypt_data err {}", e)))?;
// Ok(S3Response::with_headers((StatusCode::OK, Body::from(data)), header)) let mut header = HeaderMap::new();
header.insert(CONTENT_TYPE, "application/json".parse().unwrap());
Ok(S3Response::with_headers((StatusCode::OK, Body::from(data)), header))
} }
} }
@@ -178,39 +190,52 @@ pub struct RemoveUser {}
impl Operation for RemoveUser { impl Operation for RemoveUser {
async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> { async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
warn!("handle RemoveUser"); warn!("handle RemoveUser");
unimplemented!()
// let query = {
// if let Some(query) = req.uri.query() {
// let input: AddUserQuery =
// from_bytes(query.as_bytes()).map_err(|_e| s3_error!(InvalidArgument, "get body failed"))?;
// input
// } else {
// AddUserQuery::default()
// }
// };
// let ak = query.access_key.as_deref().unwrap_or_default(); let query = {
if let Some(query) = req.uri.query() {
let input: AddUserQuery =
from_bytes(query.as_bytes()).map_err(|_e| s3_error!(InvalidArgument, "get body failed"))?;
input
} else {
AddUserQuery::default()
}
};
// if ak.is_empty() { let ak = query.access_key.as_deref().unwrap_or_default();
// return Err(s3_error!(InvalidArgument, "access key is empty"));
// }
// let (is_temp, _) = iam::is_temp_user(ak) if ak.is_empty() {
// .await return Err(s3_error!(InvalidArgument, "access key is empty"));
// .map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, format!("is_temp_user err {}", e)))?; }
// if is_temp { let Ok(iam_store) = iam::get() else { return Err(s3_error!(InvalidRequest, "iam not init")) };
// return Err(s3_error!(InvalidArgument, "can't remove temp user"));
// }
// iam::delete_user(ak, true) let (is_temp, _) = iam_store
// .await .is_temp_user(ak)
// .map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, format!("delete_user err {}", e)))?; .await
.map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, format!("is_temp_user err {}", e)))?;
// let mut header = HeaderMap::new(); if is_temp {
// header.insert(CONTENT_TYPE, "application/json".parse().unwrap()); return Err(s3_error!(InvalidArgument, "can't remove temp user"));
}
// Ok(S3Response::with_headers((StatusCode::OK, Body::empty()), header)) let (cred, _owner) = check_key_valid(get_session_token(&req.headers), ak).await?;
let sys_cred = get_global_action_cred()
.ok_or_else(|| S3Error::with_message(S3ErrorCode::InternalError, "get_global_action_cred failed"))?;
if ak == sys_cred.access_key || ak == cred.access_key {
return Err(s3_error!(InvalidArgument, "can't remove self"));
}
iam_store
.delete_user(ak, true)
.await
.map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, format!("delete_user err {}", e)))?;
let mut header = HeaderMap::new();
header.insert(CONTENT_TYPE, "application/json".parse().unwrap());
Ok(S3Response::with_headers((StatusCode::OK, Body::empty()), header))
} }
} }
@@ -219,33 +244,36 @@ pub struct GetUserInfo {}
impl Operation for GetUserInfo { impl Operation for GetUserInfo {
async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> { async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
warn!("handle GetUserInfo"); warn!("handle GetUserInfo");
unimplemented!()
// let query = {
// if let Some(query) = req.uri.query() {
// let input: AddUserQuery =
// from_bytes(query.as_bytes()).map_err(|_e| s3_error!(InvalidArgument, "get body failed"))?;
// input
// } else {
// AddUserQuery::default()
// }
// };
// let ak = query.access_key.as_deref().unwrap_or_default(); let query = {
if let Some(query) = req.uri.query() {
let input: AddUserQuery =
from_bytes(query.as_bytes()).map_err(|_e| s3_error!(InvalidArgument, "get body failed"))?;
input
} else {
AddUserQuery::default()
}
};
// if ak.is_empty() { let ak = query.access_key.as_deref().unwrap_or_default();
// return Err(s3_error!(InvalidArgument, "access key is empty"));
// }
// let info = iam::get_user_info(ak) if ak.is_empty() {
// .await return Err(s3_error!(InvalidArgument, "access key is empty"));
// .map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, e.to_string()))?; }
// let data = serde_json::to_vec(&info) let Ok(iam_store) = iam::get() else { return Err(s3_error!(InvalidRequest, "iam not init")) };
// .map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, format!("marshal user err {}", e)))?;
// let mut header = HeaderMap::new(); let info = iam_store
// header.insert(CONTENT_TYPE, "application/json".parse().unwrap()); .get_user_info(ak)
.await
.map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, e.to_string()))?;
// Ok(S3Response::with_headers((StatusCode::OK, Body::from(data)), header)) let data = serde_json::to_vec(&info)
.map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, format!("marshal user err {}", e)))?;
let mut header = HeaderMap::new();
header.insert(CONTENT_TYPE, "application/json".parse().unwrap());
Ok(S3Response::with_headers((StatusCode::OK, Body::from(data)), header))
} }
} }
+1 -1
View File
@@ -1,6 +1,6 @@
pub mod handlers; pub mod handlers;
pub mod models;
pub mod router; pub mod router;
pub mod utils;
use common::error::Result; use common::error::Result;
// use ecstore::global::{is_dist_erasure, is_erasure}; // use ecstore::global::{is_dist_erasure, is_erasure};
-1
View File
@@ -1 +0,0 @@
pub mod service_account;
@@ -1,51 +0,0 @@
use serde::Serialize;
use time::OffsetDateTime;
// #[derive(Deserialize, Default)]
// #[serde(rename_all = "camelCase", default)]
// pub struct AddServiceAccountReq {
// pub access_key: String,
// pub secret_key: String,
// pub policy: Option<Vec<u8>>,
// pub target_user: Option<String>,
// pub name: String,
// pub description: String,
// #[serde(with = "time::serde::rfc3339::option")]
// #[serde(skip_serializing_if = "Option::is_none")]
// pub expiration: Option<OffsetDateTime>,
// }
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Credentials<'a> {
pub access_key: &'a str,
pub secret_key: &'a str,
#[serde(skip_serializing_if = "Option::is_none")]
pub session_token: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(with = "time::serde::rfc3339::option")]
pub expiration: Option<OffsetDateTime>,
}
#[derive(Serialize)]
pub struct AddServiceAccountResp<'a> {
pub credentials: Credentials<'a>,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InfoServiceAccountResp {
pub parent_user: String,
pub account_status: String,
pub implied_policy: bool,
pub policy: String,
#[serde(skip_serializing_if = "String::is_empty")]
pub name: String,
#[serde(skip_serializing_if = "String::is_empty")]
pub description: String,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(with = "time::serde::rfc3339::option")]
pub expiration: Option<OffsetDateTime>,
}
+3
View File
@@ -0,0 +1,3 @@
pub fn has_space_be(s: &str) -> bool {
s.trim().len() != s.len()
}