Closed chibenwa closed 3 months ago
https://github.com/chibenwa/openpaas-james/tree/ldap-mailing-lists
Implemented steps 1, 2, 3, 5.
Now remains the hardest: testing!
https://github.com/linagora/tmail-backend/pull/1135 tests added, the resulting code shall be pretty conclusive...
This 1 weekend coding party was great but thinking about it there's several rough corners still ahead.
The current implementation decorelates group handling from RRT
This come at a price: with the current proposed pipeline, if a user forward emails to a list, then the list will not be resolved (as the list mailet would be positionned before the RRT).
Of interest this situation can be solved by patching the local-address-error
:
IsALDAPList
matcherToProcessor
-> root mailet associated with IsALDAPList
matcher within local-address-error
processor.This is a rare occurrence so a bit of ceremony and extra LDAP requests might be acceptable here.
GIVEN bob is a member of **mygroup@lists.linagora.com**
AND bob forwards his mails to **mygroup@lists.linagora.com**
WHEN bob receives an email
THEN we shall not create an infinite loop
This means that groups shall be integrated in the loopPrevention local mechanism.
The groups are currently NOT considered a local address. This means that ValidRCPTHandler would reject the recipients, thus not allowing SMTP users / MXs to send email to the group.
We would need to implement an addition TMailListAwareValidRCPTHandler
that would take the current heuristic for lists (lists prefix, local address...) and a cache (as we do not want to lookup LDAP for each and every recipient) - or a periodical refresh from a LDAP list is maybe even better... A few minutes delays after group creation might we acceptable...
We could also ship a configurable mode that accept emails for all local domains starting with lists
. While this would accept too many mails this would clearly be less costly in terms of performance and only cost a few Cassandra queries.
[ ] This proves the importance of some integration tests exercising the SMTP layer, and the forwards.
[x] Ideally we shall use a single LDAP-POOL. We can refactor James ReadOnlyUsersRepository in order to extract the repository instanciation. And directy inject the LDAP pool in all Mailet/matcher related to the LDAP.
Mutualizing the LDAP connections will be important to scale well in our usage of LDAP.
To be noted that groupOfNames
LDAP objectClass do not have mail
attribute.
In order to do so, specific schema needs to be crafted.
This means that we should make it easy to change the objectClass used to define groups, might it be needed.
This also means that, in order to ease testing we can assume the description
attribute holds the mail. As long as the attribute is configurable, it would be easy to adapt TMail lists to the final schema. This workaround was validated with @guimard .
BTW here is the link to the LDIF, for review of non-james + LDAP specialists DEVs
PR for mutualizing the LDAP connection pool in James: https://github.com/apache/james-project/pull/2359
I merged the pull request for the time being.
This ticket needs to be validated with @guimard
What
We want to write a mailet implementing basic mailing list features on top of a LDAP.
How
Step 1: group expension
(We just implement the
openList
)We define hereby a pattern for mailing list naming:
listname@lists.domain.tld
Where
domain.tld
is a domain managed by the backend (in the domain list) andlists.domain.tld
is a domain managed too.After validating the format and existance of the domains (here
lists.domain.tld
anddomain.tld
),We search (LDAP search) from the mail address the distinguish name (dn) of the list. Here:
dn of the group:
cn=listname,ou=lists, dc=domain,dc=tld
Based on this dn, retrieves the group members (attribute memberOf) and substitute the group (removed from recipients) by the list of members mail address.
Step 2: Caching
Use cafeine in memory cache to save
user dn => mailaddress
correspondance.The cache will drop least read entries, with a ttl of 24h, and define a maximum count of entries, configurable via mailet properties.
This cache will significantly decrease LDAP calls, only needed to get DN of members (1 call) and not to get the user DNs (N calls).
This DN => mail caching can be extracted into a dedicated component, binded in Guice as a singleton.
Step 3: sender restrictions
Why? Some lists are private and only list members have the right to write to it.
What? We define several sender validation policy, encoded as part of the businessCategory:
How? After retrieving a group,
Based on the businessCategory choose the senderValidation policy, and feed it the group LDIFF in order to validate if the sender is allowed or not.
If sender is allowed , rewrite the list normally.
If sender is denied, remove the group from the recipients, and send a copy of the email (MAIL FROM: sender, RCPT TO: list) to the
rejectedSender
processor (configurable through therejectedSenderProcessor
property).Step 5 List headers
Add the following per-recipient headers for list members:
Evolutions
(NOT to be implemented for MU governement)
OpenRegistration: This kind of list allows users to enroll them selves by writing to a subscribe address, and similary unsubscribe.
Encoding: this can be done by adding the
-openRegistration
suffix to the business category. This gives for instance the following validbusinessCategory
:openList-openRegistration
,internalList-openRegistration
,domainRestrictedList-openRegistration
.In case of OpenRegistration, we would need to handle mails to:
mylist-subscribe@lists.domain.tld
andmylist-unsubscribe@lists.domain.tld
and add/remove members onto the group on the LDAP accordingly. Note that new added members needs to match the sender validation policy.List-Unsubscribe
header needs to be positionned onto a per user basis for list members:List-Unsubscribe: <mailto:news-unsubscribe@lists.linagora.com>
Please note that the act of subscribing might be spoofed and used to overflow with mails a given mail address. Thus there needs to be a validation process:
hello, we well received a request to subscribe to XyZ mailing list, in order to subscribe please answer to this mail
(loaded from a template for I8N) - the validation token is then passed onto the reply-to mail address:Reply-To: <news-unsubscribe+8f3fg3ifg232hf2if3i47fidibcwie@lists.linagora.com>
Appendix
LDIFF data proposal
Sample configuration
Put it into mailetcontainer.xml > transport BEFORE the RRT mailet