feat: supports permission mode for static user provider (#7017)

* feat: supports permission mode for static user provider

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* chore: style

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* fix: comment

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
dennis zhuang
2025-09-25 11:45:31 +08:00
committed by GitHub
parent 07b9de620e
commit 91a727790d
11 changed files with 614 additions and 33 deletions

View File

@@ -109,7 +109,6 @@ impl GreptimeDbStandaloneBuilder {
}
}
#[cfg(test)]
#[must_use]
pub fn with_plugin(self, plugin: Plugins) -> Self {
Self {

View File

@@ -17,9 +17,10 @@ use std::fmt::Display;
use std::net::SocketAddr;
use std::sync::Arc;
use auth::UserProviderRef;
use auth::{DefaultPermissionChecker, PermissionCheckerRef, UserProviderRef};
use axum::Router;
use catalog::kvbackend::KvBackendCatalogManager;
use common_base::Plugins;
use common_config::Configurable;
use common_meta::key::catalog_name::CatalogNameKey;
use common_meta::key::schema_name::SchemaNameKey;
@@ -373,6 +374,18 @@ async fn setup_standalone_instance(
.await
}
async fn setup_standalone_instance_with_plugins(
test_name: &str,
store_type: StorageType,
plugins: Plugins,
) -> GreptimeDbStandalone {
GreptimeDbStandaloneBuilder::new(test_name)
.with_default_store_type(store_type)
.with_plugin(plugins)
.build()
.await
}
pub async fn setup_test_http_app(store_type: StorageType, name: &str) -> (Router, TestGuard) {
let instance = setup_standalone_instance(name, store_type).await;
@@ -406,7 +419,13 @@ pub async fn setup_test_http_app_with_frontend_and_user_provider(
name: &str,
user_provider: Option<UserProviderRef>,
) -> (Router, TestGuard) {
let instance = setup_standalone_instance(name, store_type).await;
let plugins = Plugins::new();
if let Some(user_provider) = user_provider.clone() {
plugins.insert::<UserProviderRef>(user_provider.clone());
plugins.insert::<PermissionCheckerRef>(DefaultPermissionChecker::arc());
}
let instance = setup_standalone_instance_with_plugins(name, store_type, plugins).await;
create_test_table(instance.fe_instance(), "demo").await;
@@ -587,7 +606,13 @@ pub async fn setup_mysql_server_with_user_provider(
name: &str,
user_provider: Option<UserProviderRef>,
) -> (TestGuard, Arc<Box<dyn Server>>) {
let instance = setup_standalone_instance(name, store_type).await;
let plugins = Plugins::new();
if let Some(user_provider) = user_provider.clone() {
plugins.insert::<UserProviderRef>(user_provider.clone());
plugins.insert::<PermissionCheckerRef>(DefaultPermissionChecker::arc());
}
let instance = setup_standalone_instance_with_plugins(name, store_type, plugins).await;
let runtime = RuntimeBuilder::default()
.worker_threads(2)

View File

@@ -150,7 +150,7 @@ pub async fn test_http_auth(store_type: StorageType) {
common_telemetry::init_default_ut_logging();
let user_provider = user_provider_from_option(
&"static_user_provider:cmd:greptime_user=greptime_pwd".to_string(),
&"static_user_provider:cmd:greptime_user=greptime_pwd,readonly_user:ro=readonly_pwd,writeonly_user:wo=writeonly_pwd".to_string(),
)
.unwrap();
@@ -187,6 +187,65 @@ pub async fn test_http_auth(store_type: StorageType) {
.send()
.await;
assert_eq!(res.status(), StatusCode::OK);
// 4. readonly user cannot write
let res = client
.get("/v1/sql?db=public&sql=show tables;")
.header(
"Authorization",
"basic cmVhZG9ubHlfdXNlcjpyZWFkb25seV9wd2Q=",
)
.send()
.await;
assert_eq!(res.status(), StatusCode::OK);
let res = client
.get("/v1/sql?db=public&sql=create table auth_test(ts timestamp time index);")
.header(
"Authorization",
"basic cmVhZG9ubHlfdXNlcjpyZWFkb25seV9wd2Q=",
)
.send()
.await;
assert_eq!(res.status(), StatusCode::FORBIDDEN);
// 5. writeonly user cannot read
let res = client
.get("/v1/sql?db=public&sql=show tables;")
.header(
"Authorization",
"basic d3JpdGVvbmx5X3VzZXI6d3JpdGVvbmx5X3B3ZA==",
)
.send()
.await;
assert_eq!(res.status(), StatusCode::FORBIDDEN);
let res = client
.get("/v1/sql?db=public&sql=create table auth_test(ts timestamp time index);")
.header(
"Authorization",
"basic d3JpdGVvbmx5X3VzZXI6d3JpdGVvbmx5X3B3ZA==",
)
.send()
.await;
assert_eq!(res.status(), StatusCode::OK);
let res = client
.get("/v1/sql?db=public&sql=insert into auth_test values(1);")
.header(
"Authorization",
"basic d3JpdGVvbmx5X3VzZXI6d3JpdGVvbmx5X3B3ZA==",
)
.send()
.await;
assert_eq!(res.status(), StatusCode::OK);
let res = client
.get("/v1/sql?db=public&sql=select * from auth_test;")
.header(
"Authorization",
"basic d3JpdGVvbmx5X3VzZXI6d3JpdGVvbmx5X3B3ZA==",
)
.send()
.await;
assert_eq!(res.status(), StatusCode::FORBIDDEN);
guard.remove_all().await;
}

View File

@@ -88,7 +88,7 @@ macro_rules! sql_tests {
pub async fn test_mysql_auth(store_type: StorageType) {
let user_provider = user_provider_from_option(
&"static_user_provider:cmd:greptime_user=greptime_pwd".to_string(),
&"static_user_provider:cmd:greptime_user=greptime_pwd,readonly_user:ro=readonly_pwd,writeonly_user:wo=writeonly_pwd".to_string(),
)
.unwrap();
@@ -140,6 +140,46 @@ pub async fn test_mysql_auth(store_type: StorageType) {
assert!(conn_re.is_ok());
// 4. readonly user
let conn_re = MySqlPoolOptions::new()
.max_connections(2)
.connect(&format!("mysql://readonly_user:readonly_pwd@{addr}/public"))
.await;
assert!(conn_re.is_ok());
let pool = conn_re.unwrap();
let _ = pool.execute("SELECT 1").await.unwrap();
let err = pool
.execute("CREATE TABLE test (ts timestamp time index)")
.await
.unwrap_err();
assert!(
err.to_string()
.contains("(PermissionDenied): User is not authorized to perform this action"),
"{}",
err.to_string()
);
// 5. writeonly user
let conn_re = MySqlPoolOptions::new()
.max_connections(2)
.connect(&format!(
"mysql://writeonly_user:writeonly_pwd@{addr}/public"
))
.await;
assert!(conn_re.is_ok());
let pool = conn_re.unwrap();
let _ = pool
.execute("CREATE TABLE test (ts timestamp time index)")
.await
.unwrap();
let err = pool.execute("SHOW TABLES").await.unwrap_err();
assert!(
err.to_string()
.contains("(PermissionDenied): User is not authorized to perform this action"),
"{}",
err.to_string()
);
let _ = fe_mysql_server.shutdown().await;
guard.remove_all().await;
}