Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • tedomum/lemmy
1 result
Show changes
Commits on Source (11)
Showing
with 92 additions and 126 deletions
......@@ -39,25 +39,21 @@ steps:
- git submodule update
prettier_check:
group: format
image: tmknom/prettier:3.0.0
commands:
- prettier -c . '!**/volumes' '!**/dist' '!target' '!**/translations'
toml_fmt:
group: format
image: tamasfe/taplo:0.8.1
commands:
- taplo format --check
sql_fmt:
group: format
image: backplane/pgformatter:latest
commands:
- ./scripts/sql_format_check.sh
cargo_fmt:
group: format
image: rustlang/rust:nightly
environment:
# store cargo data in repo folder so that it gets cached between steps
......@@ -67,7 +63,6 @@ steps:
- cargo +nightly fmt -- --check
cargo_machete:
group: format
image: rustlang/rust:nightly
commands:
- wget https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-x86_64-unknown-linux-musl.tgz
......@@ -77,40 +72,12 @@ steps:
- cargo machete
ignored_files:
group: format
image: alpine:3
commands:
- apk add git
- IGNORED=$(git ls-files --cached -i --exclude-standard)
- if [[ "$IGNORED" ]]; then echo "Ignored files present:\n$IGNORED\n"; exit 1; fi
restore-cache:
image: meltwater/drone-cache:v1
pull: true
settings:
restore: true
endpoint:
from_secret: MINIO_ENDPOINT
access-key:
from_secret: MINIO_WRITE_USER
secret-key:
from_secret: MINIO_WRITE_PASSWORD
bucket:
from_secret: MINIO_BUCKET
region: us-east-1
cache_key: "rust-cache"
path-style: true
backend_operation_timeout: 30m
compression_level: 0
exit_code: true
mount:
- ".cargo_home"
- "target"
- "api_tests/node_modules"
secrets:
[MINIO_ENDPOINT, MINIO_WRITE_USER, MINIO_WRITE_PASSWORD, MINIO_BUCKET]
when: *slow_check_paths
# make sure api builds with default features (used by other crates relying on lemmy api)
check_api_common_default_features:
image: *rust_image
......@@ -188,7 +155,6 @@ steps:
when: *slow_check_paths
cargo_test:
group: tests
image: *rust_image
environment:
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
......@@ -200,7 +166,6 @@ steps:
when: *slow_check_paths
run_federation_tests:
group: tests
image: node:20-bookworm-slim
environment:
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432
......@@ -213,35 +178,6 @@ steps:
- yarn api-test
when: *slow_check_paths
rebuild-cache:
image: meltwater/drone-cache:v1
pull: true
settings:
rebuild: true
endpoint:
from_secret: MINIO_ENDPOINT
access-key:
from_secret: MINIO_WRITE_USER
secret-key:
from_secret: MINIO_WRITE_PASSWORD
bucket:
from_secret: MINIO_BUCKET
cache_key: "rust-cache"
region: us-east-1
path-style: true
backend_operation_timeout: 60m
compression_level: 0
exit_code: true
mount:
- ".cargo_home"
- "target"
- "api_tests/node_modules"
secrets:
[MINIO_ENDPOINT, MINIO_WRITE_USER, MINIO_WRITE_PASSWORD, MINIO_BUCKET]
when:
- event: push
branch: main
publish_release_docker:
image: woodpeckerci/plugin-docker-buildx
secrets: [docker_username, docker_password]
......
......@@ -2529,7 +2529,7 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lemmy_api"
version = "0.19.2"
version = "0.19.3"
dependencies = [
"activitypub_federation",
"actix-web",
......@@ -2558,7 +2558,7 @@ dependencies = [
[[package]]
name = "lemmy_api_common"
version = "0.19.2"
version = "0.19.3"
dependencies = [
"activitypub_federation",
"actix-web",
......@@ -2593,7 +2593,7 @@ dependencies = [
[[package]]
name = "lemmy_api_crud"
version = "0.19.2"
version = "0.19.3"
dependencies = [
"activitypub_federation",
"actix-web",
......@@ -2615,7 +2615,7 @@ dependencies = [
[[package]]
name = "lemmy_apub"
version = "0.19.2"
version = "0.19.3"
dependencies = [
"activitypub_federation",
"actix-web",
......@@ -2655,7 +2655,7 @@ dependencies = [
[[package]]
name = "lemmy_db_schema"
version = "0.19.2"
version = "0.19.3"
dependencies = [
"activitypub_federation",
"async-trait",
......@@ -2692,7 +2692,7 @@ dependencies = [
[[package]]
name = "lemmy_db_views"
version = "0.19.2"
version = "0.19.3"
dependencies = [
"actix-web",
"chrono",
......@@ -2712,7 +2712,7 @@ dependencies = [
[[package]]
name = "lemmy_db_views_actor"
version = "0.19.2"
version = "0.19.3"
dependencies = [
"chrono",
"diesel",
......@@ -2730,7 +2730,7 @@ dependencies = [
[[package]]
name = "lemmy_db_views_moderator"
version = "0.19.2"
version = "0.19.3"
dependencies = [
"diesel",
"diesel-async",
......@@ -2742,7 +2742,7 @@ dependencies = [
[[package]]
name = "lemmy_federate"
version = "0.19.2"
version = "0.19.3"
dependencies = [
"activitypub_federation",
"anyhow",
......@@ -2765,7 +2765,7 @@ dependencies = [
[[package]]
name = "lemmy_routes"
version = "0.19.2"
version = "0.19.3"
dependencies = [
"activitypub_federation",
"actix-web",
......@@ -2789,7 +2789,7 @@ dependencies = [
[[package]]
name = "lemmy_server"
version = "0.19.2"
version = "0.19.3"
dependencies = [
"activitypub_federation",
"actix-cors",
......@@ -2832,7 +2832,7 @@ dependencies = [
[[package]]
name = "lemmy_utils"
version = "0.19.2"
version = "0.19.3"
dependencies = [
"actix-web",
"anyhow",
......
[workspace.package]
version = "0.19.2"
version = "0.19.3"
edition = "2021"
description = "A link aggregator for the fediverse"
license = "AGPL-3.0"
......@@ -85,16 +85,16 @@ unused_self = "deny"
unwrap_used = "deny"
[workspace.dependencies]
lemmy_api = { version = "=0.19.2", path = "./crates/api" }
lemmy_api_crud = { version = "=0.19.2", path = "./crates/api_crud" }
lemmy_apub = { version = "=0.19.2", path = "./crates/apub" }
lemmy_utils = { version = "=0.19.2", path = "./crates/utils" }
lemmy_db_schema = { version = "=0.19.2", path = "./crates/db_schema" }
lemmy_api_common = { version = "=0.19.2", path = "./crates/api_common" }
lemmy_routes = { version = "=0.19.2", path = "./crates/routes" }
lemmy_db_views = { version = "=0.19.2", path = "./crates/db_views" }
lemmy_db_views_actor = { version = "=0.19.2", path = "./crates/db_views_actor" }
lemmy_db_views_moderator = { version = "=0.19.2", path = "./crates/db_views_moderator" }
lemmy_api = { version = "=0.19.3", path = "./crates/api" }
lemmy_api_crud = { version = "=0.19.3", path = "./crates/api_crud" }
lemmy_apub = { version = "=0.19.3", path = "./crates/apub" }
lemmy_utils = { version = "=0.19.3", path = "./crates/utils" }
lemmy_db_schema = { version = "=0.19.3", path = "./crates/db_schema" }
lemmy_api_common = { version = "=0.19.3", path = "./crates/api_common" }
lemmy_routes = { version = "=0.19.3", path = "./crates/routes" }
lemmy_db_views = { version = "=0.19.3", path = "./crates/db_views" }
lemmy_db_views_actor = { version = "=0.19.3", path = "./crates/db_views_actor" }
lemmy_db_views_moderator = { version = "=0.19.3", path = "./crates/db_views_moderator" }
activitypub_federation = { version = "0.5.1-beta.1", default-features = false, features = [
"actix-web",
] }
......@@ -166,7 +166,7 @@ lemmy_utils = { workspace = true }
lemmy_db_schema = { workspace = true }
lemmy_api_common = { workspace = true }
lemmy_routes = { workspace = true }
lemmy_federate = { version = "0.19.2", path = "crates/federate" }
lemmy_federate = { version = "0.19.3", path = "crates/federate" }
activitypub_federation = { workspace = true }
diesel = { workspace = true }
diesel-async = { workspace = true }
......
......@@ -76,7 +76,12 @@ test("Create a post", async () => {
throw "Missing beta community";
}
let postRes = await createPost(alpha, betaCommunity.community.id);
let postRes = await createPost(
alpha,
betaCommunity.community.id,
"https://example.com/",
"აშშ ითხოვს ირანს დაუყოვნებლივ გაანთავისუფლოს დაკავებული ნავთობის ტანკერი",
);
expect(postRes.post_view.post).toBeDefined();
expect(postRes.post_view.community.local).toBe(false);
expect(postRes.post_view.creator.local).toBe(true);
......
......@@ -202,10 +202,10 @@ export async function setupLogins() {
export async function createPost(
api: LemmyHttp,
community_id: number,
// use example.com for consistent title and embed description
url: string = "https://example.com/",
// use example.com for consistent title and embed description
name: string = randomString(5),
): Promise<PostResponse> {
let name = randomString(5);
let body = randomString(10);
let form: CreatePost = {
name,
......
......@@ -112,19 +112,18 @@ test("Delete user", async () => {
).toBe(true);
});
test("Requests with invalid auth should throw error", async () => {
test("Requests with invalid auth should be treated as unauthenticated", async () => {
let invalid_auth = new LemmyHttp(alphaUrl, {
headers: { Authorization: "Bearer foobar" },
fetchFunction,
});
await expect(getSite(invalid_auth)).rejects.toStrictEqual(
Error("incorrect_login"),
);
let site = await getSite(invalid_auth);
expect(site.my_user).toBeUndefined();
expect(site.site_view).toBeDefined();
let form: GetPosts = {};
await expect(invalid_auth.getPosts(form)).rejects.toStrictEqual(
Error("incorrect_login"),
);
let posts = invalid_auth.getPosts(form);
expect((await posts).posts).toBeDefined();
});
test("Create user with Arabic name", async () => {
......
......@@ -2,11 +2,15 @@ use actix_web::{http::header::Header, HttpRequest};
use actix_web_httpauth::headers::authorization::{Authorization, Bearer};
use base64::{engine::general_purpose::STANDARD_NO_PAD as base64, Engine};
use captcha::Captcha;
use lemmy_api_common::utils::{local_site_to_slur_regex, AUTH_COOKIE_NAME};
use lemmy_api_common::{
claims::Claims,
context::LemmyContext,
utils::{check_user_valid, local_site_to_slur_regex, AUTH_COOKIE_NAME},
};
use lemmy_db_schema::source::local_site::LocalSite;
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::{
error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult},
error::{LemmyError, LemmyErrorExt, LemmyErrorExt2, LemmyErrorType, LemmyResult},
utils::slurs::check_slurs,
};
use std::io::Cursor;
......@@ -137,6 +141,20 @@ pub(crate) fn build_totp_2fa(
.with_lemmy_type(LemmyErrorType::CouldntGenerateTotp)
}
#[tracing::instrument(skip_all)]
pub async fn local_user_view_from_jwt(
jwt: &str,
context: &LemmyContext,
) -> Result<LocalUserView, LemmyError> {
let local_user_id = Claims::validate(jwt, context)
.await
.with_lemmy_type(LemmyErrorType::NotLoggedIn)?;
let local_user_view = LocalUserView::read(&mut context.pool(), local_user_id).await?;
check_user_valid(&local_user_view.person)?;
Ok(local_user_view)
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
......
use crate::captcha_as_wav_base64;
use actix_web::web::{Data, Json};
use actix_web::{
http::{
header::{CacheControl, CacheDirective},
StatusCode,
},
web::{Data, Json},
HttpResponse,
HttpResponseBuilder,
};
use captcha::{gen, Difficulty};
use lemmy_api_common::{
context::LemmyContext,
......@@ -12,13 +20,13 @@ use lemmy_db_schema::source::{
use lemmy_utils::error::LemmyError;
#[tracing::instrument(skip(context))]
pub async fn get_captcha(
context: Data<LemmyContext>,
) -> Result<Json<GetCaptchaResponse>, LemmyError> {
pub async fn get_captcha(context: Data<LemmyContext>) -> Result<HttpResponse, LemmyError> {
let local_site = LocalSite::read(&mut context.pool()).await?;
let mut res = HttpResponseBuilder::new(StatusCode::OK);
res.insert_header(CacheControl(vec![CacheDirective::NoStore]));
if !local_site.captcha_enabled {
return Ok(Json(GetCaptchaResponse { ok: None }));
return Ok(res.json(Json(GetCaptchaResponse { ok: None })));
}
let captcha = gen(match local_site.captcha_difficulty.as_str() {
......@@ -37,11 +45,12 @@ pub async fn get_captcha(
// Stores the captcha item in the db
let captcha = CaptchaAnswer::insert(&mut context.pool(), &captcha_form).await?;
Ok(Json(GetCaptchaResponse {
let json = Json(GetCaptchaResponse {
ok: Some(CaptchaResponse {
png,
wav,
uuid: captcha.uuid.to_string(),
}),
}))
});
Ok(res.json(json))
}
use crate::read_auth_token;
use crate::{local_user_view_from_jwt, read_auth_token};
use actix_web::{
web::{Data, Json},
HttpRequest,
};
use lemmy_api_common::{claims::Claims, context::LemmyContext, SuccessResponse};
use lemmy_utils::error::{LemmyError, LemmyErrorExt2, LemmyErrorType};
use lemmy_api_common::{context::LemmyContext, SuccessResponse};
use lemmy_utils::error::{LemmyError, LemmyErrorType};
/// Returns an error message if the auth token is invalid for any reason. Necessary because other
/// endpoints silently treat any call with invalid auth as unauthenticated.
......@@ -15,9 +15,7 @@ pub async fn validate_auth(
) -> Result<Json<SuccessResponse>, LemmyError> {
let jwt = read_auth_token(&req)?;
if let Some(jwt) = jwt {
Claims::validate(&jwt, &context)
.await
.with_lemmy_type(LemmyErrorType::NotLoggedIn)?;
local_user_view_from_jwt(&jwt, &context).await?;
} else {
Err(LemmyErrorType::NotLoggedIn)?;
}
......
......@@ -6,7 +6,7 @@ use lemmy_db_schema::{
newtypes::LocalUserId,
source::login_token::{LoginToken, LoginTokenCreateForm},
};
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
......@@ -25,7 +25,8 @@ impl Claims {
validation.required_spec_claims.remove("exp");
let jwt_secret = &context.secret().jwt_secret;
let key = DecodingKey::from_secret(jwt_secret.as_ref());
let claims = decode::<Claims>(jwt, &key, &validation)?;
let claims =
decode::<Claims>(jwt, &key, &validation).with_lemmy_type(LemmyErrorType::NotLoggedIn)?;
let user_id = LocalUserId(claims.claims.sub.parse()?);
let is_valid = LoginToken::validate(&mut context.pool(), user_id, jwt).await?;
if !is_valid {
......
......@@ -125,7 +125,6 @@ impl ActivityHandler for BlockUser {
#[tracing::instrument(skip_all)]
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
verify_is_public(&self.to, &self.cc)?;
match self.target.dereference(context).await? {
SiteOrCommunity::Site(site) => {
......@@ -149,6 +148,7 @@ impl ActivityHandler for BlockUser {
#[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
let expires = self.expires.map(Into::into);
let mod_person = self.actor.dereference(context).await?;
let blocked_person = self.object.dereference(context).await?;
......
......@@ -89,7 +89,6 @@ impl ActivityHandler for UndoBlockUser {
#[tracing::instrument(skip_all)]
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
verify_is_public(&self.to, &self.cc)?;
verify_domains_match(self.actor.inner(), self.object.actor.inner())?;
self.object.verify(context).await?;
......@@ -98,6 +97,7 @@ impl ActivityHandler for UndoBlockUser {
#[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
let expires = self.object.expires.map(Into::into);
let mod_person = self.actor.dereference(context).await?;
let blocked_person = self.object.object.dereference(context).await?;
......
......@@ -145,14 +145,14 @@ impl ActivityHandler for AnnounceActivity {
}
#[tracing::instrument(skip_all)]
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
async fn verify(&self, _context: &Data<Self::DataType>) -> Result<(), LemmyError> {
verify_is_public(&self.to, &self.cc)?;
Ok(())
}
#[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
let object: AnnouncableActivities = self.object.object(context).await?.try_into()?;
// This is only for sending, not receiving so we reject it.
......
......@@ -115,7 +115,6 @@ impl ActivityHandler for CollectionAdd {
#[tracing::instrument(skip_all)]
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
verify_is_public(&self.to, &self.cc)?;
let community = self.community(context).await?;
verify_person_in_community(&self.actor, &community, context).await?;
......@@ -125,6 +124,7 @@ impl ActivityHandler for CollectionAdd {
#[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
let (community, collection_type) =
Community::get_by_collection_url(&mut context.pool(), &self.target.into()).await?;
match collection_type {
......
......@@ -110,7 +110,6 @@ impl ActivityHandler for CollectionRemove {
#[tracing::instrument(skip_all)]
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
verify_is_public(&self.to, &self.cc)?;
let community = self.community(context).await?;
verify_person_in_community(&self.actor, &community, context).await?;
......@@ -120,6 +119,7 @@ impl ActivityHandler for CollectionRemove {
#[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
let (community, collection_type) =
Community::get_by_collection_url(&mut context.pool(), &self.target.into()).await?;
match collection_type {
......
......@@ -81,7 +81,6 @@ impl ActivityHandler for UndoLockPage {
}
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
insert_received_activity(&self.id, context).await?;
verify_is_public(&self.to, &self.cc)?;
let community = self.community(context).await?;
verify_person_in_community(&self.actor, &community, context).await?;
......@@ -91,6 +90,7 @@ impl ActivityHandler for UndoLockPage {
}
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
insert_received_activity(&self.id, context).await?;
let form = PostUpdateForm {
locked: Some(false),
..Default::default()
......
......@@ -92,7 +92,6 @@ impl ActivityHandler for Report {
#[tracing::instrument(skip_all)]
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
let community = self.community(context).await?;
verify_person_in_community(&self.actor, &community, context).await?;
Ok(())
......@@ -100,6 +99,7 @@ impl ActivityHandler for Report {
#[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
let actor = self.actor.dereference(context).await?;
let reason = self.reason()?;
match self.object.dereference(context).await? {
......
......@@ -72,7 +72,6 @@ impl ActivityHandler for UpdateCommunity {
#[tracing::instrument(skip_all)]
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
verify_is_public(&self.to, &self.cc)?;
let community = self.community(context).await?;
verify_person_in_community(&self.actor, &community, context).await?;
......@@ -83,6 +82,7 @@ impl ActivityHandler for UpdateCommunity {
#[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
let community = self.community(context).await?;
let community_update_form = self.object.into_update_form();
......
......@@ -115,7 +115,6 @@ impl ActivityHandler for CreateOrUpdateNote {
#[tracing::instrument(skip_all)]
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
verify_is_public(&self.to, &self.cc)?;
let post = self.object.get_parents(context).await?.0;
let community = self.community(context).await?;
......@@ -131,6 +130,7 @@ impl ActivityHandler for CreateOrUpdateNote {
#[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
// Need to do this check here instead of Note::from_json because we need the person who
// send the activity, not the comment author.
let existing_comment = self.object.id.dereference_local(context).await.ok();
......
......@@ -105,7 +105,6 @@ impl ActivityHandler for CreateOrUpdatePage {
#[tracing::instrument(skip_all)]
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
verify_is_public(&self.to, &self.cc)?;
let community = self.community(context).await?;
verify_person_in_community(&self.actor, &community, context).await?;
......@@ -140,6 +139,7 @@ impl ActivityHandler for CreateOrUpdatePage {
#[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
let post = ApubPost::from_json(self.object, context).await?;
// author likes their own post by default
......