moderation.rst 7.38 KB
Newer Older
1 2 3 4
==========
Moderation
==========

5
Posts by members and nonmembers are subject to moderation checks during
6 7 8
incoming processing.  Different situations can cause such posts to be held for
moderator approval.

9
    >>> from mailman.app.lifecycle import create_list
10 11
    >>> mlist = create_list('test@example.com')

12 13 14 15 16 17
Members and nonmembers have a *moderation action* which can shortcut the
normal moderation checks.  The built-in chain does just a few checks first,
such as seeing if the message has a matching `Approved:` header, or if the
emergency flag has been set on the mailing list, or whether a mail loop has
been detected.

18 19 20 21 22 23 24 25
Mailing lists have a default moderation action, one for members and another
for nonmembers.  If a member's moderation action is ``None``, then the member
moderation check falls back to the appropriate list default.

A moderation action of `defer` means that no explicit moderation check is
performed and the rest of the rule chain processing proceeds as normal.  But
it is also common for first-time posters to have a `hold` action, meaning that
their messages are held for moderator approval for a while.
26 27 28 29 30

Nonmembers almost always have a `hold` action, though some mailing lists may
choose to set this default action to `discard`, meaning their posts would be
immediately thrown away.

31 32 33 34

Member moderation
=================

35 36 37 38
Posts by list members are moderated if the member's moderation action is not
deferred.  The default setting for the moderation action of new members is
determined by the mailing list's settings.  By default, a mailing list is not
set to moderate new member postings.
39

40
    >>> print(mlist.default_member_action)
41
    Action.defer
42 43

In order to find out whether the message is held or accepted, we can subscribe
44
to internal events that are triggered on each case.
45

46
    >>> from mailman.interfaces.chain import ChainEvent
47
    >>> def on_chain(event):
48
    ...     if isinstance(event, ChainEvent):
49 50 51 52
    ...         print(event)
    ...         print(event.chain)
    ...         print('Subject:', event.msg['subject'])
    ...         print('Hits:')
53
    ...         for hit in event.msgdata.get('rule_hits', []):
54 55
    ...             print('   ', hit)
    ...         print('Misses:')
56
    ...         for miss in event.msgdata.get('rule_misses', []):
57
    ...             print('   ', miss)
58

59 60
Anne is a list member with moderation action of ``None`` so that moderation
will fall back to the mailing list's ``default_member_action``.
61 62 63 64 65 66

    >>> from mailman.testing.helpers import subscribe
    >>> member = subscribe(mlist, 'Anne', email='anne@example.com')
    >>> member
    <Member: Anne Person <anne@example.com> on test@example.com
             as MemberRole.member>
67 68
    >>> print(member.moderation_action)
    None
69

70 71 72 73
Anne's post to the mailing list runs through the incoming runner's default
built-in chain.  No rules hit and so the message is accepted.
::

74 75
    >>> from mailman.testing.helpers import (specialized_message_from_string
    ...   as message_from_string)   
76 77 78 79 80 81 82 83 84
    >>> msg = message_from_string("""\
    ... From: anne@example.com
    ... To: test@example.com
    ... Subject: aardvark
    ...
    ... This is a test.
    ... """)

    >>> from mailman.core.chains import process
85 86
    >>> from mailman.testing.helpers import event_subscribers
    >>> with event_subscribers(on_chain):
87
    ...     process(mlist, msg, {}, 'default-posting-chain')
88
    <mailman.interfaces.chain.AcceptEvent ...>
89 90 91 92
    <mailman.chains.accept.AcceptChain ...>
    Subject: aardvark
    Hits:
    Misses:
93
        dmarc-mitigation
94
        no-senders
95 96 97
        approved
        emergency
        loop
98
        banned-address
99
        member-moderation
100
        nonmember-moderation
101 102 103 104 105 106
        administrivia
        implicit-dest
        max-recipients
        max-size
        news-moderation
        no-subject
107
        digests
108 109
        suspicious-header

110 111
However, when Anne's moderation action is set to `hold`, her post is held for
moderator approval.
112 113
::

