diff --git a/iam/src/auth/credentials.rs b/iam/src/auth/credentials.rs index 59edd2b71..ce33d8109 100644 --- a/iam/src/auth/credentials.rs +++ b/iam/src/auth/credentials.rs @@ -100,7 +100,7 @@ pub struct Credentials { pub status: String, pub parent_user: String, pub groups: Option>, - pub claims: Option>>, + pub claims: Option>, pub name: Option, pub description: Option, } diff --git a/iam/src/manager.rs b/iam/src/manager.rs index a2e983eca..e33a48b9a 100644 --- a/iam/src/manager.rs +++ b/iam/src/manager.rs @@ -20,7 +20,7 @@ use tokio::{ use crate::{ arn::ARN, - auth::{Credentials, UserIdentity}, + auth::{self, Credentials, UserIdentity}, cache::Cache, format::Format, handler::Handler, @@ -309,4 +309,16 @@ where Ok(m) } + + pub async fn add_user(&self, access_key: &str, secret_key: &str, status: &str) -> crate::Result<()> { + let status = { + match status { + "disabled" => auth::ACCOUNT_ON, + auth::ACCOUNT_ON => auth::ACCOUNT_ON, + _ => auth::ACCOUNT_OFF, + } + }; + + todo!() + } } diff --git a/rustfs/src/admin/handlers.rs b/rustfs/src/admin/handlers.rs index 50d1bba9f..d4d95d92b 100644 --- a/rustfs/src/admin/handlers.rs +++ b/rustfs/src/admin/handlers.rs @@ -20,9 +20,9 @@ use ecstore::utils::path::path_join; use ecstore::utils::xml; use ecstore::GLOBAL_Endpoints; use futures::{Stream, StreamExt}; -use http::Uri; +use http::{HeaderMap, Uri}; use hyper::StatusCode; -use iam::auth::create_new_credentials_with_metadata; +use iam::auth::{create_new_credentials_with_metadata, get_claims_from_token_with_secret}; use iam::{auth, get_global_action_cred}; use madmin::metrics::RealtimeMetrics; use madmin::utils::parse_duration; @@ -93,11 +93,21 @@ pub struct AssumeRoleRequest { // pub parent_user: String, // } -#[derive(Debug, Serialize, Default)] +#[derive(Debug, Serialize, Deserialize, Default)] pub struct STSClaims { - parent: String, - exp: usize, - access_key: String, + pub parent: String, + pub exp: usize, + pub access_key: String, +} + +impl STSClaims { + pub fn to_map(&self) -> HashMap { + let mut m = HashMap::new(); + m.insert("parent".to_string(), self.parent.clone()); + m.insert("exp".to_string(), self.exp.to_string()); + m.insert("access_key".to_string(), self.access_key.clone()); + m + } } fn get_token_signing_key() -> Option { @@ -108,11 +118,13 @@ fn get_token_signing_key() -> Option { } } -pub async fn check_key_valid(st: &str, ak: &str) -> S3Result<(auth::Credentials, bool)> { +pub async fn check_key_valid(token: Option, ak: &str) -> S3Result<(auth::Credentials, bool)> { let Some(mut cred) = get_global_action_cred() else { return Err(s3_error!(InternalError, "action cred not init")); }; + let sys_cred = cred.clone(); + if cred.access_key != ak { let Ok(iam_store) = iam::get() else { return Err(s3_error!(InternalError, "iam not init")) }; @@ -135,34 +147,67 @@ pub async fn check_key_valid(st: &str, ak: &str) -> S3Result<(auth::Credentials, return Err(s3_error!(InvalidRequest, "check key failed")); } } - - return Ok((cred, true)); } - unimplemented!() + + if let Some(st) = token { + let claims = check_claims_from_token(&st, &cred) + .map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, format!("check claims failed {}", e)))?; + cred.claims = Some(claims.to_map()); + } + + let owner = sys_cred.access_key == cred.access_key || cred.parent_user == sys_cred.access_key; + + // permitRootAccess + // SessionPolicyName + Ok((cred, owner)) } -pub fn check_claims_from_token(token: &str, cred: &auth::Credentials) -> crate::Result>> { - // if !token.is_empty() && cred.access_key.is_empty() { - // return Err(Error::NoAccessKey); - // } +pub fn check_claims_from_token(token: &str, cred: &auth::Credentials) -> S3Result { + if !token.is_empty() && cred.access_key.is_empty() { + return Err(s3_error!(InvalidRequest, "no access key")); + } - // if token.is_empty() && cred.is_temp() && !cred.is_service_account() { - // return Err(Error::InvalidToken); - // } + if token.is_empty() && cred.is_temp() && !cred.is_service_account() { + return Err(s3_error!(InvalidRequest, "invalid token")); + } - // if !token.is_empty() && !cred.is_temp() { - // return Err(Error::InvalidToken); - // } + if !token.is_empty() && !cred.is_temp() { + return Err(s3_error!(InvalidRequest, "invalid token")); + } - // if !cred.is_service_account() && cred.is_temp() && token != cred.session_token { - // return Err(Error::InvalidToken); - // } + if !cred.is_service_account() && cred.is_temp() && token != cred.session_token { + return Err(s3_error!(InvalidRequest, "invalid token")); + } - // if cred.is_temp() || cred.is_expired() { - // return Err(Error::InvalidAccessKey); - // } + if cred.is_temp() || cred.is_expired() { + return Err(s3_error!(InvalidRequest, "invalid access key")); + } - unimplemented!() + let Some(sys_cred) = get_global_action_cred() else { + return Err(s3_error!(InternalError, "action cred not init")); + }; + + let mut secret = sys_cred.secret_key; + + let mut token = token; + + if cred.is_service_account() { + token = cred.session_token.as_str(); + secret = cred.secret_key.clone(); + } + + if !token.is_empty() { + let claims: STSClaims = + get_claims_from_token_with_secret(token, &secret).map_err(|_e| s3_error!(InvalidRequest, "invalid token"))?; + return Ok(claims); + } + + Ok(STSClaims::default()) +} + +pub fn get_session_token(hds: &HeaderMap) -> Option { + hds.get("x-amz-security-token") + .map(|v| v.to_str().unwrap_or_default().to_string()) } pub struct AssumeRoleHandle {} diff --git a/rustfs/src/admin/handlers/user.rs b/rustfs/src/admin/handlers/user.rs index 0f6d0c063..a0a29e600 100644 --- a/rustfs/src/admin/handlers/user.rs +++ b/rustfs/src/admin/handlers/user.rs @@ -7,7 +7,10 @@ use serde::Deserialize; use serde_urlencoded::from_bytes; use tracing::warn; -use crate::admin::router::Operation; +use crate::admin::{ + handlers::{check_key_valid, get_session_token}, + router::Operation, +}; #[derive(Debug, Deserialize, Default)] pub struct AddUserQuery { @@ -29,6 +32,10 @@ impl Operation for AddUser { } }; + let Some(input_cred) = req.credentials else { + return Err(s3_error!(InvalidRequest, "get cred failed")); + }; + let ak = query.access_key.as_deref().unwrap_or_default(); if let Some(sys_cred) = get_global_action_cred() { @@ -46,6 +53,14 @@ impl Operation for AddUser { } } + 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")); + } + warn!("handle AddUser"); return Err(s3_error!(NotImplemented)); }