chapps.switchboard module
Communication handlers
This module encapsulates the particular logic of:
receiving data payloads from Postfix, and then
sending back an appropriately-formatted response once the policy manager has had a chance to weigh in on the payload contents
Classes defined here exist mainly to be factories which return the main-loop
closure for asyncio
.
TODO: RequestHandler
should be a subclass of
CascadingPolicyHandler
which simply only ever has one policy within it.
This is to avoid maintaining two nearly-identical code-paths. Running only one
policy is obviously a special case of running many.
- class chapps.switchboard.CascadingPolicyHandler[source]
Bases:
object
Second-generation handler class which cascades multiple yes/no policies
This class started out nearly identical to
RequestHandler
, but as the software has moved on, so has this handler, which is the main one in general use.This handler accepts a list of policy manager instances, all of which should produce True/False results. The handler applies each policy to each request, and passes those which pass both, or returns the message from the policy which failed. Once a policy has failed, no further policies are consulted.
Instance attributes:
- policies
a list of
EmailPolicy
objects- pprclass
the class of
PostfixPolicyRequest
to instantiate from the Postfix request payload- config
a reference to the
CHAPPSConfig
stored on the first policy in the list- listen_address
the IP address to bind to; see
listen_address()
- listen_port
the port to listen on; see
listen_port()
- __init__(policies=[], *, pprclass=<class 'chapps.util.PostfixPolicyRequest'>)[source]
- Parameters
policies (Optional[List[chapps.policy.EmailPolicy]]) –
pprclass (chapps.util.PostfixPolicyRequest) –
- property listen_address
- property listen_port
- class chapps.switchboard.RequestHandler[source]
Bases:
chapps.switchboard.CascadingPolicyHandler
Refactored intermediate base class for wrapping policy managers in an event loop
This class has been reimplemented as a subclass of
CascadingPolicyHandler
, as a special case which has only one policy to handle.Instance attributes:
- policy
the
EmailPolicy
manager instance- config
a reference to the
CHAPPSConfig
stored on the policy instance.- pprclass
a reference to the particular kind of
PostfixPolicyRequest
to instantiate
- __init__(policy, *, pprclass=<class 'chapps.util.PostfixPolicyRequest'>)[source]
Setup a Postfix policy request handler
- Parameters
policy (chapps.policy.EmailPolicy) – an instance of a policy manager (a subclass of
EmailPolicy
)pprclass (Type[PostfixPolicyRequest]) – the subclass of
PostfixPolicyRequest
to instantiate from the Postfix payloads; defaults toPostfixPolicyRequest
Note
Unlike other class families within CHAPPS, the handlers in this module do not accept config-override arguments. They obtain their references to the config from their attached policy managers.
- property listen_address
- property listen_port
- property policy
- class chapps.switchboard.OutboundMultipolicyHandler[source]
Bases:
chapps.switchboard.CascadingPolicyHandler
Convenience subclass for combining outbound P/F policies
Could be thought of as a concrete subclass of
CascadingPolicyHandler
, but meant more as a convenience.- __init__(policies=[], *, pprclass=<class 'chapps.outbound.OutboundPPR'>)[source]
Setup an OutboundMultipolicyHandler
- Parameters
policies (List[EmailPolicy]) – a list of policy manager instances
pprclass (Type[PostfixPolicyRequest]) – kind of
PostfixPolicyRequest
to instantiate from Postfix request payloads; defaults toOutboundPPR
If none are provided, default-configured instances of
SenderDomainAuthPolicy
andOutboundQuotaPolicy
are used, in that order.
- class chapps.switchboard.OutboundQuotaHandler[source]
Bases:
chapps.switchboard.RequestHandler
Convenience class for wrapping
OutboundQuotaPolicy
- __init__(policy=None)[source]
Setup an OutboundQuotaHandler
- Parameters
policy (chapps.policy.OutboundQuotaPolicy) – an instance of
OutboundQuotaPolicy
- class chapps.switchboard.GreylistingHandler[source]
Bases:
chapps.switchboard.RequestHandler
Convenience class for wrapping
GreylistingPolicy
- __init__(policy=None)[source]
Setup a GreylistingHandler
- Parameters
policy (chapps.policy.GreylistingPolicy) – an instance of
GreylistingPolicy
- class chapps.switchboard.SenderDomainAuthHandler[source]
Bases:
chapps.switchboard.RequestHandler
Convenience class for wrapping
SenderDomainAuthPolicy
- __init__(policy=None)[source]
Setup a SenderDomainAuthHandler
- Parameters
policy (chapps.policy.SenderDomainAuthPolicy) – an instance of
SenderDomainAuthPolicy
- class chapps.switchboard.CascadingMultiresultPolicyHandler[source]
Bases:
chapps.switchboard.CascadingPolicyHandler
Cascading handler for policies which produce more than two results
Some policies, such as the SPF enforcement policy, need to be able to generate responses with more nuance than simply pass/fail, and so there is a need for a handler which can deal with policies which return strings (Postfix directives), or possibly a custom class to encapsulate also some idea of pass/fail, in order to know whether to abort the policy-evaluation loop early.
- async_policy_handler()[source]
Returns a coroutine which handles results according to the configuration
The policy being enforced is stored in the SPF-related TXT record on the sender’s domain. The local configuration of this policy amounts to instructions about responses to different outcomes of the SPF check, along with what IP address and port to listen on.
This policy handler is different from others in that, because it does not expect a PASS/FAIL response, it simply wraps the return value of
approve_policy_request()
in a Postfix response packet, and sends it. Rather than refer to pre-configured acceptance and rejection messages, it expects the approval routine to send a string which can be interpreted by Postfix as a command.TODO: In order to be able to cascade through this kind of policy, it is going to have to return a first-class object which can be annotated as being a pass or a fail, so that a cascading handler can decide whether to continue. That object’s
__str__()
method will need to return the Postfix command.
- class chapps.switchboard.MultiresultPolicyHandler[source]
Bases:
chapps.switchboard.CascadingMultiresultPolicyHandler
A subclass for handling just one policy
- __init__(policy, *, pprclass=<class 'chapps.util.PostfixPolicyRequest'>)[source]
- Parameters
policy (chapps.policy.EmailPolicy) –
pprclass (Optional[chapps.util.PostfixPolicyRequest]) –
- class chapps.switchboard.SPFEnforcementHandler[source]
Bases:
chapps.switchboard.MultiresultPolicyHandler
Special handler class for
SPFEnforcementPolicy
This one came along last and forced a reconsideration of how all this worked, because it produces more than two possible states as output. The plan is to retrofit all the older policies so that they also can use an action-translation layer, but that will also require some adjustment of the cascading handler.
Note
This class will not be defined if the relevant SPF libraries could not be loaded. They may be installed via
pip
using the extras mechanism:pip install chapps[SPF]
- __init__(policy=None, pprclass=<class 'chapps.inbound.InboundPPR'>)[source]
Set up an SPFEnforcementHandler
- Parameters
policy (chapps.spf_policy.SPFEnforcementPolicy) – an instance of
SPFEnforcementPolicy
pprclass (Optional[chapps.util.PostfixPolicyRequest]) –
- property policy
- class chapps.switchboard.InboundMultipolicyHandler[source]
Bases:
chapps.switchboard.CascadingMultiresultPolicyHandler
Implements SPF and Greylisting simultaneously
This is a template for an inbound multipolicy handler, which by default checks SPF and also performs Greylisting, each of which depends upon options which the email account administrator may set about whether either of SPF or Greylisting or both should be applied to inbound mail for the particular domain.
- __init__(policies=None, *, pprclass=<class 'chapps.inbound.InboundPPR'>)[source]
Create an inbound policy handler for SPF + Greylisting
- Parameters
policies (Optional[List[chapps.policy.EmailPolicy]]) –
pprclass (Optional[chapps.util.PostfixPolicyRequest]) –