chore: minor auth crate change (#2176)

* chore: pub auth_mysql

* chore: pub all error

* chore: remove back to error

* chore: wrap failed permission check result to err

* chore: minor change
This commit is contained in:
shuiyisong
2023-08-15 18:49:22 +08:00
committed by GitHub
parent 24dc827ff9
commit c8cde704cf
12 changed files with 117 additions and 102 deletions

View File

@@ -14,10 +14,12 @@
use std::sync::Arc;
use digest::Digest;
use secrecy::SecretString;
use snafu::OptionExt;
use sha1::Sha1;
use snafu::{ensure, OptionExt};
use crate::error::{InvalidConfigSnafu, Result};
use crate::error::{IllegalParamSnafu, InvalidConfigSnafu, Result, UserPasswordMismatchSnafu};
use crate::user_info::DefaultUserInfo;
use crate::user_provider::static_user_provider::{StaticUserProvider, STATIC_USER_PROVIDER};
use crate::{UserInfoRef, UserProviderRef};
@@ -66,3 +68,80 @@ pub enum Password<'a> {
MysqlNativePassword(HashedPassword<'a>, Salt<'a>),
PgMD5(HashedPassword<'a>, Salt<'a>),
}
pub fn auth_mysql(
auth_data: HashedPassword,
salt: Salt,
username: &str,
save_pwd: &[u8],
) -> Result<()> {
ensure!(
auth_data.len() == 20,
IllegalParamSnafu {
msg: "Illegal mysql password length"
}
);
// 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);
// xor auth_data and tmp
let mut xor_result = [0u8; 20];
for i in 0..20 {
xor_result[i] = auth_data[i] ^ tmp[i];
}
let candidate_stage_2 = sha1_one(&xor_result);
if candidate_stage_2 == hash_stage_2 {
Ok(())
} else {
UserPasswordMismatchSnafu {
username: username.to_string(),
}
.fail()
}
}
fn sha1_two(input_1: &[u8], input_2: &[u8]) -> Vec<u8> {
let mut hasher = Sha1::new();
hasher.update(input_1);
hasher.update(input_2);
hasher.finalize().to_vec()
}
fn sha1_one(data: &[u8]) -> Vec<u8> {
let mut hasher = Sha1::new();
hasher.update(data);
hasher.finalize().to_vec()
}
fn double_sha1(data: &[u8]) -> Vec<u8> {
sha1_one(&sha1_one(data))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sha() {
let sha_1_answer: Vec<u8> = vec![
124, 74, 141, 9, 202, 55, 98, 175, 97, 229, 149, 32, 148, 61, 194, 100, 148, 248, 148,
27,
];
let sha_1 = sha1_one("123456".as_bytes());
assert_eq!(sha_1, sha_1_answer);
let double_sha1_answer: Vec<u8> = vec![
107, 180, 131, 126, 183, 67, 41, 16, 94, 228, 86, 141, 218, 125, 198, 126, 210, 202,
42, 217,
];
let double_sha1 = double_sha1("123456".as_bytes());
assert_eq!(double_sha1, double_sha1_answer);
let sha1_2_answer: Vec<u8> = vec![
132, 115, 215, 211, 99, 186, 164, 206, 168, 152, 217, 192, 117, 47, 240, 252, 142, 244,
37, 204,
];
let sha1_2 = sha1_two("123456".as_bytes(), "654321".as_bytes());
assert_eq!(sha1_2, sha1_2_answer);
}
}

View File

@@ -60,6 +60,9 @@ pub enum Error {
schema: String,
username: String,
},
#[snafu(display("User is not authorized to perform this action"))]
PermissionDenied { location: Location },
}
impl ErrorExt for Error {
@@ -75,6 +78,7 @@ impl ErrorExt for Error {
Error::UnsupportedPasswordType { .. } => StatusCode::UnsupportedPasswordType,
Error::UserPasswordMismatch { .. } => StatusCode::UserPasswordMismatch,
Error::AccessDenied { .. } => StatusCode::AccessDenied,
Error::PermissionDenied { .. } => StatusCode::PermissionDenied,
}
}

View File

