iredapd / src / plugins / ldap_maillist_access_policy.py

Zhang Huangbin 23ba9de 
Zhang Huangbin 8b3f08b 


Zhang Huangbin f728586 
Zhang Huangbin f0e8ba4 
Zhang Huangbin 4cf7460 
Zhang Huangbin f728586 

Zhang Huangbin e746e96 

Zhang Huangbin f728586 

Zhang Huangbin 8b3f08b 

Zhang Huangbin f0e8ba4 
Zhang Huangbin 8b3f08b 
Zhang Huangbin 40fe3c3 
Zhang Huangbin 8b3f08b 
Zhang Huangbin 8ede84b 
Zhang Huangbin 8b3f08b 

Zhang Huangbin 8ede84b 

Zhang Huangbin 64db595 
Zhang Huangbin 8b3f08b 
Zhang Huangbin 64db595 







Zhang Huangbin e746e96 
Zhang Huangbin 64db595 

Zhang Huangbin 8b3f08b 
Zhang Huangbin 64db595 



Zhang Huangbin e746e96 
Zhang Huangbin 64db595 
Zhang Huangbin 8b3f08b 
Zhang Huangbin 64db595 
Zhang Huangbin 8b3f08b 
Zhang Huangbin 64db595 

Zhang Huangbin 8b3f08b 
Zhang Huangbin 64db595 

Zhang Huangbin 8b3f08b 

Zhang Huangbin 64db595 
Zhang Huangbin 8b3f08b 
Zhang Huangbin 8ede84b 


Zhang Huangbin 64db595 
Zhang Huangbin 8ede84b 
Zhang Huangbin 8b3f08b 
Zhang Huangbin 64db595 
Zhang Huangbin 8b3f08b 

Zhang Huangbin 64db595 
Zhang Huangbin 8b3f08b 





Zhang Huangbin 64db595 




Zhang Huangbin 8ede84b 
Zhang Huangbin 64db595 





Zhang Huangbin 90fd621 
Zhang Huangbin 64db595 
































Zhang Huangbin 8b3f08b 

Zhang Huangbin 8ede84b 
Zhang Huangbin 8b3f08b 

Zhang Huangbin 8ede84b 
Zhang Huangbin 8b3f08b 
Zhang Huangbin 0bfab3a 
Zhang Huangbin ca03b65 
Zhang Huangbin 8b3f08b 

Zhang Huangbin 217fa10 
Zhang Huangbin f728586 
Zhang Huangbin 8b3f08b 
Zhang Huangbin 217fa10 
Zhang Huangbin 9d49c0c 
Zhang Huangbin f728586 
Zhang Huangbin 8b3f08b 

Zhang Huangbin 9d49c0c 



Zhang Huangbin 5898452 
Zhang Huangbin 9d49c0c 


Zhang Huangbin f8db03e 
Zhang Huangbin 9d49c0c 



Zhang Huangbin f8db03e 
Zhang Huangbin 9d49c0c 
Zhang Huangbin e746e96 
Zhang Huangbin 8ede84b 
Zhang Huangbin 4cf7460 
Zhang Huangbin 0bfab3a 
Zhang Huangbin ca03b65 
Zhang Huangbin 8b3f08b 

Zhang Huangbin 9d49c0c 
Zhang Huangbin ca03b65 
Zhang Huangbin f728586 
Zhang Huangbin f0e8ba4 
Zhang Huangbin f728586 

Zhang Huangbin 9d49c0c 

Zhang Huangbin 78143bb 
Zhang Huangbin 9d49c0c 


Zhang Huangbin f0e8ba4 
Zhang Huangbin 8b3f08b 
Zhang Huangbin d66ca42 
Zhang Huangbin 8b3f08b 
Zhang Huangbin 8ede84b 







Zhang Huangbin 8b3f08b 
Zhang Huangbin 0bfab3a 
Zhang Huangbin ca03b65 
Zhang Huangbin 8b3f08b 
Zhang Huangbin f0e8ba4 
# Author: Zhang Huangbin <zhb _at_ iredmail.org>

