summaryrefslogtreecommitdiff
path: root/src/mailman/workflows/common.py
diff options
context:
space:
mode:
authorJ08nY2017-07-06 19:30:22 +0200
committerJ08nY2017-08-30 13:18:10 +0200
commit6a2de987622aad67ea3929d9a1b6a347a748b30d (patch)
tree221720b3dcd9c24d3dfc4c658c14805b606f3ac8 /src/mailman/workflows/common.py
parentf2cf2d0c96c0cf47e6dfa80137bb705ec4e8321b (diff)
downloadmailman-6a2de987622aad67ea3929d9a1b6a347a748b30d.tar.gz
mailman-6a2de987622aad67ea3929d9a1b6a347a748b30d.tar.zst
mailman-6a2de987622aad67ea3929d9a1b6a347a748b30d.zip
Diffstat (limited to 'src/mailman/workflows/common.py')
-rw-r--r--src/mailman/workflows/common.py123
1 files changed, 123 insertions, 0 deletions
diff --git a/src/mailman/workflows/common.py b/src/mailman/workflows/common.py
new file mode 100644
index 000000000..940152272
--- /dev/null
+++ b/src/mailman/workflows/common.py
@@ -0,0 +1,123 @@
+# Copyright (C) 2015-2017 by the Free Software Foundation, Inc.
+#
+# This file is part of GNU Mailman.
+#
+# GNU Mailman 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.
+#
+# GNU Mailman 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
+# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
+
+"""Common support between subscription and unsubscription."""
+
+import uuid
+
+from datetime import timedelta
+from enum import Enum
+from mailman.interfaces.address import IAddress
+from mailman.interfaces.pending import IPendings
+from mailman.interfaces.subscriptions import TokenOwner
+from mailman.interfaces.user import IUser
+from mailman.interfaces.usermanager import IUserManager
+from mailman.utilities.datetime import now
+from mailman.workflows.base import Workflow
+from zope.component import getUtility
+
+
+class WhichSubscriber(Enum):
+ address = 1
+ user = 2
+
+
+class SubscriptionWorkflowCommon(Workflow):
+ """Common support between subscription and unsubscription."""
+
+ PENDABLE_CLASS = None
+
+ def __init__(self, mlist, subscriber):
+ super().__init__()
+ self.mlist = mlist
+ self.address = None
+ self.user = None
+ self.which = None
+ self.member = None
+ self._set_token(TokenOwner.no_one)
+ # The subscriber must be either an IUser or IAddress.
+ if IAddress.providedBy(subscriber):
+ self.address = subscriber
+ self.user = self.address.user
+ self.which = WhichSubscriber.address
+ elif IUser.providedBy(subscriber):
+ self.address = subscriber.preferred_address
+ self.user = subscriber
+ self.which = WhichSubscriber.user
+ self.subscriber = subscriber
+
+ @property
+ def user_key(self):
+ # For save.
+ return self.user.user_id.hex
+
+ @user_key.setter
+ def user_key(self, hex_key):
+ # For restore.
+ uid = uuid.UUID(hex_key)
+ self.user = getUtility(IUserManager).get_user_by_id(uid)
+ if self.user is None:
+ self.user = self.address.user
+
+ @property
+ def address_key(self):
+ # For save.
+ return self.address.email
+
+ @address_key.setter
+ def address_key(self, email):
+ # For restore.
+ self.address = getUtility(IUserManager).get_address(email)
+ assert self.address is not None
+
+ @property
+ def subscriber_key(self):
+ return self.which.value
+
+ @subscriber_key.setter
+ def subscriber_key(self, key):
+ self.which = WhichSubscriber(key)
+
+ @property
+ def token_owner_key(self):
+ return self.token_owner.value
+
+ @token_owner_key.setter
+ def token_owner_key(self, value):
+ self.token_owner = TokenOwner(value)
+
+ def _set_token(self, token_owner):
+ assert isinstance(token_owner, TokenOwner)
+ pendings = getUtility(IPendings)
+ # Clear out the previous pending token if there is one.
+ if self.token is not None:
+ pendings.confirm(self.token)
+ # Create a new token to prevent replay attacks. It seems like this
+ # would produce the same token, but it won't because the pending adds a
+ # bit of randomization.
+ self.token_owner = token_owner
+ if token_owner is TokenOwner.no_one:
+ self.token = None
+ return
+ pendable = self.PENDABLE_CLASS(
+ list_id=self.mlist.list_id,
+ email=self.address.email,
+ display_name=self.address.display_name,
+ when=now().replace(microsecond=0).isoformat(),
+ token_owner=token_owner.name,
+ )
+ self.token = pendings.add(pendable, timedelta(days=3650))