114 115 116
    >>> from mailman.interfaces.action import Action
    >>> member.moderation_action = Action.hold

117 118 119 120 121 122 123 124
    >>> msg = message_from_string("""\
    ... From: anne@example.com
    ... To: test@example.com
    ... Subject: badger
    ...
    ... This is a test.
    ... """)

125
    >>> with event_subscribers(on_chain):
126
    ...     process(mlist, msg, {}, 'default-posting-chain')
127
    <mailman.interfaces.chain.HoldEvent ...>
128 129 130
    <mailman.chains.hold.HoldChain ...>
    Subject: badger
    Hits:
131
        member-moderation
132
    Misses:
133
        dmarc-mitigation
134
        no-senders
135 136 137
        approved
        emergency
        loop
138
        banned-address
139

140
Anne's moderation action can also be set to `discard`...
141 142
::

143
    >>> member.moderation_action = Action.discard
144 145 146 147 148 149 150 151 152

    >>> msg = message_from_string("""\
    ... From: anne@example.com
    ... To: test@example.com
    ... Subject: cougar
    ...
    ... This is a test.
    ... """)

153
    >>> with event_subscribers(on_chain):
154
    ...     process(mlist, msg, {}, 'default-posting-chain')
155
    <mailman.interfaces.chain.DiscardEvent ...>
156 157 158
    <mailman.chains.discard.DiscardChain ...>
    Subject: cougar
    Hits:
159
        member-moderation
160
    Misses:
161
        dmarc-mitigation
162
        no-senders
163 164 165
        approved
        emergency
        loop
166
        banned-address
167 168 169

... or `reject`.

170
    >>> member.moderation_action = Action.reject
171 172 173 174 175 176 177 178 179

    >>> msg = message_from_string("""\
    ... From: anne@example.com
    ... To: test@example.com
    ... Subject: dingo
    ...
    ... This is a test.
    ... """)

180
    >>> with event_subscribers(on_chain):
181
    ...     process(mlist, msg, {}, 'default-posting-chain')
182
    <mailman.interfaces.chain.RejectEvent ...>
183 184 185
    <mailman.chains.reject.RejectChain ...>
    Subject: dingo
    Hits:
186
        member-moderation
187
    Misses:
188
        dmarc-mitigation
189
        no-senders
190 191 192
        approved
        emergency
        loop
193
        banned-address
194 195 196 197 198


Nonmembers
==========

199 200 201
Registered nonmembers are handled very similarly to members, except that a
different list default setting is used when moderating nonmemberds.  This is
how the incoming runner adds sender addresses as nonmembers.
202

203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
    >>> from zope.component import getUtility
    >>> from mailman.interfaces.usermanager import IUserManager
    >>> user_manager = getUtility(IUserManager)
    >>> address = user_manager.create_address('bart@example.com')
    >>> address
    <Address: bart@example.com [not verified] at ...>

When the moderation rule runs on a message from this sender, this address will
be registered as a nonmember of the mailing list, and it will be held for
moderator approval.
::

    >>> msg = message_from_string("""\
    ... From: bart@example.com
    ... To: test@example.com
    ... Subject: elephant
    ...
    ... """)

    >>> with event_subscribers(on_chain):
223
    ...     process(mlist, msg, {}, 'default-posting-chain')
224
    <mailman.interfaces.chain.HoldEvent ...>
225 226 227 228 229
    <mailman.chains.hold.HoldChain ...>
    Subject: elephant
    Hits:
        nonmember-moderation
    Misses:
230
        dmarc-mitigation
231
        no-senders
232 233 234
        approved
        emergency
        loop
235
        banned-address
236
        member-moderation
237

238 239 240
    >>> nonmember = mlist.nonmembers.get_member('bart@example.com')
    >>> nonmember
    <Member: bart@example.com on test@example.com as MemberRole.nonmember>
241

242
When a nonmember's default moderation action is ``None``, the rule will use
243 244
the mailing list's ``default_nonmember_action``.

245
    >>> print(nonmember.moderation_action)
246 247
    None
    >>> print(mlist.default_nonmember_action)
248
    Action.hold