core: Add MimeReject filter stub

axum-login-upgrade
Manos Pitsidianakis 2023-07-29 10:59:29 +03:00
parent e82eb8fe84
commit 89059c1411
Signed by: Manos Pitsidianakis
GPG Key ID: 7729C7707F7E09D0
9 changed files with 299 additions and 2 deletions

View File

@ -27,7 +27,7 @@ lint:
.PHONY: test
test: check lint
@$(CARGOBIN) test --all --no-fail-fast --all-features
@$(CARGOBIN) nextest run --all --no-fail-fast --all-features
.PHONY: rustdoc
rustdoc:

View File

@ -0,0 +1,87 @@
import json
from pathlib import Path
import re
import sys
import pprint
import argparse
def make_undo(id: str) -> str:
return f"DELETE FROM settings_json_schema WHERE id = '{id}';"
def make_redo(id: str, value: str) -> str:
return f"""INSERT OR REPLACE INTO settings_json_schema(id, value) VALUES('{id}', '{value}');"""
class Migration:
patt = re.compile(r"(\d+)[.].*sql")
def __init__(self, path: Path):
name = path.name
self.path = path
self.is_data = "data" in name
self.is_undo = "undo" in name
m = self.patt.match(name)
self.seq = int(m.group(1))
self.name = name
def __str__(self) -> str:
return str(self.seq)
def __repr__(self) -> str:
return f"Migration(seq={self.seq},name={self.name},path={self.path},is_data={self.is_data},is_undo={self.is_undo})"
if __name__ == "__main__":
parser = argparse.ArgumentParser(
prog="Create migrations", description="", epilog=""
)
parser.add_argument("--data", action="store_true")
parser.add_argument("--settings", action="store_true")
parser.add_argument("--name", type=str, default=None)
parser.add_argument("--dry-run", action="store_true")
args = parser.parse_args()
migrations = {}
last = -1
for f in Path(".").glob("migrations/*.sql"):
m = Migration(f)
last = max(last, m.seq)
seq = str(m)
if seq not in migrations:
if m.is_undo:
migrations[seq] = (None, m)
else:
migrations[seq] = (m, None)
else:
if m.is_undo:
redo, _ = migrations[seq]
migrations[seq] = (redo, m)
else:
_, undo = migrations[seq]
migrations[seq] = (m, undo)
# pprint.pprint(migrations)
if args.data:
data = ".data"
else:
data = ""
new_name = f"{last+1:0>3}{data}.sql"
new_undo_name = f"{last+1:0>3}{data}.undo.sql"
if not args.dry_run:
redo = ""
undo = ""
if args.settings:
if not args.name:
print("Please define a --name.")
sys.exit(1)
redo = make_redo(args.name, "{}")
undo = make_undo(args.name)
name = args.name.lower() + ".json"
with open(Path("settings_json_schemas") / name, "x") as file:
file.write("{}")
with open(Path("migrations") / new_name, "x") as file, open(
Path("migrations") / new_undo_name, "x"
) as undo_file:
file.write(redo)
undo_file.write(undo)
print(f"Created to {new_name} and {new_undo_name}.")

View File

@ -0,0 +1,33 @@
INSERT OR REPLACE INTO settings_json_schema(id, value) VALUES('MimeRejectSettings', '{
"$schema": "http://json-schema.org/draft-07/schema",
"$ref": "#/$defs/MimeRejectSettings",
"$defs": {
"MimeRejectSettings": {
"title": "MimeRejectSettings",
"description": "Settings for MimeReject message filter",
"type": "object",
"properties": {
"enabled": {
"title": "If true, list posts that contain mime types in the reject array are rejected.",
"type": "boolean"
},
"reject": {
"title": "Mime types to reject.",
"type": "array",
"minLength": 0,
"items": { "$ref": "#/$defs/MimeType" }
},
"required": [
"enabled"
]
}
},
"MimeType": {
"type": "string",
"maxLength": 127,
"minLength": 3,
"uniqueItems": true,
"pattern": "^[a-zA-Z!#$&-^_]+[/][a-zA-Z!#$&-^_]+$"
}
}
}');

View File

@ -0,0 +1 @@
DELETE FROM settings_json_schema WHERE id = 'MimeRejectSettings';

View File

@ -0,0 +1,33 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"$ref": "#/$defs/MimeRejectSettings",
"$defs": {
"MimeRejectSettings": {
"title": "MimeRejectSettings",
"description": "Settings for MimeReject message filter",
"type": "object",
"properties": {
"enabled": {
"title": "If true, list posts that contain mime types in the reject array are rejected.",
"type": "boolean"
},
"reject": {
"title": "Mime types to reject.",
"type": "array",
"minLength": 0,
"items": { "$ref": "#/$defs/MimeType" }
},
"required": [
"enabled"
]
}
},
"MimeType": {
"type": "string",
"maxLength": 127,
"minLength": 3,
"uniqueItems": true,
"pattern": "^[a-zA-Z!#$&-^_]+[/][a-zA-Z!#$&-^_]+$"
}
}
}

View File

