chapps.policy module

Policy managers

All email policy managers inherit from EmailPolicy, which provides a fair amount of base functionality useful to its subclasses. So far, all but the SPFEnforcementPolicy are contained here. That one has extra dependencies which are thus kept isolated. Find it in spf_policy.

class chapps.policy.EmailPolicy[source]

Bases: object

Abstract policy manager

Subclasses must:

This abstract superclass provides a standard framework for constructing Redis keys, since the main purpose of the policy manager is to make decisions about email by consulting Redis.

Instance attributes:

config

chapps.config.CHAPPSConfig either passed in during initialization or inherited from the environment

params

chapps.util.AttrDict corresponding to the config for the policy manager

sentinel

a redis.Sentinel handle, or None if using only Redis

redis

a redis.Redis handle

redis_key_prefix = 'chapps'

A placeholder value, since this class is abstract

Subclasses should set this to a unique prefix identifying the policy manager. For examples, see the included subclasses.

static rediskey(prefix, *args)[source]

Format a string to serve as a Redis key for arbitrary data

Parameters
  • prefix (str) – a prefix unique to the policy manager (subclass)

  • args (List[str]) – a list of strings to use to construct the rest of the key

In CHAPPS, each policy has its own prefix. What other data the policy uses to construct the key is not relevant to any other entities, though it must be sent as a string.

This routine simply joins up all the tokens with colon (:) characters, so it is not recommended to use them as part of the key-components (although it should ‘just work’).

classmethod _fmtkey(*args)[source]

Convenience classmethod for Redis key construction

Parameters

args (List[str]) – a list of key components

Subclasses may use this method which automatically discovers the prefix.

__init__(cfg=None)[source]

Sets up a new policy manager

Parameters

cfg (chapps.config.CHAPPSConfig) – optional CHAPPSConfig object for config override

Store the config and get the params for the specific policy class, which are in a config block named for the class. Using that config, set the policy manager up with a Redis handle, and an instance cache (from expiring_dict.ExpiringDict)

approve_policy_request(ppr)[source]

Placeholder method which must be implemented by subclasses.

Return type

Union[str, bool]

Parameters

ppr (chapps.util.PostfixPolicyRequest) –

class chapps.policy.GreylistingPolicy[source]

Bases: chapps.policy.EmailPolicy

Policy manager which implements greylisting

Greylisting is a well-defined and frequently-implemented pattern. This implementation stores the tracking information in Redis.

Instance attributes (in addition to those of EmailPolicy):

min_defer

minimum time between retries, in seconds

cache_ttl

how long to store tracking data, in seconds

allow_after

success threshold, after which the client may be whitelisted

redis_key_prefix = 'grl'

Greylisting Redis key prefix

__init__(cfg=None, *, minimum_deferral=60, cache_ttl=86400, auto_allow_after=10)[source]

Initialize a greylisting policy manager

Parameters
  • cfg (chapps.config.CHAPPSConfig) – optional config override

  • minimum_deferral (int) – min time between retries, in seconds

  • cache_ttl (int) – tracking data cache expiration time in seconds

  • auto_allow_after (int) – number of successful attempts needed before source client is considered trusted, and no longer incurs deferrals

tuple_key(ppr)[source]

Return the greylisting tuple as a Redis key

The names of the values taken from ppr are as follows (in order):

  • client_address

  • sender

  • recipient

Return type

str

Parameters

ppr (chapps.util.PostfixPolicyRequest) –

client_key(ppr)[source]

Return the greylisting client key

This key indicates whether the client has enough successful resubmissions to be whitelisted.

approve_policy_request(ppr)[source]

Return True if the email should be accepted

Parameters

ppr (chapps.util.PostfixPolicyRequest) – the Postfix payload

class chapps.policy.OutboundQuotaPolicy[source]

Bases: chapps.policy.EmailPolicy

Policy manager which implements an outbound quota limitation

Outbound email is controlled based on the count of (attempted) transmissions in the last 24 hours. Some parameters are provided to fine-tune the behavior of the limiting algorithm.

Instance attributes (in addition to those of EmailPolicy):

interval

number of seconds to store transmission attemps, and to use for quota evaluation; defaults to one day

counting_recipients

a boolean determined from the config; whether to count each recipient of a multi-recipient email as a separate transmission for quota purposes

min_delta

defaults to 0; if set, the number of seconds which must elapse between send attempts. Currently experimental

redis_key_prefix = 'oqp'

OutboundQuotaPolicy Redis prefix

__init__(cfg=None, *, enforcement_interval=None, min_delta=0)[source]

Set up an outbound quota policy manager

Parameters
  • cfg (chapps.config.CHAPPSConfig) – optional config override

  • enforcement_interval (int) – number of seconds to store transmission attemps, and to use for quota evaluation; defaults to one day

  • min_delta (int) – Minimum time which must pass between transmission attempts; defaults to 5 seconds to prevent spamming. Set to 0 to disable

current_quota(user, quota)[source]

Provide real-time remaining quota for a user

Parameters
Returns

(remaining quota count, [remarks,…])

Return type

Tuple[int, List[str]]

The caller is anticipated to be the API. The User and Quota are both available, so the Quota may be provided, but it is not required. The user parameter is expected to contain a string at present, though this may change as the pydantic data models become more tightly integrated into the codebase.

