From b0fe35f69ef1e21c1950dfa459fd210390631cce Mon Sep 17 00:00:00 2001 From: subcrip Date: Sat, 30 Mar 2024 17:10:29 +0800 Subject: [PATCH] feat(db): basic application relations --- Cargo.lock | 25 +++++++++++++++++++ Cargo.toml | 4 ++- src/db.rs | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++-- src/main.rs | 1 + src/misc.rs | 9 +++++++ 5 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 src/misc.rs diff --git a/Cargo.lock b/Cargo.lock index 4cac40f..4d2b2f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1253,11 +1253,13 @@ version = "0.1.0" dependencies = [ "async-std", "futures", + "rand 0.8.5", "serde", "serde_json", "tide", "tokio", "tokio-postgres", + "uuid", ] [[package]] @@ -1452,6 +1454,7 @@ dependencies = [ "bytes", "fallible-iterator", "postgres-protocol", + "uuid", ] [[package]] @@ -2200,6 +2203,28 @@ dependencies = [ "serde", ] +[[package]] +name = "uuid" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +dependencies = [ + "getrandom 0.2.12", + "rand 0.8.5", + "uuid-macro-internal", +] + +[[package]] +name = "uuid-macro-internal" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9881bea7cbe687e36c9ab3b778c36cd0487402e270304e8b1296d5085303c1a2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.55", +] + [[package]] name = "value-bag" version = "1.8.1" diff --git a/Cargo.toml b/Cargo.toml index 3511510..bc7a68b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,5 +11,7 @@ async-std = { version = "1.8.0", features = ["attributes"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tokio = { version = "1.37.0", features = ["full"] } -tokio-postgres = "0.7.10" +tokio-postgres = { version = "0.7.10", features = ["with-uuid-1"] } futures = "0.3.30" +uuid = { verion = "1.0.0", features = ["v4", "fast-rng", "macro-diagnostics"] } +rand = "0.8.5" diff --git a/src/db.rs b/src/db.rs index 1afd276..b250802 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,33 +1,102 @@ -use std::time::Duration; +use uuid::Uuid; +pub struct OAuthApplication { + pub client_id: Uuid, + pub client_secret: crate::misc::U256, +} + +impl OAuthApplication { + // TEST: functionality + pub fn new() -> Self { + Self { client_id: Uuid::new_v4(), client_secret: crate::misc::random_256() } + } + + pub fn from(client_id: Uuid, client_secret: crate::misc::U256) -> Self { + Self { client_id, client_secret } + } +} + +pub struct OAuthScope { + pub scope_id: Uuid, + pub scope_desc: String, +} + +impl OAuthScope { + // TEST: functionality + pub fn new(description: String) -> Self { + Self { scope_id: Uuid::new_v4(), scope_desc: description } + } + + pub fn from(scope_id: Uuid, scope_desc: String) -> Self { + Self { scope_id, scope_desc } + } +} + +/// Wrapper struct for Postgres database with APIs related to OAuth database operations. pub struct OAuthDatabase { client: tokio_postgres::Client, handle: tokio::task::JoinHandle>, } impl OAuthDatabase { + /// Establish a new connection to a database. pub async fn connect(host: &str, port: u16, user: &str, password: &str, db_name: &str) -> Result { let (client, connection) = tokio_postgres::connect(format!("host={} port={} user={} password={} dbname={}", host, port, user, password, db_name).as_str(), tokio_postgres::NoTls).await?; Ok(Self::new(client, connection).await?) } + /// Attach a connection to a new `OAuthDatabase` instance. pub async fn new(client: tokio_postgres::Client, connection: tokio_postgres::Connection) -> Result { let handle = tokio::spawn(connection); let obj = Self { client, handle }; Ok(obj) } + /// Perform arbitrary SQL operations on the database. This method shouldn't be called directly + /// by other modules. This method has the same signature with + /// `tokio_postgres::Client::execute`. async fn psql_execute(&mut self, query: &T, params: &[&(dyn tokio_postgres::types::ToSql + Sync)]) -> Result { self.client.execute(query, params).await } + /// Perform arbitrary SQL operations on the database (with return value). This method shouldn't be called directly + /// by other modules. This method has the same signature with `tokio_postgres::Client::query`. async fn psql_query(&mut self, query: &T, params: &[&(dyn tokio_postgres::types::ToSql + Sync)]) -> Result, tokio_postgres::Error> { self.client.query(query, params).await } + /// Disconnect from the current database. The instance will become invalid since then. pub fn disconnect(&mut self) { self.handle.abort(); } + + /// Initialize the database. + pub async fn init(&mut self) -> Result<(), tokio_postgres::Error> { + // TEST: functionality + self.psql_execute("create table Applications(client_id uuid primary key, client_secret bytea)", &[]).await?; + self.psql_execute("create table Scopes(scope_id uuid primary key, scope_desc text)", &[]).await?; + // TODO: OAuthApplicationAccess struct + self.psql_execute("create table ApplicationAccess(access_id uuid primary key, client_id uuid, scope_id uuid)", &[]).await?; + Ok(()) + } + + /// Register an application. + /// Applicant: client + pub async fn trusted_register_application(&mut self) -> Result { + // TEST: functionality + let app = OAuthApplication::new(); + self.psql_execute("insert into Applications (client_id, client_secret) values ($1, $2)", &[&app.client_id, &&app.client_secret[..]]).await?; + Ok(app) + } + + /// Register a scope. + /// Applicant: Resource + pub async fn trusted_register_scope(&mut self, description: String) -> Result { + // TEST: functionality + let scope = OAuthScope::new(description); + self.psql_execute("insert into Scopes (scope_id, scope_desc) values ($1, $2)", &[&scope.scope_id, &scope.scope_desc]).await?; + Ok(scope) + } } #[cfg(test)] @@ -61,7 +130,6 @@ pub mod db_tests { } pub async fn read(&mut self) -> Result, tokio_postgres::Error> { - // TODO: let v = self.d.psql_query("select * from rust_test", &[]).await?; let mut res = Vec::new(); for row in v { diff --git a/src/main.rs b/src/main.rs index 88a89bd..8d5d7b3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ use tide::Request; use tide::prelude::*; mod db; +mod misc; #[derive(Serialize, Deserialize)] struct Test { diff --git a/src/misc.rs b/src/misc.rs new file mode 100644 index 0000000..31c474b --- /dev/null +++ b/src/misc.rs @@ -0,0 +1,9 @@ +pub type U256 = [u8; 8]; + +pub fn random_256() -> U256 { + let mut res = [0; 8]; + for x in &mut res { + *x = rand::random::() as u8; + } + res +}