core/db: Add auth levels for each connection
parent
8053422f18
commit
46b942b843
|
@ -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
|
||||
|
|
|
@ -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| {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
309
core/src/db.rs
309
core/src/db.rs
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
}
|
|
@ -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))
|
||||
|
|
Loading…
Reference in New Issue