# ----------------------------------------------------------------------------
# This plugin is used for mail deliver restriction.
#
# Available access policies:
#   - public:   Unrestricted
#   - domain:   Only users under same domain are allowed.
#   - subdomain:    Only users under same domain and sub domains are allowed.
#   - membersOnly or members:  Only members are allowed.
#   - moderatorsOnly or moderators:   Only moderators are allowed.
#   - membersAndModeratorsOnly: Only members and moderators are allowed.

# ----------------------------------------------------------------------------

from libs import SMTP_ACTIONS

PLUGIN_NAME = 'ldap_maillist_access_policy'

def __get_allowed_senders(ldapConn, ldapBaseDn, listDn, sender, recipient, policy, logger, *kw, **kargs):
    """return search_result_list_based_on_access_policy"""

    logger.debug('(%s) Get allowed senders...' % (PLUGIN_NAME))

    recipient_domain = recipient.split('@', 1)[-1]

    # Set base dn as domain dn.
    domaindn = 'domainName=' + recipient_domain + ',' + ldapBaseDn

    # Default search scope. 2==ldap.SCOPE_SUBTREE
    searchScope = 2

    # Set search filter, attributes based on policy.
    # Override base dn, scope if necessary.
    if policy in ['membersonly', 'members']:
        basedn = domaindn
        # Filter: get mail list members.
        searchFilter = "(&(|(objectclass=mailUser)(objectClass=mailExternalUser))(accountStatus=active)(memberOfGroup=%s))" % (recipient, )

        # Get both mail and shadowAddress.
        searchAttrs = ['mail', 'shadowAddress',]

    elif policy in ['allowedonly', 'moderatorsonly', 'moderators']:
        # Get mail list moderators.
        basedn = listDn
        searchScope = 0     # Use ldap.SCOPE_BASE to improve performance.
        searchFilter = "(&(objectclass=mailList)(mail=%s))" % (recipient, )
        searchAttrs = ['listAllowedUser']

    else:
        basedn = domaindn
        # Policy: policy==membersAndModeratorsOnly or not set.
        # Filter used to get both members and moderators.
        searchFilter = "(|(&(|(objectClass=mailUser)(objectClass=mailExternalUser))(memberOfGroup=%s))(&(objectclass=mailList)(mail=%s)))" % (recipient, recipient, )
        searchAttrs = ['mail', 'shadowAddress', 'listAllowedUser',]

    logger.debug('(%s) base dn: %s' % (PLUGIN_NAME, basedn))
    logger.debug('(%s) search scope: %s' % (PLUGIN_NAME, searchScope))
    logger.debug('(%s) search filter: %s' % (PLUGIN_NAME, searchFilter))
    logger.debug('(%s) search attributes: %s' % (PLUGIN_NAME, ', '.join(searchAttrs)))

    try:
        result = ldapConn.search_s(basedn, searchScope, searchFilter, searchAttrs)
        userList = []
        for obj in result:
            for k in searchAttrs:
                if k in obj[1].keys():
                    # Example of result data:
                    # [('dn', {'listAllowedUser': ['user@domain.ltd']})]
                    userList += obj[1][k]
                else:
                    pass

        # Exclude mail list itself.
        if recipient in userList:
            userList.remove(recipient)

        logger.debug('(%s) search result: %s' % (PLUGIN_NAME, str(userList)))

        # Query once more to get 'shadowAddress'.
        if len(userList) > 0 and (policy == 'allowedonly' or policy == 'moderatorsonly'):
            logger.debug('(%s) Addition query to get user aliases...' % (PLUGIN_NAME))

            basedn = 'ou=Users,' + domaindn
            searchFilter = '(&(objectClass=mailUser)(enabledService=shadowaddress)(|'
            for i in userList:
                searchFilter += '(mail=%s)' % i
            searchFilter += '))'

            searchAttrs = ['shadowAddress',]

            logger.debug('(%s) base dn: %s' % (PLUGIN_NAME, basedn))
            logger.debug('(%s) search scope: 2 (ldap.SCOPE_SUBTREE)' % (PLUGIN_NAME))
            logger.debug('(%s) search filter: %s' % (PLUGIN_NAME, searchFilter))
            logger.debug('(%s) search attributes: %s' % (PLUGIN_NAME, ', '.join(searchAttrs)))

            try:
                resultOfShadowAddresses = ldapConn.search_s(
                    'ou=Users,'+domaindn,
                    2,  # ldap.SCOPE_SUBTREE
                    searchFilter,
                    ['mail', 'shadowAddress',],
                )

                for obj in resultOfShadowAddresses:
                    for k in searchAttrs:
                        if k in obj[1].keys():
                            # Example of result data:
                            # [('dn', {'listAllowedUser': ['user@domain.ltd']})]
                            userList += obj[1][k]
                        else:
                            pass

                logger.debug('(%s) final result: %s' % (PLUGIN_NAME, str(userList)))

            except Exception, e:
                logger.debug(str(e))

        return userList
    except Exception, e:
        logger.debug('(%s) Error: %s' % (PLUGIN_NAME, str(e)))
        return []