@@ -13,7 +13,7 @@
// limitations under the License.
mod common;
mod error;
pub mod error;
mod permission;
mod user_info;
mod user_provider;
@@ -21,8 +21,9 @@ mod user_provider;
#[cfg(feature = "testing")]
pub mod tests;
pub use common::{user_provider_from_option, userinfo_by_name, HashedPassword, Identity, Password};
pub use error::{Error, Result};
pub use common::{
auth_mysql, user_provider_from_option, userinfo_by_name, HashedPassword, Identity, Password,
};
pub use permission::{PermissionChecker, PermissionReq, PermissionResp};
pub use user_info::UserInfo;
pub use user_provider::UserProvider;

View File

@@ -17,7 +17,7 @@ use std::fmt::Debug;
use api::v1::greptime_request::Request;
use sql::statements::statement::Statement;
use crate::error::Result;
use crate::error::{PermissionDeniedSnafu, Result};
use crate::{PermissionCheckerRef, UserInfoRef};
#[derive(Debug, Clone)]
@@ -53,7 +53,11 @@ impl PermissionChecker for Option<&PermissionCheckerRef> {
req: PermissionReq,
) -> Result<PermissionResp> {
match self {
Some(checker) => checker.check_permission(user_info, req),
Some(checker) => match checker.check_permission(user_info, req) {
Ok(PermissionResp::Reject) => PermissionDeniedSnafu.fail(),
Ok(PermissionResp::Allow) => Ok(PermissionResp::Allow),
Err(e) => Err(e),
},
None => Ok(PermissionResp::Allow),
}
}

View File

