feat:admin auth (#512)

* feat:admin auth

* fix:#509
This commit is contained in:
weisd
2025-09-11 16:49:07 +08:00
committed by GitHub
parent cf863ba059
commit 70e6bec2a4
10 changed files with 558 additions and 100 deletions
+65
View File
@@ -0,0 +1,65 @@
use std::collections::HashMap;
use std::sync::Arc;
use http::HeaderMap;
use rustfs_iam::store::object::ObjectStore;
use rustfs_iam::sys::IamSys;
use rustfs_policy::auth;
use rustfs_policy::policy::Args;
use rustfs_policy::policy::action::Action;
use s3s::S3Result;
use s3s::s3_error;
use crate::auth::get_condition_values;
pub async fn validate_admin_request(
headers: &HeaderMap,
cred: &auth::Credentials,
is_owner: bool,
deny_only: bool,
actions: Vec<Action>,
) -> S3Result<()> {
let Ok(iam_store) = rustfs_iam::get() else {
return Err(s3_error!(InternalError, "iam not init"));
};
for action in actions {
match check_admin_request_auth(iam_store.clone(), headers, cred, is_owner, deny_only, action).await {
Ok(_) => return Ok(()),
Err(_) => {
continue;
}
}
}
Err(s3_error!(AccessDenied, "Access Denied"))
}
async fn check_admin_request_auth(
iam_store: Arc<IamSys<ObjectStore>>,
headers: &HeaderMap,
cred: &auth::Credentials,
is_owner: bool,
deny_only: bool,
action: Action,
) -> S3Result<()> {
let conditions = get_condition_values(headers, cred);
if !iam_store
.is_allowed(&Args {
account: &cred.access_key,
groups: &cred.groups,
action,
conditions: &conditions,
is_owner,
claims: cred.claims.as_ref().unwrap_or(&HashMap::new()),
deny_only,
bucket: "",
object: "",
})
.await
{
return Err(s3_error!(AccessDenied, "Access Denied"));
}
Ok(())
}
+53 -3
View File
@@ -13,6 +13,7 @@
// limitations under the License.
use super::router::Operation;
use crate::admin::auth::validate_admin_request;
use crate::auth::check_key_valid;
use crate::auth::get_condition_values;
use crate::auth::get_session_token;
@@ -45,6 +46,7 @@ use rustfs_madmin::utils::parse_duration;
use rustfs_policy::policy::Args;
use rustfs_policy::policy::BucketPolicy;
use rustfs_policy::policy::action::Action;
use rustfs_policy::policy::action::AdminAction;
use rustfs_policy::policy::action::S3Action;
use rustfs_policy::policy::default::DEFAULT_POLICIES;
use rustfs_utils::path::path_join;
@@ -300,7 +302,23 @@ pub struct ServerInfoHandler {}
#[async_trait::async_trait]
impl Operation for ServerInfoHandler {
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)>> {
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.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(
&req.headers,
&cred,
owner,
false,
vec![Action::AdminAction(AdminAction::ServerInfoAdminAction)],
)
.await?;
let info = get_server_info(true).await;
let data = serde_json::to_vec(&info)
@@ -328,9 +346,25 @@ pub struct StorageInfoHandler {}
#[async_trait::async_trait]
impl Operation for StorageInfoHandler {
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 StorageInfoHandler");
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.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(
&req.headers,
&cred,
owner,
false,
vec![Action::AdminAction(AdminAction::StorageInfoAdminAction)],
)
.await?;
let Some(store) = new_object_layer_fn() else {
return Err(S3Error::with_message(S3ErrorCode::InternalError, "Not init".to_string()));
};
@@ -353,9 +387,25 @@ pub struct DataUsageInfoHandler {}
#[async_trait::async_trait]
impl Operation for DataUsageInfoHandler {
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 DataUsageInfoHandler");
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.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(
&req.headers,
&cred,
owner,
false,
vec![Action::AdminAction(AdminAction::DataUsageInfoAdminAction)],
)
.await?;
let Some(store) = new_object_layer_fn() else {
return Err(S3Error::with_message(S3ErrorCode::InternalError, "Not init".to_string()));
};
+25 -4
View File
@@ -18,7 +18,7 @@ use std::{
};
use crate::{
admin::router::Operation,
admin::{auth::validate_admin_request, router::Operation},
auth::{check_key_valid, get_session_token},
};
@@ -44,7 +44,10 @@ use rustfs_ecstore::{
bucket::utils::{deserialize, serialize},
store_api::MakeBucketOptions,
};
use rustfs_policy::policy::BucketPolicy;
use rustfs_policy::policy::{
BucketPolicy,
action::{Action, AdminAction},
};
use rustfs_utils::path::{SLASH_SEPARATOR, path_join_buf};
use s3s::{
Body, S3Request, S3Response, S3Result,
@@ -85,9 +88,18 @@ impl Operation for ExportBucketMetadata {
return Err(s3_error!(InvalidRequest, "get cred failed"));
};
let (_cred, _owner) =
let (cred, owner) =
check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(
&req.headers,
&cred,
owner,
false,
vec![Action::AdminAction(AdminAction::ExportBucketMetadataAction)],
)
.await?;
let Some(store) = new_object_layer_fn() else {
return Err(s3_error!(InvalidRequest, "object store not init"));
};
@@ -369,9 +381,18 @@ impl Operation for ImportBucketMetadata {
return Err(s3_error!(InvalidRequest, "get cred failed"));
};
let (_cred, _owner) =
let (cred, owner) =
check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(
&req.headers,
&cred,
owner,
false,
vec![Action::AdminAction(AdminAction::ImportBucketMetadataAction)],
)
.await?;
let mut input = req.input;
let body = match input.store_all_unlimited().await {
Ok(b) => b,
+70 -2
View File
@@ -17,6 +17,7 @@ use matchit::Params;
use rustfs_ecstore::global::get_global_action_cred;
use rustfs_iam::error::{is_err_no_such_group, is_err_no_such_user};
use rustfs_madmin::GroupAddRemove;
use rustfs_policy::policy::action::{Action, AdminAction};
use s3s::{
Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result,
header::{CONTENT_LENGTH, CONTENT_TYPE},
@@ -26,7 +27,10 @@ use serde::Deserialize;
use serde_urlencoded::from_bytes;
use tracing::warn;
use crate::admin::{router::Operation, utils::has_space_be};
use crate::{
admin::{auth::validate_admin_request, router::Operation, utils::has_space_be},
auth::{check_key_valid, get_session_token},
};
#[derive(Debug, Deserialize, Default)]
pub struct GroupQuery {
@@ -37,9 +41,25 @@ pub struct GroupQuery {
pub struct ListGroups {}
#[async_trait::async_trait]
impl Operation for ListGroups {
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 ListGroups");
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.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(
&req.headers,
&cred,
owner,
false,
vec![Action::AdminAction(AdminAction::ListGroupsAdminAction)],
)
.await?;
let Ok(iam_store) = rustfs_iam::get() else { return Err(s3_error!(InternalError, "iam not init")) };
let groups = iam_store.list_groups().await.map_err(|e| {
@@ -62,6 +82,22 @@ impl Operation for GetGroup {
async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
warn!("handle GetGroup");
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.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(
&req.headers,
&cred,
owner,
false,
vec![Action::AdminAction(AdminAction::GetGroupAdminAction)],
)
.await?;
let query = {
if let Some(query) = req.uri.query() {
let input: GroupQuery =
@@ -93,6 +129,22 @@ impl Operation for SetGroupStatus {
async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
warn!("handle SetGroupStatus");
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.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(
&req.headers,
&cred,
owner,
false,
vec![Action::AdminAction(AdminAction::EnableGroupAdminAction)],
)
.await?;
let query = {
if let Some(query) = req.uri.query() {
let input: GroupQuery =
@@ -144,6 +196,22 @@ impl Operation for UpdateGroupMembers {
async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
warn!("handle UpdateGroupMembers");
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.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(
&req.headers,
&cred,
owner,
false,
vec![Action::AdminAction(AdminAction::AddUserToGroupAdminAction)],
)
.await?;
let mut input = req.input;
let body = match input.store_all_unlimited().await {
Ok(b) => b,
+88 -6
View File
@@ -12,13 +12,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::admin::{router::Operation, utils::has_space_be};
use crate::{
admin::{auth::validate_admin_request, router::Operation, utils::has_space_be},
auth::{check_key_valid, get_session_token},
};
use http::{HeaderMap, StatusCode};
use matchit::Params;
use rustfs_ecstore::global::get_global_action_cred;
use rustfs_iam::error::is_err_no_such_user;
use rustfs_iam::store::MappedPolicy;
use rustfs_policy::policy::Policy;
use rustfs_policy::policy::{
Policy,
action::{Action, AdminAction},
};
use s3s::{
Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result,
header::{CONTENT_LENGTH, CONTENT_TYPE},
@@ -40,6 +46,22 @@ impl Operation for ListCannedPolicies {
async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
warn!("handle ListCannedPolicies");
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.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(
&req.headers,
&cred,
owner,
false,
vec![Action::AdminAction(AdminAction::ListUserPoliciesAdminAction)],
)
.await?;
let query = {
if let Some(query) = req.uri.query() {
let input: BucketQuery =
@@ -82,6 +104,22 @@ impl Operation for AddCannedPolicy {
async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
warn!("handle AddCannedPolicy");
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.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(
&req.headers,
&cred,
owner,
false,
vec![Action::AdminAction(AdminAction::CreatePolicyAdminAction)],
)
.await?;
let query = {
if let Some(query) = req.uri.query() {
let input: PolicyNameQuery =
@@ -138,6 +176,22 @@ impl Operation for InfoCannedPolicy {
async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
warn!("handle InfoCannedPolicy");
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.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(
&req.headers,
&cred,
owner,
false,
vec![Action::AdminAction(AdminAction::GetPolicyAdminAction)],
)
.await?;
let query = {
if let Some(query) = req.uri.query() {
let input: PolicyNameQuery =
@@ -179,6 +233,22 @@ impl Operation for RemoveCannedPolicy {
async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
warn!("handle RemoveCannedPolicy");
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.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(
&req.headers,
&cred,
owner,
false,
vec![Action::AdminAction(AdminAction::DeletePolicyAdminAction)],
)
.await?;
let query = {
if let Some(query) = req.uri.query() {
let input: PolicyNameQuery =
@@ -223,6 +293,22 @@ impl Operation for SetPolicyForUserOrGroup {
async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
warn!("handle SetPolicyForUserOrGroup");
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.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(
&req.headers,
&cred,
owner,
false,
vec![Action::AdminAction(AdminAction::AttachPolicyAdminAction)],
)
.await?;
let query = {
if let Some(query) = req.uri.query() {
let input: SetPolicyForUserOrGroupQuery =
@@ -233,10 +319,6 @@ impl Operation for SetPolicyForUserOrGroup {
}
};
if query.policy_name.is_empty() {
return Err(s3_error!(InvalidArgument, "policy name is empty"));
}
if query.user_or_group.is_empty() {
return Err(s3_error!(InvalidArgument, "user or group is empty"));
}
+77 -2
View File
@@ -15,13 +15,18 @@
use http::{HeaderMap, StatusCode};
use matchit::Params;
use rustfs_ecstore::{GLOBAL_Endpoints, new_object_layer_fn};
use rustfs_policy::policy::action::{Action, AdminAction};
use s3s::{Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result, header::CONTENT_TYPE, s3_error};
use serde::Deserialize;
use serde_urlencoded::from_bytes;
use tokio::sync::broadcast;
use tracing::warn;
use crate::{admin::router::Operation, error::ApiError};
use crate::{
admin::{auth::validate_admin_request, router::Operation},
auth::{check_key_valid, get_session_token},
error::ApiError,
};
pub struct ListPools {}
@@ -29,9 +34,28 @@ pub struct ListPools {}
impl Operation for ListPools {
// GET <endpoint>/<admin-API>/pools/list
#[tracing::instrument(skip_all)]
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 ListPools");
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.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(
&req.headers,
&cred,
owner,
false,
vec![
Action::AdminAction(AdminAction::ServerInfoAdminAction),
Action::AdminAction(AdminAction::DecommissionAdminAction),
],
)
.await?;
let Some(store) = new_object_layer_fn() else {
return Err(S3Error::with_message(S3ErrorCode::InternalError, "Not init".to_string()));
};
@@ -79,6 +103,25 @@ impl Operation for StatusPool {
async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
warn!("handle StatusPool");
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.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(
&req.headers,
&cred,
owner,
false,
vec![
Action::AdminAction(AdminAction::ServerInfoAdminAction),
Action::AdminAction(AdminAction::DecommissionAdminAction),
],
)
.await?;
let Some(endpoints) = GLOBAL_Endpoints.get() else {
return Err(s3_error!(NotImplemented));
};
@@ -138,6 +181,22 @@ impl Operation for StartDecommission {
async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
warn!("handle StartDecommission");
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.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(
&req.headers,
&cred,
owner,
false,
vec![Action::AdminAction(AdminAction::DecommissionAdminAction)],
)
.await?;
let Some(endpoints) = GLOBAL_Endpoints.get() else {
return Err(s3_error!(NotImplemented));
};
@@ -221,6 +280,22 @@ impl Operation for CancelDecommission {
async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
warn!("handle CancelDecommission");
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.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(
&req.headers,
&cred,
owner,
false,
vec![Action::AdminAction(AdminAction::DecommissionAdminAction)],
)
.await?;
let Some(endpoints) = GLOBAL_Endpoints.get() else {
return Err(s3_error!(NotImplemented));
};
+56 -4
View File
@@ -22,6 +22,7 @@ use rustfs_ecstore::{
rebalance::{DiskStat, RebalSaveOpt},
store_api::BucketOptions,
};
use rustfs_policy::policy::action::{Action, AdminAction};
use s3s::{
Body, S3Request, S3Response, S3Result,
header::{CONTENT_LENGTH, CONTENT_TYPE},
@@ -32,7 +33,10 @@ use std::time::Duration;
use time::OffsetDateTime;
use tracing::warn;
use crate::admin::router::Operation;
use crate::{
admin::{auth::validate_admin_request, router::Operation},
auth::{check_key_valid, get_session_token},
};
use rustfs_ecstore::rebalance::RebalanceMeta;
#[derive(Debug, Clone, Deserialize, Serialize)]
@@ -84,9 +88,25 @@ pub struct RebalanceStart {}
#[async_trait::async_trait]
impl Operation for RebalanceStart {
#[tracing::instrument(skip_all)]
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 RebalanceStart");
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.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(
&req.headers,
&cred,
owner,
false,
vec![Action::AdminAction(AdminAction::RebalanceAdminAction)],
)
.await?;
let Some(store) = new_object_layer_fn() else {
return Err(s3_error!(InternalError, "Not init"));
};
@@ -145,9 +165,25 @@ pub struct RebalanceStatus {}
#[async_trait::async_trait]
impl Operation for RebalanceStatus {
#[tracing::instrument(skip_all)]
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 RebalanceStatus");
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.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(
&req.headers,
&cred,
owner,
false,
vec![Action::AdminAction(AdminAction::RebalanceAdminAction)],
)
.await?;
let Some(store) = new_object_layer_fn() else {
return Err(s3_error!(InternalError, "Not init"));
};
@@ -246,9 +282,25 @@ pub struct RebalanceStop {}
#[async_trait::async_trait]
impl Operation for RebalanceStop {
#[tracing::instrument(skip_all)]
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 RebalanceStop");
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.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(
&req.headers,
&cred,
owner,
false,
vec![Action::AdminAction(AdminAction::RebalanceAdminAction)],
)
.await?;
let Some(store) = new_object_layer_fn() else {
return Err(s3_error!(InternalError, "Not init"));
};
+37 -14
View File
@@ -16,6 +16,7 @@
use http::{HeaderMap, StatusCode};
//use iam::get_global_action_cred;
use matchit::Params;
use rustfs_policy::policy::action::{Action, AdminAction};
use s3s::{
Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result,
header::{CONTENT_LENGTH, CONTENT_TYPE},
@@ -26,7 +27,7 @@ use time::OffsetDateTime;
use tracing::{debug, warn};
use crate::{
admin::router::Operation,
admin::{auth::validate_admin_request, router::Operation},
auth::{check_key_valid, get_session_token},
};
@@ -88,9 +89,11 @@ impl Operation for AddTier {
return Err(s3_error!(InvalidRequest, "get cred failed"));
};
let (cred, _owner) =
let (cred, owner) =
check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(&req.headers, &cred, owner, false, vec![Action::AdminAction(AdminAction::SetTierAction)]).await?;
let mut input = req.input;
let body = match input.store_all_unlimited().await {
Ok(b) => b,
@@ -196,9 +199,11 @@ impl Operation for EditTier {
return Err(s3_error!(InvalidRequest, "get cred failed"));
};
let (cred, _owner) =
let (cred, owner) =
check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(&req.headers, &cred, owner, false, vec![Action::AdminAction(AdminAction::SetTierAction)]).await?;
let mut input = req.input;
let body = match input.store_all_unlimited().await {
Ok(b) => b,
@@ -265,6 +270,15 @@ impl Operation for ListTiers {
}
};
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.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(&req.headers, &cred, owner, false, vec![Action::AdminAction(AdminAction::ListTierAction)]).await?;
let mut tier_config_mgr = GLOBAL_TierConfigMgr.read().await;
let tiers = tier_config_mgr.list_tiers();
@@ -296,11 +310,10 @@ impl Operation for RemoveTier {
return Err(s3_error!(InvalidRequest, "get cred failed"));
};
let (cred, _owner) =
let (cred, owner) =
check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
//let sys_cred = get_global_action_cred()
// .ok_or_else(|| S3Error::with_message(S3ErrorCode::InternalError, "get_global_action_cred failed"))?;
validate_admin_request(&req.headers, &cred, owner, false, vec![Action::AdminAction(AdminAction::SetTierAction)]).await?;
let mut force: bool = false;
let force_str = query.force.clone().unwrap_or_default();
@@ -360,11 +373,10 @@ impl Operation for VerifyTier {
return Err(s3_error!(InvalidRequest, "get cred failed"));
};
let (cred, _owner) =
let (cred, owner) =
check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
//let sys_cred = get_global_action_cred()
// .ok_or_else(|| S3Error::with_message(S3ErrorCode::InternalError, "get_global_action_cred failed"))?;
validate_admin_request(&req.headers, &cred, owner, false, vec![Action::AdminAction(AdminAction::ListTierAction)]).await?;
let mut tier_config_mgr = GLOBAL_TierConfigMgr.write().await;
tier_config_mgr.verify(&query.tier.unwrap()).await;
@@ -380,6 +392,15 @@ pub struct GetTierInfo {}
#[async_trait::async_trait]
impl Operation for GetTierInfo {
async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
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.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(&req.headers, &cred, owner, false, vec![Action::AdminAction(AdminAction::ListTierAction)]).await?;
let query = {
if let Some(query) = req.uri.query() {
let input: AddTierQuery =
@@ -423,12 +444,14 @@ impl Operation for ClearTier {
}
};
//let Some(input_cred) = req.credentials else {
// return Err(s3_error!(InvalidRequest, "get cred failed"));
//};
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.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
let (cred, owner) =
check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(&req.headers, &cred, owner, false, vec![Action::AdminAction(AdminAction::SetTierAction)]).await?;
let mut force: bool = false;
let force_str = query.force;
+86 -65
View File
@@ -13,8 +13,8 @@
// limitations under the License.
use crate::{
admin::{router::Operation, utils::has_space_be},
auth::{check_key_valid, get_condition_values, get_session_token},
admin::{auth::validate_admin_request, router::Operation, utils::has_space_be},
auth::{check_key_valid, get_session_token},
};
use http::{HeaderMap, StatusCode};
use matchit::Params;
@@ -27,10 +27,7 @@ use rustfs_madmin::{
AccountStatus, AddOrUpdateUserReq, IAMEntities, IAMErrEntities, IAMErrEntity, IAMErrPolicyEntity,
user::{ImportIAMResult, SRSessionPolicy, SRSvcAccCreate},
};
use rustfs_policy::policy::{
Args,
action::{Action, AdminAction},
};
use rustfs_policy::policy::action::{Action, AdminAction};
use rustfs_utils::path::path_join_buf;
use s3s::{
@@ -121,23 +118,14 @@ impl Operation for AddUser {
}
let deny_only = ak == cred.access_key;
let conditions = get_condition_values(&req.headers, &cred);
if !iam_store
.is_allowed(&Args {
account: &cred.access_key,
groups: &cred.groups,
action: Action::AdminAction(AdminAction::CreateUserAdminAction),
bucket: "",
conditions: &conditions,
is_owner: owner,
object: "",
claims: cred.claims.as_ref().unwrap_or(&HashMap::new()),
deny_only,
})
.await
{
return Err(s3_error!(AccessDenied, "access denied"));
}
validate_admin_request(
&req.headers,
&cred,
owner,
deny_only,
vec![Action::AdminAction(AdminAction::CreateUserAdminAction)],
)
.await?;
iam_store
.create_user(ak, &args)
@@ -179,6 +167,18 @@ impl Operation for SetUserStatus {
return Err(s3_error!(InvalidArgument, "can't change status of self"));
}
let (cred, owner) =
check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(
&req.headers,
&cred,
owner,
false,
vec![Action::AdminAction(AdminAction::EnableUserAdminAction)],
)
.await?;
let status = AccountStatus::try_from(query.status.as_deref().unwrap_or_default())
.map_err(|e| S3Error::with_message(S3ErrorCode::InvalidArgument, e))?;
@@ -207,6 +207,22 @@ pub struct ListUsers {}
#[async_trait::async_trait]
impl Operation for ListUsers {
async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
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.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(
&req.headers,
&cred,
owner,
false,
vec![Action::AdminAction(AdminAction::ListUsersAdminAction)],
)
.await?;
let query = {
if let Some(query) = req.uri.query() {
let input: BucketQuery =
@@ -238,13 +254,6 @@ impl Operation for ListUsers {
let data = serde_json::to_vec(&users)
.map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, format!("marshal users err {e}")))?;
// let Some(input_cred) = req.credentials else {
// return Err(s3_error!(InvalidRequest, "get cred failed"));
// };
// let body = encrypt_data(input_cred.secret_key.expose().as_bytes(), &data)
// .map_err(|e| S3Error::with_message(S3ErrorCode::InvalidArgument, format!("encrypt_data err {}", e)))?;
let mut header = HeaderMap::new();
header.insert(CONTENT_TYPE, "application/json".parse().unwrap());
@@ -256,6 +265,22 @@ pub struct RemoveUser {}
#[async_trait::async_trait]
impl Operation for RemoveUser {
async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
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.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(
&req.headers,
&cred,
owner,
false,
vec![Action::AdminAction(AdminAction::DeleteUserAdminAction)],
)
.await?;
let query = {
if let Some(query) = req.uri.query() {
let input: AddUserQuery =
@@ -272,6 +297,13 @@ impl Operation for RemoveUser {
return Err(s3_error!(InvalidArgument, "access key is empty"));
}
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 || cred.parent_user == ak {
return Err(s3_error!(InvalidArgument, "can't remove self"));
}
let Ok(iam_store) = rustfs_iam::get() else {
return Err(s3_error!(InvalidRequest, "iam not init"));
};
@@ -285,22 +317,12 @@ impl Operation for RemoveUser {
return Err(s3_error!(InvalidArgument, "can't remove temp user"));
}
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.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).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 {
warn!(
"can't remove self or system access key {}, {}, {}",
ak, sys_cred.access_key, cred.access_key
);
return Err(s3_error!(InvalidArgument, "can't remove self"));
let (is_service_account, _) = iam_store
.is_service_account(ak)
.await
.map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, format!("is_service_account err {e}")))?;
if is_service_account {
return Err(s3_error!(InvalidArgument, "can't remove service account"));
}
iam_store
@@ -308,6 +330,8 @@ impl Operation for RemoveUser {
.await
.map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, format!("delete_user err {e}")))?;
// TODO: IAMChangeHook
let mut header = HeaderMap::new();
header.insert(CONTENT_TYPE, "application/json".parse().unwrap());
header.insert(CONTENT_LENGTH, "0".parse().unwrap());
@@ -347,23 +371,14 @@ impl Operation for GetUserInfo {
check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
let deny_only = ak == cred.access_key;
let conditions = get_condition_values(&req.headers, &cred);
if !iam_store
.is_allowed(&Args {
account: &cred.access_key,
groups: &cred.groups,
action: Action::AdminAction(AdminAction::GetUserAdminAction),
bucket: "",
conditions: &conditions,
is_owner: owner,
object: "",
claims: cred.claims.as_ref().unwrap_or(&HashMap::new()),
deny_only,
})
.await
{
return Err(s3_error!(AccessDenied, "access denied"));
}
validate_admin_request(
&req.headers,
&cred,
owner,
deny_only,
vec![Action::AdminAction(AdminAction::GetUserAdminAction)],
)
.await?;
let info = iam_store
.get_user_info(ak)
@@ -408,9 +423,12 @@ impl Operation for ExportIam {
return Err(s3_error!(InvalidRequest, "get cred failed"));
};
let (_cred, _owner) =
let (cred, owner) =
check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(&req.headers, &cred, owner, false, vec![Action::AdminAction(AdminAction::ExportIAMAction)])
.await?;
let Ok(iam_store) = rustfs_iam::get() else {
return Err(s3_error!(InvalidRequest, "iam not init"));
};
@@ -612,9 +630,12 @@ impl Operation for ImportIam {
return Err(s3_error!(InvalidRequest, "get cred failed"));
};
let (_cred, _owner) =
let (cred, owner) =
check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
validate_admin_request(&req.headers, &cred, owner, false, vec![Action::AdminAction(AdminAction::ExportIAMAction)])
.await?;
let mut input = req.input;
let body = match input.store_all_unlimited().await {
Ok(b) => b,
+1
View File
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
mod auth;
pub mod console;
pub mod handlers;
pub mod router;