def restriction(ldapConn, ldapBaseDn, ldapRecipientDn, ldapRecipientLdif, smtpSessionData, logger, **kargs):
    # Return if recipient is not a mail list object.
    if 'maillist' not in [v.lower() for v in ldapRecipientLdif['objectClass']]:
        return 'DUNNO Not a mail list account.'

    sender = smtpSessionData['sender'].lower()
    sender_domain = sender.split('@')[-1]

    recipient = smtpSessionData['recipient'].lower()
    recipient_domain = recipient.split('@')[-1]
    recipient_alias_domains = []

    policy = ldapRecipientLdif.get('accessPolicy', ['public'])[0].lower()

    if policy in ['domain', 'subdomain',]:
        try:
            qr = ldapConn.search_s(
                ldapBaseDn,
                1, # 1 == ldap.SCOPE_ONELEVEL
                "(&(objectClass=mailDomain)(|(domainName=%s)(domainAliasName=%s)))" % (recipient_domain, recipient_domain),
                ['domainName', 'domainAliasName',]
            )
            if len(qr) > 0:
                recipient_alias_domains = qr[0][1].get('domainName', []) + qr[0][1].get('domainAliasName', [])
        except Exception, e:
            logger.debug('(%s) Error while fetch domainAliasName: %s' % (PLUGIN_NAME, str(e),))

        logger.debug('(%s) Recipient domain and alias domains: %s' % (PLUGIN_NAME, ','.join(recipient_alias_domains)))

    logger.debug('(%s) %s -> %s, policy: %s' % (PLUGIN_NAME, sender, recipient, policy))

    if policy == 'public':
        # No restriction.
        return 'DUNNO Access policy: public.'
    elif policy == "domain":
        # Bypass all users under the same domain.
        if sender_domain in recipient_alias_domains:
            return 'DUNNO Access policy: domain'
        else:
            return SMTP_ACTIONS['reject']
    elif policy == "subdomain":
        # Bypass all users under the same domain and sub domains.
        returned = False
        for d in recipient_alias_domains:
            if sender.endswith(d) or sender.endswith('.' + d):
                return 'DUNNO Access policy: subdomain (%s)' % (d)

        if returned is False:
            return SMTP_ACTIONS['reject']
    else:
        # Handle other access policies: membersOnly, allowedOnly, membersAndModeratorsOnly.
        allowedSenders = __get_allowed_senders(
            ldapConn=ldapConn,
            ldapBaseDn=ldapBaseDn,
            listDn=ldapRecipientDn,
            sender=sender,
            recipient=recipient,
            policy=policy,
            logger=logger,
        )

        if sender.lower() in [v.lower() for v in allowedSenders]:
            return 'DUNNO Allowed sender.'
        else:
            return SMTP_ACTIONS['reject']
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.