feat(db): basic application relations

This commit is contained in:
subcrip 2024-03-30 17:10:29 +08:00
parent 935ea5555d
commit b0fe35f69e
Signed by: subcrip
SSH Key Fingerprint: SHA256:dFPFi68d8C87YkFkEBU4TkcrYRySWpekRR1hbnDWUCw
5 changed files with 108 additions and 3 deletions

25
Cargo.lock generated
View File

@ -1253,11 +1253,13 @@ version = "0.1.0"
dependencies = [ dependencies = [
"async-std", "async-std",
"futures", "futures",
"rand 0.8.5",
"serde", "serde",
"serde_json", "serde_json",
"tide", "tide",
"tokio", "tokio",
"tokio-postgres", "tokio-postgres",
"uuid",
] ]
[[package]] [[package]]
@ -1452,6 +1454,7 @@ dependencies = [
"bytes", "bytes",
"fallible-iterator", "fallible-iterator",
"postgres-protocol", "postgres-protocol",
"uuid",
] ]
[[package]] [[package]]
@ -2200,6 +2203,28 @@ dependencies = [
"serde", "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]] [[package]]
name = "value-bag" name = "value-bag"
version = "1.8.1" version = "1.8.1"

View File

@ -11,5 +11,7 @@ async-std = { version = "1.8.0", features = ["attributes"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
tokio = { version = "1.37.0", features = ["full"] } 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" futures = "0.3.30"
uuid = { verion = "1.0.0", features = ["v4", "fast-rng", "macro-diagnostics"] }
rand = "0.8.5"

View File

@ -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 { pub struct OAuthDatabase {
client: tokio_postgres::Client, client: tokio_postgres::Client,
handle: tokio::task::JoinHandle<Result<(), tokio_postgres::Error>>, handle: tokio::task::JoinHandle<Result<(), tokio_postgres::Error>>,
} }
impl OAuthDatabase { 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<Self, tokio_postgres::Error> { pub async fn connect(host: &str, port: u16, user: &str, password: &str, db_name: &str) -> Result<Self, tokio_postgres::Error> {
let (client, connection) = tokio_postgres::connect(format!("host={} port={} user={} password={} dbname={}", host, port, user, password, db_name).as_str(), tokio_postgres::NoTls).await?; 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?) 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<tokio_postgres::Socket, tokio_postgres::tls::NoTlsStream>) -> Result<Self, tokio_postgres::Error> { pub async fn new(client: tokio_postgres::Client, connection: tokio_postgres::Connection<tokio_postgres::Socket, tokio_postgres::tls::NoTlsStream>) -> Result<Self, tokio_postgres::Error> {
let handle = tokio::spawn(connection); let handle = tokio::spawn(connection);
let obj = Self { client, handle }; let obj = Self { client, handle };
Ok(obj) 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<T: ?Sized + tokio_postgres::ToStatement>(&mut self, query: &T, params: &[&(dyn tokio_postgres::types::ToSql + Sync)]) -> Result<u64, tokio_postgres::Error> { async fn psql_execute<T: ?Sized + tokio_postgres::ToStatement>(&mut self, query: &T, params: &[&(dyn tokio_postgres::types::ToSql + Sync)]) -> Result<u64, tokio_postgres::Error> {
self.client.execute(query, params).await 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<T: ?Sized + tokio_postgres::ToStatement>(&mut self, query: &T, params: &[&(dyn tokio_postgres::types::ToSql + Sync)]) -> Result<Vec<tokio_postgres::Row>, tokio_postgres::Error> { async fn psql_query<T: ?Sized + tokio_postgres::ToStatement>(&mut self, query: &T, params: &[&(dyn tokio_postgres::types::ToSql + Sync)]) -> Result<Vec<tokio_postgres::Row>, tokio_postgres::Error> {
self.client.query(query, params).await self.client.query(query, params).await
} }
/// Disconnect from the current database. The instance will become invalid since then.
pub fn disconnect(&mut self) { pub fn disconnect(&mut self) {
self.handle.abort(); 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<OAuthApplication, tokio_postgres::Error> {
// 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<OAuthScope, tokio_postgres::Error> {
// 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)] #[cfg(test)]
@ -61,7 +130,6 @@ pub mod db_tests {
} }
pub async fn read(&mut self) -> Result<Vec<DBTestEntryType>, tokio_postgres::Error> { pub async fn read(&mut self) -> Result<Vec<DBTestEntryType>, tokio_postgres::Error> {
// TODO:
let v = self.d.psql_query("select * from rust_test", &[]).await?; let v = self.d.psql_query("select * from rust_test", &[]).await?;
let mut res = Vec::new(); let mut res = Vec::new();
for row in v { for row in v {

View File

@ -2,6 +2,7 @@ use tide::Request;
use tide::prelude::*; use tide::prelude::*;
mod db; mod db;
mod misc;
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
struct Test { struct Test {

9
src/misc.rs Normal file
View File

@ -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::<i8>() as u8;
}
res
}