From bda146cf050061ed2ec0998517ac3afb1380ea7c Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Fri, 8 Dec 2023 22:21:31 +0100 Subject: [PATCH] stuff --- .../2023-11-28-150402_fediwiki_setup/down.sql | 2 + .../2023-11-28-150402_fediwiki_setup/up.sql | 27 +++++--- src/api.rs | 42 +++++++++++++ src/database/schema.rs | 17 ++++- src/database/user.rs | 63 +++++++++++++------ src/federation/objects/user.rs | 17 ++--- tests/test.rs | 12 +++- 7 files changed, 141 insertions(+), 39 deletions(-) diff --git a/migrations/2023-11-28-150402_fediwiki_setup/down.sql b/migrations/2023-11-28-150402_fediwiki_setup/down.sql index 9a5a480..06c62b9 100644 --- a/migrations/2023-11-28-150402_fediwiki_setup/down.sql +++ b/migrations/2023-11-28-150402_fediwiki_setup/down.sql @@ -2,4 +2,6 @@ drop table conflict; drop table edit; drop table article; drop table instance_follow; +drop table local_user; +drop table person; drop table instance; diff --git a/migrations/2023-11-28-150402_fediwiki_setup/up.sql b/migrations/2023-11-28-150402_fediwiki_setup/up.sql index b645549..a79d35d 100644 --- a/migrations/2023-11-28-150402_fediwiki_setup/up.sql +++ b/migrations/2023-11-28-150402_fediwiki_setup/up.sql @@ -9,20 +9,27 @@ create table instance ( local bool not null ); -create table user_ ( - id serial primary key, - ap_id varchar(255) not null unique, - inbox_url text not null, - public_key text not null, - private_key text, - last_refreshed_at timestamptz not null default now(), - local bool not null - ); +create table person ( + id serial primary key, + username text not null, + ap_id varchar(255) not null unique, + inbox_url text not null, + public_key text not null, + private_key text, + last_refreshed_at timestamptz not null default now(), + local bool not null +); + +create table local_user ( + id serial primary key, + password_encrypted text not null, + person_id int REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE NOT NULL +); create table instance_follow ( id serial primary key, instance_id int REFERENCES instance ON UPDATE CASCADE ON DELETE CASCADE NOT NULL, - follower_id int REFERENCES user_ ON UPDATE CASCADE ON DELETE CASCADE NOT NULL, + follower_id int REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE NOT NULL, pending boolean not null, unique(instance_id, follower_id) ); diff --git a/src/api.rs b/src/api.rs index 0b9ff32..7f6d59d 100644 --- a/src/api.rs +++ b/src/api.rs @@ -19,6 +19,7 @@ use diffy::create_patch; use futures::future::try_join_all; use serde::{Deserialize, Serialize}; use url::Url; +use crate::database::user::{DbLocalUserForm, DbPerson, DbPersonForm}; pub fn api_routes() -> Router { Router::new() @@ -33,6 +34,8 @@ pub fn api_routes() -> Router { .route("/instance", get(get_local_instance)) .route("/instance/follow", post(follow_instance)) .route("/search", get(search_article)) + .route("/user/register", post(register_user)) + .route("/user/login", post(login_user)) } #[derive(Deserialize, Serialize)] @@ -291,3 +294,42 @@ async fn fork_article( Ok(Json(DbArticle::read_view(article.id, &data.db_connection)?)) } + +#[derive(Deserialize, Serialize)] +pub struct RegisterUserData { + name: String, + password: String, +} + +#[derive(Deserialize, Serialize)] +#[serde(transparent)] +pub struct Jwt(String); + +#[debug_handler] +async fn register_user( + data: Data, + Form(form): Form, +) -> MyResult> { + let local_user_form = DbLocalUserForm { + + }; + let person_form = DbPersonForm { + + }; + DbPerson::create(&person_form, Some(&local_user_form), &data.db_connection)?; + +} + +#[derive(Deserialize, Serialize)] +pub struct LoginUserData { + name: String, + password: String, +} + +#[debug_handler] +async fn login_user( + data: Data, + Form(form): Form, +) -> MyResult> { + todo!() +} \ No newline at end of file diff --git a/src/database/schema.rs b/src/database/schema.rs index f9b8fac..9afd047 100644 --- a/src/database/schema.rs +++ b/src/database/schema.rs @@ -58,8 +58,17 @@ diesel::table! { } diesel::table! { - user_ (id) { + local_user (id) { id -> Int4, + password_encrypted -> Text, + person_id -> Int4, + } +} + +diesel::table! { + person (id) { + id -> Int4, + username -> Text, #[max_length = 255] ap_id -> Varchar, inbox_url -> Text, @@ -74,7 +83,8 @@ diesel::joinable!(article -> instance (instance_id)); diesel::joinable!(conflict -> article (article_id)); diesel::joinable!(edit -> article (article_id)); diesel::joinable!(instance_follow -> instance (instance_id)); -diesel::joinable!(instance_follow -> user_ (follower_id)); +diesel::joinable!(instance_follow -> person (follower_id)); +diesel::joinable!(local_user -> person (person_id)); diesel::allow_tables_to_appear_in_same_query!( article, @@ -82,5 +92,6 @@ diesel::allow_tables_to_appear_in_same_query!( edit, instance, instance_follow, - user_, + local_user, + person, ); diff --git a/src/database/user.rs b/src/database/user.rs index 686bba8..e5cf875 100644 --- a/src/database/user.rs +++ b/src/database/user.rs @@ -1,4 +1,4 @@ -use crate::database::schema::user_; +use crate::database::schema::{local_user, person}; use crate::database::MyDataHandle; use crate::error::MyResult; use activitypub_federation::config::Data; @@ -14,11 +14,29 @@ use serde::{Deserialize, Serialize}; use std::ops::DerefMut; use std::sync::Mutex; +/// A user with account registered on local instance. #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Queryable, Selectable, Identifiable)] -#[diesel(table_name = user_, check_for_backend(diesel::pg::Pg))] -pub struct DbUser { +#[diesel(table_name = local_user, check_for_backend(diesel::pg::Pg))] +pub struct DbLocalUser { pub id: i32, - pub ap_id: ObjectId, + pub password_encrypted: String, + pub person_id: i32, +} + +#[derive(Debug, Clone, Insertable, AsChangeset)] +#[diesel(table_name = local_user, check_for_backend(diesel::pg::Pg))] +pub struct DbLocalUserForm { + pub password_encrypted: String, + pub person_id: i32, +} + +/// Federation related data from a local or remote user. +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Queryable, Selectable, Identifiable)] +#[diesel(table_name = person, check_for_backend(diesel::pg::Pg))] +pub struct DbPerson { + pub id: i32, + pub username: String, + pub ap_id: ObjectId, pub inbox_url: String, #[serde(skip)] pub public_key: String, @@ -30,9 +48,10 @@ pub struct DbUser { } #[derive(Debug, Clone, Insertable, AsChangeset)] -#[diesel(table_name = user_, check_for_backend(diesel::pg::Pg))] -pub struct DbUserForm { - pub ap_id: ObjectId, +#[diesel(table_name = person, check_for_backend(diesel::pg::Pg))] +pub struct DbPersonForm { + pub username: String, + pub ap_id: ObjectId, pub inbox_url: String, pub public_key: String, pub private_key: Option, @@ -40,24 +59,32 @@ pub struct DbUserForm { pub local: bool, } -impl DbUser { - pub fn create(form: &DbUserForm, conn: &Mutex) -> MyResult { +impl DbPerson { + pub fn create(person_form: &DbPersonForm, local_user_form: Option<&DbLocalUserForm>, conn: &Mutex) -> MyResult { let mut conn = conn.lock().unwrap(); - Ok(insert_into(user_::table) - .values(form) - .on_conflict(user_::dsl::ap_id) + let person = insert_into(person::table) + .values(person_form) + .on_conflict(person::dsl::ap_id) .do_update() - .set(form) - .get_result(conn.deref_mut())?) + .set(person_form) + .get_result::(conn.deref_mut())?; + + if let Some(local_user_form) = local_user_form { + insert_into(local_user::table) + .values(local_user_form) + .get_result::(conn.deref_mut())?; + } + + Ok(person) } pub fn read_from_ap_id( - ap_id: &ObjectId, + ap_id: &ObjectId, data: &Data, - ) -> MyResult { + ) -> MyResult { let mut conn = data.db_connection.lock().unwrap(); - Ok(user_::table - .filter(user_::dsl::ap_id.eq(ap_id)) + Ok(person::table + .filter(person::dsl::ap_id.eq(ap_id)) .get_result(conn.deref_mut())?) } } diff --git a/src/federation/objects/user.rs b/src/federation/objects/user.rs index 2522915..129a842 100644 --- a/src/federation/objects/user.rs +++ b/src/federation/objects/user.rs @@ -1,4 +1,4 @@ -use crate::database::user::{DbUser, DbUserForm}; +use crate::database::user::{DbLocalUser, DbLocalUserForm, DbPerson, DbPersonForm}; use crate::database::MyDataHandle; use crate::error::Error; use activitypub_federation::kinds::actor::PersonType; @@ -18,13 +18,14 @@ use url::Url; pub struct ApubUser { #[serde(rename = "type")] kind: PersonType, - id: ObjectId, + id: ObjectId, + preferred_username: String, inbox: Url, public_key: PublicKey, } #[async_trait::async_trait] -impl Object for DbUser { +impl Object for DbPerson { type DataType = MyDataHandle; type Kind = ApubUser; type Error = Error; @@ -37,13 +38,14 @@ impl Object for DbUser { object_id: Url, data: &Data, ) -> Result, Self::Error> { - Ok(DbUser::read_from_ap_id(&object_id.into(), data).ok()) + Ok(DbPerson::read_from_ap_id(&object_id.into(), data).ok()) } async fn into_json(self, _data: &Data) -> Result { Ok(ApubUser { kind: Default::default(), id: self.ap_id.clone(), + preferred_username: self.username.clone(), inbox: Url::parse(&self.inbox_url)?, public_key: self.public_key(), }) @@ -59,7 +61,8 @@ impl Object for DbUser { } async fn from_json(json: Self::Kind, data: &Data) -> Result { - let form = DbUserForm { + let form = DbPersonForm { + username: json.preferred_username, ap_id: json.id, inbox_url: json.inbox.to_string(), public_key: json.public_key.public_key_pem, @@ -67,11 +70,11 @@ impl Object for DbUser { last_refreshed_at: Local::now().into(), local: false, }; - DbUser::create(&form, &data.db_connection) + DbPerson::create(&form, None, &data.db_connection) } } -impl Actor for DbUser { +impl Actor for DbPerson { fn id(&self) -> Url { self.ap_id.inner().clone() } diff --git a/tests/test.rs b/tests/test.rs index feb0709..8305413 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -7,7 +7,7 @@ use crate::common::{ get_query, post, TestData, TEST_ARTICLE_DEFAULT_TEXT, }; use common::get; -use fediwiki::api::{EditArticleData, ForkArticleData, ResolveObject, SearchArticleData}; +use fediwiki::api::{EditArticleData, ForkArticleData, RegisterUserData, ResolveObject, SearchArticleData}; use fediwiki::database::article::{ArticleView, DbArticle}; use fediwiki::error::MyResult; @@ -464,3 +464,13 @@ async fn test_fork_article() -> MyResult<()> { data.stop() } + +#[tokio::test] +async fn test_user_registration_login() -> MyResult<()> { + let data = TestData::start(); + let data = RegisterUserData { + + } + post(data.alpha.hostname, "user/register") + data.stop() +} \ No newline at end of file