Skip to content

Commit

Permalink
[server] Add add user to db endpoint #815 (#835)
Browse files Browse the repository at this point in the history
* add test

* implement add user

* expand db test

* fix query
  • Loading branch information
michaelvlach authored Dec 8, 2023
1 parent 2550ce5 commit bcfc857
Show file tree
Hide file tree
Showing 10 changed files with 341 additions and 32 deletions.
67 changes: 67 additions & 0 deletions agdb_server/openapi/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,46 @@
]
}
},
"/api/v1/db/user/add": {
"post": {
"tags": [
"crate::routes::db::user"
],
"operationId": "add",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/AddDatabaseUser"
}
}
},
"required": true
},
"responses": {
"201": {
"description": "User added"
},
"403": {
"description": "Can only be done by a db admin"
},
"461": {
"description": "Database not found"
},
"462": {
"description": "User not found"
},
"463": {
"description": "Cannot add self"
}
},
"security": [
{
"Token": []
}
]
}
},
"/api/v1/status": {
"get": {
"tags": [
Expand Down Expand Up @@ -392,6 +432,33 @@
"file"
]
},
"DbUser": {
"type": "object",
"required": [
"database",
"user",
"role"
],
"properties": {
"database": {
"type": "string"
},
"role": {
"$ref": "#/components/schemas/DbUserRole"
},
"user": {
"type": "string"
}
}
},
"DbUserRole": {
"type": "string",
"enum": [
"admin",
"write",
"read"
]
},
"ServerDatabase": {
"type": "object",
"required": [
Expand Down
9 changes: 6 additions & 3 deletions agdb_server/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,17 @@ use utoipa::OpenApi;
crate::routes::db::delete,
crate::routes::db::list,
crate::routes::db::remove,
crate::routes::db::user::add,
),
components(schemas(
crate::routes::user::ChangePassword,
crate::routes::user::UserCredentials,
crate::routes::admin::user::UserStatus,
crate::routes::db::DbType,
crate::routes::db::ServerDatabase,
crate::routes::db::ServerDatabaseName,
crate::routes::admin::user::UserStatus,
crate::routes::db::user::DbUser,
crate::routes::db::user::DbUserRole,
crate::routes::user::ChangePassword,
crate::routes::user::UserCredentials,
)),
modifiers(&BearerToken),
)]
Expand Down
5 changes: 4 additions & 1 deletion agdb_server/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,14 @@ pub(crate) fn app(config: Config, shutdown_sender: Sender<()>, db_pool: DbPool)
)
.route("/login", routing::post(routes::user::login));

let db_user_router_v1 = Router::new().route("/add", routing::post(routes::db::user::add));

let db_router_v1 = Router::new()
.route("/add", routing::post(routes::db::add))
.route("/delete", routing::post(routes::db::delete))
.route("/list", routing::get(routes::db::list))
.route("/remove", routing::post(routes::db::remove));
.route("/remove", routing::post(routes::db::remove))
.nest("/user", db_user_router_v1);

Router::new()
.merge(RapiDoc::with_openapi("/api/v1/openapi.json", Api::openapi()).path("/api/v1"))
Expand Down
106 changes: 81 additions & 25 deletions agdb_server/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use crate::config::Config;
use crate::db::server_db::ServerDb;
use crate::password::Password;
use crate::server_error::ServerResult;
use agdb::Comparison;
use agdb::CountComparison;
use agdb::DbId;
use agdb::QueryBuilder;
use agdb::QueryId;
Expand Down Expand Up @@ -100,13 +102,25 @@ impl DbPool {
.edges()
.from(vec![QueryId::from(user), "dbs".into()])
.to(db)
.values(vec![vec![("owner", 1).into()], vec![]])
.values(vec![vec![("role", "admin").into()], vec![]])
.query(),
)
})?;
Ok(())
}

pub(crate) fn add_database_user(&self, database: DbId, user: DbId, role: &str) -> ServerResult {
self.0.server_db.get_mut()?.exec_mut(
&QueryBuilder::insert()
.edges()
.from(user)
.to(database)
.values_uniform(vec![("role", role).into()])
.query(),
)?;
Ok(())
}

pub(crate) fn create_user(&self, user: DbUser) -> ServerResult {
self.0.server_db.get_mut()?.transaction_mut(|t| {
let user = t.exec_mut(&QueryBuilder::insert().nodes().values(&user).query())?;
Expand Down Expand Up @@ -149,35 +163,34 @@ impl DbPool {
QueryBuilder::search()
.from("dbs")
.where_()
.distance(agdb::CountComparison::Equal(2))
.distance(CountComparison::Equal(2))
.query(),
)
.query(),
)?
.try_into()?)
}

