aboutsummaryrefslogtreecommitdiff
path: root/src/mailman_pgp/model
diff options
context:
space:
mode:
Diffstat (limited to 'src/mailman_pgp/model')
-rw-r--r--src/mailman_pgp/model/address.py44
-rw-r--r--src/mailman_pgp/model/db_key.py53
-rw-r--r--src/mailman_pgp/model/fs_key.py95
-rw-r--r--src/mailman_pgp/model/list.py66
4 files changed, 190 insertions, 68 deletions
diff --git a/src/mailman_pgp/model/address.py b/src/mailman_pgp/model/address.py
index 0cfefe2..a912e87 100644
--- a/src/mailman_pgp/model/address.py
+++ b/src/mailman_pgp/model/address.py
@@ -16,18 +16,16 @@
# this program. If not, see <http://www.gnu.org/licenses/>.
"""Model for PGP enabled addresses."""
-import os
-from os.path import exists, isfile, join
from mailman.database.types import SAUnicode
from mailman.interfaces.usermanager import IUserManager
-from pgpy import PGPKey
from sqlalchemy import Boolean, Column, Integer, String
from sqlalchemy.orm import reconstructor
from zope.component import getUtility
from mailman_pgp.config import config
from mailman_pgp.model.base import Base
+from mailman_pgp.model.fs_key import FSKey
class PGPAddress(Base):
@@ -41,15 +39,15 @@ class PGPAddress(Base):
key_confirmed = Column(Boolean, default=False)
def __init__(self, address):
- super().__init__()
+ super().__init__(email=address.email)
self._init()
- self.email = address.email
self._address = address
@reconstructor
def _init(self):
self._address = None
- self._key = None
+ self._key = FSKey(config.pgp.keydir_config['user_keydir'],
+ self.email + '.asc', True)
@property
def key(self):
@@ -58,33 +56,22 @@ class PGPAddress(Base):
:return:
:rtype: pgpy.PGPKey
"""
- if self.key_fingerprint is None:
- return None
- if self._key is None:
- if exists(self.key_path) and isfile(self.key_path):
- self._key, _ = PGPKey.from_file(self.key_path)
- return self._key
+ self._key.reload()
+ return self._key.key
@key.setter
- def key(self, new_key):
+ def key(self, value):
"""
- :param new_key:
- :type new_key: PGPKey
+ :param value:
+ :type value: pgpy.PGPKey
"""
- if self.key_fingerprint is not None:
- try:
- os.remove(self.key_path)
- except FileNotFoundError:
- pass
- if new_key is None:
+ if value is None:
self.key_fingerprint = None
- self._key = None
else:
- self.key_fingerprint = str(new_key.fingerprint)
- with open(self.key_path, 'w') as out:
- out.write(str(new_key))
- self._key = new_key
+ self.key_fingerprint = value.fingerprint
+ self._key.key = value
+ self._key.save()
@property
def key_path(self):
@@ -93,10 +80,7 @@ class PGPAddress(Base):
:return:
:rtype: str
"""
- if self.key_fingerprint is None:
- return None
- return join(config.pgp.keydir_config['user_keydir'],
- self.key_fingerprint + '.asc')
+ return self._key.key_path
@property
def address(self):
diff --git a/src/mailman_pgp/model/db_key.py b/src/mailman_pgp/model/db_key.py
new file mode 100644
index 0000000..45eafc7
--- /dev/null
+++ b/src/mailman_pgp/model/db_key.py
@@ -0,0 +1,53 @@
+# Copyright (C) 2017 Jan Jancar
+#
+# This file is a part of the Mailman PGP plugin.
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU 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 General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Database stored PGP key."""
+from sqlalchemy import Column, Integer, LargeBinary
+from sqlalchemy.orm import reconstructor
+
+from mailman_pgp.model.base import Base
+from mailman_pgp.utils.pgp import key_from_blob
+
+
+class DBKey(Base):
+ """Database stored PGP key."""
+ __tablename__ = 'keys'
+
+ id = Column(Integer, primary_key=True)
+ key_material = Column(LargeBinary)
+
+ def __init__(self):
+ super().__init__()
+
+ @reconstructor
+ def _init(self):
+ self._key = None
+
+ @property
+ def key(self):
+ if self._key is None:
+ self._key = key_from_blob(self.key_material)
+ return self._key
+
+ @key.setter
+ def key(self, value):
+ if value is None:
+ self._key = None
+ self.key_material = bytes()
+ else:
+ self._key = value
+ self.key_material = bytes(value)
diff --git a/src/mailman_pgp/model/fs_key.py b/src/mailman_pgp/model/fs_key.py
new file mode 100644
index 0000000..c7c86eb
--- /dev/null
+++ b/src/mailman_pgp/model/fs_key.py
@@ -0,0 +1,95 @@
+# Copyright (C) 2017 Jan Jancar
+#
+# This file is a part of the Mailman PGP plugin.
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU 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 General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Filesystem stored PGP key."""
+from os import remove
+from os.path import getmtime, join
+
+from mailman_pgp.utils.file import locked_obj
+from mailman_pgp.utils.pgp import key_from_file
+
+
+class FSKey:
+ """Filesystem stored PGP key."""
+
+ def __init__(self, keydir, keyfile, load=False):
+ self._key = None
+ self._mtime = None
+ self.keydir = keydir
+ self.keyfile = keyfile
+ if load:
+ self.load()
+
+ @property
+ def key(self):
+ """
+
+ :rtype: pgpy.PGPKey
+ """
+ return self._key
+
+ @key.setter
+ def key(self, value):
+ """
+
+ :param value:
+ :type value: pgpy.PGPKey
+ """
+ self._key = value
+
+ @property
+ def key_path(self):
+ return join(self.keydir, self.keyfile)
+
+ @property
+ def lock_path(self):
+ return self.key_path + '.lock'
+
+ def _load(self):
+ try:
+ self.key = key_from_file(self.key_path)
+ self._mtime = getmtime(self.key_path)
+ except FileNotFoundError:
+ self.key = None
+ self._mtime = None
+
+ @locked_obj('lock_path')
+ def load(self):
+ self._load()
+
+ @locked_obj('lock_path')
+ def reload(self):
+ if self.key is None:
+ self._load()
+ else:
+ mtime = getmtime(self.key_path)
+ if self._mtime is None or mtime > self._mtime:
+ self._load()
+
+ @locked_obj('lock_path')
+ def save(self):
+ if self.key is None:
+ remove(self.key_path)
+ self._mtime = None
+ else:
+ with open(self.key_path, 'w') as key_file:
+ key_file.write(str(self.key))
+ self._mtime = getmtime(self.key_path)
+
+ @locked_obj('lock_path')
+ def delete(self):
+ remove(self.key_path)
diff --git a/src/mailman_pgp/model/list.py b/src/mailman_pgp/model/list.py
index 8448368..838bcab 100644
--- a/src/mailman_pgp/model/list.py
+++ b/src/mailman_pgp/model/list.py
@@ -17,14 +17,9 @@
"""Model for PGP enabled mailing lists."""
-from os import remove
-from os.path import exists, isfile, join
-
-from flufl.lock import Lock
from mailman.database.types import Enum, SAUnicode
from mailman.interfaces.action import Action
from mailman.interfaces.listmanager import (IListManager, ListDeletingEvent)
-from pgpy import PGPKey
from public import public
from sqlalchemy import Boolean, Column, Integer
from sqlalchemy.orm import reconstructor
@@ -34,7 +29,7 @@ from zope.event import classhandler
from mailman_pgp.config import config
from mailman_pgp.database import transaction
from mailman_pgp.model.base import Base
-from mailman_pgp.pgp.keygen import ListKeyGenerator
+from mailman_pgp.model.fs_key import FSKey
@public
@@ -44,7 +39,7 @@ class PGPMailingList(Base):
__tablename__ = 'pgp_lists'
id = Column(Integer, primary_key=True)
- list_id = Column(SAUnicode, index=True)
+ list_id = Column(SAUnicode, index=True, unique=True)
# Signature related properties
unsigned_msg_action = Column(Enum(Action), default=Action.reject)
@@ -61,16 +56,20 @@ class PGPMailingList(Base):
encrypt_outgoing = Column(Boolean, default=True)
def __init__(self, mlist):
- super().__init__()
+ """
+
+ :param mlist:
+ :type mlist: mailman.model.mailinglist.MailingList
+ """
+ super().__init__(list_id=mlist.list_id)
self._init()
- self.list_id = mlist.list_id
self._mlist = mlist
@reconstructor
def _init(self):
self._mlist = None
- self._key = None
- self._key_generator = None
+ self._key = FSKey(config.pgp.keydir_config['list_keydir'],
+ self.list_id + '.asc', True)
@property
def mlist(self):
@@ -84,44 +83,34 @@ class PGPMailingList(Base):
return self._mlist
@property
+ def fs_key(self):
+ return self._key
+
+ @property
def key(self):
"""
+ The private part of the list's keypair.
:return:
:rtype: pgpy.PGPKey
"""
- if self._key is None:
- # Check the file
- if exists(self.key_path) and isfile(self.key_path):
- self._key, _ = PGPKey.from_file(self.key_path)
- return self._key
+ self._key.reload()
+ return self._key.key
@key.setter
def key(self, value):
- with Lock(self.key_path + '.lock'):
- self._key = value
- if value is None:
- remove(self.key_path)
- else:
- with open(self.key_path, 'w') as key_file:
- key_file.write(str(value))
+ """
- def generate_key(self, block=False):
- self._key = None
- self._key_generator = ListKeyGenerator(config.pgp.primary_key_args,
- config.pgp.sub_key_args,
- self.mlist.display_name,
- self.mlist.posting_address,
- self.mlist.request_address,
- self.key_path)
- self._key_generator.start()
- if block:
- self._key_generator.join()
- return self.key
+ :param value:
+ :type value:
+ """
+ self._key.key = value
+ self._key.save()
@property
def pubkey(self):
"""
+ The public part of the list's keypair.
:return:
:rtype: pgpy.PGPKey
@@ -133,18 +122,19 @@ class PGPMailingList(Base):
@property
def key_path(self):
"""
+ The path to this list's key in the `list_keydir`.
- :return:
+ :return: List key path.
:rtype: str
"""
- return join(config.pgp.keydir_config['list_keydir'],
- self.list_id + '.asc')
+ return self._key.key_path
@staticmethod
def for_list(mlist):
"""
:param mlist:
+ :type mlist: mailman.model.mailinglist.MailingList
:return:
:rtype: PGPMailingList|None
"""