This commit is contained in:
parent
341c516fcb
commit
3b8c529183
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -612,6 +612,7 @@ dependencies = [
|
|||||||
"tower-http",
|
"tower-http",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1445,6 +1446,15 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8cfcd319456c4d6ea10087ed423473267e1a071f3bc0aa89f80d60997843c6f0"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "valuable"
|
name = "valuable"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -17,3 +17,4 @@ anyhow = "1.0"
|
|||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
argon2 = { version = "0.4", features = ["std"] }
|
argon2 = { version = "0.4", features = ["std"] }
|
||||||
rand_core = { version = "0.6", features = ["std"] }
|
rand_core = { version = "0.6", features = ["std"] }
|
||||||
|
uuid = { version = "1.0", features = ["v4"] }
|
@ -1,6 +1,6 @@
|
|||||||
-- Add migration script here
|
-- Add migration script here
|
||||||
CREATE TABLE users(
|
CREATE TABLE users(
|
||||||
id INTEGER PRIMARY KEY NOT NULL,
|
uuid TEXT PRIMARY KEY NOT NULL,
|
||||||
user_id CHAR(255) NOT NULL,
|
user_id CHAR(255) NOT NULL,
|
||||||
display_name TEXT NOT NULL,
|
display_name TEXT NOT NULL,
|
||||||
password TEXT NOT NULL
|
password TEXT NOT NULL
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
-- Add migration script here
|
-- Add migration script here
|
||||||
|
|
||||||
CREATE TABLE devices(
|
CREATE TABLE devices(
|
||||||
id INTEGER PRIMARY KEY NOT NULL,
|
uuid TEXT PRIMARY KEY NOT NULL,
|
||||||
user_id INT NOT NULL,
|
user_uuid INT NOT NULL,
|
||||||
device_id TEXT NOT NULL,
|
device_id TEXT NOT NULL,
|
||||||
display_name TEXT NOT NULL,
|
display_name TEXT NOT NULL,
|
||||||
FOREIGN KEY(user_id) REFERENCES users(id)
|
FOREIGN KEY(user_uuid) REFERENCES users(uuid)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX device_id_index ON devices (device_id);
|
CREATE INDEX device_id_index ON devices (device_id);
|
@ -1,10 +1,10 @@
|
|||||||
-- Add migration script here
|
-- Add migration script here
|
||||||
|
|
||||||
CREATE TABLE sessions(
|
CREATE TABLE sessions(
|
||||||
id INTEGER PRIMARY KEY NOT NULL,
|
uuid TEXT PRIMARY KEY NOT NULL,
|
||||||
device_id INT NOT NULL,
|
device_uuid INT NOT NULL,
|
||||||
value TEXT NOT NULL,
|
key TEXT NOT NULL,
|
||||||
FOREIGN KEY(device_id) REFERENCES devices(id)
|
FOREIGN KEY(device_uuid) REFERENCES devices(uuid)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX value_index ON sessions (value);
|
CREATE INDEX key_index ON sessions (key);
|
180
sqlx-data.json
180
sqlx-data.json
@ -1,15 +1,81 @@
|
|||||||
{
|
{
|
||||||
"db": "SQLite",
|
"db": "SQLite",
|
||||||
"22e18063d81e86afceca0e0c74c9070a8b21a406cba7cf7c01a966a869a9dad8": {
|
"112b5723b62084ee14191a2f8773d2493814d70bfc4d4ce046655958d2ae472b": {
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [
|
"columns": [
|
||||||
{
|
{
|
||||||
"name": "id",
|
"name": "uuid: Uuid",
|
||||||
"ordinal": 0,
|
"ordinal": 0,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "user_uuid: Uuid",
|
||||||
|
"ordinal": 1,
|
||||||
"type_info": "Int64"
|
"type_info": "Int64"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "user_id",
|
"name": "device_id",
|
||||||
|
"ordinal": 2,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "display_name",
|
||||||
|
"ordinal": 3,
|
||||||
|
"type_info": "Text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"query": "insert into devices(uuid, user_uuid, device_id, display_name)\n values(?, ?, ?, ?)\n returning uuid as 'uuid: Uuid', user_uuid as 'user_uuid: Uuid', device_id, display_name"
|
||||||
|
},
|
||||||
|
"221d0935dff8911fe58ac047d39e11b0472d2180d7c297291a5dc440e00efb80": {
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"name": "uuid: Uuid",
|
||||||
|
"ordinal": 0,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "device_uuid: Uuid",
|
||||||
|
"ordinal": 1,
|
||||||
|
"type_info": "Int64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"ordinal": 2,
|
||||||
|
"type_info": "Text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"query": "insert into sessions(uuid, device_uuid, key)\n values(?, ?, ?)\n returning uuid as 'uuid: Uuid', device_uuid as 'device_uuid: Uuid', key"
|
||||||
|
},
|
||||||
|
"3fead3dac0e110757bc30be40bb0c6c2bc02127b6d9b6145bfc40fa5fe22ad06": {
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"name": "uuid: Uuid",
|
||||||
|
"ordinal": 0,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "user_id: UserId",
|
||||||
"ordinal": 1,
|
"ordinal": 1,
|
||||||
"type_info": "Text"
|
"type_info": "Text"
|
||||||
},
|
},
|
||||||
@ -31,10 +97,10 @@
|
|||||||
false
|
false
|
||||||
],
|
],
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"Right": 1
|
"Right": 4
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"query": "select id, user_id, display_name, password from users where user_id = ?"
|
"query": "insert into users(uuid, user_id, display_name, password)\n values (?, ?, ?, ?)\n returning uuid as 'uuid: Uuid', user_id as 'user_id: UserId', display_name, password"
|
||||||
},
|
},
|
||||||
"58d27b1d424297504f1da2e3b9b4020121251c1155fbf5dc870dafbef97659f3": {
|
"58d27b1d424297504f1da2e3b9b4020121251c1155fbf5dc870dafbef97659f3": {
|
||||||
"describe": {
|
"describe": {
|
||||||
@ -54,88 +120,16 @@
|
|||||||
},
|
},
|
||||||
"query": "select user_id from users where user_id = ?"
|
"query": "select user_id from users where user_id = ?"
|
||||||
},
|
},
|
||||||
"87395ffa7fe0382080056bdf67805044fbe89770ef366b4553d9797059034e44": {
|
"778b7f0a1c66f00812f0232a5904b7b6b295720ebb75d1c2720afeeda4f66936": {
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [
|
"columns": [
|
||||||
{
|
{
|
||||||
"name": "id",
|
"name": "uuid: Uuid",
|
||||||
"ordinal": 0,
|
"ordinal": 0,
|
||||||
"type_info": "Int64"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "user_id",
|
|
||||||
"ordinal": 1,
|
|
||||||
"type_info": "Int64"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "device_id",
|
|
||||||
"ordinal": 2,
|
|
||||||
"type_info": "Text"
|
"type_info": "Text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "display_name",
|
"name": "user_id: UserId",
|
||||||
"ordinal": 3,
|
|
||||||
"type_info": "Text"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"nullable": [
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false
|
|
||||||
],
|
|
||||||
"parameters": {
|
|
||||||
"Right": 3
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "insert into devices(user_id, device_id, display_name) values(?, ?, ?) returning id, user_id, device_id, display_name"
|
|
||||||
},
|
|
||||||
"9a092e024bfe2854631e0572880f761125a2261a973b46d721db2e1401ce9aec": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"name": "id",
|
|
||||||
"ordinal": 0,
|
|
||||||
"type_info": "Int64"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "user_id",
|
|
||||||
"ordinal": 1,
|
|
||||||
"type_info": "Int64"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "device_id",
|
|
||||||
"ordinal": 2,
|
|
||||||
"type_info": "Text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "display_name",
|
|
||||||
"ordinal": 3,
|
|
||||||
"type_info": "Text"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"nullable": [
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false
|
|
||||||
],
|
|
||||||
"parameters": {
|
|
||||||
"Right": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "select id, user_id, device_id, display_name from devices where user_id = ?"
|
|
||||||
},
|
|
||||||
"bb4e2386cf2987aa86f2c4dcac1159687b7220c770542bf52485e7e16f9ce987": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"name": "id",
|
|
||||||
"ordinal": 0,
|
|
||||||
"type_info": "Int64"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "user_id",
|
|
||||||
"ordinal": 1,
|
"ordinal": 1,
|
||||||
"type_info": "Text"
|
"type_info": "Text"
|
||||||
},
|
},
|
||||||
@ -157,39 +151,45 @@
|
|||||||
false
|
false
|
||||||
],
|
],
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"Right": 3
|
"Right": 5
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"query": "insert into users(user_id, display_name, password) values (?, ?, ?) returning id, user_id, display_name, password"
|
"query": "update users set uuid = ?, user_id = ?, display_name = ?, password = ?\n where uuid = ?\n returning uuid as 'uuid: Uuid', user_id as 'user_id: UserId', display_name, password"
|
||||||
},
|
},
|
||||||
"cd7c9d3ae02f9553d4b7c4e8d98f63c6f556ac230a339683fe25025592812033": {
|
"f57c49e5390c81f971851ff9ab35242a472b9efbb1ffa658de9b102188769750": {
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [
|
"columns": [
|
||||||
{
|
{
|
||||||
"name": "id",
|
"name": "uuid: Uuid",
|
||||||
"ordinal": 0,
|
"ordinal": 0,
|
||||||
"type_info": "Int64"
|
"type_info": "Text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "device_id",
|
"name": "user_id: UserId",
|
||||||
"ordinal": 1,
|
"ordinal": 1,
|
||||||
"type_info": "Int64"
|
"type_info": "Text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "value",
|
"name": "display_name",
|
||||||
"ordinal": 2,
|
"ordinal": 2,
|
||||||
"type_info": "Text"
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "password",
|
||||||
|
"ordinal": 3,
|
||||||
|
"type_info": "Text"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"nullable": [
|
"nullable": [
|
||||||
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false
|
false
|
||||||
],
|
],
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"Right": 2
|
"Right": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"query": "insert into sessions(device_id, value) values(?, ?) returning id, device_id, value"
|
"query": "select uuid as 'uuid: Uuid', user_id as 'user_id: UserId', display_name, password\n from users where user_id = ?"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,7 +10,14 @@ use axum::{
|
|||||||
use rand_core::OsRng;
|
use rand_core::OsRng;
|
||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
|
|
||||||
use crate::{responses::{registration::RegistrationResponse, authentication::{AuthenticationResponse, AuthenticationSuccess}}, api::client_server::errors::authentication_error::AuthenticationError};
|
use crate::{
|
||||||
|
api::client_server::errors::authentication_error::AuthenticationError,
|
||||||
|
models::sessions::Session,
|
||||||
|
responses::{
|
||||||
|
authentication::{AuthenticationResponse, AuthenticationSuccess},
|
||||||
|
registration::RegistrationResponse,
|
||||||
|
},
|
||||||
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
models::devices::Device,
|
models::devices::Device,
|
||||||
responses::{flow::Flows, registration::RegistrationSuccess},
|
responses::{flow::Flows, registration::RegistrationSuccess},
|
||||||
@ -41,10 +48,11 @@ async fn get_login() -> Result<Json<Flows>, ApiError> {
|
|||||||
async fn post_login(
|
async fn post_login(
|
||||||
Extension(config): Extension<Arc<Config>>,
|
Extension(config): Extension<Arc<Config>>,
|
||||||
Extension(db): Extension<SqlitePool>,
|
Extension(db): Extension<SqlitePool>,
|
||||||
Json(body): Json<AuthenticationData>
|
Json(body): Json<AuthenticationData>,
|
||||||
) -> Result<Json<AuthenticationResponse>, ApiError> {
|
) -> Result<Json<AuthenticationResponse>, ApiError> {
|
||||||
let user = UserId::new("name", "server_name").ok().ok_or(AuthenticationError::InvalidUserId)?;
|
let user = UserId::new("name", "server_name")
|
||||||
todo!("Flesh this out more");
|
.ok()
|
||||||
|
.ok_or(AuthenticationError::InvalidUserId)?;
|
||||||
let resp = AuthenticationSuccess::new("", "", &user);
|
let resp = AuthenticationSuccess::new("", "", &user);
|
||||||
|
|
||||||
Ok(Json(AuthenticationResponse::Success(resp)))
|
Ok(Json(AuthenticationResponse::Success(resp)))
|
||||||
@ -92,22 +100,29 @@ async fn post_register(
|
|||||||
None => "Random displayname",
|
None => "Random displayname",
|
||||||
};
|
};
|
||||||
|
|
||||||
let user =
|
let user = User::new(&user_id, &user_id.to_string(), auth_data.password())?
|
||||||
User::create(&db, &user_id, &user_id.to_string(), auth_data.password()).await?;
|
.create(&db)
|
||||||
let device = Device::create(&db, &user, "test", display_name).await?;
|
.await?;
|
||||||
|
|
||||||
|
let device = Device::new(&user, "test", display_name)?
|
||||||
|
.create(&db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
(user, device)
|
(user, device)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if body.inhibit_login().unwrap_or(false) {
|
if body.inhibit_login().unwrap_or(false) {
|
||||||
let resp = RegistrationSuccess::new(None, device.device_id(), user.user_id());
|
let resp = RegistrationSuccess::new(None, device.device_id(), &user.user_id().to_string());
|
||||||
|
|
||||||
Ok(Json(RegistrationResponse::Success(resp)))
|
Ok(Json(RegistrationResponse::Success(resp)))
|
||||||
} else {
|
} else {
|
||||||
let session = device.create_session(&db).await?;
|
let session = Session::new(&device)?.create(&db).await?;
|
||||||
let resp =
|
let resp = RegistrationSuccess::new(
|
||||||
RegistrationSuccess::new(Some(session.value()), device.device_id(), user.user_id());
|
Some(session.key()),
|
||||||
|
device.device_id(),
|
||||||
|
&user.user_id().to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
Ok(Json(RegistrationResponse::Success(resp)))
|
Ok(Json(RegistrationResponse::Success(resp)))
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,8 @@ impl From<anyhow::Error> for ApiError {
|
|||||||
fn from(err: anyhow::Error) -> Self {
|
fn from(err: anyhow::Error) -> Self {
|
||||||
map_err!(err,
|
map_err!(err,
|
||||||
sqlx::Error => ApiError::DBError,
|
sqlx::Error => ApiError::DBError,
|
||||||
RegistrationError => ApiError::RegistrationError
|
RegistrationError => ApiError::RegistrationError,
|
||||||
|
AuthenticationError => ApiError::AuthenticationError
|
||||||
);
|
);
|
||||||
|
|
||||||
ApiError::Generic(err)
|
ApiError::Generic(err)
|
||||||
|
@ -30,12 +30,22 @@ impl IntoResponse for AuthenticationError {
|
|||||||
.into_response(),
|
.into_response(),
|
||||||
Self::Forbidden => (
|
Self::Forbidden => (
|
||||||
StatusCode::FORBIDDEN,
|
StatusCode::FORBIDDEN,
|
||||||
Json(ErrorResponse::new(ErrorCode::Forbidden, &self.to_string(), None)),
|
Json(ErrorResponse::new(
|
||||||
).into_response(),
|
ErrorCode::Forbidden,
|
||||||
|
&self.to_string(),
|
||||||
|
None,
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.into_response(),
|
||||||
Self::UserDeactivated => (
|
Self::UserDeactivated => (
|
||||||
StatusCode::FORBIDDEN,
|
StatusCode::FORBIDDEN,
|
||||||
Json(ErrorResponse::new(ErrorCode::UserDeactivated, &self.to_string(), None)),
|
Json(ErrorResponse::new(
|
||||||
).into_response(),
|
ErrorCode::UserDeactivated,
|
||||||
|
&self.to_string(),
|
||||||
|
None,
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.into_response(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,50 +1,50 @@
|
|||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
|
|
||||||
|
use crate::types::uuid::Uuid;
|
||||||
|
|
||||||
use super::{sessions::Session, users::User};
|
use super::{sessions::Session, users::User};
|
||||||
|
|
||||||
pub struct Device {
|
pub struct Device {
|
||||||
id: i64,
|
uuid: Uuid,
|
||||||
user_id: i64,
|
user_uuid: Uuid,
|
||||||
device_id: String,
|
device_id: String,
|
||||||
display_name: String,
|
display_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Device {
|
impl Device {
|
||||||
pub async fn create(
|
pub fn new(user: &User, device_id: &str, display_name: &str) -> anyhow::Result<Self> {
|
||||||
conn: &SqlitePool,
|
Ok(Self {
|
||||||
user: &User,
|
uuid: uuid::Uuid::new_v4().into(),
|
||||||
device_id: &str,
|
user_uuid: user.uuid().clone(),
|
||||||
display_name: &str,
|
device_id: device_id.to_owned(),
|
||||||
) -> anyhow::Result<Self> {
|
display_name: display_name.to_owned(),
|
||||||
let user_id = user.id();
|
})
|
||||||
Ok(sqlx::query_as!(Self, "insert into devices(user_id, device_id, display_name) values(?, ?, ?) returning id, user_id, device_id, display_name", user_id, device_id, display_name).fetch_one(conn).await?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn by_user(conn: &SqlitePool, user: &User) -> anyhow::Result<Self> {
|
pub async fn create(&self, conn: &SqlitePool) -> anyhow::Result<Self> {
|
||||||
let user_id = user.id();
|
|
||||||
Ok(sqlx::query_as!(
|
Ok(sqlx::query_as!(
|
||||||
Self,
|
Self,
|
||||||
"select id, user_id, device_id, display_name from devices where user_id = ?",
|
"insert into devices(uuid, user_uuid, device_id, display_name)
|
||||||
user_id
|
values(?, ?, ?, ?)
|
||||||
|
returning uuid as 'uuid: Uuid', user_uuid as 'user_uuid: Uuid', device_id, display_name",
|
||||||
|
self.uuid,
|
||||||
|
self.user_uuid,
|
||||||
|
self.device_id,
|
||||||
|
self.display_name)
|
||||||
|
.fetch_one(conn).await?
|
||||||
)
|
)
|
||||||
.fetch_one(conn)
|
|
||||||
.await?)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn create_session(&self, conn: &SqlitePool) -> anyhow::Result<Session> {
|
|
||||||
Ok(Session::create(conn, self, "random_session_id").await?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the device's id.
|
/// Get the device's id.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn id(&self) -> i64 {
|
pub fn uuid(&self) -> &Uuid {
|
||||||
self.id
|
&self.uuid
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the device's user id.
|
/// Get the device's user id.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn user_id(&self) -> i64 {
|
pub fn user_uuid(&self) -> &Uuid {
|
||||||
self.user_id
|
&self.user_uuid
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a reference to the device's device id.
|
/// Get a reference to the device's device id.
|
||||||
|
@ -1,21 +1,33 @@
|
|||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
|
|
||||||
|
use crate::types::uuid::Uuid;
|
||||||
|
|
||||||
use super::devices::Device;
|
use super::devices::Device;
|
||||||
|
|
||||||
pub struct Session {
|
pub struct Session {
|
||||||
id: i64,
|
uuid: Uuid,
|
||||||
device_id: i64,
|
device_uuid: Uuid,
|
||||||
value: String,
|
key: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Session {
|
impl Session {
|
||||||
pub async fn create(conn: &SqlitePool, device: &Device, value: &str) -> anyhow::Result<Self> {
|
pub fn new(device: &Device) -> anyhow::Result<Self> {
|
||||||
let device_id = device.id();
|
Ok(Self {
|
||||||
|
uuid: uuid::Uuid::new_v4().into(),
|
||||||
|
device_uuid: device.uuid().clone(),
|
||||||
|
key: String::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create(&self, conn: &SqlitePool) -> anyhow::Result<Self> {
|
||||||
Ok(sqlx::query_as!(
|
Ok(sqlx::query_as!(
|
||||||
Self,
|
Self,
|
||||||
"insert into sessions(device_id, value) values(?, ?) returning id, device_id, value",
|
"insert into sessions(uuid, device_uuid, key)
|
||||||
device_id,
|
values(?, ?, ?)
|
||||||
value
|
returning uuid as 'uuid: Uuid', device_uuid as 'device_uuid: Uuid', key",
|
||||||
|
self.uuid,
|
||||||
|
self.device_uuid,
|
||||||
|
self.key
|
||||||
)
|
)
|
||||||
.fetch_one(conn)
|
.fetch_one(conn)
|
||||||
.await?)
|
.await?)
|
||||||
@ -23,19 +35,19 @@ impl Session {
|
|||||||
|
|
||||||
/// Get the session's id.
|
/// Get the session's id.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn id(&self) -> i64 {
|
pub fn uuid(&self) -> &Uuid {
|
||||||
self.id
|
&self.uuid
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the session's device id.
|
/// Get the session's device id.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn device_id(&self) -> i64 {
|
pub fn device_uuid(&self) -> &Uuid {
|
||||||
self.device_id
|
&self.device_uuid
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a reference to the session's value.
|
/// Get a reference to the session's value.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn value(&self) -> &str {
|
pub fn key(&self) -> &str {
|
||||||
self.value.as_ref()
|
self.key.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,33 @@
|
|||||||
|
use crate::types::uuid::Uuid;
|
||||||
use argon2::{password_hash::SaltString, Argon2, PasswordHasher};
|
use argon2::{password_hash::SaltString, Argon2, PasswordHasher};
|
||||||
use rand_core::OsRng;
|
use rand_core::OsRng;
|
||||||
use sqlx::SqlitePool;
|
use sqlx::{encode::IsNull, sqlite::SqliteTypeInfo, FromRow, Sqlite, SqlitePool};
|
||||||
|
|
||||||
use crate::types::user_id::UserId;
|
use crate::types::user_id::UserId;
|
||||||
|
|
||||||
pub struct User {
|
pub struct User {
|
||||||
id: i64,
|
uuid: Uuid,
|
||||||
user_id: String,
|
user_id: UserId,
|
||||||
display_name: String,
|
display_name: String,
|
||||||
password: String,
|
password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl User {
|
impl User {
|
||||||
|
pub fn new(user_id: &UserId, display_name: &str, password: &str) -> anyhow::Result<Self> {
|
||||||
|
let argon2 = Argon2::default();
|
||||||
|
let salt = SaltString::generate(OsRng);
|
||||||
|
let password = argon2
|
||||||
|
.hash_password(password.as_bytes(), &salt)?
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
uuid: uuid::Uuid::new_v4().into(),
|
||||||
|
user_id: user_id.clone(),
|
||||||
|
display_name: display_name.to_owned(),
|
||||||
|
password,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn exists(conn: &SqlitePool, user_id: &UserId) -> anyhow::Result<bool> {
|
pub async fn exists(conn: &SqlitePool, user_id: &UserId) -> anyhow::Result<bool> {
|
||||||
Ok(
|
Ok(
|
||||||
sqlx::query!("select user_id from users where user_id = ?", user_id)
|
sqlx::query!("select user_id from users where user_id = ?", user_id)
|
||||||
@ -21,25 +37,42 @@ impl User {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create(
|
pub async fn create(&self, conn: &SqlitePool) -> anyhow::Result<Self> {
|
||||||
conn: &SqlitePool,
|
Ok(sqlx::query_as!(
|
||||||
user_id: &UserId,
|
Self,
|
||||||
display_name: &str,
|
"insert into users(uuid, user_id, display_name, password)
|
||||||
password: &str,
|
values (?, ?, ?, ?)
|
||||||
) -> anyhow::Result<Self> {
|
returning uuid as 'uuid: Uuid', user_id as 'user_id: UserId', display_name, password",
|
||||||
let salt = SaltString::generate(OsRng);
|
self.uuid,
|
||||||
let argon2 = Argon2::default();
|
self.user_id,
|
||||||
let pw_hash = argon2
|
self.display_name,
|
||||||
.hash_password(password.as_bytes(), &salt)?
|
self.password
|
||||||
.to_string();
|
)
|
||||||
|
.fetch_one(conn)
|
||||||
|
.await?)
|
||||||
|
}
|
||||||
|
|
||||||
Ok(sqlx::query_as!(Self, "insert into users(user_id, display_name, password) values (?, ?, ?) returning id, user_id, display_name, password", user_id, display_name, pw_hash).fetch_one(conn).await?)
|
pub async fn update(&self, conn: &SqlitePool) -> anyhow::Result<Self> {
|
||||||
|
Ok(sqlx::query_as!(
|
||||||
|
Self,
|
||||||
|
"update users set uuid = ?, user_id = ?, display_name = ?, password = ?
|
||||||
|
where uuid = ?
|
||||||
|
returning uuid as 'uuid: Uuid', user_id as 'user_id: UserId', display_name, password",
|
||||||
|
self.uuid,
|
||||||
|
self.user_id,
|
||||||
|
self.display_name,
|
||||||
|
self.password,
|
||||||
|
self.uuid
|
||||||
|
)
|
||||||
|
.fetch_one(conn)
|
||||||
|
.await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn by_user_id(conn: &SqlitePool, user_id: &UserId) -> anyhow::Result<Self> {
|
pub async fn by_user_id(conn: &SqlitePool, user_id: &UserId) -> anyhow::Result<Self> {
|
||||||
Ok(sqlx::query_as!(
|
Ok(sqlx::query_as!(
|
||||||
Self,
|
Self,
|
||||||
"select id, user_id, display_name, password from users where user_id = ?",
|
"select uuid as 'uuid: Uuid', user_id as 'user_id: UserId', display_name, password
|
||||||
|
from users where user_id = ?",
|
||||||
user_id
|
user_id
|
||||||
)
|
)
|
||||||
.fetch_one(conn)
|
.fetch_one(conn)
|
||||||
@ -48,14 +81,14 @@ impl User {
|
|||||||
|
|
||||||
/// Get the user's id.
|
/// Get the user's id.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn id(&self) -> i64 {
|
pub fn uuid(&self) -> &Uuid {
|
||||||
self.id
|
&self.uuid
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a reference to the user's user id.
|
/// Get a reference to the user's user id.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn user_id(&self) -> &str {
|
pub fn user_id(&self) -> &UserId {
|
||||||
self.user_id.as_ref()
|
&self.user_id
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a reference to the user's password.
|
/// Get a reference to the user's password.
|
||||||
|
@ -5,7 +5,7 @@ use crate::types::user_id::UserId;
|
|||||||
#[derive(Debug, serde::Serialize)]
|
#[derive(Debug, serde::Serialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum AuthenticationResponse {
|
pub enum AuthenticationResponse {
|
||||||
Success(AuthenticationSuccess)
|
Success(AuthenticationSuccess),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Serialize)]
|
#[derive(Debug, serde::Serialize)]
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
pub mod authentication;
|
||||||
pub mod flow;
|
pub mod flow;
|
||||||
pub mod registration;
|
pub mod registration;
|
||||||
pub mod username_available;
|
pub mod username_available;
|
||||||
pub mod versions;
|
pub mod versions;
|
||||||
pub mod authentication;
|
|
@ -12,7 +12,7 @@ pub enum ErrorCode {
|
|||||||
UserInUse,
|
UserInUse,
|
||||||
InvalidUsername,
|
InvalidUsername,
|
||||||
Exclusive,
|
Exclusive,
|
||||||
UserDeactivated
|
UserDeactivated,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl serde::Serialize for ErrorCode {
|
impl serde::Serialize for ErrorCode {
|
||||||
|
@ -5,3 +5,4 @@ pub mod identifier;
|
|||||||
pub mod identifier_type;
|
pub mod identifier_type;
|
||||||
pub mod user_id;
|
pub mod user_id;
|
||||||
pub mod user_interactive_authorization;
|
pub mod user_interactive_authorization;
|
||||||
|
pub mod uuid;
|
||||||
|
@ -1,10 +1,40 @@
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
#[derive(sqlx::Type)]
|
use sqlx::{encode::IsNull, Sqlite};
|
||||||
#[sqlx(transparent)]
|
|
||||||
|
#[derive(Clone)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct UserId(String);
|
pub struct UserId(String);
|
||||||
|
|
||||||
|
impl sqlx::Type<Sqlite> for UserId {
|
||||||
|
fn type_info() -> <Sqlite as sqlx::Database>::TypeInfo {
|
||||||
|
<&str as sqlx::Type<Sqlite>>::type_info()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'e> sqlx::Encode<'e, Sqlite> for UserId {
|
||||||
|
fn encode_by_ref(
|
||||||
|
&self,
|
||||||
|
buf: &mut <Sqlite as sqlx::database::HasArguments<'e>>::ArgumentBuffer,
|
||||||
|
) -> sqlx::encode::IsNull {
|
||||||
|
buf.push(sqlx::sqlite::SqliteArgumentValue::Text(
|
||||||
|
self.0.to_string().into(),
|
||||||
|
));
|
||||||
|
|
||||||
|
IsNull::No
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d> sqlx::Decode<'d, Sqlite> for UserId {
|
||||||
|
fn decode(
|
||||||
|
value: <Sqlite as sqlx::database::HasValueRef<'d>>::ValueRef,
|
||||||
|
) -> Result<Self, sqlx::error::BoxDynError> {
|
||||||
|
let value = <String as sqlx::Decode<Sqlite>>::decode(value)?;
|
||||||
|
|
||||||
|
Ok(UserId(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl UserId {
|
impl UserId {
|
||||||
pub fn new(name: &str, server_name: &str) -> anyhow::Result<Self> {
|
pub fn new(name: &str, server_name: &str) -> anyhow::Result<Self> {
|
||||||
let user_id = Self(format!("@{name}:{server_name}"));
|
let user_id = Self(format!("@{name}:{server_name}"));
|
||||||
|
45
src/types/uuid.rs
Normal file
45
src/types/uuid.rs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
use sqlx::{encode::IsNull, Sqlite, Type};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Uuid(pub uuid::Uuid);
|
||||||
|
|
||||||
|
impl Uuid {
|
||||||
|
pub fn new_v4() -> Self {
|
||||||
|
Uuid(uuid::Uuid::new_v4())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Type<Sqlite> for Uuid {
|
||||||
|
fn type_info() -> <Sqlite as sqlx::Database>::TypeInfo {
|
||||||
|
<&str as Type<Sqlite>>::type_info()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'e> sqlx::Encode<'e, Sqlite> for Uuid {
|
||||||
|
fn encode_by_ref(
|
||||||
|
&self,
|
||||||
|
buf: &mut <Sqlite as sqlx::database::HasArguments<'e>>::ArgumentBuffer,
|
||||||
|
) -> sqlx::encode::IsNull {
|
||||||
|
buf.push(sqlx::sqlite::SqliteArgumentValue::Text(
|
||||||
|
self.0.to_string().into(),
|
||||||
|
));
|
||||||
|
|
||||||
|
IsNull::No
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d> sqlx::Decode<'d, Sqlite> for Uuid {
|
||||||
|
fn decode(
|
||||||
|
value: <Sqlite as sqlx::database::HasValueRef<'d>>::ValueRef,
|
||||||
|
) -> Result<Self, sqlx::error::BoxDynError> {
|
||||||
|
let value = <&str as sqlx::Decode<Sqlite>>::decode(value)?;
|
||||||
|
|
||||||
|
Ok(Uuid(uuid::Uuid::parse_str(value)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<uuid::Uuid> for Uuid {
|
||||||
|
fn from(uuid: uuid::Uuid) -> Self {
|
||||||
|
Uuid(uuid)
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user