@ -55,6 +55,7 @@ impl Connection {
pub fn list_filters(&self, _list: &DbVal<MailingList>) -> Vec<Box<dyn PostFilter>> {
vec![
Box::new(PostRightsCheck),
Box::new(MimeReject),
Box::new(FixCRLF),
Box::new(AddListHeaders),
Box::new(ArchivedAtLink),
@ -376,3 +377,32 @@ impl PostFilter for FinalizeRecipients {
Ok((post, ctx))
}
}
/// Allow specific MIMEs only.
pub struct MimeReject;
impl PostFilter for MimeReject {
fn feed<'p, 'list>(
self: Box<Self>,
post: &'p mut PostEntry,
ctx: &'p mut ListContext<'list>,
) -> std::result::Result<(&'p mut PostEntry, &'p mut ListContext<'list>), ()> {
let reject = if let Some(mut settings) = ctx.filter_settings.remove("MimeRejectSettings") {
let map = settings.as_object_mut().unwrap();
let enabled = serde_json::from_value::<bool>(map.remove("enabled").unwrap()).unwrap();
if !enabled {
trace!(
"MimeReject is disabled from settings found for list.pk = {} \
skipping filter",
ctx.list.pk
);
return Ok((post, ctx));
}
serde_json::from_value::<Vec<String>>(map.remove("reject").unwrap())
} else {
return Ok((post, ctx));
};
trace!("Running MimeReject filter with reject = {:?}", reject);
Ok((post, ctx))
}
}

View File

@ -242,4 +242,36 @@ DROP TABLE list_settings_json;"###),(5,r###"INSERT OR REPLACE INTO settings_json
]
}
}
}');"###,r###"DELETE FROM settings_json_schema WHERE id = 'AddSubjectTagPrefixSettings';"###),]
}');"###,r###"DELETE FROM settings_json_schema WHERE id = 'AddSubjectTagPrefixSettings';"###),(7,r###"INSERT OR REPLACE INTO settings_json_schema(id, value) VALUES('MimeRejectSettings', '{
"$schema": "http://json-schema.org/draft-07/schema",
"$ref": "#/$defs/MimeRejectSettings",
"$defs": {
"MimeRejectSettings": {
"title": "MimeRejectSettings",
"description": "Settings for MimeReject message filter",
"type": "object",
"properties": {
"enabled": {
"title": "If true, list posts that contain mime types in the reject array are rejected.",
"type": "boolean"
},
"reject": {
"title": "Mime types to reject.",
"type": "array",
"minLength": 0,
"items": { "$ref": "#/$defs/MimeType" }
},
"required": [
"enabled"
]
}
},
"MimeType": {
"type": "string",
"maxLength": 127,
"minLength": 3,
"uniqueItems": true,
"pattern": "^[a-zA-Z!#$&-^_]+[/][a-zA-Z!#$&-^_]+$"
}
}
}');"###,r###"DELETE FROM settings_json_schema WHERE id = 'MimeRejectSettings';"###),]

View File

@ -613,3 +613,40 @@ INSERT OR REPLACE INTO settings_json_schema(id, value) VALUES('AddSubjectTagPref
}
}
}');
-- 007.data.sql
INSERT OR REPLACE INTO settings_json_schema(id, value) VALUES('MimeRejectSettings', '{
"$schema": "http://json-schema.org/draft-07/schema",
"$ref": "#/$defs/MimeRejectSettings",
"$defs": {
"MimeRejectSettings": {
"title": "MimeRejectSettings",
"description": "Settings for MimeReject message filter",
"type": "object",
"properties": {
"enabled": {
"title": "If true, list posts that contain mime types in the reject array are rejected.",
"type": "boolean"
},
"reject": {
"title": "Mime types to reject.",
"type": "array",
"minLength": 0,
"items": { "$ref": "#/$defs/MimeType" }
},
"required": [
"enabled"
]
}
},
"MimeType": {
"type": "string",
"maxLength": 127,
"minLength": 3,
"uniqueItems": true,
"pattern": "^[a-zA-Z!#$&-^_]+[/][a-zA-Z!#$&-^_]+$"
}
}
}');

View File

@ -177,3 +177,47 @@ fn test_settings_json() {
assert!(is_valid);
assert!(new_last_modified != last_modified);
}
#[test]
fn test_settings_json_schemas() {
init_stderr_logging();
let tmp_dir = TempDir::new().unwrap();
let db_path = tmp_dir.path().join("mpot.db");
std::fs::copy("../mailpot-tests/for_testing.db", &db_path).unwrap();
let mut perms = std::fs::metadata(&db_path).unwrap().permissions();
#[allow(clippy::permissions_set_readonly_false)]
perms.set_readonly(false);
std::fs::set_permissions(&db_path, perms).unwrap();
let config = Configuration {
send_mail: SendMail::ShellCommand("/usr/bin/false".to_string()),
db_path,
data_path: tmp_dir.path().to_path_buf(),
administrators: vec![],
};
let db = Connection::open_or_create_db(config).unwrap().trusted();
let schemas: Vec<String> = {
let mut stmt = db
.connection
.prepare("SELECT value FROM list_settings_json;")
.unwrap();
let iter = stmt
.query_map([], |row| {
let value: String = row.get("value")?;
Ok(value)
})
.unwrap();
let mut ret = vec![];
for item in iter {
ret.push(item.unwrap());
}
ret
};
println!("Testing that schemas are valid…");
for schema in schemas {
let schema: Value = serde_json::from_str(&schema).unwrap();
let _compiled = JSONSchema::compile(&schema).expect("A valid schema");
}
}