The return value, intended for wrapping by the API and transmission to a client, is a tuple composed of:

  1. the number of transmission attempts remaining to the user at the moment the query executed

  2. a list of remarks created by the inspection routine

reset_quota(user)[source]

Reset quota for user

Parameters

user (str) – user-identifier

Returns

(number of records dropped, [remarks,…])

Return type

Tuple[int,List[str]]

This method is intended for real-time management of the Redis configuration mirror. It will drop all the attempts from the outbound-quota transmission-tracking list for the named user.

refresh_policy_cache(user, quota)[source]

API adapter method for refreshing policy config cache

Parameters
acquire_policy_for(user, quota=None)[source]

Populate Redis with policy config data for a user

Parameters
  • user (str) – user-identifier

  • quota (int) – optional quota to load for the user. This is provided mainly to optimize actions taken by the API.

Go get the policy for a sender from the policy adapter.

If the margin needs to be configured on a per-sender basis, this is the place to adjust that. Right now, the margin is set in the config file, and applied to each user as policy config is loaded.

approve_policy_request(ppr)[source]

Determine whether this email falls within the quota

Parameters

ppr (chapps.outbound.OutboundPPR) – the Postfix payload

Returns True if this email is within the quota.

This routine implements memoization on ppr.instance in order to overcome the Postfix double-checking weirdness. Sometimes, Postfix sends a request about a given email twice, but this is easy to spot because they will have the same value for ppr.instance.

class chapps.policy.SenderDomainAuthPolicy[source]

Bases: chapps.policy.EmailPolicy

Policy manager implementing domain and whole-email matching for senders

This class encapsulates explicit policy regarding what sorts of masquerading authenticated users are allowed to do. Currently, two sorts of matches are handled, in succession.

First, the domain part of the email address, the entire string after the @, is matched against Domain entries linked to the User.

If there is no Domain match, then Email entries linked to the User are checked. Email entries must match the entirety of a policy request’s sender attribute in order to pass.

redis_key_prefix = 'sda'

Sender domain auth Redis key prefix

__init__(cfg=None)[source]

Set up a new sender domain authorization policy manager

Parameters

cfg (chapps.config.CHAPPSConfig) – optional config override

sender_domain_key(ppr)[source]

Create a Redis key for a user->domain mapping

Parameters

ppr (chapps.outbound.OutboundPPR) – a Postfix payload

Returns

the sender domain key, by obtaining the domain part of the email address from ppr.sender

Return type

str

sender_email_key(ppr)[source]

Create a Redis key for a user->email mapping

Parameters

ppr (chapps.outbound.OutboundPPR) – a Postfix payload

Returns

the sender email key, by obtaining the email address from ppr.sender

Return type

str

_sender_domain_key(user, domain)[source]

Passes its two string params to _fmtkey

Parameters
  • user (str) – user-identifier

  • domain (str) – origin domain or email address

Returns

a Redis key

Return type

str

Should be called _sender_auth_key since it works with both domains and email addresses.

_get_sender_domain(ppr)[source]

Returns the domain portion of ppr.sender

Parameters

ppr (chapps.outbound.OutboundPPR) – a Postfix payload

Returns

the domain part of ppr.sender

Return type

str

Raises
acquire_policy_for(ppr)[source]

Populate Redis with policy config

Parameters

ppr (chapps.outbound.OutboundPPR) – a Postfix payload

Returns

whether the policy allows ppr

Return type

bool

Populates Redis and return the policy result for ppr.

approve_policy_request(ppr)[source]

Returns true if ppr represents an authorized email

Parameters

ppr (chapps.outbound.OutboundPPR) – a Postfix payload

Returns

whether the email represented by ppr should be transmitted

Return type

bool

Given a PPR, say whether this user is allowed to send as the apparent sender domain or address. Memoize result in the instance cache.

check_policy_cache(user, domain)[source]

Check a particular policy cache entry for the API

Parameters
  • user (str) – user-identifier

  • domain (str) – domain or email to check

Returns

the cached policy

Return type

chapps.rest.models.SDAStatus

clear_policy_cache(user, domain)[source]

Clear a specific policy cache entry

Parameters
  • user (str) – user-identifier

  • domain (str) – domain or email to clear

Returns

the previous policy

Return type

SDAStatus

bulk_clear_policy_cache(users, domains=None, emails=None)[source]

Clear SDA policy cache for Users x [Domains + Users]

Parameters
  • users (List[str]) – a list of user-identifiers

  • domains (Optional[List[str]]) – a list of domain names

  • emails (Optional[List[str]]) – a list of email addresses

Return type

None

bulk_check_policy_cache(users, domains=None, emails=None)[source]

Map auth subject onto user status

Parameters
  • users (List[str]) – a list of user-identifiers

  • domains (Optional[List[str]]) – a list of domain names

  • emails (Optional[List[str]]) – a list of email addresses

Returns

an auth subject => user => status map as described below

Return type

Dict[str, Dict[str, SDAStatus]]

Builds a map keyed on auth subject (Domain and/or Email), full of maps from username to status. It looks a bit like this:

Mainly intended for use by the API.