diff options
author | Colin Reeder <vpzomtrrfrt@gmail.com> | 2020-12-31 11:46:10 -0700 |
---|---|---|
committer | Colin Reeder <vpzomtrrfrt@gmail.com> | 2020-12-31 12:15:12 -0700 |
commit | 4d4220ad3f429dfa06247d5cf17c62196acd97ae (patch) | |
tree | 2abd8be859b021793b8eb7238668ba1bfd322fea | |
parent | 2289ae102a460afd6ffffa8cff019f5ddd083401 (diff) |
Distinguish between html & text in user & community descriptions
-rw-r--r-- | Cargo.lock | 70 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | migrations/20201231175007_description-html/down.sql | 4 | ||||
-rw-r--r-- | migrations/20201231175007_description-html/up.sql | 4 | ||||
-rw-r--r-- | src/apub_util/ingest.rs | 18 | ||||
-rw-r--r-- | src/routes/api/communities.rs | 44 | ||||
-rw-r--r-- | src/routes/api/users.rs | 29 | ||||
-rw-r--r-- | src/routes/apub/communities.rs | 7 | ||||
-rw-r--r-- | src/routes/apub/mod.rs | 9 |
9 files changed, 150 insertions, 36 deletions
@@ -220,6 +220,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "476e9cd489f9e121e02ffa6014a8ef220ecb15c05ed23fc34cca13925dc283fb" [[package]] +name = "buf-min" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa17aa1cf56bdd6bb30518767d00e58019d326f3f05d8c3e0730b549d332ea83" +dependencies = [ + "bytes", +] + +[[package]] name = "bumpalo" version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -291,7 +300,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b076e143e1d9538dde65da30f8481c2a6c44040edb8e02b9bf1351edb92ce3" dependencies = [ "lazy_static", - "nom", + "nom 5.1.2", "serde", ] @@ -469,7 +478,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" dependencies = [ "backtrace", - "version_check", + "version_check 0.9.2", ] [[package]] @@ -713,7 +722,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac746a5f3bbfdadd6106868134545e684693d54d9d44f6e9588a7d54af0bf980" dependencies = [ "typenum", - "version_check", + "version_check 0.9.2", ] [[package]] @@ -1063,7 +1072,7 @@ dependencies = [ "idna", "mime", "native-tls", - "nom", + "nom 5.1.2", "once_cell", "quoted_printable", "r2d2", @@ -1176,6 +1185,7 @@ dependencies = [ "unic-langid", "url", "uuid", + "v_htmlescape", ] [[package]] @@ -1397,13 +1407,23 @@ checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" [[package]] name = "nom" +version = "4.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" +dependencies = [ + "memchr", + "version_check 0.1.5", +] + +[[package]] +name = "nom" version = "5.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" dependencies = [ "lexical-core", "memchr", - "version_check", + "version_check 0.9.2", ] [[package]] @@ -2417,7 +2437,7 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" dependencies = [ - "version_check", + "version_check 0.9.2", ] [[package]] @@ -2478,6 +2498,38 @@ dependencies = [ ] [[package]] +name = "v_escape" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3e0ab5fab1db278a9413d2ea794cb66f471f898c5b020c3c394f6447625d9d4" +dependencies = [ + "buf-min", + "v_escape_derive", +] + +[[package]] +name = "v_escape_derive" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c860ad1273f4eee7006cee05db20c9e60e5d24cba024a32e1094aa8e574f3668" +dependencies = [ + "nom 4.2.3", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "v_htmlescape" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f9a8af610ad6f7fc9989c9d2590d9764bc61f294884e9ee93baa58795174572" +dependencies = [ + "cfg-if 1.0.0", + "v_escape", +] + +[[package]] name = "vcpkg" version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2485,6 +2537,12 @@ checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c" [[package]] name = "version_check" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" + +[[package]] +name = "version_check" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" @@ -54,6 +54,7 @@ migrant_lib = { version = "0.30.0", features = ["d-postgres"] } pdcm-linkify = "0.1.0" log = "0.4" env_logger = "0.8" +v_htmlescape = "0.12.0" [dev-dependencies] rand = "0.7.3" diff --git a/migrations/20201231175007_description-html/down.sql b/migrations/20201231175007_description-html/down.sql new file mode 100644 index 0000000..d24bd35 --- /dev/null +++ b/migrations/20201231175007_description-html/down.sql @@ -0,0 +1,4 @@ +BEGIN; + ALTER TABLE person DROP COLUMN description_html; + ALTER TABLE community DROP COLUMN description_html; +COMMIT; diff --git a/migrations/20201231175007_description-html/up.sql b/migrations/20201231175007_description-html/up.sql new file mode 100644 index 0000000..44e9d07 --- /dev/null +++ b/migrations/20201231175007_description-html/up.sql @@ -0,0 +1,4 @@ +BEGIN; + ALTER TABLE person ADD COLUMN description_html TEXT; + ALTER TABLE community ADD COLUMN description_html TEXT; +COMMIT; diff --git a/src/apub_util/ingest.rs b/src/apub_util/ingest.rs index e85cdfd..95cb9c6 100644 --- a/src/apub_util/ingest.rs +++ b/src/apub_util/ingest.rs @@ -215,10 +215,9 @@ pub async fn ingest_object( .and_then(|maybe| maybe.iter().filter_map(|x| x.as_xsd_string()).next()) }) .unwrap_or(""); - let description = group + let description_html = group .summary() - .and_then(|maybe| maybe.iter().filter_map(|x| x.as_xsd_string()).next()) - .unwrap_or(""); + .and_then(|maybe| maybe.iter().filter_map(|x| x.as_xsd_string()).next()); let inbox = group.inbox_unchecked().as_str(); let shared_inbox = group .endpoints_unchecked() @@ -236,8 +235,8 @@ pub async fn ingest_object( .and_then(|key| key.signature_algorithm.as_deref()); let id = CommunityLocalID(db.query_one( - "INSERT INTO community (name, local, ap_id, ap_inbox, ap_shared_inbox, public_key, public_key_sigalg, description) VALUES ($1, FALSE, $2, $3, $4, $5, $6, $7) ON CONFLICT (ap_id) DO UPDATE SET ap_inbox=$3, ap_shared_inbox=$4, public_key=$5, public_key_sigalg=$6, description=$7 RETURNING id", - &[&name, &ap_id.as_str(), &inbox, &shared_inbox, &public_key, &public_key_sigalg, &description], + "INSERT INTO community (name, local, ap_id, ap_inbox, ap_shared_inbox, public_key, public_key_sigalg, description_html) VALUES ($1, FALSE, $2, $3, $4, $5, $6, $7) ON CONFLICT (ap_id) DO UPDATE SET ap_inbox=$3, ap_shared_inbox=$4, public_key=$5, public_key_sigalg=$6, description_html=$7 RETURNING id", + &[&name, &ap_id.as_str(), &inbox, &shared_inbox, &public_key, &public_key_sigalg, &description_html], ).await?.get(0)); Ok(Some(IngestResult::Actor( @@ -291,10 +290,9 @@ pub async fn ingest_object( .public_key .as_ref() .and_then(|key| key.signature_algorithm.as_deref()); - let description = person + let description_html = person .summary() - .and_then(|maybe| maybe.iter().filter_map(|x| x.as_xsd_string()).next()) - .unwrap_or(""); + .and_then(|maybe| maybe.iter().filter_map(|x| x.as_xsd_string()).next()); let avatar = person.icon().and_then(|icon| { icon.iter() @@ -316,8 +314,8 @@ pub async fn ingest_object( .map(|x| x.as_str()); let id = UserLocalID(db.query_one( - "INSERT INTO person (username, local, created_local, ap_id, ap_inbox, ap_shared_inbox, public_key, public_key_sigalg, description, avatar) VALUES ($1, FALSE, localtimestamp, $2, $3, $4, $5, $6, $7, $8) ON CONFLICT (ap_id) DO UPDATE SET ap_inbox=$3, ap_shared_inbox=$4, public_key=$5, public_key_sigalg=$6, description=$7, avatar=$8 RETURNING id", - &[&username, &ap_id.as_str(), &inbox, &shared_inbox, &public_key, &public_key_sigalg, &description, &avatar], + "INSERT INTO person (username, local, created_local, ap_id, ap_inbox, ap_shared_inbox, public_key, public_key_sigalg, description_html, avatar) VALUES ($1, FALSE, localtimestamp, $2, $3, $4, $5, $6, $7, $8) ON CONFLICT (ap_id) DO UPDATE SET ap_inbox=$3, ap_shared_inbox=$4, public_key=$5, public_key_sigalg=$6, description_html=$7, avatar=$8 RETURNING id", + &[&username, &ap_id.as_str(), &inbox, &shared_inbox, &public_key, &public_key_sigalg, &description_html, &avatar], ).await?.get(0)); Ok(Some(IngestResult::Actor(super::ActorLocalInfo::User { diff --git a/src/routes/api/communities.rs b/src/routes/api/communities.rs index 4392983..d3f79c8 100644 --- a/src/routes/api/communities.rs +++ b/src/routes/api/communities.rs @@ -13,6 +13,8 @@ struct RespCommunityInfo<'a> { base: RespMinimalCommunityInfo<'a>, description: &'a str, + description_html: Option<String>, + description_text: Option<&'a str>, #[serde(skip_serializing_if = "Option::is_none")] you_are_moderator: Option<bool>, @@ -25,6 +27,20 @@ struct RespYourFollowInfo { accepted: bool, } +fn get_community_description_fields<'a>( + description_text: &'a str, + description_html: Option<&'a str>, +) -> (&'a str, Option<&'a str>, Option<String>) { + match description_html { + Some(description_html) => ( + description_html, + None, + Some(ammonia::clean(description_html)), + ), + None => (description_text, Some(description_text), None), + } +} + async fn route_unstable_communities_list( _: (), ctx: Arc<crate::RouteContext>, @@ -43,7 +59,7 @@ async fn route_unstable_communities_list( let query: CommunitiesListQuery = serde_urlencoded::from_str(req.uri().query().unwrap_or(""))?; - let mut sql = String::from("SELECT id, name, local, ap_id, description"); + let mut sql = String::from("SELECT id, name, local, ap_id, description, description_html"); let mut values: Vec<&(dyn tokio_postgres::types::ToSql + Sync)> = Vec::new(); let db = ctx.db_pool.get().await?; @@ -77,7 +93,9 @@ async fn route_unstable_communities_list( let name = row.get(1); let local = row.get(2); let ap_id = row.get(3); - let description = row.get(4); + + let (description, description_text, description_html) = + get_community_description_fields(row.get(4), row.get(5)); let host = crate::get_actor_host_or_unknown(local, ap_id, &ctx.local_hostname); @@ -91,13 +109,16 @@ async fn route_unstable_communities_list( }, description, + description_html, + description_text, + you_are_moderator: if query.include_your { - Some(row.get(6)) + Some(row.get(7)) } else { None }, your_follow: if query.include_your { - Some(match row.get(5) { + Some(match row.get(6) { Some(accepted) => Some(RespYourFollowInfo { accepted }), None => None, }) @@ -204,12 +225,12 @@ async fn route_unstable_communities_get( (if query.include_your { let user = crate::require_login(&req, &db).await?; db.query_opt( - "SELECT name, local, ap_id, description, (SELECT accepted FROM community_follow WHERE community=community.id AND follower=$2), EXISTS(SELECT 1 FROM community_moderator WHERE community=community.id AND person=$2) FROM community WHERE id=$1", + "SELECT name, local, ap_id, description, description_html, (SELECT accepted FROM community_follow WHERE community=community.id AND follower=$2), EXISTS(SELECT 1 FROM community_moderator WHERE community=community.id AND person=$2) FROM community WHERE id=$1", &[&community_id.raw(), &user.raw()], ).await? } else { db.query_opt( - "SELECT name, local, ap_id, description FROM community WHERE id=$1", + "SELECT name, local, ap_id, description, description_html FROM community WHERE id=$1", &[&community_id.raw()], ).await? }) @@ -224,6 +245,9 @@ async fn route_unstable_communities_get( let community_local = row.get(1); let community_ap_id: Option<&str> = row.get(2); + let (description, description_text, description_html) = + get_community_description_fields(row.get(3), row.get(4)); + let info = RespCommunityInfo { base: RespMinimalCommunityInfo { id: community_id, @@ -239,14 +263,16 @@ async fn route_unstable_communities_get( }, remote_url: community_ap_id, }, - description: row.get(3), + description, + description_html, + description_text, you_are_moderator: if query.include_your { - Some(row.get(5)) + Some(row.get(6)) } else { None }, your_follow: if query.include_your { - Some(match row.get(4) { + Some(match row.get(5) { Some(accepted) => Some(RespYourFollowInfo { accepted }), None => None, }) diff --git a/src/routes/api/users.rs b/src/routes/api/users.rs index ad1d77d..efe25da 100644 --- a/src/routes/api/users.rs +++ b/src/routes/api/users.rs @@ -143,6 +143,8 @@ struct RespUserInfo<'a> { base: RespMinimalAuthorInfo<'a>, description: &'a str, + description_html: Option<&'a str>, + description_text: Option<&'a str>, #[serde(skip_serializing_if = "Option::is_none")] suspended: Option<bool>, #[serde(skip_serializing_if = "Option::is_none")] @@ -252,7 +254,8 @@ async fn route_unstable_users_patch( #[derive(Deserialize)] struct UsersEditBody<'a> { - description: Option<Cow<'a, str>>, + #[serde(alias = "description")] + description_text: Option<Cow<'a, str>>, email_address: Option<Cow<'a, str>>, password: Option<String>, avatar: Option<Cow<'a, str>>, @@ -266,7 +269,7 @@ async fn route_unstable_users_patch( let mut changes = Vec::<(&str, &(dyn tokio_postgres::types::ToSql + Sync))>::new(); - if let Some(description) = body.description.as_ref() { + if let Some(description) = body.description_text.as_ref() { changes.push(("description", description)); } if let Some(email_address) = body.email_address.as_ref() { @@ -534,7 +537,7 @@ async fn route_unstable_users_get( let row = db .query_opt( - "SELECT username, local, ap_id, description, avatar, suspended FROM person WHERE id=$1", + "SELECT username, local, ap_id, description, description_html, avatar, suspended FROM person WHERE id=$1", &[&user_id], ) .await?; @@ -548,7 +551,7 @@ async fn route_unstable_users_get( let local = row.get(1); let ap_id = row.get(2); - let avatar: Option<&str> = row.get(4); + let avatar: Option<&str> = row.get(5); let info = RespMinimalAuthorInfo { id: user_id, @@ -561,10 +564,24 @@ async fn route_unstable_users_get( }), }; + let (description, description_text, description_html) = match row.get(4) { + Some(description_html) => ( + description_html, + None, + Some(ammonia::clean(description_html)), + ), + None => { + let description_text: &str = row.get(3); + (description_text, Some(description_text), None) + } + }; + let info = RespUserInfo { base: info, - description: row.get(3), - suspended: if local { Some(row.get(5)) } else { None }, + description, + description_html: description_html.as_deref(), + description_text, + suspended: if local { Some(row.get(6)) } else { None }, your_note, }; diff --git a/src/routes/apub/communities.rs b/src/routes/apub/communities.rs index dd249f7..f9fe71b 100644 --- a/src/routes/apub/communities.rs +++ b/src/routes/apub/communities.rs @@ -88,7 +88,7 @@ async fn handler_communities_get( match db .query_opt( - "SELECT name, local, public_key, description FROM community WHERE id=$1", + "SELECT name, local, public_key, description, description_html FROM community WHERE id=$1", &[&community_id], ) .await? @@ -116,7 +116,10 @@ async fn handler_communities_get( None } }); - let description: &str = row.get(3); + let description = match row.get(4) { + Some(description_html) => description_html, + None => v_htmlescape::escape(row.get(3)).to_string(), + }; let community_ap_id = crate::apub_util::get_local_community_apub_id(community_id, &ctx.host_url_apub); diff --git a/src/routes/apub/mod.rs b/src/routes/apub/mod.rs index d6a9583..b352cd4 100644 --- a/src/routes/apub/mod.rs +++ b/src/routes/apub/mod.rs @@ -118,7 +118,7 @@ async fn handler_users_get( match db .query_opt( - "SELECT username, local, public_key, description, avatar FROM person WHERE id=$1", + "SELECT username, local, public_key, description, description_html, avatar FROM person WHERE id=$1", &[&user_id.raw()], ) .await? @@ -148,9 +148,12 @@ async fn handler_users_get( } }); - let description: &str = row.get(3); + let description = match row.get(4) { + Some(description_html) => description_html, + None => v_htmlescape::escape(row.get(3)).to_string(), + }; - let avatar: Option<&str> = row.get(4); + let avatar: Option<&str> = row.get(5); let user_ap_id = crate::apub_util::get_local_person_apub_id(user_id, &ctx.host_url_apub); |