core/db: Add auth levels for each connection

newstuff
Manos Pitsidianakis 2023-04-03 17:18:14 +03:00
parent 8053422f18
commit 46b942b843
Signed by: Manos Pitsidianakis
GPG Key ID: 7729C7707F7E09D0
12 changed files with 536 additions and 173 deletions

View File

@ -223,7 +223,7 @@ fn run_app() -> std::result::Result<(), Box<dyn std::error::Error>> {
let conf = Configuration::from_file(config_path)
.map_err(|err| format!("Could not load config {config_path}: {err}"))?;
let db = Database::open_db(&conf).map_err(|err| format!("Couldn't open db: {err}"))?;
let db = Database::open_db(conf).map_err(|err| format!("Couldn't open db: {err}"))?;
let lists_values = db.list_lists()?;
{
//index.html

View File

@ -25,8 +25,6 @@ pub use mailpot::errors::*;
pub use mailpot::models::*;
pub use mailpot::*;
use std::sync::Arc;
use minijinja::{Environment, Source};
use percent_encoding::percent_decode_str;
use warp::Filter;
@ -45,11 +43,11 @@ async fn main() {
let config_path = std::env::args()
.nth(1)
.expect("Expected configuration file path as first argument.");
let conf = Arc::new(Configuration::from_file(config_path).unwrap());
let conf = Configuration::from_file(config_path).unwrap();
let conf1 = conf.clone();
let list_handler = warp::path!("lists" / i64).map(move |list_pk: i64| {
let db = Database::open_db(&conf1).unwrap();
let db = Database::open_db(conf1.clone()).unwrap();
let list = db.get_list(list_pk).unwrap().unwrap();
let months = db.months(list_pk).unwrap();
let posts = db
@ -90,7 +88,7 @@ async fn main() {
let post_handler =
warp::path!("list" / i64 / String).map(move |list_pk: i64, message_id: String| {
let message_id = percent_decode_str(&message_id).decode_utf8().unwrap();
let db = Database::open_db(&conf2).unwrap();
let db = Database::open_db(conf2.clone()).unwrap();
let list = db.get_list(list_pk).unwrap().unwrap();
let posts = db.list_posts(list_pk, None).unwrap();
let post = posts
@ -124,9 +122,8 @@ async fn main() {
});
let conf3 = conf.clone();
let index_handler = warp::path::end().map(move || {
let db = Database::open_db(&conf3).unwrap();
let db = Database::open_db(conf3.clone()).unwrap();
let lists_values = db.list_lists().unwrap();
dbg!(&lists_values);
let lists = lists_values
.iter()
.map(|list| {

View File

@ -259,7 +259,7 @@ fn run_app(opt: Opt) -> Result<()> {
};
let config = Configuration::from_file(opt.config.as_path())?;
use Command::*;
let mut db = Database::open_or_create_db(&config)?;
let mut db = Database::open_or_create_db(config)?;
match opt.cmd {
SampleConfig => {}
DumpDatabase => {
@ -449,7 +449,7 @@ fn run_app(opt: Opt) -> Result<()> {
no_subscriptions,
custom,
};
let new_val = db.set_list_policy(list.pk, policy)?;
let new_val = db.set_list_policy(policy)?;
println!("Added new policy with pk = {}", new_val.pk());
}
RemovePolicy { pk } => {
@ -463,7 +463,7 @@ fn run_app(opt: Opt) -> Result<()> {
address,
name,
};
let new_val = db.add_list_owner(list.pk, list_owner)?;
let new_val = db.add_list_owner(list_owner)?;
println!("Added new list owner {}", new_val);
}
RemoveListOwner { pk } => {
@ -572,7 +572,7 @@ fn run_app(opt: Opt) -> Result<()> {
match Envelope::from_bytes(input.as_bytes(), None) {
Ok(env) => {
if opt.debug {
std::dbg!(&env);
eprintln!("{:?}", &env);
}
db.post(&env, input.as_bytes(), dry_run)?;
}
@ -582,7 +582,7 @@ fn run_app(opt: Opt) -> Result<()> {
}
Err(err) => {
eprintln!("Could not parse message: {}", err);
let p = config.save_message(input)?;
let p = db.conf().save_message(input)?;
eprintln!("Message saved at {}", p.display());
return Err(err.into());
}

View File

@ -16,7 +16,7 @@ chrono = { version = "^0.4", features = ["serde", ] }
error-chain = { version = "0.12.4", default-features = false }
log = "0.4"
melib = { version = "*", default-features = false, features = ["smtp", "unicode_algorithms", "maildir_backend"], git = "https://github.com/meli/meli", rev = "2447a2c" }
rusqlite = { version = "^0.28", features = ["bundled"] }
rusqlite = { version = "^0.28", features = ["bundled", "trace", "hooks"] }
serde = { version = "^1", features = ["derive", ] }
serde_json = "^1"
toml = "^0.5"

View File

@ -42,30 +42,85 @@ pub use posts::*;
mod members;
pub use members::*;
fn log_callback(error_code: std::ffi::c_int, message: &str) {
match error_code {
rusqlite::ffi::SQLITE_NOTICE => log::info!("{}", message),
rusqlite::ffi::SQLITE_WARNING => log::warn!("{}", message),
_ => log::error!("{error_code} {}", message),
}
}
fn user_authorizer_callback(
auth_context: rusqlite::hooks::AuthContext<'_>,
) -> rusqlite::hooks::Authorization {
use rusqlite::hooks::{AuthAction, Authorization};
match auth_context.action {
AuthAction::Delete {
table_name: "error_queue" | "queue" | "candidate_membership" | "membership",
}
| AuthAction::Insert {
table_name: "post" | "error_queue" | "queue" | "candidate_membership" | "membership",
}
| AuthAction::Select
| AuthAction::Savepoint { .. }
| AuthAction::Transaction { .. }
| AuthAction::Read { .. }
| AuthAction::Function {
function_name: "strftime",
} => Authorization::Allow,
_ => Authorization::Deny,
}
}
impl Database {
pub fn open_db(conf: &Configuration) -> Result<Self> {
pub fn open_db(conf: Configuration) -> Result<Self> {
use rusqlite::config::DbConfig;
use std::sync::Once;
static INIT_SQLITE_LOGGING: Once = Once::new();
if !conf.db_path.exists() {
return Err("Database doesn't exist".into());
}
INIT_SQLITE_LOGGING.call_once(|| {
unsafe { rusqlite::trace::config_log(Some(log_callback)).unwrap() };
});
let conn = DbConnection::open(conf.db_path.to_str().unwrap())?;
conn.set_db_config(DbConfig::SQLITE_DBCONFIG_ENABLE_FKEY, true)?;
conn.set_db_config(DbConfig::SQLITE_DBCONFIG_ENABLE_TRIGGER, true)?;
conn.set_db_config(DbConfig::SQLITE_DBCONFIG_DEFENSIVE, true)?;
conn.set_db_config(DbConfig::SQLITE_DBCONFIG_TRUSTED_SCHEMA, false)?;
conn.busy_timeout(core::time::Duration::from_millis(500))?;
conn.busy_handler(Some(|times: i32| -> bool { times < 5 }))?;
conn.authorizer(Some(user_authorizer_callback));
Ok(Database {
conf: conf.clone(),
connection: DbConnection::open(conf.db_path.to_str().unwrap())?,
conf,
connection: conn,
})
}
pub fn open_or_create_db(conf: &Configuration) -> Result<Self> {
let mut db_path = conf.db_path.to_path_buf();
if db_path.is_dir() {
db_path.push(DB_NAME);
}
let mut create = false;
if !db_path.exists() {
info!("Creating {} database in {}", DB_NAME, db_path.display());
create = true;
std::fs::File::create(&db_path).context("Could not create db path")?;
}
if create {
pub fn trusted(self) -> Self {
self.connection
.authorizer::<fn(rusqlite::hooks::AuthContext<'_>) -> rusqlite::hooks::Authorization>(
None,
);
self
}
pub fn untrusted(self) -> Self {
self.connection.authorizer(Some(user_authorizer_callback));
self
}
pub fn open_or_create_db(conf: Configuration) -> Result<Self> {
if !conf.db_path.exists() {
let db_path = &conf.db_path;
use std::os::unix::fs::PermissionsExt;
info!("Creating database in {}", db_path.display());
std::fs::File::create(&db_path).context("Could not create db path")?;
let mut child = Command::new("sqlite3")
.arg(&db_path)
.stdin(Stdio::piped())
@ -91,26 +146,20 @@ impl Database {
permissions.set_mode(0o600); // Read/write for owner only.
file.set_permissions(permissions)?;
}
db_path = db_path
.canonicalize()
.context("Could not canonicalize db path")?;
let conn = DbConnection::open(db_path.to_str().unwrap())?;
Ok(Database {
conf: conf.clone(),
connection: conn,
})
Self::open_db(conf)
}
pub fn load_archives(&mut self, conf: &Configuration) -> Result<&mut Self> {
let archives_path = conf.data_path.clone();
pub fn conf(&self) -> &Configuration {
&self.conf
}
pub fn load_archives(&self) -> Result<()> {
let mut stmt = self.connection.prepare("ATTACH ? AS ?;")?;
for archive in std::fs::read_dir(&archives_path)? {
for archive in std::fs::read_dir(&self.conf.data_path)? {
let archive = archive?;
let path = archive.path();
let name = path.file_name().unwrap_or_default();
if name == DB_NAME {
if path == self.conf.db_path {
continue;
}
stmt.execute(rusqlite::params![
@ -118,9 +167,8 @@ impl Database {
name.to_str().unwrap()
])?;
}
drop(stmt);
Ok(self)
Ok(())
}
pub fn list_lists(&self) -> Result<Vec<DbVal<MailingList>>> {
@ -229,17 +277,83 @@ impl Database {
Ok(ret)
}
/// Remove an existing list policy.
///
/// ```
/// # use mailpot::{models::*, Configuration, Database, SendMail};
/// # use tempfile::TempDir;
///
/// # let tmp_dir = TempDir::new().unwrap();
/// # let db_path = tmp_dir.path().join("mpot.db");
/// # let config = Configuration {
/// # send_mail: SendMail::ShellCommand("/usr/bin/false".to_string()),
/// # db_path: db_path.clone(),
/// # storage: "sqlite3".to_string(),
/// # data_path: tmp_dir.path().to_path_buf(),
/// # };
///
/// # fn do_test(config: Configuration) {
/// let db = Database::open_or_create_db(config).unwrap().trusted();
/// let list_pk = db.create_list(MailingList {
/// pk: 0,
/// name: "foobar chat".into(),
/// id: "foo-chat".into(),
/// address: "foo-chat@example.com".into(),
/// description: None,
/// archive_url: None,
/// }).unwrap().pk;
/// db.set_list_policy(
/// PostPolicy {
/// pk: 0,
/// list: list_pk,
/// announce_only: false,
/// subscriber_only: true,
/// approval_needed: false,
/// no_subscriptions: false,
/// custom: false,
/// },
/// ).unwrap();
/// db.remove_list_policy(1, 1).unwrap();
/// # }
/// # do_test(config);
/// ```
/// ```should_panic
/// # use mailpot::{models::*, Configuration, Database, SendMail};
/// # use tempfile::TempDir;
///
/// # let tmp_dir = TempDir::new().unwrap();
/// # let db_path = tmp_dir.path().join("mpot.db");
/// # let config = Configuration {
/// # send_mail: SendMail::ShellCommand("/usr/bin/false".to_string()),
/// # db_path: db_path.clone(),
/// # storage: "sqlite3".to_string(),
/// # data_path: tmp_dir.path().to_path_buf(),
/// # };
///
/// # fn do_test(config: Configuration) {
/// let db = Database::open_or_create_db(config).unwrap().trusted();
/// db.remove_list_policy(1, 1).unwrap();
/// # }
/// # do_test(config);
/// ```
pub fn remove_list_policy(&self, list_pk: i64, policy_pk: i64) -> Result<()> {
let mut stmt = self
.connection
.prepare("DELETE FROM post_policy WHERE pk = ? AND list = ?;")?;
stmt.execute(rusqlite::params![&policy_pk, &list_pk,])?;
.prepare("DELETE FROM post_policy WHERE pk = ? AND list = ? RETURNING *;")?;
stmt.query_row(rusqlite::params![&policy_pk, &list_pk,], |_| Ok(()))
.map_err(|err| {
if matches!(err, rusqlite::Error::QueryReturnedNoRows) {
Error::from(err).chain_err(|| NotFound("list or list policy not found!"))
} else {
err.into()
}
})?;
trace!("remove_list_policy {} {}.", list_pk, policy_pk);
Ok(())
}
pub fn set_list_policy(&self, list_pk: i64, policy: PostPolicy) -> Result<DbVal<PostPolicy>> {
pub fn set_list_policy(&self, policy: PostPolicy) -> Result<DbVal<PostPolicy>> {
if !(policy.announce_only
|| policy.subscriber_only
|| policy.approval_needed
@ -251,33 +365,51 @@ impl Database {
.into(),
);
}
let list_pk = policy.list;
let mut stmt = self.connection.prepare("INSERT OR REPLACE INTO post_policy(list, announce_only, subscriber_only, approval_needed, no_subscriptions, custom) VALUES (?, ?, ?, ?, ?, ?) RETURNING *;")?;
let ret = stmt.query_row(
rusqlite::params![
&list_pk,
&policy.announce_only,
&policy.subscriber_only,
&policy.approval_needed,
&policy.no_subscriptions,
&policy.custom,
],
|row| {
let pk = row.get("pk")?;
Ok(DbVal(
PostPolicy {
let ret = stmt
.query_row(
rusqlite::params![
&list_pk,
&policy.announce_only,
&policy.subscriber_only,
&policy.approval_needed,
&policy.no_subscriptions,
&policy.custom,
],
|row| {
let pk = row.get("pk")?;
Ok(DbVal(
PostPolicy {
pk,
list: row.get("list")?,
announce_only: row.get("announce_only")?,
subscriber_only: row.get("subscriber_only")?,
approval_needed: row.get("approval_needed")?,
no_subscriptions: row.get("no_subscriptions")?,
custom: row.get("custom")?,
},
pk,
list: row.get("list")?,
announce_only: row.get("announce_only")?,
subscriber_only: row.get("subscriber_only")?,
approval_needed: row.get("approval_needed")?,
no_subscriptions: row.get("no_subscriptions")?,
custom: row.get("custom")?,
},
pk,
))
},
)?;
))
},
)
.map_err(|err| {
if matches!(
err,
rusqlite::Error::SqliteFailure(
rusqlite::ffi::Error {
code: rusqlite::ffi::ErrorCode::ConstraintViolation,
extended_code: 787
},
_
)
) {
Error::from(err).chain_err(|| NotFound("Could not find a list with this pk."))
} else {
err.into()
}
})?;
trace!("set_list_policy {:?}.", &ret);
Ok(ret)
@ -378,33 +510,58 @@ impl Database {
pub fn remove_list_owner(&self, list_pk: i64, owner_pk: i64) -> Result<()> {
self.connection
.execute(
"DELETE FROM list_owners WHERE list_pk = ? AND pk = ?;",
.query_row(
"DELETE FROM list_owner WHERE list = ? AND pk = ? RETURNING *;",
rusqlite::params![&list_pk, &owner_pk],
|_| Ok(()),
)
.chain_err(|| NotFound("List owner"))?;
.map_err(|err| {
if matches!(err, rusqlite::Error::QueryReturnedNoRows) {
Error::from(err).chain_err(|| NotFound("list or list owner not found!"))
} else {
err.into()
}
})?;
Ok(())
}
pub fn add_list_owner(&self, list_pk: i64, list_owner: ListOwner) -> Result<DbVal<ListOwner>> {
pub fn add_list_owner(&self, list_owner: ListOwner) -> Result<DbVal<ListOwner>> {
let mut stmt = self.connection.prepare(
"INSERT OR REPLACE INTO list_owner(list, address, name) VALUES (?, ?, ?) RETURNING *;",
)?;
let ret = stmt.query_row(
rusqlite::params![&list_pk, &list_owner.address, &list_owner.name,],
|row| {
let pk = row.get("pk")?;
Ok(DbVal(
ListOwner {
let list_pk = list_owner.list;
let ret = stmt
.query_row(
rusqlite::params![&list_pk, &list_owner.address, &list_owner.name,],
|row| {
let pk = row.get("pk")?;
Ok(DbVal(
ListOwner {
pk,
list: row.get("list")?,
address: row.get("address")?,
name: row.get("name")?,
},
pk,
list: row.get("list")?,
address: row.get("address")?,
name: row.get("name")?,
},
pk,
))
},
)?;
))
},
)
.map_err(|err| {
if matches!(
err,
rusqlite::Error::SqliteFailure(
rusqlite::ffi::Error {
code: rusqlite::ffi::ErrorCode::ConstraintViolation,
extended_code: 787
},
_
)
) {
Error::from(err).chain_err(|| NotFound("Could not find a list with this pk."))
} else {
err.into()
}
})?;
trace!("add_list_owner {:?}.", &ret);
Ok(ret)

View File

@ -0,0 +1,112 @@
/*
* This file is part of mailpot
*
* Copyright 2020 - Manos Pitsidianakis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
mod utils;
use mailpot::{models::*, Configuration, Database, SendMail};
use std::error::Error;
use tempfile::TempDir;
#[test]
fn test_authorizer() {
utils::init_stderr_logging();
let tmp_dir = TempDir::new().unwrap();
let db_path = tmp_dir.path().join("mpot.db");
let config = Configuration {
send_mail: SendMail::ShellCommand("/usr/bin/false".to_string()),
db_path: db_path.clone(),
storage: "sqlite3".to_string(),
data_path: tmp_dir.path().to_path_buf(),
};
let db = Database::open_or_create_db(config).unwrap();
assert!(db.list_lists().unwrap().is_empty());
for err in [
db.create_list(MailingList {
pk: 0,
name: "foobar chat".into(),
id: "foo-chat".into(),
address: "foo-chat@example.com".into(),
description: None,
archive_url: None,
})
.unwrap_err(),
db.remove_list_owner(1, 1).unwrap_err(),
db.remove_list_policy(1, 1).unwrap_err(),
db.set_list_policy(PostPolicy {
pk: 0,
list: 1,
announce_only: false,
subscriber_only: true,
approval_needed: false,
no_subscriptions: false,
custom: false,
})
.unwrap_err(),
] {
assert_eq!(
err.source()
.unwrap()
.downcast_ref::<rusqlite::ffi::Error>()
.unwrap(),
&rusqlite::ffi::Error {
code: rusqlite::ErrorCode::AuthorizationForStatementDenied,
extended_code: 23
},
);
}
assert!(db.list_lists().unwrap().is_empty());
let db = db.trusted();
for ok in [
db.create_list(MailingList {
pk: 0,
name: "foobar chat".into(),
id: "foo-chat".into(),
address: "foo-chat@example.com".into(),
description: None,
archive_url: None,
})
.map(|_| ()),
db.add_list_owner(ListOwner {
pk: 0,
list: 1,
address: String::new(),
name: None,
})
.map(|_| ()),
db.set_list_policy(PostPolicy {
pk: 0,
list: 1,
announce_only: false,
subscriber_only: true,
approval_needed: false,
no_subscriptions: false,
custom: false,
})
.map(|_| ()),
db.remove_list_policy(1, 1).map(|_| ()),
db.remove_list_owner(1, 1).map(|_| ()),
] {
ok.unwrap();
}
}

View File

@ -1,8 +1,30 @@
/*
* This file is part of mailpot
*
* Copyright 2020 - Manos Pitsidianakis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
mod utils;
use mailpot::{models::*, Configuration, Database, SendMail};
use tempfile::TempDir;
#[test]
fn test_init_empty() {
utils::init_stderr_logging();
let tmp_dir = TempDir::new().unwrap();
let db_path = tmp_dir.path().join("mpot.db");
@ -13,13 +35,14 @@ fn test_init_empty() {
data_path: tmp_dir.path().to_path_buf(),
};
let db = Database::open_or_create_db(&config).unwrap();
let db = Database::open_or_create_db(config).unwrap();
assert!(db.list_lists().unwrap().is_empty());
}
#[test]
fn test_list_creation() {
utils::init_stderr_logging();
let tmp_dir = TempDir::new().unwrap();
let db_path = tmp_dir.path().join("mpot.db");
@ -30,7 +53,7 @@ fn test_list_creation() {
data_path: tmp_dir.path().to_path_buf(),
};
let db = Database::open_or_create_db(&config).unwrap();
let db = Database::open_or_create_db(config).unwrap().trusted();
assert!(db.list_lists().unwrap().is_empty());
let foo_chat = db
.create_list(MailingList {

View File

@ -1,3 +1,24 @@
/*
* This file is part of mailpot
*
* Copyright 2020 - Manos Pitsidianakis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
mod utils;
use mailpot::{melib, models::*, Configuration, Database, SendMail};
use tempfile::TempDir;
@ -15,13 +36,7 @@ fn get_smtp_conf() -> melib::smtp::SmtpServerConf {
#[test]
fn test_error_queue() {
stderrlog::new()
.quiet(false)
.verbosity(15)
.show_module_names(true)
.timestamp(stderrlog::Timestamp::Millisecond)
.init()
.unwrap();
utils::init_stderr_logging();
let tmp_dir = TempDir::new().unwrap();
let db_path = tmp_dir.path().join("mpot.db");
@ -32,7 +47,7 @@ fn test_error_queue() {
data_path: tmp_dir.path().to_path_buf(),
};
let db = Database::open_or_create_db(&config).unwrap();
let db = Database::open_or_create_db(config).unwrap().trusted();
assert!(db.list_lists().unwrap().is_empty());
let foo_chat = db
.create_list(MailingList {
@ -47,23 +62,23 @@ fn test_error_queue() {
assert_eq!(foo_chat.pk(), 1);
let post_policy = db
.set_list_policy(
foo_chat.pk(),
PostPolicy {
pk: 0,
list: foo_chat.pk(),
announce_only: false,
subscriber_only: true,
approval_needed: false,
no_subscriptions: false,
custom: false,
},
)
.set_list_policy(PostPolicy {
pk: 0,
list: foo_chat.pk(),
announce_only: false,
subscriber_only: true,
approval_needed: false,
no_subscriptions: false,
custom: false,
})
.unwrap();
assert_eq!(post_policy.pk(), 1);
assert_eq!(db.error_queue().unwrap().len(), 0);
// drop privileges
let db = db.untrusted();
let input_bytes = include_bytes!("./test_sample_longmessage.eml");
let envelope = melib::Envelope::from_bytes(input_bytes, None).expect("Could not parse message");
match db

View File

@ -1,3 +1,24 @@
/*
* This file is part of mailpot
*
* Copyright 2020 - Manos Pitsidianakis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
mod utils;
use log::{trace, warn};
use mailin_embedded::{Handler, Response, Server, SslConfig};
use mailpot::{melib, models::*, Configuration, Database, SendMail};
@ -157,13 +178,8 @@ fn get_smtp_conf() -> melib::smtp::SmtpServerConf {
#[test]
fn test_smtp() {
stderrlog::new()
.quiet(false)
.verbosity(15)
.show_module_names(true)
.timestamp(stderrlog::Timestamp::Millisecond)
.init()
.unwrap();
utils::init_stderr_logging();
let tmp_dir = TempDir::new().unwrap();
let handler = MyHandler {
@ -192,7 +208,7 @@ fn test_smtp() {
data_path: tmp_dir.path().to_path_buf(),
};
let db = Database::open_or_create_db(&config).unwrap();
let db = Database::open_or_create_db(config).unwrap().trusted();
assert!(db.list_lists().unwrap().is_empty());
let foo_chat = db
.create_list(MailingList {
@ -207,18 +223,15 @@ fn test_smtp() {
assert_eq!(foo_chat.pk(), 1);
let post_policy = db
.set_list_policy(
foo_chat.pk(),
PostPolicy {
pk: 0,
list: foo_chat.pk(),
announce_only: false,
subscriber_only: true,
approval_needed: false,
no_subscriptions: false,
custom: false,
},
)
.set_list_policy(PostPolicy {
pk: 0,
list: foo_chat.pk(),
announce_only: false,
subscriber_only: true,
approval_needed: false,
no_subscriptions: false,
custom: false,
})
.unwrap();
assert_eq!(post_policy.pk(), 1);
@ -283,6 +296,7 @@ fn test_smtp() {
#[test]
fn test_smtp_mailcrab() {
use std::env;
utils::init_stderr_logging();
fn get_smtp_conf() -> melib::smtp::SmtpServerConf {
use melib::smtp::*;
@ -296,13 +310,6 @@ fn test_smtp_mailcrab() {
}
}
stderrlog::new()
.quiet(false)
.verbosity(15)
.show_module_names(true)
.timestamp(stderrlog::Timestamp::Millisecond)
.init()
.unwrap();
let Ok(mailcrab_ip) = env::var("MAILCRAB_IP") else {
warn!("MAILCRAB_IP env var not set, is mailcrab server running?");
return;
@ -320,7 +327,7 @@ fn test_smtp_mailcrab() {
data_path: tmp_dir.path().to_path_buf(),
};
let db = Database::open_or_create_db(&config).unwrap();
let db = Database::open_or_create_db(config).unwrap().trusted();
assert!(db.list_lists().unwrap().is_empty());
let foo_chat = db
.create_list(MailingList {
@ -335,18 +342,15 @@ fn test_smtp_mailcrab() {
assert_eq!(foo_chat.pk(), 1);
let post_policy = db
.set_list_policy(
foo_chat.pk(),
PostPolicy {
pk: 0,
list: foo_chat.pk(),
announce_only: false,
subscriber_only: true,
approval_needed: false,
no_subscriptions: false,
custom: false,
},
)
.set_list_policy(PostPolicy {
pk: 0,
list: foo_chat.pk(),
announce_only: false,
subscriber_only: true,
approval_needed: false,
no_subscriptions: false,
custom: false,
})
.unwrap();
assert_eq!(post_policy.pk(), 1);

View File

@ -1,8 +1,31 @@
/*
* This file is part of mailpot
*
* Copyright 2020 - Manos Pitsidianakis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
mod utils;
use mailpot::{models::*, Configuration, Database, SendMail};
use tempfile::TempDir;
#[test]
fn test_list_subscription() {
utils::init_stderr_logging();
let tmp_dir = TempDir::new().unwrap();
let db_path = tmp_dir.path().join("mpot.db");
@ -13,7 +36,7 @@ fn test_list_subscription() {
data_path: tmp_dir.path().to_path_buf(),
};
let db = Database::open_or_create_db(&config).unwrap();
let db = Database::open_or_create_db(config).unwrap().trusted();
assert!(db.list_lists().unwrap().is_empty());
let foo_chat = db
.create_list(MailingList {
@ -31,22 +54,22 @@ fn test_list_subscription() {
assert_eq!(lists.len(), 1);
assert_eq!(lists[0], foo_chat);
let post_policy = db
.set_list_policy(
foo_chat.pk(),
PostPolicy {
pk: 0,
list: foo_chat.pk(),
announce_only: false,
subscriber_only: true,
approval_needed: false,
no_subscriptions: false,
custom: false,
},
)
.set_list_policy(PostPolicy {
pk: 0,
list: foo_chat.pk(),
announce_only: false,
subscriber_only: true,
approval_needed: false,
no_subscriptions: false,
custom: false,
})
.unwrap();
assert_eq!(post_policy.pk(), 1);
assert_eq!(db.error_queue().unwrap().len(), 0);
let db = db.untrusted();
let input_bytes_1 = b"From: Name <user@example.com>
To: <foo-chat@example.com>
Subject: This is a post

View File

@ -0,0 +1,34 @@
/*
* This file is part of mailpot
*
* Copyright 2020 - Manos Pitsidianakis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use std::sync::Once;
static INIT_STDERR_LOGGING: Once = Once::new();
pub fn init_stderr_logging() {
INIT_STDERR_LOGGING.call_once(|| {
stderrlog::new()
.quiet(false)
.verbosity(15)
.show_module_names(true)
.timestamp(stderrlog::Timestamp::Millisecond)
.init()
.unwrap();
});
}

View File

@ -27,8 +27,6 @@ pub use mailpot::*;
use warp::Filter;
use std::sync::Arc;
/*
fn json_body() -> impl Filter<Extract = (String,), Error = warp::Rejection> + Clone {
// When accepting a body, we want a JSON body
@ -42,12 +40,12 @@ async fn main() {
let config_path = std::env::args()
.nth(1)
.expect("Expected configuration file path as first argument.");
let conf = Arc::new(Configuration::from_file(config_path).unwrap());
let conf = Configuration::from_file(config_path).unwrap();
let conf1 = conf.clone();
// GET /lists/:i64/policy
let policy = warp::path!("lists" / i64 / "policy").map(move |list_pk| {
let db = Database::open_or_create_db(&conf1).unwrap();
let db = Database::open_db(conf1.clone()).unwrap();
db.get_list_policy(list_pk)
.ok()
.map(|l| warp::reply::json(&l.unwrap()))
@ -57,7 +55,7 @@ async fn main() {
let conf2 = conf.clone();
//get("/lists")]
let lists = warp::path!("lists").map(move || {
let db = Database::open_or_create_db(&conf2).unwrap();
let db = Database::open_db(conf2.clone()).unwrap();
let lists = db.list_lists().unwrap();
warp::reply::json(&lists)
});
@ -65,7 +63,7 @@ async fn main() {
let conf3 = conf.clone();
//get("/lists/<num>")]
let lists_num = warp::path!("lists" / i64).map(move |list_pk| {
let db = Database::open_or_create_db(&conf3).unwrap();
let db = Database::open_db(conf3.clone()).unwrap();
let list = db.get_list(list_pk).unwrap();
warp::reply::json(&list)
});
@ -73,7 +71,7 @@ async fn main() {
let conf4 = conf.clone();
//get("/lists/<num>/members")]
let lists_members = warp::path!("lists" / i64 / "members").map(move |list_pk| {
let db = Database::open_or_create_db(&conf4).unwrap();
let db = Database::open_db(conf4.clone()).unwrap();
db.list_members(list_pk)
.ok()
.map(|l| warp::reply::json(&l))
@ -83,7 +81,7 @@ async fn main() {
let conf5 = conf.clone();
//get("/lists/<num>/owners")]
let lists_owners = warp::path!("lists" / i64 / "owners").map(move |list_pk| {
let db = Database::open_or_create_db(&conf5).unwrap();
let db = Database::open_db(conf.clone()).unwrap();
db.get_list_owners(list_pk)
.ok()
.map(|l| warp::reply::json(&l))