@@ -18,10 +18,7 @@ use crate::error::{
UserPasswordMismatchSnafu,
};
use crate::user_info::DefaultUserInfo;
use crate::user_provider::static_user_provider::auth_mysql;
#[allow(unused_imports)]
use crate::Error;
use crate::{Identity, Password, UserInfoRef, UserProvider};
use crate::{auth_mysql, Identity, Password, UserInfoRef, UserProvider};
pub struct DatabaseAuthInfo<'a> {
pub catalog: &'a str,
@@ -108,6 +105,8 @@ impl UserProvider for MockUserProvider {
#[tokio::test]
async fn test_auth_by_plain_text() {
use crate::error;
let user_provider = MockUserProvider::default();
assert_eq!("mock_user_provider", user_provider.name());
@@ -131,7 +130,7 @@ async fn test_auth_by_plain_text() {
assert!(auth_result.is_err());
assert!(matches!(
auth_result.err().unwrap(),
Error::UnsupportedPasswordType { .. }
error::Error::UnsupportedPasswordType { .. }
));
// auth failed, err: user not exist.
@@ -144,7 +143,7 @@ async fn test_auth_by_plain_text() {
assert!(auth_result.is_err());
assert!(matches!(
auth_result.err().unwrap(),
Error::UserNotFound { .. }
error::Error::UserNotFound { .. }
));
// auth failed, err: wrong password
@@ -157,7 +156,7 @@ async fn test_auth_by_plain_text() {
assert!(auth_result.is_err());
assert!(matches!(
auth_result.err().unwrap(),
Error::UserPasswordMismatch { .. }
error::Error::UserPasswordMismatch { .. }
))
}

View File

@@ -19,18 +19,15 @@ use std::io::BufRead;
use std::path::Path;
use async_trait::async_trait;
use digest::Digest;
use secrecy::ExposeSecret;
use sha1::Sha1;
use snafu::{ensure, OptionExt, ResultExt};
use crate::common::Salt;
use crate::error::{
Error, IllegalParamSnafu, InvalidConfigSnafu, IoSnafu, Result, UnsupportedPasswordTypeSnafu,
UserNotFoundSnafu, UserPasswordMismatchSnafu,
};
use crate::user_info::DefaultUserInfo;
use crate::{HashedPassword, Identity, Password, UserInfoRef, UserProvider};
use crate::{auth_mysql, Identity, Password, UserInfoRef, UserProvider};
pub(crate) const STATIC_USER_PROVIDER: &str = "static_user_provider";
@@ -136,12 +133,6 @@ impl UserProvider for StaticUserProvider {
};
}
Password::MysqlNativePassword(auth_data, salt) => {
ensure!(
auth_data.len() == 20,
IllegalParamSnafu {
msg: "Illegal MySQL native password format, length != 20"
}
);
auth_mysql(auth_data, salt, username, save_pwd)
.map(|_| DefaultUserInfo::with_name(username))
}
@@ -165,48 +156,6 @@ impl UserProvider for StaticUserProvider {
}
}
pub fn auth_mysql(
auth_data: HashedPassword,
salt: Salt,
username: &str,
save_pwd: &[u8],
) -> 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);
// xor auth_data and tmp
let mut xor_result = [0u8; 20];
for i in 0..20 {
xor_result[i] = auth_data[i] ^ tmp[i];
}
let candidate_stage_2 = sha1_one(&xor_result);
if candidate_stage_2 == hash_stage_2 {
Ok(())
} else {
UserPasswordMismatchSnafu {
username: username.to_string(),
}
.fail()
}
}
fn sha1_two(input_1: &[u8], input_2: &[u8]) -> Vec<u8> {
let mut hasher = Sha1::new();
hasher.update(input_1);
hasher.update(input_2);
hasher.finalize().to_vec()
}
fn sha1_one(data: &[u8]) -> Vec<u8> {
let mut hasher = Sha1::new();
hasher.update(data);
hasher.finalize().to_vec()
}
fn double_sha1(data: &[u8]) -> Vec<u8> {
sha1_one(&sha1_one(data))
}
#[cfg(test)]
pub mod test {
use std::fs::File;
@@ -215,36 +164,10 @@ pub mod test {
use common_test_util::temp_dir::create_temp_dir;
use crate::user_info::DefaultUserInfo;
use crate::user_provider::static_user_provider::{
double_sha1, sha1_one, sha1_two, StaticUserProvider,
};
use crate::user_provider::static_user_provider::StaticUserProvider;
use crate::user_provider::{Identity, Password};
use crate::UserProvider;
#[test]
fn test_sha() {
let sha_1_answer: Vec<u8> = vec![
124, 74, 141, 9, 202, 55, 98, 175, 97, 229, 149, 32, 148, 61, 194, 100, 148, 248, 148,
27,
];
let sha_1 = sha1_one("123456".as_bytes());
assert_eq!(sha_1, sha_1_answer);
let double_sha1_answer: Vec<u8> = vec![
107, 180, 131, 126, 183, 67, 41, 16, 94, 228, 86, 141, 218, 125, 198, 126, 210, 202,
42, 217,
];
let double_sha1 = double_sha1("123456".as_bytes());
assert_eq!(double_sha1, double_sha1_answer);
let sha1_2_answer: Vec<u8> = vec![
132, 115, 215, 211, 99, 186, 164, 206, 168, 152, 217, 192, 117, 47, 240, 252, 142, 244,
37, 204,
];
let sha1_2 = sha1_two("123456".as_bytes(), "654321".as_bytes());
assert_eq!(sha1_2, sha1_2_answer);
}
async fn test_authenticate(provider: &dyn UserProvider, username: &str, password: &str) {
let re = provider
.authenticate(

View File

@@ -17,7 +17,7 @@ use std::assert_matches::assert_matches;
use std::sync::Arc;
use api::v1::greptime_request::Request;
use auth::Error::InternalState;
use auth::error::Error::InternalState;
use auth::{PermissionChecker, PermissionCheckerRef, PermissionReq, PermissionResp, UserInfoRef};
use sql::statements::show::{ShowDatabases, ShowKind};
use sql::statements::statement::Statement;
@@ -29,7 +29,7 @@ impl PermissionChecker for DummyPermissionChecker {
&self,
_user_info: Option<UserInfoRef>,
req: PermissionReq,
) -> auth::Result<PermissionResp> {
) -> auth::error::Result<PermissionResp> {
match req {
PermissionReq::GrpcRequest(_) => Ok(PermissionResp::Allow),
PermissionReq::SqlStatement(_) => Ok(PermissionResp::Reject),