From 0f0f83bc3a5ba68dd01efb36ce4202e746f0cf5b Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Thu, 8 Feb 2024 12:20:01 +0100 Subject: [PATCH] various changes - only admin can edit main page - adjust config values - better text for default page --- config/config.toml | 6 +++--- config/defaults.toml | 4 ++-- src/backend/api/article.rs | 10 +++------- src/{ => backend}/config.rs | 6 ++++-- src/backend/database/mod.rs | 2 +- .../activities/update_local_article.rs | 5 ++++- src/backend/federation/mod.rs | 2 +- src/backend/mod.rs | 10 ++++++++-- src/backend/utils.rs | 7 +++++-- src/common/mod.rs | 2 ++ src/common/validation.rs | 10 ++++++++++ src/frontend/components/article_nav.rs | 20 +++++++++++-------- src/lib.rs | 1 - src/main.rs | 2 +- tests/common.rs | 2 +- 15 files changed, 57 insertions(+), 32 deletions(-) rename src/{ => backend}/config.rs (94%) create mode 100644 src/common/validation.rs diff --git a/config/config.toml b/config/config.toml index b963440..457f475 100644 --- a/config/config.toml +++ b/config/config.toml @@ -1,3 +1,3 @@ -[setup] -admin_username = "ibis" -admin_password = "ibis" +[federation] +# necessary for auth cookie to work +domain = "127.0.0.1:8080" diff --git a/config/defaults.toml b/config/defaults.toml index 8faf3d6..1c082b8 100644 --- a/config/defaults.toml +++ b/config/defaults.toml @@ -9,8 +9,8 @@ registration_open = true # Details of the initial admin account [setup] -admin_username = "admin" -admin_password = "hunter2" +admin_username = "ibis" +admin_password = "ibis" [federation] # Domain name of the instance, mandatory for federation diff --git a/src/backend/api/article.rs b/src/backend/api/article.rs index 76eb9ae..dff542c 100644 --- a/src/backend/api/article.rs +++ b/src/backend/api/article.rs @@ -6,12 +6,13 @@ use crate::backend::error::MyResult; use crate::backend::federation::activities::create_article::CreateArticle; use crate::backend::federation::activities::submit_article_update; use crate::backend::utils::generate_article_version; +use crate::common::validation::can_edit_article; +use crate::common::LocalUserView; use crate::common::{ApiConflict, ResolveObject}; use crate::common::{ArticleView, DbArticle, DbEdit}; use crate::common::{CreateArticleData, EditArticleData, EditVersion, ForkArticleData}; use crate::common::{DbInstance, SearchArticleData}; use crate::common::{GetArticleData, ListArticlesData}; -use crate::common::{LocalUserView, MAIN_PAGE_NAME}; use activitypub_federation::config::Data; use activitypub_federation::fetch::object_id::ObjectId; use anyhow::anyhow; @@ -91,12 +92,7 @@ pub(in crate::backend::api) async fn edit_article( if edit_form.summary.is_empty() { return Err(anyhow!("No summary given").into()); } - if original_article.article.local - && original_article.article.title == MAIN_PAGE_NAME - && !user.local_user.admin - { - return Err(anyhow!("Only admin can edit main page").into()); - } + can_edit_article(&original_article.article, user.local_user.admin)?; // ensure trailing newline for clean diffs if !edit_form.new_text.ends_with('\n') { edit_form.new_text.push('\n'); diff --git a/src/config.rs b/src/backend/config.rs similarity index 94% rename from src/config.rs rename to src/backend/config.rs index 017ab2a..50e863e 100644 --- a/src/config.rs +++ b/src/backend/config.rs @@ -26,9 +26,11 @@ pub struct IbisConfig { #[derive(Debug, Deserialize, PartialEq, Eq, Clone, Document, SmartDefault)] #[serde(default)] pub struct IbisConfigSetup { - #[doku(example = "admin")] + #[default("ibis")] + #[doku(example = "ibis")] pub admin_username: String, - #[doku(example = "hunter2")] + #[default("ibis")] + #[doku(example = "ibis")] pub admin_password: String, } diff --git a/src/backend/database/mod.rs b/src/backend/database/mod.rs index 7ebe13b..3e4798e 100644 --- a/src/backend/database/mod.rs +++ b/src/backend/database/mod.rs @@ -1,6 +1,6 @@ +use crate::backend::config::IbisConfig; use crate::backend::database::schema::jwt_secret; use crate::backend::error::MyResult; -use crate::config::IbisConfig; use diesel::PgConnection; use diesel::{QueryDsl, RunQueryDsl}; use std::ops::Deref; diff --git a/src/backend/federation/activities/update_local_article.rs b/src/backend/federation/activities/update_local_article.rs index f223112..2aaf13f 100644 --- a/src/backend/federation/activities/update_local_article.rs +++ b/src/backend/federation/activities/update_local_article.rs @@ -12,6 +12,7 @@ use activitypub_federation::{ traits::{ActivityHandler, Object}, }; +use crate::common::validation::can_edit_article; use crate::common::DbArticle; use serde::{Deserialize, Serialize}; use url::Url; @@ -67,7 +68,9 @@ impl ActivityHandler for UpdateLocalArticle { self.actor.inner() } - async fn verify(&self, _data: &Data) -> Result<(), Self::Error> { + async fn verify(&self, data: &Data) -> Result<(), Self::Error> { + let article = DbArticle::read_from_ap_id(&self.object.id, &data.db_connection)?; + can_edit_article(&article, false)?; Ok(()) } diff --git a/src/backend/federation/mod.rs b/src/backend/federation/mod.rs index 0275cd2..b9783c5 100644 --- a/src/backend/federation/mod.rs +++ b/src/backend/federation/mod.rs @@ -1,5 +1,5 @@ +use crate::backend::config::IbisConfig; use crate::backend::database::IbisData; -use crate::config::IbisConfig; use activitypub_federation::activity_sending::SendActivityTask; use activitypub_federation::config::{Data, UrlVerifier}; use activitypub_federation::error::Error as ActivityPubError; diff --git a/src/backend/mod.rs b/src/backend/mod.rs index bdd9481..31ab86d 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -1,3 +1,4 @@ +use crate::backend::config::IbisConfig; use crate::backend::database::article::DbArticleForm; use crate::backend::database::instance::DbInstanceForm; use crate::backend::database::IbisData; @@ -7,7 +8,6 @@ use crate::backend::federation::routes::federation_routes; use crate::backend::federation::VerifyUrlData; use crate::backend::utils::generate_activity_id; use crate::common::{DbArticle, DbInstance, DbPerson, MAIN_PAGE_NAME}; -use crate::config::IbisConfig; use crate::frontend::app::App; use activitypub_federation::config::{FederationConfig, FederationMiddleware}; use activitypub_federation::fetch::collection_id::CollectionId; @@ -33,6 +33,7 @@ use tower_http::cors::CorsLayer; use tower_http::services::{ServeDir, ServeFile}; pub mod api; +pub mod config; pub mod database; pub mod error; pub mod federation; @@ -103,6 +104,11 @@ pub async fn start(config: IbisConfig) -> MyResult<()> { Ok(()) } +const MAIN_PAGE_DEFAULT_TEXT: &str = "Welcome to Ibis, the federated Wikipedia alternative! + +This main page can only be edited by the admin. Use it as an introduction for new users, \ +and to list interesting articles."; + fn setup(data: &IbisData) -> Result<(), Error> { let domain = &data.config.federation.domain; let ap_id = ObjectId::parse(&format!("http://{domain}"))?; @@ -124,7 +130,7 @@ fn setup(data: &IbisData) -> Result<(), Error> { // Create the main page which is shown by default let form = DbArticleForm { title: MAIN_PAGE_NAME.to_string(), - text: "Hello world!".to_string(), + text: MAIN_PAGE_DEFAULT_TEXT.to_string(), ap_id: ObjectId::parse(&format!("http://{domain}/article/{MAIN_PAGE_NAME}"))?, instance_id: instance.id, local: true, diff --git a/src/backend/utils.rs b/src/backend/utils.rs index fb6b706..d1f9238 100644 --- a/src/backend/utils.rs +++ b/src/backend/utils.rs @@ -8,14 +8,17 @@ use rand::{distributions::Alphanumeric, thread_rng, Rng}; use url::{ParseError, Url}; pub fn generate_activity_id(domain: &Url) -> Result { - let port = domain.port().unwrap(); + let port = match domain.port() { + Some(p) => format!(":{p}"), + None => String::new(), + }; let domain = domain.host_str().unwrap(); let id: String = thread_rng() .sample_iter(&Alphanumeric) .take(7) .map(char::from) .collect(); - Url::parse(&format!("http://{}:{}/objects/{}", domain, port, id)) + Url::parse(&format!("http://{}{}/objects/{}", domain, port, id)) } /// Starting from empty string, apply edits until the specified version is reached. If no version is diff --git a/src/common/mod.rs b/src/common/mod.rs index 54c0ee6..dc82709 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -1,3 +1,5 @@ +pub mod validation; + use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; diff --git a/src/common/validation.rs b/src/common/validation.rs new file mode 100644 index 0000000..6d81fdd --- /dev/null +++ b/src/common/validation.rs @@ -0,0 +1,10 @@ +use crate::common::{DbArticle, MAIN_PAGE_NAME}; +use anyhow::anyhow; +use anyhow::Result; + +pub fn can_edit_article(article: &DbArticle, is_admin: bool) -> Result<()> { + if article.local && article.title == MAIN_PAGE_NAME && !is_admin { + return Err(anyhow!("Only admin can edit main page")); + } + Ok(()) +} diff --git a/src/frontend/components/article_nav.rs b/src/frontend/components/article_nav.rs index 4ee052d..898063c 100644 --- a/src/frontend/components/article_nav.rs +++ b/src/frontend/components/article_nav.rs @@ -1,3 +1,4 @@ +use crate::common::validation::can_edit_article; use crate::common::ArticleView; use crate::frontend::app::GlobalState; use leptos::*; @@ -9,15 +10,18 @@ pub fn ArticleNav(article: Resource, ArticleView>) -> impl IntoVi view! { {move || article.get().map(|article| { - let title = article.article.title; + let title = article.article.title.clone(); view!{ - + }})} } diff --git a/src/lib.rs b/src/lib.rs index 97d661a..47c649d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,4 @@ #[cfg(feature = "ssr")] pub mod backend; pub mod common; -pub mod config; pub mod frontend; diff --git a/src/main.rs b/src/main.rs index 449ffb7..5e5d6ce 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ #[tokio::main] pub async fn main() -> ibis_lib::backend::error::MyResult<()> { use config::Config; - use ibis_lib::config::IbisConfig; + use ibis_lib::backend::config::IbisConfig; use log::LevelFilter; if std::env::args().collect::>().get(1) == Some(&"--print-config".to_string()) { diff --git a/tests/common.rs b/tests/common.rs index e8fa0d6..98451cf 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -1,6 +1,6 @@ +use ibis_lib::backend::config::{IbisConfig, IbisConfigFederation}; use ibis_lib::backend::start; use ibis_lib::common::RegisterUserData; -use ibis_lib::config::{IbisConfig, IbisConfigFederation}; use ibis_lib::frontend::api::ApiClient; use ibis_lib::frontend::error::MyResult; use reqwest::ClientBuilder;