This commit is contained in:
weisd
2025-06-06 11:35:27 +08:00
parent 9384b831ec
commit db355bb26b
37 changed files with 2169 additions and 668 deletions
+9 -9
View File
@@ -1,4 +1,4 @@
use common::error::{Error, Result};
use crate::error::{Error, Result};
use regex::Regex;
const ARN_PREFIX_ARN: &str = "arn";
@@ -19,7 +19,7 @@ impl ARN {
pub fn new_iam_role_arn(resource_id: &str, server_region: &str) -> Result<Self> {
let valid_resource_id_regex = Regex::new(r"^[A-Za-z0-9_/\.-]+$")?;
if !valid_resource_id_regex.is_match(resource_id) {
return Err(Error::msg("ARN resource ID invalid"));
return Err(Error::other("ARN resource ID invalid"));
}
Ok(ARN {
partition: ARN_PARTITION_RUSTFS.to_string(),
@@ -33,33 +33,33 @@ impl ARN {
pub fn parse(arn_str: &str) -> Result<Self> {
let ps: Vec<&str> = arn_str.split(':').collect();
if ps.len() != 6 || ps[0] != ARN_PREFIX_ARN {
return Err(Error::msg("ARN format invalid"));
return Err(Error::other("ARN format invalid"));
}
if ps[1] != ARN_PARTITION_RUSTFS {
return Err(Error::msg("ARN partition invalid"));
return Err(Error::other("ARN partition invalid"));
}
if ps[2] != ARN_SERVICE_IAM {
return Err(Error::msg("ARN service invalid"));
return Err(Error::other("ARN service invalid"));
}
if !ps[4].is_empty() {
return Err(Error::msg("ARN account-id invalid"));
return Err(Error::other("ARN account-id invalid"));
}
let res: Vec<&str> = ps[5].splitn(2, '/').collect();
if res.len() != 2 {
return Err(Error::msg("ARN resource invalid"));
return Err(Error::other("ARN resource invalid"));
}
if res[0] != ARN_RESOURCE_TYPE_ROLE {
return Err(Error::msg("ARN resource type invalid"));
return Err(Error::other("ARN resource type invalid"));
}
let valid_resource_id_regex = Regex::new(r"^[A-Za-z0-9_/\.-]+$")?;
if !valid_resource_id_regex.is_match(res[1]) {
return Err(Error::msg("ARN resource ID invalid"));
return Err(Error::other("ARN resource ID invalid"));
}
Ok(ARN {
+22 -22
View File
@@ -1,8 +1,8 @@
use crate::error::Error as IamError;
use crate::error::{Error, Result};
use crate::policy::{iam_policy_claim_name_sa, Policy, Validator, INHERITED_POLICY_TYPE};
use crate::utils;
use crate::utils::extract_claims;
use common::error::{Error, Result};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
@@ -54,41 +54,41 @@ pub fn is_secret_key_valid(secret_key: &str) -> bool {
// fn try_from(value: &str) -> Result<Self, Self::Error> {
// let mut elem = value.trim().splitn(2, '=');
// let (Some(h), Some(cred_elems)) = (elem.next(), elem.next()) else {
// return Err(Error::new(IamError::ErrCredMalformed));
// return Err(IamError::ErrCredMalformed));
// };
// if h != "Credential" {
// return Err(Error::new(IamError::ErrCredMalformed));
// return Err(IamError::ErrCredMalformed));
// }
// let mut cred_elems = cred_elems.trim().rsplitn(5, '/');
// let Some(request) = cred_elems.next() else {
// return Err(Error::new(IamError::ErrCredMalformed));
// return Err(IamError::ErrCredMalformed));
// };
// let Some(service) = cred_elems.next() else {
// return Err(Error::new(IamError::ErrCredMalformed));
// return Err(IamError::ErrCredMalformed));
// };
// let Some(region) = cred_elems.next() else {
// return Err(Error::new(IamError::ErrCredMalformed));
// return Err(IamError::ErrCredMalformed));
// };
// let Some(date) = cred_elems.next() else {
// return Err(Error::new(IamError::ErrCredMalformed));
// return Err(IamError::ErrCredMalformed));
// };
// let Some(ak) = cred_elems.next() else {
// return Err(Error::new(IamError::ErrCredMalformed));
// return Err(IamError::ErrCredMalformed));
// };
// if ak.len() < 3 {
// return Err(Error::new(IamError::ErrCredMalformed));
// return Err(IamError::ErrCredMalformed));
// }
// if request != "aws4_request" {
// return Err(Error::new(IamError::ErrCredMalformed));
// return Err(IamError::ErrCredMalformed));
// }
// Ok(CredentialHeader {
@@ -98,7 +98,7 @@ pub fn is_secret_key_valid(secret_key: &str) -> bool {
// const FORMATTER: LazyCell<Vec<BorrowedFormatItem<'static>>> =
// LazyCell::new(|| time::format_description::parse("[year][month][day]").unwrap());
// Date::parse(date, &FORMATTER).map_err(|_| Error::new(IamError::ErrCredMalformed))?
// Date::parse(date, &FORMATTER).map_err(|_| IamError::ErrCredMalformed))?
// },
// region: region.to_owned(),
// service: service.try_into()?,
@@ -199,11 +199,11 @@ pub fn create_new_credentials_with_metadata(
token_secret: &str,
) -> Result<Credentials> {
if ak.len() < ACCESS_KEY_MIN_LEN || ak.len() > ACCESS_KEY_MAX_LEN {
return Err(Error::new(IamError::InvalidAccessKeyLength));
return Err(IamError::InvalidAccessKeyLength);
}
if sk.len() < SECRET_KEY_MIN_LEN || sk.len() > SECRET_KEY_MAX_LEN {
return Err(Error::new(IamError::InvalidAccessKeyLength));
return Err(IamError::InvalidAccessKeyLength);
}
if token_secret.is_empty() {
@@ -326,23 +326,23 @@ impl CredentialsBuilder {
impl TryFrom<CredentialsBuilder> for Credentials {
type Error = Error;
fn try_from(mut value: CredentialsBuilder) -> Result<Self, Self::Error> {
fn try_from(mut value: CredentialsBuilder) -> std::result::Result<Self, Self::Error> {
if value.parent_user.is_empty() {
return Err(Error::new(IamError::InvalidArgument));
return Err(IamError::InvalidArgument);
}
if (value.access_key.is_empty() && !value.secret_key.is_empty())
|| (!value.access_key.is_empty() && value.secret_key.is_empty())
{
return Err(Error::msg("Either ak or sk is empty"));
return Err(Error::other("Either ak or sk is empty"));
}
if value.parent_user == value.access_key.as_str() {
return Err(Error::new(IamError::InvalidArgument));
return Err(IamError::InvalidArgument);
}
if value.access_key == "site-replicator-0" && !value.allow_site_replicator_account {
return Err(Error::new(IamError::InvalidArgument));
return Err(IamError::InvalidArgument);
}
let mut claim = serde_json::json!({
@@ -351,9 +351,9 @@ impl TryFrom<CredentialsBuilder> for Credentials {
if let Some(p) = value.session_policy {
p.is_valid()?;
let policy_buf = serde_json::to_vec(&p).map_err(|_| Error::new(IamError::InvalidArgument))?;
let policy_buf = serde_json::to_vec(&p).map_err(|_| IamError::InvalidArgument)?;
if policy_buf.len() > 4096 {
return Err(Error::msg("session policy is too large"));
return Err(Error::other("session policy is too large"));
}
claim["sessionPolicy"] = serde_json::json!(base64_simd::STANDARD.encode_to_string(&policy_buf));
claim["sa-policy"] = serde_json::json!("embedded-policy");
@@ -390,8 +390,8 @@ impl TryFrom<CredentialsBuilder> for Credentials {
};
if !value.secret_key.is_empty() {
let session_token =
crypto::jwt_encode(value.access_key.as_bytes(), &claim).map_err(|_| Error::msg("session policy is too large"))?;
let session_token = crypto::jwt_encode(value.access_key.as_bytes(), &claim)
.map_err(|_| Error::other("session policy is too large"))?;
cred.session_token = session_token;
// cred.expiration = Some(
// OffsetDateTime::from_unix_timestamp(
+57 -40
View File
@@ -1,13 +1,12 @@
use crate::policy;
pub type Result<T> = core::result::Result<T, Error>;
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error(transparent)]
PolicyError(#[from] policy::Error),
#[error("ecsotre error: {0}")]
EcstoreError(common::error::Error),
#[error("{0}")]
StringError(String),
@@ -66,7 +65,7 @@ pub enum Error {
GroupNameContainsReservedChars,
#[error("jwt err {0}")]
JWTError(jsonwebtoken::errors::Error),
JWTError(#[from] jsonwebtoken::errors::Error),
#[error("no access key")]
NoAccessKey,
@@ -90,56 +89,74 @@ pub enum Error {
#[error("policy too large")]
PolicyTooLarge,
#[error("io error: {0}")]
Io(std::io::Error),
}
impl Error {
pub fn other<E>(error: E) -> Self
where
E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
Error::Io(std::io::Error::other(error))
}
}
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Self {
Error::Io(e)
}
}
impl From<time::error::ComponentRange> for Error {
fn from(e: time::error::ComponentRange) -> Self {
Error::other(e)
}
}
impl From<serde_json::Error> for Error {
fn from(e: serde_json::Error) -> Self {
Error::other(e)
}
}
// impl From<jsonwebtoken::errors::Error> for Error {
// fn from(e: jsonwebtoken::errors::Error) -> Self {
// Error::JWTError(e)
// }
// }
impl From<regex::Error> for Error {
fn from(e: regex::Error) -> Self {
Error::other(e)
}
}
// pub fn is_err_no_such_user(e: &Error) -> bool {
// matches!(e, Error::NoSuchUser(_))
// }
pub fn is_err_no_such_policy(err: &common::error::Error) -> bool {
if let Some(e) = err.downcast_ref::<Error>() {
matches!(e, Error::NoSuchPolicy)
} else {
false
}
pub fn is_err_no_such_policy(err: &Error) -> bool {
matches!(err, Error::NoSuchPolicy)
}
pub fn is_err_no_such_user(err: &common::error::Error) -> bool {
if let Some(e) = err.downcast_ref::<Error>() {
matches!(e, Error::NoSuchUser(_))
} else {
false
}
pub fn is_err_no_such_user(err: &Error) -> bool {
matches!(err, Error::NoSuchUser(_))
}
pub fn is_err_no_such_account(err: &common::error::Error) -> bool {
if let Some(e) = err.downcast_ref::<Error>() {
matches!(e, Error::NoSuchAccount(_))
} else {
false
}
pub fn is_err_no_such_account(err: &Error) -> bool {
matches!(err, Error::NoSuchAccount(_))
}
pub fn is_err_no_such_temp_account(err: &common::error::Error) -> bool {
if let Some(e) = err.downcast_ref::<Error>() {
matches!(e, Error::NoSuchTempAccount(_))
} else {
false
}
pub fn is_err_no_such_temp_account(err: &Error) -> bool {
matches!(err, Error::NoSuchTempAccount(_))
}
pub fn is_err_no_such_group(err: &common::error::Error) -> bool {
if let Some(e) = err.downcast_ref::<Error>() {
matches!(e, Error::NoSuchGroup(_))
} else {
false
}
pub fn is_err_no_such_group(err: &Error) -> bool {
matches!(err, Error::NoSuchGroup(_))
}
pub fn is_err_no_such_service_account(err: &common::error::Error) -> bool {
if let Some(e) = err.downcast_ref::<Error>() {
matches!(e, Error::NoSuchServiceAccount(_))
} else {
false
}
pub fn is_err_no_such_service_account(err: &Error) -> bool {
matches!(err, Error::NoSuchServiceAccount(_))
}
+2 -2
View File
@@ -1,4 +1,4 @@
use common::error::{Error, Result};
use crate::error::{Error, Result};
use serde::{Deserialize, Serialize};
use std::{collections::HashSet, ops::Deref};
use strum::{EnumString, IntoStaticStr};
@@ -84,7 +84,7 @@ impl Action {
impl TryFrom<&str> for Action {
type Error = Error;
fn try_from(value: &str) -> Result<Self, Self::Error> {
fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
if value.starts_with(Self::S3_PREFIX) {
Ok(Self::S3Action(
S3Action::try_from(value).map_err(|_| IamError::InvalidAction(value.into()))?,
+1 -1
View File
@@ -1,4 +1,4 @@
use common::error::{Error, Result};
use crate::error::{Error, Result};
use serde::{Deserialize, Serialize};
use strum::{EnumString, IntoStaticStr};
+1 -1
View File
@@ -1,6 +1,6 @@
use super::key_name::KeyName;
use crate::error::Error;
use crate::policy::{Error as PolicyError, Validator};
use common::error::Error;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
+1 -1
View File
@@ -1,4 +1,4 @@
use common::error::{Error, Result};
use crate::error::{Error, Result};
use serde::{Deserialize, Serialize};
use std::ops::Deref;
+2 -2
View File
@@ -1,5 +1,5 @@
use super::{action::Action, statement::BPStatement, Effect, Error as IamError, Statement, ID};
use common::error::{Error, Result};
use crate::error::{Error, Result};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::{HashMap, HashSet};
@@ -449,7 +449,7 @@ pub mod default {
#[cfg(test)]
mod test {
use super::*;
use common::error::Result;
use crate::error::Result;
#[tokio::test]
async fn test_parse_policy() -> Result<()> {
+2 -2
View File
@@ -1,5 +1,5 @@
use super::{utils::wildcard, Validator};
use common::error::{Error, Result};
use crate::error::{Error, Result};
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
@@ -25,7 +25,7 @@ impl Validator for Principal {
type Error = Error;
fn is_valid(&self) -> Result<()> {
if self.aws.is_empty() {
return Err(Error::msg("Principal is empty"));
return Err(Error::other("Principal is empty"));
}
Ok(())
}
+5 -5
View File
@@ -1,4 +1,4 @@
use common::error::{Error, Result};
use crate::error::{Error, Result};
use serde::{Deserialize, Serialize};
use std::{
collections::{HashMap, HashSet},
@@ -101,7 +101,7 @@ impl Resource {
impl TryFrom<&str> for Resource {
type Error = Error;
fn try_from(value: &str) -> Result<Self, Self::Error> {
fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
let resource = if value.starts_with(Self::S3_PREFIX) {
Resource::S3(value.strip_prefix(Self::S3_PREFIX).unwrap().into())
} else {
@@ -115,7 +115,7 @@ impl TryFrom<&str> for Resource {
impl Validator for Resource {
type Error = Error;
fn is_valid(&self) -> Result<(), Error> {
fn is_valid(&self) -> std::result::Result<(), Error> {
match self {
Self::S3(pattern) => {
if pattern.is_empty() || pattern.starts_with('/') {
@@ -139,7 +139,7 @@ impl Validator for Resource {
}
impl Serialize for Resource {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
@@ -151,7 +151,7 @@ impl Serialize for Resource {
}
impl<'de> Deserialize<'de> for Resource {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
+1 -1
View File
@@ -2,7 +2,7 @@ use super::{
action::Action, ActionSet, Args, BucketPolicyArgs, Effect, Error as IamError, Functions, Principal, ResourceSet, Validator,
ID,
};
use common::error::{Error, Result};
use crate::error::{Error, Result};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Default, Debug)]
+5 -5
View File
@@ -1,7 +1,7 @@
use common::error::{Error, Result};
use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header};
use rand::{Rng, RngCore};
use serde::{de::DeserializeOwned, Serialize};
use std::io::{Error, Result};
pub fn gen_access_key(length: usize) -> Result<String> {
const ALPHA_NUMERIC_TABLE: [char; 36] = [
@@ -10,7 +10,7 @@ pub fn gen_access_key(length: usize) -> Result<String> {
];
if length < 3 {
return Err(Error::msg("access key length is too short"));
return Err(Error::other("access key length is too short"));
}
let mut result = String::with_capacity(length);
@@ -27,7 +27,7 @@ pub fn gen_secret_key(length: usize) -> Result<String> {
use base64_simd::URL_SAFE_NO_PAD;
if length < 8 {
return Err(Error::msg("secret key length is too short"));
return Err(Error::other("secret key length is too short"));
}
let mut rng = rand::thread_rng();
@@ -40,7 +40,7 @@ pub fn gen_secret_key(length: usize) -> Result<String> {
Ok(key_str)
}
pub fn generate_jwt<T: Serialize>(claims: &T, secret: &str) -> Result<String, jsonwebtoken::errors::Error> {
pub fn generate_jwt<T: Serialize>(claims: &T, secret: &str) -> std::result::Result<String, jsonwebtoken::errors::Error> {
let header = Header::new(Algorithm::HS512);
jsonwebtoken::encode(&header, &claims, &EncodingKey::from_secret(secret.as_bytes()))
}
@@ -48,7 +48,7 @@ pub fn generate_jwt<T: Serialize>(claims: &T, secret: &str) -> Result<String, js
pub fn extract_claims<T: DeserializeOwned>(
token: &str,
secret: &str,
) -> Result<jsonwebtoken::TokenData<T>, jsonwebtoken::errors::Error> {
) -> std::result::Result<jsonwebtoken::TokenData<T>, jsonwebtoken::errors::Error> {
jsonwebtoken::decode::<T>(
token,
&DecodingKey::from_secret(secret.as_bytes()),