diff options
| author | J08nY | 2017-06-07 01:27:54 +0200 |
|---|---|---|
| committer | J08nY | 2017-06-07 01:27:54 +0200 |
| commit | 25487795779c05ff8e97680550948443924b98c0 (patch) | |
| tree | 8646cdebfa7f780851437b4c4099ab8e06c63dea /src | |
| parent | a662f9f9681964e10d375c6380cf17e7203421d7 (diff) | |
| download | mailman-pgp-25487795779c05ff8e97680550948443924b98c0.tar.gz mailman-pgp-25487795779c05ff8e97680550948443924b98c0.tar.zst mailman-pgp-25487795779c05ff8e97680550948443924b98c0.zip | |
Diffstat (limited to 'src')
| -rw-r--r-- | src/pgpmailman/commands/eml_key.py | 2 | ||||
| -rw-r--r-- | src/pgpmailman/config/__init__.py | 8 | ||||
| -rw-r--r-- | src/pgpmailman/config/mailman.cfg | 18 | ||||
| -rw-r--r-- | src/pgpmailman/config/pgpmailman.cfg | 10 | ||||
| -rw-r--r-- | src/pgpmailman/database/__init__.py | 36 | ||||
| -rw-r--r-- | src/pgpmailman/model/__init__.py | 0 | ||||
| -rw-r--r-- | src/pgpmailman/model/base.py | 8 | ||||
| -rw-r--r-- | src/pgpmailman/model/list.py | 24 | ||||
| -rw-r--r-- | src/pgpmailman/plugin.py | 34 | ||||
| -rw-r--r-- | src/pgpmailman/rest/lists.py | 49 | ||||
| -rw-r--r-- | src/pgpmailman/rest/root.py | 11 | ||||
| -rw-r--r-- | src/pgpmailman/rest/users.py | 1 | ||||
| -rw-r--r-- | src/pgpmailman/runners/incoming.py | 30 | ||||
| -rw-r--r-- | src/pgpmailman/runners/outgoing.py | 17 | ||||
| -rw-r--r-- | src/pgpmailman/styles/announce.py | 11 | ||||
| -rw-r--r-- | src/pgpmailman/styles/base.py | 23 | ||||
| -rw-r--r-- | src/pgpmailman/styles/discussion.py | 10 |
17 files changed, 260 insertions, 32 deletions
diff --git a/src/pgpmailman/commands/eml_key.py b/src/pgpmailman/commands/eml_key.py index ba538fa..03c0877 100644 --- a/src/pgpmailman/commands/eml_key.py +++ b/src/pgpmailman/commands/eml_key.py @@ -13,7 +13,7 @@ class KeyCommand: short_description = '' description = '' - def process(mlist, msg, msgdata, arguments, results): + def process(self, mlist, msg, msgdata, arguments, results): """See `IEmailCommand`.""" if len(arguments) == 0: print('No sub-command specified,' diff --git a/src/pgpmailman/config/__init__.py b/src/pgpmailman/config/__init__.py new file mode 100644 index 0000000..a6f7004 --- /dev/null +++ b/src/pgpmailman/config/__init__.py @@ -0,0 +1,8 @@ +"""""" + +from configparser import ConfigParser + +from public.public import public + +config = ConfigParser() +public(config=config) diff --git a/src/pgpmailman/config/mailman.cfg b/src/pgpmailman/config/mailman.cfg new file mode 100644 index 0000000..38ff416 --- /dev/null +++ b/src/pgpmailman/config/mailman.cfg @@ -0,0 +1,18 @@ + +[plugin.pgp] +class: pgpmailman.plugin.PGPMailman +path: pgpmailman +enable: yes +configuration: python:pgpmailman.config.pgpmailman + +[runner.in] +class: pgpmailman.runners.incoming.IncomingRunner + +[runner.in_default] +class: mailman.runners.incoming.IncomingRunner + +[runner.out] +class: pgpmailman.runners.outgoing.OutgoingRunner + +[runner.out_default] +class: mailman.runners.outgoing.OutgoingRunner diff --git a/src/pgpmailman/config/pgpmailman.cfg b/src/pgpmailman/config/pgpmailman.cfg new file mode 100644 index 0000000..541e9c6 --- /dev/null +++ b/src/pgpmailman/config/pgpmailman.cfg @@ -0,0 +1,10 @@ +[db] +url = sqlite:////$DATA_DIR/pgp.db + +[keyrings] +core = $DATA_DIR/pgp_core.gpp +users = $DATA_DIR/pgp_users.gpg + +[queues] +in = in_default +out = out_default
\ No newline at end of file diff --git a/src/pgpmailman/database/__init__.py b/src/pgpmailman/database/__init__.py index e69de29..01975ad 100644 --- a/src/pgpmailman/database/__init__.py +++ b/src/pgpmailman/database/__init__.py @@ -0,0 +1,36 @@ +"""""" + +from contextlib import contextmanager + +from mailman.config import config as mailman_config +from mailman.utilities.string import expand +from public import public +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker + +from pgpmailman.config import config +from pgpmailman.model.base import Base + + +@public +class Database: + def __init__(self): + url = config.get('db', 'url') + self.url = expand(url, None, mailman_config.paths) + self.engine = create_engine(self.url) + Session = sessionmaker(bind=self.engine) + self.session = Session() + Base.metadata.create_all(self.engine) + self.session.commit() + + +@public +@contextmanager +def transaction(): + try: + yield + except: + config.db.session.abort() + raise + else: + config.db.session.commit() diff --git a/src/pgpmailman/model/__init__.py b/src/pgpmailman/model/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/pgpmailman/model/__init__.py diff --git a/src/pgpmailman/model/base.py b/src/pgpmailman/model/base.py new file mode 100644 index 0000000..e25407f --- /dev/null +++ b/src/pgpmailman/model/base.py @@ -0,0 +1,8 @@ +"""""" + +from public import public +from sqlalchemy.ext.declarative import declarative_base + + +Base = declarative_base() +public(Base=Base) diff --git a/src/pgpmailman/model/list.py b/src/pgpmailman/model/list.py new file mode 100644 index 0000000..98b0078 --- /dev/null +++ b/src/pgpmailman/model/list.py @@ -0,0 +1,24 @@ +"""""" + +from mailman.database.types import Enum, SAUnicode +from mailman.interfaces.action import Action +from public import public +from sqlalchemy import Boolean, Column, Integer + +from pgpmailman.model.base import Base + + +@public +class EncryptedMailingList(Base): + __tablename__ = 'encrypted_lists' + + id = Column(Integer, primary_key=True) + list_id = Column(SAUnicode) + key_fingerprint = Column(SAUnicode) + unsigned_msg_action = Column(Enum(Action)) + nonencrypted_msg_action = Column(Enum(Action)) + strip_original_signature = Column(Boolean) + sign_outgoing = Column(Boolean) + + def __init__(self, mlist): + self.list_id = mlist.list_id diff --git a/src/pgpmailman/plugin.py b/src/pgpmailman/plugin.py index 5ae51a0..820d0a9 100644 --- a/src/pgpmailman/plugin.py +++ b/src/pgpmailman/plugin.py @@ -1,21 +1,28 @@ -""" A PGP plugin for GNU Mailman.""" - +"""A PGP plugin for GNU Mailman.""" +from mailman.app import events +from mailman.config import config as mailman_config +from mailman.interfaces.listmanager import ListDeletedEvent from mailman.interfaces.plugin import IPlugin -from pgpmailman.rest.root import RESTRoot +from mailman.utilities.modules import expand_path from public import public from zope.interface import implementer +from pgpmailman.config import config +from pgpmailman.database import Database, transaction +from pgpmailman.model.list import EncryptedMailingList +from pgpmailman.rest.root import RESTRoot + @public @implementer(IPlugin) class PGPMailman: - - def __init__(self): - self._rest = RESTRoot() - def pre_hook(self): """See `IPlugin`.""" - pass + config.read( + expand_path( + dict(mailman_config.plugin_configs)[self.name].configuration)) + config.db = Database() + config.name = self.name def post_hook(self): """See `IPlugin`.""" @@ -23,4 +30,13 @@ class PGPMailman: def rest_object(self): """See `IPlugin`.""" - return self._rest + return RESTRoot() + + +@events.subscribe(ListDeletedEvent) +def on_delete(mlist): + encrypted_list = config.db.session.query(EncryptedMailingList).filter_by( + list_id=mlist.list_id).first() + if encrypted_list: + with transaction(): + config.db.session.delete(encrypted_list) diff --git a/src/pgpmailman/rest/lists.py b/src/pgpmailman/rest/lists.py index 2c4a58a..fa785ee 100644 --- a/src/pgpmailman/rest/lists.py +++ b/src/pgpmailman/rest/lists.py @@ -1,15 +1,54 @@ """""" +from mailman.rest.helpers import ( + child, CollectionMixin, etag, not_found, NotFound, okay) from public import public +from pgpmailman.config import config +from pgpmailman.model.list import EncryptedMailingList + + +class _EncryptedBase(CollectionMixin): + def _resource_as_dict(self, emlist): + """See `CollectionMixin`.""" + return dict(list_id=emlist.list_id, + key_fingerprint=emlist.key_fingerprint, + unsigned_msg_action=emlist.unsigned_msg_action, + nonencrypted_msg_action=emlist.nonencrypted_msg_action, + strip_original_signature=emlist.strip_original_signature, + sign_outgoing=emlist.sign_outgoing, + self_link=self.api.path_to( + '/plugins/{}/lists/{}'.format(config.name, + emlist.list_id))) + + def _get_collection(self, request): + """See `CollectionMixin`.""" + return config.db.session.query(EncryptedMailingList).all() + @public -class AllEncryptedLists: - pass +class AllEncryptedLists(_EncryptedBase): + def on_get(self, request, response): + """/lists""" + resource = self._make_collection(response) + return okay(response, etag(resource)) @public -class AnEncryptedList: +class AnEncryptedList(_EncryptedBase): + def __init__(self, list_id): + self._mlist = config.db.session.query(EncryptedMailingList).filter_by( + list_id=list_id).first() + + def on_get(self, request, response): + if self._mlist is None: + return not_found() + else: + okay(response, self._resource_as_json(self._mlist)) - def __init__(self, list_name): - pass + @child + def key(self, context, segments): + if self._mlist is None: + return NotFound(), [] + else: + pass diff --git a/src/pgpmailman/rest/root.py b/src/pgpmailman/rest/root.py index b937fd3..68afa96 100644 --- a/src/pgpmailman/rest/root.py +++ b/src/pgpmailman/rest/root.py @@ -14,10 +14,11 @@ REST root. """ from mailman.rest.helpers import child -from pgpmailman.rest.lists import AllEncryptedLists, AnEncryptedList -from pgpmailman.rest.users import AUser, AllUsers from public import public +from pgpmailman.rest.lists import AllEncryptedLists, AnEncryptedList +from pgpmailman.rest.users import AllUsers, AUser + @public class RESTRoot: @@ -26,9 +27,8 @@ class RESTRoot: if len(segments) == 0: return AllEncryptedLists(), [] else: - list_name = segments.pop(0) - # WIP Check whether it's an encrypted list we know of here. - return AnEncryptedList(list_name), segments + list_id = segments.pop(0) + return AnEncryptedList(list_id), segments @child() def users(self, context, segments): @@ -36,5 +36,4 @@ class RESTRoot: return AllUsers(), [] else: uid = segments.pop(0) - # WIP Check whether it's an encrypted user we know of here. return AUser(uid), segments diff --git a/src/pgpmailman/rest/users.py b/src/pgpmailman/rest/users.py index bc6798d..09990f6 100644 --- a/src/pgpmailman/rest/users.py +++ b/src/pgpmailman/rest/users.py @@ -10,5 +10,4 @@ class AllUsers: @public class AUser: - pass diff --git a/src/pgpmailman/runners/incoming.py b/src/pgpmailman/runners/incoming.py index ccf90f4..69bbba9 100644 --- a/src/pgpmailman/runners/incoming.py +++ b/src/pgpmailman/runners/incoming.py @@ -1,15 +1,39 @@ """The encryption-aware incoming runner.""" +from mailman.config import config as mailman_config from mailman.core.runner import Runner +from mailman.email.message import Message +from mailman.model.mailinglist import MailingList from public import public +from pgpmailman.config import config +from pgpmailman.model.list import EncryptedMailingList + @public class IncomingRunner(Runner): - def _dispose(self, mlist, msg, msgdata): + def _dispose(self, mlist: MailingList, msg: Message, msgdata: dict): """See `IRunner`.""" - pass # Is the message for an encrypted mailing list? If not, pass to default # incoming runner. If yes, go on. - + encrypted_list = config.db.query(EncryptedMailingList).filter_by( + list_id=mlist.list_id).first() + if not encrypted_list: + inq = config.get('queues', 'in') + mailman_config.switchboards[inq].enqueue(msg, msgdata, + listid=mlist.list_id) + return False # Is the message encrypted? + if msg.get_content_type() == 'multipart/signed' and msg.get_param( + 'protocol') == 'application/pgp-signature': + # only signed. + pass + elif msg.get_content_type() == 'multipart/encrypted' and msg.get_param( + 'protocol') == 'application/pgp-encrypted': + # definitely encrypted, might still be signed + pass + else: + # not encrypted or signed + pass + from email.iterators import _structure + _structure(msg) diff --git a/src/pgpmailman/runners/outgoing.py b/src/pgpmailman/runners/outgoing.py index d7d74bc..11118a2 100644 --- a/src/pgpmailman/runners/outgoing.py +++ b/src/pgpmailman/runners/outgoing.py @@ -1,11 +1,24 @@ """The encryption-aware outgoing runner""" +from mailman.config import config as mailman_config from mailman.core.runner import Runner +from mailman.email.message import Message +from mailman.model.mailinglist import MailingList from public import public +from pgpmailman.config import config +from pgpmailman.model.list import EncryptedMailingList + @public class OutgoingRunner(Runner): - def _dispose(self, mlist, msg, msgdata): + def _dispose(self, mlist: MailingList, msg: Message, msgdata: dict): """See `IRunner`.""" - pass + encrypted_list = config.db.query(EncryptedMailingList).filter_by( + list_id=mlist.list_id).first() + if not encrypted_list: + outq = config.get('queues', 'out') + mailman_config.switchboards[outq].enqueue(msg, + msgdata, + listid=mlist.list_id) + return False diff --git a/src/pgpmailman/styles/announce.py b/src/pgpmailman/styles/announce.py index f83c2ea..26fc01f 100644 --- a/src/pgpmailman/styles/announce.py +++ b/src/pgpmailman/styles/announce.py @@ -3,10 +3,15 @@ from mailman.styles.default import LegacyAnnounceOnly from public import public +from pgpmailman.styles.base import EncryptedStyle + @public -class Announce(LegacyAnnounceOnly): +class AnnounceStyle(LegacyAnnounceOnly, EncryptedStyle): + name = 'encrypted-announce' + description = 'Announce only encrypted mailing list style.' + def apply(self, mailing_list): """See `IStyle`.""" - super().apply(mailing_list) - # + LegacyAnnounceOnly.apply(self, mailing_list) + EncryptedStyle.apply(self, mailing_list) diff --git a/src/pgpmailman/styles/base.py b/src/pgpmailman/styles/base.py new file mode 100644 index 0000000..633b95b --- /dev/null +++ b/src/pgpmailman/styles/base.py @@ -0,0 +1,23 @@ +"""""" + +from public import public + +from pgpmailman.config import config +from pgpmailman.database import transaction +from pgpmailman.model.list import EncryptedMailingList + + +@public +class EncryptedStyle: + def apply(self, mailing_list): + """Creates the encrypted mailing list instance for the list it's + applied to. + """ + enc_list = config.db.session.query(EncryptedMailingList).filter_by( + list_id=mailing_list.list_id).first() + if enc_list: + return + + enc_list = EncryptedMailingList(mailing_list) + with transaction(): + config.db.session.add(enc_list) diff --git a/src/pgpmailman/styles/discussion.py b/src/pgpmailman/styles/discussion.py index 55304cd..e8c516a 100644 --- a/src/pgpmailman/styles/discussion.py +++ b/src/pgpmailman/styles/discussion.py @@ -3,9 +3,15 @@ from mailman.styles.default import LegacyDefaultStyle from public import public +from pgpmailman.styles.base import EncryptedStyle + @public -class Discussion(LegacyDefaultStyle): +class DiscussionStyle(LegacyDefaultStyle, EncryptedStyle): + name = 'encrypted-default' + description = 'Ordinary discussion encrypted mailing list style.' + def apply(self, mailing_list): """See `IStyle`.""" - super().apply(mailing_list) + LegacyDefaultStyle.apply(self, mailing_list) + EncryptedStyle.apply(self, mailing_list) |