pub(crate) fn find_database(&self, name: &str) -> ServerResult<Database> {
pub(crate) fn find_database_id(&self, name: &str) -> ServerResult<DbId> {
Ok(self
.0
.server_db
.get()?
.exec(
&QueryBuilder::select()
.ids(
QueryBuilder::search()
.from("dbs")
.limit(1)
.where_()
.distance(agdb::CountComparison::Equal(2))
.and()
.key("name")
.value(agdb::Comparison::Equal(name.into()))
.query(),
)
&QueryBuilder::search()
.from("dbs")
.limit(1)
.where_()
.distance(CountComparison::Equal(2))
.and()
.key("name")
.value(Comparison::Equal(name.into()))
.query(),
)?
.try_into()?)
.elements
.get(0)
.ok_or(format!("Database '{name}' not found"))?
.id)
}

pub(crate) fn find_users(&self) -> ServerResult<Vec<String>> {
Expand All @@ -192,7 +205,7 @@ impl DbPool {
QueryBuilder::search()
.from("users")
.where_()
.distance(agdb::CountComparison::Equal(2))
.distance(CountComparison::Equal(2))
.and()
.keys(vec!["name".into()])
.query(),
Expand All @@ -216,7 +229,7 @@ impl DbPool {
QueryBuilder::search()
.from(user)
.where_()
.distance(agdb::CountComparison::Equal(2))
.distance(CountComparison::Equal(2))
.query(),
)
.query(),
Expand All @@ -235,10 +248,10 @@ impl DbPool {
QueryBuilder::search()
.from(user)
.where_()
.distance(agdb::CountComparison::Equal(2))
.distance(CountComparison::Equal(2))
.and()
.key("name")
.value(agdb::Comparison::Equal(name.into()))
.value(Comparison::Equal(name.into()))
.query(),
)
.query(),
Expand All @@ -258,18 +271,40 @@ impl DbPool {
.from("users")
.limit(1)
.where_()
.distance(agdb::CountComparison::Equal(2))
.distance(CountComparison::Equal(2))
.and()
.key("name")
.value(agdb::Comparison::Equal(name.into()))
.value(Comparison::Equal(name.into()))
.query(),
)
.query(),
)?
.try_into()?)
}

pub(crate) fn find_user_id(&self, token: &str) -> ServerResult<DbId> {
pub(crate) fn find_user_id(&self, name: &str) -> ServerResult<DbId> {
Ok(self
.0
.server_db
.get()?
.exec(
&QueryBuilder::search()
.from("users")
.limit(1)
.where_()
.distance(CountComparison::Equal(2))
.and()
.key("name")
.value(Comparison::Equal(name.into()))
.query(),
)?
.elements
.get(0)
.ok_or(format!("User '{name}' not found"))?
.id)
}

pub(crate) fn find_user_id_by_token(&self, token: &str) -> ServerResult<DbId> {
Ok(self
.0
.server_db
Expand All @@ -279,10 +314,10 @@ impl DbPool {
.from("users")
.limit(1)
.where_()
.distance(agdb::CountComparison::Equal(2))
.distance(CountComparison::Equal(2))
.and()
.key("token")
.value(agdb::Comparison::Equal(token.into()))
.value(Comparison::Equal(token.into()))
.query(),
)?
.elements
Expand All @@ -291,6 +326,27 @@ impl DbPool {
.id)
}

pub(crate) fn is_db_admin(&self, user: DbId, db: DbId) -> ServerResult<bool> {
Ok(self
.0
.server_db
.get()?
.exec(
&QueryBuilder::search()
.from(user)
.to(db)
.limit(1)
.where_()
.distance(CountComparison::LessThanOrEqual(2))
.and()
.key("role")
.value(Comparison::Equal("admin".into()))
.query(),
)?
.result
== 1)
}

pub(crate) fn remove_database(&self, db: Database) -> ServerResult<ServerDb> {
self.0
.server_db
Expand Down
2 changes: 1 addition & 1 deletion agdb_server/src/routes/admin/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ pub(crate) async fn create(
return Ok(StatusCode::from_u16(462_u16)?);
}

if db_pool.find_user(&request.name).is_ok() {
if db_pool.find_user_id(&request.name).is_ok() {
return Ok(StatusCode::from_u16(463_u16)?);
}

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

use crate::db::Database;
use crate::db::DbPool;
use crate::server_error::ServerError;
Expand Down Expand Up @@ -67,7 +69,7 @@ pub(crate) async fn add(
State(db_pool): State<DbPool>,
Json(request): Json<ServerDatabase>,
) -> Result<StatusCode, ServerError> {
if db_pool.find_database(&request.name).is_ok() {
if db_pool.find_database_id(&request.name).is_ok() {
return Ok(StatusCode::FORBIDDEN);
}

Expand Down
Loading

0 comments on commit bcfc857

Please sign in to comment.