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:
objectSecond-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
EmailPolicyobjects- pprclass
the class of
PostfixPolicyRequestto instantiate from the Postfix request payload- config
a reference to the
CHAPPSConfigstored 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.CascadingPolicyHandlerRefactored 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
EmailPolicymanager instance- config
a reference to the
CHAPPSConfigstored on the policy instance.- pprclass
a reference to the particular kind of
PostfixPolicyRequestto 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
PostfixPolicyRequestto 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.CascadingPolicyHandlerConvenience 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
PostfixPolicyRequestto instantiate from Postfix request payloads; defaults toOutboundPPR
If none are provided, default-configured instances of
SenderDomainAuthPolicyandOutboundQuotaPolicyare used, in that order.
- class chapps.switchboard.OutboundQuotaHandler[source]
Bases:
chapps.switchboard.RequestHandlerConvenience 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.RequestHandlerConvenience 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.RequestHandlerConvenience 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.CascadingPolicyHandlerCascading 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.CascadingMultiresultPolicyHandlerA 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.MultiresultPolicyHandlerSpecial handler class for
SPFEnforcementPolicyThis 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
pipusing 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
SPFEnforcementPolicypprclass (Optional[chapps.util.PostfixPolicyRequest]) –
- property policy
- class chapps.switchboard.InboundMultipolicyHandler[source]
Bases:
chapps.switchboard.CascadingMultiresultPolicyHandlerImplements 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]) –