Skip to content

Commit

Permalink
[server] Add admin db user list #858 (#867)
Browse files Browse the repository at this point in the history
add admin db user list
  • Loading branch information
michaelvlach authored Dec 14, 2023
1 parent 0a3f3f7 commit 3002e5c
Show file tree
Hide file tree
Showing 14 changed files with 183 additions and 55 deletions.
40 changes: 37 additions & 3 deletions agdb_server/openapi/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,40 @@
]
}
},
"/api/v1/admin/db/user/list": {
"get": {
"tags": [
"crate::routes::admin::db::user"
],
"operationId": "list",
"parameters": [
{
"name": "db",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "ok"
},
"401": {
"description": "unauthorized"
},
"466": {
"description": "db not found"
}
},
"security": [
{
"Token": []
}
]
}
},
"/api/v1/admin/shutdown": {
"get": {
"tags": [
Expand Down Expand Up @@ -393,7 +427,7 @@
"operationId": "list",
"parameters": [
{
"name": "name",
"name": "db",
"in": "path",
"required": true,
"schema": {
Expand Down Expand Up @@ -635,10 +669,10 @@
"ServerDatabaseName": {
"type": "object",
"required": [
"name"
"db"
],
"properties": {
"name": {
"db": {
"type": "string"
}
}
Expand Down
1 change: 1 addition & 0 deletions agdb_server/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use utoipa::OpenApi;
crate::routes::status,
crate::routes::admin::db::list,
crate::routes::admin::db::remove,
crate::routes::admin::db::user::list,
crate::routes::admin::user::change_password,
crate::routes::admin::user::create,
crate::routes::admin::user::list,
Expand Down
6 changes: 5 additions & 1 deletion agdb_server/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,13 @@ pub(crate) fn app(config: Config, shutdown_sender: Sender<()>, db_pool: DbPool)
shutdown_sender,
};

let admin_db_user_router_v1 =
Router::new().route("/list", routing::get(routes::admin::db::user::list));

let admin_db_router_v1 = Router::new()
.route("/list", routing::get(routes::admin::db::list))
.route("/remove", routing::post(routes::admin::db::remove));
.route("/remove", routing::post(routes::admin::db::remove))
.nest("/user", admin_db_user_router_v1);

let admin_user_router_v1 = Router::new()
.route(
Expand Down
4 changes: 3 additions & 1 deletion agdb_server/src/routes/admin/db.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
pub(crate) mod user;

use crate::db::DbPool;
use crate::routes::db::ServerDatabase;
use crate::routes::db::ServerDatabaseName;
Expand Down Expand Up @@ -42,7 +44,7 @@ pub(crate) async fn remove(
State(db_pool): State<DbPool>,
Json(request): Json<ServerDatabaseName>,
) -> ServerResponse {
let db = db_pool.find_db(&request.name)?;
let db = db_pool.find_db(&request.db)?;
db_pool.remove_db(db)?;

Ok(StatusCode::NO_CONTENT)
Expand Down
40 changes: 40 additions & 0 deletions agdb_server/src/routes/admin/db/user.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use crate::db::DbPool;
use crate::routes::db::user::DbUser;
use crate::routes::db::ServerDatabaseName;
use crate::server_error::ServerResponse;
use crate::user_id::AdminId;
use axum::extract::Query;
use axum::extract::State;
use axum::http::StatusCode;
use axum::Json;

#[utoipa::path(get,
path = "/api/v1/admin/db/user/list",
security(("Token" = [])),
params(
ServerDatabaseName,
),
responses(
(status = 200, description = "ok"),
(status = 401, description = "unauthorized"),
(status = 466, description = "db not found"),
)
)]
pub(crate) async fn list(
_admin: AdminId,
State(db_pool): State<DbPool>,
request: Query<ServerDatabaseName>,
) -> ServerResponse<(StatusCode, Json<Vec<DbUser>>)> {
let db = db_pool.find_db_id(&request.db)?;
let users = db_pool
.db_users(db)?
.into_iter()
.map(|(name, role)| DbUser {
database: request.db.clone(),
user: name,
role: role.into(),
})
.collect();

Ok((StatusCode::OK, Json(users)))
}
6 changes: 3 additions & 3 deletions agdb_server/src/routes/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub(crate) struct ServerDatabase {

#[derive(Deserialize, ToSchema, IntoParams)]
pub(crate) struct ServerDatabaseName {
pub(crate) name: String,
pub(crate) db: String,
}

impl Display for DbType {
Expand Down Expand Up @@ -113,7 +113,7 @@ pub(crate) async fn delete(
State(config): State<Config>,
Json(request): Json<ServerDatabaseName>,
) -> ServerResponse {
let db = db_pool.find_user_db(user.0, &request.name)?;
let db = db_pool.find_user_db(user.0, &request.db)?;

if !db_pool.is_db_admin(user.0, db.db_id.unwrap())? {
return Ok(StatusCode::FORBIDDEN);
Expand Down Expand Up @@ -160,7 +160,7 @@ pub(crate) async fn remove(
State(db_pool): State<DbPool>,
Json(request): Json<ServerDatabaseName>,
) -> ServerResponse {
let db = db_pool.find_user_db(user.0, &request.name)?;
let db = db_pool.find_user_db(user.0, &request.db)?;

if !db_pool.is_db_admin(user.0, db.db_id.unwrap())? {
return Ok(StatusCode::FORBIDDEN);
Expand Down
4 changes: 2 additions & 2 deletions agdb_server/src/routes/db/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,12 @@ pub(crate) async fn list(
State(db_pool): State<DbPool>,
request: Query<ServerDatabaseName>,
) -> ServerResponse<(StatusCode, Json<Vec<DbUser>>)> {
let db = db_pool.find_user_db(user.0, &request.name)?;
let db = db_pool.find_user_db(user.0, &request.db)?;
let users = db_pool
.db_users(db.db_id.unwrap())?
.into_iter()
.map(|(name, role)| DbUser {
database: request.name.clone(),
database: request.db.clone(),
user: name,
role: role.into(),
})
Expand Down
3 changes: 2 additions & 1 deletion agdb_server/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,16 @@ use std::time::Duration;
use tokio::sync::Mutex;

pub const USER_CHANGE_PASSWORD_URI: &str = "/user/change_password";
pub const ADMIN_USER_CREATE_URI: &str = "/admin/user/create";
pub const DB_ADD_URI: &str = "/db/add";
pub const DB_USER_ADD_URI: &str = "/db/user/add";
pub const DB_USER_LIST_URI: &str = "/db/user/list";
pub const DB_USER_REMOVE_URI: &str = "/db/user/remove";
pub const DB_DELETE_URI: &str = "/db/delete";
pub const DB_LIST_URI: &str = "/db/list";
pub const ADMIN_USER_CREATE_URI: &str = "/admin/user/create";
pub const ADMIN_DB_LIST_URI: &str = "/admin/db/list";
pub const ADMIN_DB_REMOVE_URI: &str = "/admin/db/remove";
pub const ADMIN_DB_USER_LIST_URI: &str = "/admin/db/user/list";
pub const ADMIN_USER_LIST_URI: &str = "/admin/user/list";
pub const ADMIN_CHANGE_PASSWORD_URI: &str = "/admin/user/change_password";
pub const DB_REMOVE_URI: &str = "/db/remove";
Expand Down
10 changes: 5 additions & 5 deletions agdb_server/tests/routes/admin_db_remove_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use serde::Serialize;

#[derive(Serialize)]
struct DbName {
name: String,
db: String,
}

#[tokio::test]
Expand All @@ -23,7 +23,7 @@ async fn remove() -> anyhow::Result<()> {
name: db.clone(),
db_type: "mapped".to_string(),
}));
let rem = DbName { name: db.clone() };
let rem = DbName { db: db.clone() };
assert_eq!(
server
.post(ADMIN_DB_REMOVE_URI, &rem, &server.admin_token)
Expand All @@ -43,7 +43,7 @@ async fn db_not_found() -> anyhow::Result<()> {
let server = TestServer::new().await?;
let user = server.init_user().await?;
let db = DbName {
name: format!("{}/some_db", user.name),
db: format!("{}/some_db", user.name),
};
assert_eq!(
server
Expand All @@ -59,7 +59,7 @@ async fn db_not_found() -> anyhow::Result<()> {
async fn user_not_found() -> anyhow::Result<()> {
let server = TestServer::new().await?;
let db = DbName {
name: "missing_user/some_db".to_string(),
db: "missing_user/some_db".to_string(),
};
assert_eq!(
server
Expand All @@ -75,7 +75,7 @@ async fn user_not_found() -> anyhow::Result<()> {
async fn no_admin_token() -> anyhow::Result<()> {
let server = TestServer::new().await?;
let db = DbName {
name: "user/some_db".to_string(),
db: "user/some_db".to_string(),
};
assert_eq!(
server.post(ADMIN_DB_REMOVE_URI, &db, NO_TOKEN).await?.0,
Expand Down
66 changes: 66 additions & 0 deletions agdb_server/tests/routes/admin_db_user_list_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use crate::AddUser;
use crate::TestServer;
use crate::ADMIN_DB_USER_LIST_URI;
use crate::DB_USER_ADD_URI;
use crate::NO_TOKEN;
use serde::Deserialize;

#[derive(Debug, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
struct DbUser {
database: String,
user: String,
role: String,
}

#[tokio::test]
async fn list_users() -> anyhow::Result<()> {
let server = TestServer::new().await?;
let user = server.init_user().await?;
let other = server.init_user().await?;
let db = server.init_db("memory", &user).await?;
let role = AddUser {
database: &db,
user: &other.name,
role: "read",
};
assert_eq!(
server.post(DB_USER_ADD_URI, &role, &user.token).await?.0,
201
);
let (_, list) = server
.get::<Vec<DbUser>>(
&format!("{ADMIN_DB_USER_LIST_URI}?db={}", &db),
&server.admin_token,
)
.await?;
let mut list = list?;
list.sort();
let mut expected = vec![
DbUser {
database: db.clone(),
user: user.name,
role: "admin".to_string(),
},
DbUser {
database: db,
user: other.name,
role: "read".to_string(),
},
];
expected.sort();
assert_eq!(list, expected);
Ok(())
}

#[tokio::test]
async fn no_token() -> anyhow::Result<()> {
let server = TestServer::new().await?;
assert_eq!(
server
.get::<Vec<DbUser>>(&format!("{ADMIN_DB_USER_LIST_URI}?db=some_db"), NO_TOKEN)
.await?
.0,
401
);
Ok(())
}
18 changes: 8 additions & 10 deletions agdb_server/tests/routes/db_delete_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use std::path::Path;

#[derive(Serialize, Deserialize)]
struct DeleteDb {
name: String,
db: String,
}

#[tokio::test]
Expand All @@ -20,7 +20,7 @@ async fn delete() -> anyhow::Result<()> {
let user = server.init_user().await?;
let db = server.init_db("mapped", &user).await?;
assert!(Path::new(&server.data_dir).join(&db).exists());
let del = DeleteDb { name: db.clone() };
let del = DeleteDb { db: db.clone() };
assert_eq!(server.post(DB_DELETE_URI, &del, &user.token).await?.0, 204);
let (_, list) = server.get::<Vec<Db>>(DB_LIST_URI, &user.token).await?;
assert_eq!(list?, vec![]);
Expand All @@ -33,7 +33,7 @@ async fn db_not_found() -> anyhow::Result<()> {
let server = TestServer::new().await?;
let user = server.init_user().await?;
let del = DeleteDb {
name: format!("{}/delete_db_not_found", user.name),
db: format!("{}/delete_db_not_found", user.name),
};
assert_eq!(server.post(DB_DELETE_URI, &del, &user.token).await?.0, 466);
Ok(())
Expand All @@ -45,7 +45,7 @@ async fn other_user() -> anyhow::Result<()> {
let user = server.init_user().await?;
let db = server.init_db("mapped", &user).await?;
let other = server.init_user().await?;
let del = DeleteDb { name: db.clone() };
let del = DeleteDb { db: db.clone() };
assert_eq!(server.post(DB_DELETE_URI, &del, &other.token).await?.0, 466);
let (_, list) = server.get::<Vec<Db>>(DB_LIST_URI, &user.token).await?;
let expected = vec![Db {
Expand All @@ -72,7 +72,7 @@ async fn with_read_role() -> anyhow::Result<()> {
server.post(DB_USER_ADD_URI, &role, &user.token).await?.0,
201
);
let del = DeleteDb { name: db.clone() };
let del = DeleteDb { db: db.clone() };
assert_eq!(
server.post(DB_DELETE_URI, &del, &reader.token).await?.0,
403
Expand Down Expand Up @@ -102,7 +102,7 @@ async fn with_write_role() -> anyhow::Result<()> {
server.post(DB_USER_ADD_URI, &role, &user.token).await?.0,
201
);
let del = DeleteDb { name: db.clone() };
let del = DeleteDb { db: db.clone() };
assert_eq!(
server.post(DB_DELETE_URI, &del, &writer.token).await?.0,
403
Expand Down Expand Up @@ -132,7 +132,7 @@ async fn with_admin_role() -> anyhow::Result<()> {
server.post(DB_USER_ADD_URI, &role, &user.token).await?.0,
201
);
let del = DeleteDb { name: db.clone() };
let del = DeleteDb { db: db.clone() };
assert_eq!(server.post(DB_DELETE_URI, &del, &admin.token).await?.0, 204);
let (_, list) = server.get::<Vec<Db>>(DB_LIST_URI, &user.token).await?;
assert_eq!(list?, vec![]);
Expand All @@ -143,9 +143,7 @@ async fn with_admin_role() -> anyhow::Result<()> {
#[tokio::test]
async fn no_token() -> anyhow::Result<()> {
let server = TestServer::new().await?;
let del = DeleteDb {
name: String::new(),
};
let del = DeleteDb { db: String::new() };
assert_eq!(server.post(DB_DELETE_URI, &del, NO_TOKEN).await?.0, 401);
Ok(())
}
Loading

0 comments on commit 3002e5c

Please sign in to comment.