~whbboyd/russet

1ff23e6c646aa1cd2b6ea86af9945407ab890717 — Will Boyd 5 months ago 5437305
Quick disable_logins hack for safer exposure to the public Internet
M Cargo.lock => Cargo.lock +1 -1
@@ 1879,7 1879,7 @@ dependencies = [

[[package]]
name = "russet"
version = "0.10.0"
version = "0.10.1"
dependencies = [
 "argon2",
 "atom_syndication",

M Cargo.toml => Cargo.toml +1 -1
@@ 1,6 1,6 @@
[package]
name = "russet"
version = "0.10.0"
version = "0.10.1"
edition = "2021"
license = "AGPL-3.0"


M src/conf.rs => src/conf.rs +10 -0
@@ 44,8 44,17 @@ pub struct Config {
	)]
	pub feed_check_interval: Option<Duration>,

	/// Disable logins.
	///
	/// This option will go away once more robust rate-limiting of the login
	/// endpoint is available. For now, it's a quick toggle to prevent
	/// credential stuffing or resource exhaustion attacks.
	#[arg(long)]
	pub disable_logins: Option<bool>,

	#[command(flatten)]
	pub rate_limiting: RateLimitingConfig,

}
impl Default for Config {
	fn default() -> Self {


@@ 56,6 65,7 @@ impl Default for Config {
			listen_address: Some("127.0.0.1:9892".to_string()),
			pepper: Some("IzvoEPMQIi82NSXTz7cZ".to_string()),
			feed_check_interval: Some(Duration::from_secs(3_600)),
			disable_logins: Some(false),
			rate_limiting: RateLimitingConfig::default(),
		}
	}

M src/domain/mod.rs => src/domain/mod.rs +5 -0
@@ 15,6 15,7 @@ where Persistence: std::fmt::Debug {
	readers: Vec<Box<dyn RussetFeedReader>>,
	pepper: Vec<u8>,
	pub feed_check_interval: Duration,
	disable_logins: bool,
}
impl <Persistence> RussetDomainService<Persistence>
where Persistence: std::fmt::Debug {


@@ 23,6 24,7 @@ where Persistence: std::fmt::Debug {
		readers: Vec<Box<dyn RussetFeedReader>>,
		pepper: Vec<u8>,
		feed_check_interval: Duration,
		disable_logins: bool,
	) -> Result<RussetDomainService<Persistence>> {
		if feed_check_interval < MIN_CHECK_INTERVAL {
			let interval = feed_check_interval.as_secs_f64();


@@ 35,6 37,7 @@ where Persistence: std::fmt::Debug {
			readers,
			pepper,
			feed_check_interval,
			disable_logins,
		} )
	}
}


@@ 45,6 48,8 @@ where Persistence: std::fmt::Debug {
			.field("persistence", &self.persistence)
			.field("readers", &self.readers)
			.field("pepper", &"<redacted>")
			.field("feed_check_interva", &self.feed_check_interval)
			.field("disable_logins", &self.disable_logins)
			.finish()
	}
}

M src/domain/user.rs => src/domain/user.rs +1 -0
@@ 23,6 23,7 @@ where Persistence: RussetUserPersistenceLayer {
		plaintext_password: String,
		permanent_session: bool,
	) -> Result<Option<Session>> {
		if self.disable_logins { return Ok(None) }
		let password_hash = Argon2::new_with_secret(
				self.pepper.as_slice(),
				argon2::Algorithm::Argon2id,

M src/main.rs => src/main.rs +3 -1
@@ 50,7 50,7 @@ async fn main() -> Result<()> {
		// Commandline flags override all
		let mut config = Config::parse();

		// If present, load config
		// If present, load config file
		if let Some(&ref config_file) = config.config_file.as_ref() {
				let s = read_to_string(config_file)?;
				let file_config = toml::from_str(&s)?;


@@ 69,6 69,7 @@ async fn main() -> Result<()> {
	let pepper = config.pepper.expect("No pepper");
	let feed_check_interval =
		config.feed_check_interval.expect("No feed_check_interval");
	let disable_logins = config.disable_logins.expect("No disable_logins");
	let global_concurrent_limit = config
		.rate_limiting
		.global_concurrent_limit


@@ 88,6 89,7 @@ async fn main() -> Result<()> {
		readers,
		pepper.as_bytes().to_vec(),
		feed_check_interval,
		disable_logins,
	)?);

	match command {

M src/persistence/mod.rs => src/persistence/mod.rs +6 -1
@@ 57,10 57,15 @@ pub trait RussetEntryPersistenceLayer: Send + Sync + std::fmt::Debug + 'static {
	fn get_and_increment_fetch_index(&self)
		-> impl Future<Output = Result<u32>> + Send;

	/// get all the entries for all the feeds to which the given user is subscribed.
	/// Get entries for all the feeds to which the given user is subscribed.
	fn get_entries_for_user(&self, user_id: &UserId, pagination: &Pagination)
		-> impl Future<Output = impl IntoIterator<Item = Result<(Entry, Option<UserEntry>)>>> + Send;

	/// Get entries for the given feed to which the given user is subscribed
	fn get_entries_for_user_feed(&self, user_id: &UserId, feed_id: &FeedId, pagination: &Pagination)
		-> impl Future<Output = impl IntoIterator<Item = Result<(Entry, Option<UserEntry>)>>> + Send;

	/// Atomically get an entry and set the userentry for the given entry and user.
	fn get_entry_and_set_userentry(
		&self,
		entry_id: &EntryId,

M src/persistence/sql/entry.rs => src/persistence/sql/entry.rs +11 -0
@@ 186,6 186,17 @@ impl RussetEntryPersistenceLayer for SqlDatabase {
	}

	#[tracing::instrument]
	async fn get_entries_for_user_feed(
		&self,
		user_id: &UserId,
		feed_id: &FeedId,
		pagination: &Pagination,
	) -> impl IntoIterator<Item = Result<(Entry, Option<UserEntry>)>> {
		todo!();
		vec![]
	}

	#[tracing::instrument]
	async fn get_entry_and_set_userentry(
		&self,
		entry_id: &EntryId,