GSoC 2017 - Pluggable Workflows

As I note in one of my previous GSoC project updates, the PGP-enabled mailing lists require two new features from Mailman Core, both related to workflows. The first is to be able to inject steps into a subscription workflow to request the users pubkey before moderation checks. The second it to be able to require confirmation on all commands (to prevent replay attacks), although this could be avoided by storing the hashes from all the user sent signed commands. In this post I propose a way of refactoring the workflows into pluggable components.

Current workflows#

Both of these requirements require changes to the way [Un]SubscriptionWorkflows and workflows in general work. Currently the subscription policy of a mailing list is determined by its subscription_policy attribute of the SubscriptionPolicy enum. That policy is enforced in the SubscriptionWorkflow state machine, which curently works with a queue of states, and looks like this:

sub_flow_before

It is quite complex, and the UnsubscriptionWorkflow has much of the same code except the verification and moderation parts.

Proposal#

What I propose is to make the [Un]SubscriptionWorkflows, more generally workflows, pluggable components which would work on a stack state machine and be composed of mixins like so:

sub_flow_components

This way a mailing list will have the name of its SubscriptionWorkflow component in the subscription_policy attribute. Which would mean for example an SubscriptionPolicy.open would be transformed into a class like so:

@public
@implementer(ISubscriptionWorkflow)
class OpenSubscriptionPolicy(SubscriptionBase, VerificationMixin):
    """"""

    name = 'policy-open'
    description = 'An open subscription policy, only requires verification.'
    initial_state = 'prepare'

    def _step_prepare(self):
        self.push('do_subscription')
        self.push('verification_checks')
        self.push('sanity_checks')

I believe this simplifies the policies and also allows chaining and composing of workflow steps, which would enable plugins to add custom subscription policies enforcing more steps/checks during the [un]subscription process. For example an organization-only mailing list could add a check that checks for the subscribing address in the organization database, therefore removing the need for manual moderation of subscriptions.

This should also allow for requiring confirmation of all commands, by attaching a workflow instance to them. For the membership commands, this would be the corresponding [Un]SubscriptionWorkflows, and for other commands it would be currently disabled/empty workflow. Then a plugin could set the confirm workflow on commands it needs to.

Comments?#

I am currently working on this as a MR, although I am not sure if this is the right track to go on with workflows. I think something to split the monolithic [Un]SubscriptionWorkflows would be a nice thing in Mailman Core, as they share a lot of the same code and are overly complex for what they are, a linear series of checks with some skipping possible.