mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-01-05 21:02:58 +00:00
feat: pub auth_mysql & add auth boxed err (#788)
* chore: minor openup * chore: open up auth_mysql and return () * chore: typo change * chore: change according to ci * chore: change according to ci * chore: remove tonic status in auth error
This commit is contained in:
@@ -18,6 +18,7 @@ pub const DEFAULT_USERNAME: &str = "greptime";
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use common_error::ext::BoxedError;
|
||||
use common_error::prelude::ErrorExt;
|
||||
use common_error::status_code::StatusCode;
|
||||
use snafu::{Backtrace, ErrorCompat, OptionExt, Snafu};
|
||||
@@ -28,7 +29,7 @@ use crate::auth::user_provider::StaticUserProvider;
|
||||
pub trait UserProvider: Send + Sync {
|
||||
fn name(&self) -> &str;
|
||||
|
||||
async fn auth(&self, id: Identity<'_>, password: Password<'_>) -> Result<UserInfo, Error>;
|
||||
async fn auth(&self, id: Identity<'_>, password: Password<'_>) -> Result<UserInfo>;
|
||||
}
|
||||
|
||||
pub type UserProviderRef = Arc<dyn UserProvider>;
|
||||
@@ -76,7 +77,7 @@ impl UserInfo {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn user_provider_from_option(opt: &String) -> Result<UserProviderRef, Error> {
|
||||
pub fn user_provider_from_option(opt: &String) -> Result<UserProviderRef> {
|
||||
let (name, content) = opt.split_once(':').context(InvalidConfigSnafu {
|
||||
value: opt.to_string(),
|
||||
msg: "UserProviderOption must be in format `<option>:<value>`",
|
||||
@@ -99,23 +100,25 @@ pub fn user_provider_from_option(opt: &String) -> Result<UserProviderRef, Error>
|
||||
#[snafu(visibility(pub))]
|
||||
pub enum Error {
|
||||
#[snafu(display("Invalid config value: {}, {}", value, msg))]
|
||||
InvalidConfig {
|
||||
value: String,
|
||||
msg: String,
|
||||
InvalidConfig { value: String, msg: String },
|
||||
|
||||
#[snafu(display("IO error, source: {}", source))]
|
||||
Io {
|
||||
source: std::io::Error,
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
|
||||
#[snafu(display("Encounter IO error, source: {}", source))]
|
||||
IOErr { source: std::io::Error },
|
||||
#[snafu(display("Auth failed, source: {}", source))]
|
||||
AuthBackend {
|
||||
#[snafu(backtrace)]
|
||||
source: BoxedError,
|
||||
},
|
||||
|
||||
#[snafu(display("User not found, username: {}", username))]
|
||||
UserNotFound { username: String },
|
||||
|
||||
#[snafu(display("Unsupported password type: {}", password_type))]
|
||||
UnsupportedPasswordType {
|
||||
password_type: String,
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
UnsupportedPasswordType { password_type: String },
|
||||
|
||||
#[snafu(display("Username and password does not match, username: {}", username))]
|
||||
UserPasswordMismatch { username: String },
|
||||
@@ -125,7 +128,8 @@ impl ErrorExt for Error {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match self {
|
||||
Error::InvalidConfig { .. } => StatusCode::InvalidArguments,
|
||||
Error::IOErr { .. } => StatusCode::Internal,
|
||||
Error::Io { .. } => StatusCode::Internal,
|
||||
Error::AuthBackend { .. } => StatusCode::Internal,
|
||||
|
||||
Error::UserNotFound { .. } => StatusCode::UserNotFound,
|
||||
Error::UnsupportedPasswordType { .. } => StatusCode::UnsupportedPasswordType,
|
||||
@@ -142,6 +146,8 @@ impl ErrorExt for Error {
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use super::{Identity, Password, UserInfo, UserProvider};
|
||||
|
||||
@@ -25,7 +25,7 @@ use sha1::Sha1;
|
||||
use snafu::{ensure, OptionExt, ResultExt};
|
||||
|
||||
use crate::auth::{
|
||||
Error, HashedPassword, IOErrSnafu, Identity, InvalidConfigSnafu, Password, Salt,
|
||||
Error, HashedPassword, Identity, InvalidConfigSnafu, IoSnafu, Password, Result, Salt,
|
||||
UnsupportedPasswordTypeSnafu, UserInfo, UserNotFoundSnafu, UserPasswordMismatchSnafu,
|
||||
UserProvider,
|
||||
};
|
||||
@@ -35,7 +35,7 @@ pub const STATIC_USER_PROVIDER: &str = "static_user_provider";
|
||||
impl TryFrom<&str> for StaticUserProvider {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
fn try_from(value: &str) -> Result<Self> {
|
||||
let (mode, content) = value.split_once(':').context(InvalidConfigSnafu {
|
||||
value: value.to_string(),
|
||||
msg: "StaticUserProviderOption must be in format `<option>:<value>`",
|
||||
@@ -49,7 +49,7 @@ impl TryFrom<&str> for StaticUserProvider {
|
||||
msg: "StaticUserProviderOption file must be a valid file path",
|
||||
});
|
||||
|
||||
let file = File::open(path).context(IOErrSnafu)?;
|
||||
let file = File::open(path).context(IoSnafu)?;
|
||||
let credential = io::BufReader::new(file)
|
||||
.lines()
|
||||
.filter_map(|line| line.ok())
|
||||
@@ -78,7 +78,7 @@ impl TryFrom<&str> for StaticUserProvider {
|
||||
})?;
|
||||
Ok((k.to_string(), v.as_bytes().to_vec()))
|
||||
})
|
||||
.collect::<Result<HashMap<String, Vec<u8>>, Error>>()
|
||||
.collect::<Result<HashMap<String, Vec<u8>>>>()
|
||||
.map(|users| StaticUserProvider { users }),
|
||||
_ => InvalidConfigSnafu {
|
||||
value: mode.to_string(),
|
||||
@@ -99,11 +99,7 @@ impl UserProvider for StaticUserProvider {
|
||||
STATIC_USER_PROVIDER
|
||||
}
|
||||
|
||||
async fn auth(
|
||||
&self,
|
||||
input_id: Identity<'_>,
|
||||
input_pwd: Password<'_>,
|
||||
) -> Result<UserInfo, Error> {
|
||||
async fn auth(&self, input_id: Identity<'_>, input_pwd: Password<'_>) -> Result<UserInfo> {
|
||||
match input_id {
|
||||
Identity::UserId(username, _) => {
|
||||
let save_pwd = self.users.get(username).context(UserNotFoundSnafu {
|
||||
@@ -122,7 +118,8 @@ impl UserProvider for StaticUserProvider {
|
||||
}
|
||||
}
|
||||
Password::MysqlNativePassword(auth_data, salt) => {
|
||||
auth_mysql(auth_data, salt, username.to_string(), save_pwd)
|
||||
auth_mysql(auth_data, salt, username, save_pwd)
|
||||
.map(|_| UserInfo::new(username))
|
||||
}
|
||||
Password::PgMD5(_, _) => UnsupportedPasswordTypeSnafu {
|
||||
password_type: "pg_md5",
|
||||
@@ -134,12 +131,12 @@ impl UserProvider for StaticUserProvider {
|
||||
}
|
||||
}
|
||||
|
||||
fn auth_mysql(
|
||||
pub fn auth_mysql(
|
||||
auth_data: HashedPassword,
|
||||
salt: Salt,
|
||||
username: String,
|
||||
username: &str,
|
||||
save_pwd: &[u8],
|
||||
) -> Result<UserInfo, Error> {
|
||||
) -> Result<()> {
|
||||
// ref: https://github.com/mysql/mysql-server/blob/a246bad76b9271cb4333634e954040a970222e0a/sql/auth/password.cc#L62
|
||||
let hash_stage_2 = double_sha1(save_pwd);
|
||||
let tmp = sha1_two(salt, &hash_stage_2);
|
||||
@@ -150,9 +147,12 @@ fn auth_mysql(
|
||||
}
|
||||
let candidate_stage_2 = sha1_one(&xor_result);
|
||||
if candidate_stage_2 == hash_stage_2 {
|
||||
Ok(UserInfo::new(username))
|
||||
Ok(())
|
||||
} else {
|
||||
UserPasswordMismatchSnafu { username }.fail()
|
||||
UserPasswordMismatchSnafu {
|
||||
username: username.to_string(),
|
||||
}
|
||||
.fail()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user