mirror of
https://github.com/marcus-alicia/iRedAdmin-Pro-SQL.git
synced 2026-05-26 15:13:38 +00:00
Add files via upload
This commit is contained in:
0
controllers/iredapd/__init__.py
Normal file
0
controllers/iredapd/__init__.py
Normal file
416
controllers/iredapd/api_greylist.py
Normal file
416
controllers/iredapd/api_greylist.py
Normal file
@@ -0,0 +1,416 @@
|
||||
import web
|
||||
from controllers.utils import api_render
|
||||
from libs import iredutils
|
||||
from libs.iredapd import greylist as lib_greylist
|
||||
|
||||
import settings
|
||||
|
||||
if settings.backend == 'ldap':
|
||||
from libs.ldaplib import decorators
|
||||
else:
|
||||
from libs.sqllib import decorators
|
||||
|
||||
|
||||
def convert_greylist_setting_to_api_json(greylist_setting=None):
|
||||
"""Return dict with simplified information as API result."""
|
||||
if not greylist_setting:
|
||||
greylist_setting = {}
|
||||
|
||||
_status = greylist_setting.get('active', 'inherit')
|
||||
|
||||
status = 'inherit'
|
||||
if _status == 1:
|
||||
status = 'enabled'
|
||||
elif _status == 0:
|
||||
status = 'disabled'
|
||||
|
||||
return api_render((True, {'status': status}))
|
||||
|
||||
|
||||
class APIAllSettings:
|
||||
@decorators.api_require_global_admin
|
||||
def GET(self):
|
||||
"""Get all existing greylisting settings.
|
||||
|
||||
curl -X GET -i -b cookie.txt https://<server>/api/greylisting/all
|
||||
"""
|
||||
s = lib_greylist.get_all_greylist_settings()
|
||||
|
||||
_all_settings = {}
|
||||
for i in s:
|
||||
_sender = str(i.sender).lower()
|
||||
_account = str(i.account).lower()
|
||||
_active = int(i.active)
|
||||
|
||||
_setting = {'sender': _sender,
|
||||
'account': _account}
|
||||
|
||||
if _active == 1:
|
||||
_setting['status'] = 'enabled'
|
||||
else:
|
||||
_setting['status'] = 'disabled'
|
||||
|
||||
if _account in _all_settings:
|
||||
_all_settings[_account] += [_setting]
|
||||
else:
|
||||
_all_settings[_account] = [_setting]
|
||||
|
||||
return api_render((True, _all_settings))
|
||||
|
||||
|
||||
class APIGlobalSetting:
|
||||
@decorators.api_require_global_admin
|
||||
def GET(self):
|
||||
"""Get global greylisting setting.
|
||||
|
||||
curl -X GET -i -b cookie.txt https://<server>/api/greylisting/global
|
||||
"""
|
||||
s = lib_greylist.get_greylist_setting(account='@.')
|
||||
|
||||
# If no greylisting setting, mark it as explicitly disabled.
|
||||
if not s:
|
||||
s = {'active': 0}
|
||||
|
||||
return convert_greylist_setting_to_api_json(s)
|
||||
|
||||
@decorators.api_require_global_admin
|
||||
def POST(self):
|
||||
"""Set global greylisting setting.
|
||||
|
||||
curl -X POST -i -b cookie.txt -d "status=enable" https://<server>/api/greylisting/global
|
||||
|
||||
Required parameters:
|
||||
|
||||
@status -- Explicitly enable or disable greylisting globally.
|
||||
Possible values: enable, disable.
|
||||
"""
|
||||
form = web.input(_unicode=False)
|
||||
|
||||
enable = True
|
||||
if form.get('status') == 'disable':
|
||||
enable = False
|
||||
|
||||
qr = lib_greylist.enable_disable_greylist_setting(account='@.', enable=enable)
|
||||
return api_render(qr)
|
||||
|
||||
|
||||
class APIDomainSetting:
|
||||
@decorators.api_require_domain_access
|
||||
def GET(self, domain):
|
||||
"""Get per-domain greylisting setting.
|
||||
|
||||
curl -X GET -i -b cookie.txt https://<server>/api/greylisting/<domain>
|
||||
"""
|
||||
domain = str(domain).lower()
|
||||
|
||||
s = lib_greylist.get_greylist_setting(account='@' + domain)
|
||||
return convert_greylist_setting_to_api_json(s)
|
||||
|
||||
@decorators.api_require_domain_access
|
||||
def POST(self, domain):
|
||||
"""Set per-domain greylisting setting.
|
||||
|
||||
curl -X POST -i -b cookie.txt -d "status=enable" https://<server>/api/greylisting/<domain>
|
||||
|
||||
Required parameters:
|
||||
|
||||
@status -- Explicitly enable or disable greylisting globally.
|
||||
Possible values: enable, disable.
|
||||
"""
|
||||
form = web.input(_unicode=False)
|
||||
|
||||
domain = str(domain).lower()
|
||||
status = form.get('status', 'inherit').lower()
|
||||
|
||||
if status in ['enable', 'disable']:
|
||||
enable = (status == 'enable')
|
||||
qr = lib_greylist.enable_disable_greylist_setting(account='@' + domain, enable=enable)
|
||||
else:
|
||||
# Remove setting
|
||||
qr = lib_greylist.delete_greylist_setting(account='@' + domain)
|
||||
|
||||
return api_render(qr)
|
||||
|
||||
|
||||
class APIUserSetting:
|
||||
@decorators.api_require_domain_access
|
||||
def GET(self, mail):
|
||||
"""Get per-user greylisting setting.
|
||||
|
||||
curl -X GET -i -b cookie.txt https://<server>/api/greylisting/<mail>
|
||||
"""
|
||||
mail = str(mail).lower()
|
||||
s = lib_greylist.get_greylist_setting(account=mail)
|
||||
return convert_greylist_setting_to_api_json(s)
|
||||
|
||||
@decorators.api_require_domain_access
|
||||
def POST(self, mail):
|
||||
"""Set per-user greylisting setting.
|
||||
|
||||
curl -X POST -i -b cookie.txt -d "status=enable" https://<server>/api/greylisting/<mail>
|
||||
|
||||
Required parameters:
|
||||
|
||||
@status -- Explicitly enable or disable greylisting globally.
|
||||
Possible values: enable, disable.
|
||||
"""
|
||||
form = web.input(_unicode=False)
|
||||
status = form.get('status', 'inherit').lower()
|
||||
|
||||
mail = str(mail).lower()
|
||||
|
||||
if status in ['enable', 'disable']:
|
||||
enable = (status == 'enable')
|
||||
qr = lib_greylist.enable_disable_greylist_setting(account=mail, enable=enable)
|
||||
else:
|
||||
# Remove setting
|
||||
qr = lib_greylist.delete_greylist_setting(account=mail)
|
||||
|
||||
return api_render(qr)
|
||||
|
||||
|
||||
def _get_account_whitelists(account):
|
||||
account = str(account).lower()
|
||||
|
||||
if not (iredutils.is_domain(account)
|
||||
or iredutils.is_email(account)
|
||||
or account == '@.'):
|
||||
return False, 'INVALID_ACCOUNT'
|
||||
|
||||
if iredutils.is_domain(account):
|
||||
account = '@' + account
|
||||
|
||||
wl = lib_greylist.get_greylist_whitelists(account=account, address_only=True)
|
||||
_result = {'whitelists': wl}
|
||||
|
||||
if account == '@.':
|
||||
wl_domains = lib_greylist.get_greylist_whitelist_domains()
|
||||
_result['whitelist_domains'] = wl_domains
|
||||
|
||||
return True, _result
|
||||
|
||||
|
||||
def _update_account_whitelists(account, form):
|
||||
account = str(account).lower()
|
||||
|
||||
if not (iredutils.is_domain(account)
|
||||
or iredutils.is_email(account)
|
||||
or account == '@.'):
|
||||
return False, 'INVALID_ACCOUNT'
|
||||
|
||||
if iredutils.is_domain(account):
|
||||
account = '@' + account
|
||||
|
||||
if 'senders' in form:
|
||||
# Reset whitelisted senders
|
||||
_senders = form.get('senders', '').strip().split(',')
|
||||
|
||||
_senders = [str(i).lower()
|
||||
for i in _senders
|
||||
if iredutils.is_valid_wblist_address(i)]
|
||||
|
||||
_senders = list(set(_senders))
|
||||
|
||||
qr = lib_greylist.reset_greylist_whitelists(account=account,
|
||||
whitelists=_senders)
|
||||
if not qr[0]:
|
||||
return qr
|
||||
else:
|
||||
# Add new whitelist senders
|
||||
_new = []
|
||||
if 'addSenders' in form:
|
||||
_new = form.get('addSenders', '').strip().split(',')
|
||||
|
||||
# Remove existing ones
|
||||
_removed = []
|
||||
if 'removeSenders' in form:
|
||||
_removed = form.get('removeSenders', '').strip().split(',')
|
||||
|
||||
qr = lib_greylist.update_greylist_whitelists(account=account,
|
||||
new=_new,
|
||||
removed=_removed)
|
||||
|
||||
if not qr[0]:
|
||||
return qr
|
||||
|
||||
return True,
|
||||
|
||||
|
||||
class APIGlobalWhitelists:
|
||||
@decorators.api_require_global_admin
|
||||
def GET(self):
|
||||
"""Get globally whitelisted senders for greylisting service.
|
||||
|
||||
curl -X GET -i -b cookie.txt https://<server>/api/greylisting/global/whitelists
|
||||
"""
|
||||
qr = _get_account_whitelists(account='@.')
|
||||
return api_render(qr)
|
||||
|
||||
@decorators.api_require_global_admin
|
||||
def POST(self):
|
||||
"""Set global greylisting setting.
|
||||
|
||||
curl -X POST -i -b cookie.txt -d "var=value&var2=value2" https://<server>/api/greylisting/global/whitelists
|
||||
|
||||
Optional parameters:
|
||||
|
||||
@senders - Reset whitelisted senders for global greylisting
|
||||
service to given senders. Multiple addresses must
|
||||
be separated by comma. Conflicts with parameter
|
||||
`addSenders` and `removeSenders`.
|
||||
@addSenders - Whitelist new senders for greylisting service
|
||||
globally. Multiple addresses must be separated by
|
||||
comma. Conflicts with parameter `senders`.
|
||||
@removeSenders - Remove existing whitelisted senders for
|
||||
greylisting service globally. Multiple
|
||||
addresses must be separated by comma.
|
||||
Conflicts with parameter `senders`.
|
||||
"""
|
||||
form = web.input(_unicode=False)
|
||||
|
||||
qr = _update_account_whitelists(account='@.', form=form)
|
||||
if not qr[0]:
|
||||
return api_render(qr)
|
||||
|
||||
return api_render(True)
|
||||
|
||||
|
||||
class APIGlobalWhitelist:
|
||||
"""Handle single whitelist."""
|
||||
@decorators.api_require_global_admin
|
||||
def PUT(self, ip):
|
||||
"""
|
||||
Whitelist given IP address globally.
|
||||
curl -X PUT -i -b cookie.txt https://<server>/api/greylisting/global/whitelist/<ip>
|
||||
"""
|
||||
qr = lib_greylist.update_greylist_whitelists(account='@.', new=[ip], removed=None)
|
||||
return api_render(qr)
|
||||
|
||||
|
||||
class APIDomainWhitelists:
|
||||
@decorators.api_require_domain_access
|
||||
def GET(self, domain):
|
||||
"""Get whitelisted senders for greylisting service for given domain.
|
||||
|
||||
curl -X GET -i -b cookie.txt https://<server>/api/greylisting/<domain>/whitelists
|
||||
"""
|
||||
qr = _get_account_whitelists(account=domain)
|
||||
return api_render(qr)
|
||||
|
||||
@decorators.api_require_domain_access
|
||||
def POST(self, domain):
|
||||
"""Set global greylisting setting.
|
||||
|
||||
curl -X POST -i -b cookie.txt -d "var=value&var2=value2" https://<server>/api/greylisting/<domain>/whitelists
|
||||
|
||||
Optional parameters:
|
||||
|
||||
@senders - Reset whitelisted senders
|
||||
@addSenders - Whitelist new senders for greylisting service
|
||||
@removeSenders - Remove existing whitelisted senders
|
||||
"""
|
||||
form = web.input(_unicode=False)
|
||||
|
||||
qr = _update_account_whitelists(account=domain, form=form)
|
||||
if not qr[0]:
|
||||
return api_render(qr)
|
||||
|
||||
return api_render(True)
|
||||
|
||||
|
||||
class APIUserWhitelists:
|
||||
@decorators.api_require_domain_access
|
||||
def GET(self, mail):
|
||||
"""Get whitelisted senders for greylisting service for given user.
|
||||
|
||||
curl -X GET -i -b cookie.txt https://<server>/api/greylisting/<mail>/whitelists
|
||||
"""
|
||||
qr = _get_account_whitelists(account=mail)
|
||||
return api_render(qr)
|
||||
|
||||
@decorators.api_require_domain_access
|
||||
def POST(self, mail):
|
||||
"""Set global greylisting setting.
|
||||
|
||||
curl -X POST -i -b cookie.txt -d "var=value&var2=value2" https://<server>/api/greylisting/<mail>/whitelists
|
||||
|
||||
Optional parameters:
|
||||
|
||||
@senders - Reset whitelisted senders
|
||||
@addSenders - Whitelist new senders for greylisting service
|
||||
@removeSenders - Remove existing whitelisted senders
|
||||
"""
|
||||
form = web.input(_unicode=False)
|
||||
|
||||
qr = _update_account_whitelists(account=mail, form=form)
|
||||
if not qr[0]:
|
||||
return api_render(qr)
|
||||
|
||||
return api_render(True)
|
||||
|
||||
|
||||
def _update_whitelist_spf_domains(form):
|
||||
if 'domains' in form:
|
||||
# Reset
|
||||
_domains = form.get('domains', '').strip().split(',')
|
||||
|
||||
_domains = [str(i).lower()
|
||||
for i in _domains
|
||||
if iredutils.is_domain(i)]
|
||||
|
||||
_domains = list(set(_domains))
|
||||
|
||||
qr = lib_greylist.reset_greylist_whitelist_domains(domains=_domains)
|
||||
if not qr[0]:
|
||||
return qr
|
||||
else:
|
||||
# Add new
|
||||
_new = []
|
||||
if 'addDomains' in form:
|
||||
_new = form.get('addDomains', '').strip().split(',')
|
||||
|
||||
# Remove existing ones
|
||||
_removed = []
|
||||
if 'removeDomains' in form:
|
||||
_removed = form.get('removeDomains', '').strip().split(',')
|
||||
|
||||
qr = lib_greylist.update_greylist_whitelist_domains(new=_new, removed=_removed)
|
||||
|
||||
if not qr[0]:
|
||||
return qr
|
||||
|
||||
return True,
|
||||
|
||||
|
||||
class APIWhitelistSPFDomain:
|
||||
@decorators.api_require_global_admin
|
||||
def GET(self):
|
||||
"""Get whitelisted sender domains (for SPF query) for greylisting service.
|
||||
|
||||
curl -X GET -i -b cookie.txt https://<server>/api/greylisting/whitelist_spf_domains
|
||||
"""
|
||||
domains = lib_greylist.get_greylist_whitelist_domains()
|
||||
return api_render((True, {'domains': domains}))
|
||||
|
||||
@decorators.api_require_global_admin
|
||||
def POST(self):
|
||||
"""Manage whitelisted sender domains (for SPF query) for greylisting service.
|
||||
|
||||
curl -X POST -i -b cookie.txt -d "var=value&var2=value2" https://<server>/api/greylisting/whitelist_spf_domains
|
||||
|
||||
Optional parameters:
|
||||
|
||||
@domains - Reset sender domains
|
||||
@addDomains - Add new sender domains
|
||||
@removeDomains - Remove existing sender domains
|
||||
|
||||
Note: given sender domain names are not used directly while checking
|
||||
whitelisting, instead, there's a cron job to query SPF and MX
|
||||
DNS records of given sender domains, then whitelist the IP
|
||||
addresses/networks listed in DNS records. Multiple domains must
|
||||
be separated by comma.
|
||||
|
||||
"""
|
||||
form = web.input(_unicode=False)
|
||||
qr = _update_whitelist_spf_domains(form)
|
||||
return api_render(qr)
|
||||
118
controllers/iredapd/api_throttle.py
Normal file
118
controllers/iredapd/api_throttle.py
Normal file
@@ -0,0 +1,118 @@
|
||||
import web
|
||||
from controllers.utils import api_render
|
||||
from libs import form_utils
|
||||
from libs.iredapd import throttle as iredapd_throttle
|
||||
|
||||
import settings
|
||||
|
||||
if settings.backend == 'ldap':
|
||||
from libs.ldaplib import decorators
|
||||
else:
|
||||
from libs.sqllib import decorators
|
||||
|
||||
|
||||
# TODO able to specify quota unit for msg_size and max_quota. e.g. 10MB, 2GB.
|
||||
# Build form from API POST data and submit the throttle setting
|
||||
def _add_throttle(form, account, kind):
|
||||
form['enable_' + kind + '_throttling'] = 'on'
|
||||
|
||||
if 'period' in form:
|
||||
form[kind + '_period'] = form.pop('period')
|
||||
else:
|
||||
return False, 'MISS_PERIOD'
|
||||
|
||||
_has_rule = False
|
||||
for i in ['msg_size', 'max_quota', 'max_msgs']:
|
||||
if i in form:
|
||||
_has_rule = True
|
||||
|
||||
# radio/checkboxes are toggled
|
||||
form[kind + '_' + i] = 'on'
|
||||
|
||||
# value
|
||||
form['custom_' + kind + '_' + i] = form.pop(i)
|
||||
|
||||
if not _has_rule:
|
||||
return False, 'MISS_THROTTLE_SETTING'
|
||||
|
||||
ts = form_utils.get_throttle_setting(form, account=account, inout_type=kind)
|
||||
qr = iredapd_throttle.add_throttle(account=account, setting=ts, inout_type=kind)
|
||||
return qr
|
||||
|
||||
|
||||
class APIGlobalThrottle:
|
||||
@decorators.require_global_admin
|
||||
def GET(self, kind):
|
||||
"""Get global inbound and outbound throttle settings.
|
||||
|
||||
curl -X GET -i -b cookie.txt https://<server>/api/throttle/global/inbound
|
||||
curl -X GET -i -b cookie.txt https://<server>/api/throttle/global/outbound
|
||||
"""
|
||||
ts = iredapd_throttle.get_throttle_setting(account='@.', inout_type=kind)
|
||||
return api_render({'_success': True, 'setting': ts})
|
||||
|
||||
@decorators.require_global_admin
|
||||
def POST(self, kind):
|
||||
"""Set global throttle settings.
|
||||
|
||||
curl -X POST -i -b cookie.txt -d "var=value1&var2=value2&..." https://<server>/api/throttle/global/inbound
|
||||
curl -X POST -i -b cookie.txt -d "var=value1&var2=value2&..." https://<server>/api/throttle/global/outbound
|
||||
|
||||
Required POST parameters:
|
||||
|
||||
@period - Period of time (in seconds)
|
||||
@msg_size - Max size of single email
|
||||
@max_msgs - Number of max inbound emails
|
||||
@max_quota - Cumulative size of all inbound emails
|
||||
|
||||
Note: at least one of msg_size, max_msgs, max_quota is required.
|
||||
"""
|
||||
form = web.input(_unicode=False)
|
||||
qr = _add_throttle(form, account='@.', kind=kind)
|
||||
return api_render(qr)
|
||||
|
||||
|
||||
class APIDomainThrottle:
|
||||
@decorators.api_require_domain_access
|
||||
def GET(self, domain, kind):
|
||||
"""Set per-domain throttle settings.
|
||||
|
||||
curl -X GET -i -b cookie.txt -d "var=value1&var2=value2&..." https://<server>/api/throttle/<domain>/inbound
|
||||
curl -X GET -i -b cookie.txt -d "var=value1&var2=value2&..." https://<server>/api/throttle/<domain>/outbound
|
||||
"""
|
||||
ts = iredapd_throttle.get_throttle_setting(account='@' + domain, inout_type=kind)
|
||||
return api_render({'_success': True, 'setting': ts})
|
||||
|
||||
@decorators.api_require_domain_access
|
||||
def POST(self, domain, kind):
|
||||
"""Set per-domain throttle settings.
|
||||
|
||||
curl -X POST -i -b cookie.txt -d "var=value1&var2=value2&..." https://<server>/api/throttle/<domain>/inbound
|
||||
curl -X POST -i -b cookie.txt -d "var=value1&var2=value2&..." https://<server>/api/throttle/<domain>/outbound
|
||||
"""
|
||||
form = web.input(_unicode=False)
|
||||
qr = _add_throttle(form, account='@' + domain, kind=kind)
|
||||
return api_render(qr)
|
||||
|
||||
|
||||
class APIUserThrottle:
|
||||
@decorators.api_require_domain_access
|
||||
def GET(self, mail, kind):
|
||||
"""Set per-user throttle settings.
|
||||
|
||||
curl -X GET -i -b cookie.txt -d "var=value1&var2=value2&..." https://<server>/api/throttle/<mail>/inbound
|
||||
curl -X GET -i -b cookie.txt -d "var=value1&var2=value2&..." https://<server>/api/throttle/<mail>/outbound
|
||||
"""
|
||||
ts = iredapd_throttle.get_throttle_setting(account=mail, inout_type=kind)
|
||||
return api_render({'_success': True, 'setting': ts})
|
||||
|
||||
@decorators.api_require_domain_access
|
||||
def POST(self, mail, kind):
|
||||
"""Set per-user throttle settings.
|
||||
|
||||
curl -X POST -i -b cookie.txt -d "var=value1&var2=value2&..." https://<server>/api/throttle/<mail>/inbound
|
||||
curl -X POST -i -b cookie.txt -d "var=value1&var2=value2&..." https://<server>/api/throttle/<mail>/outbound
|
||||
"""
|
||||
form = web.input(_unicode=False)
|
||||
qr = _add_throttle(form, account=mail, kind=kind)
|
||||
return api_render(qr)
|
||||
55
controllers/iredapd/greylist.py
Normal file
55
controllers/iredapd/greylist.py
Normal file
@@ -0,0 +1,55 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
import web
|
||||
from libs.iredapd import greylist as iredapd_greylist
|
||||
import settings
|
||||
|
||||
if settings.backend == 'ldap':
|
||||
from libs.ldaplib import decorators
|
||||
else:
|
||||
from libs.sqllib import decorators
|
||||
|
||||
|
||||
class DefaultGreylisting:
|
||||
@decorators.require_global_admin
|
||||
def GET(self):
|
||||
gl_setting = iredapd_greylist.get_greylist_setting(account='@.')
|
||||
gl_whitelists = iredapd_greylist.get_greylist_whitelists(account='@.')
|
||||
gl_whitelist_domains = iredapd_greylist.get_greylist_whitelist_domains()
|
||||
|
||||
# Get greylisting tracking data
|
||||
(_status, _result) = iredapd_greylist.get_tracking_data(account='@.')
|
||||
if not _status:
|
||||
raise web.seeother('/domains?msg=%s' % web.urlquote(_result))
|
||||
else:
|
||||
tracking_records = _result
|
||||
|
||||
return web.render('iredapd/greylisting_global.html',
|
||||
gl_setting=gl_setting,
|
||||
gl_whitelists=gl_whitelists,
|
||||
gl_whitelist_domains=gl_whitelist_domains,
|
||||
parent_setting={},
|
||||
tracking_records=tracking_records,
|
||||
msg=web.input().get('msg'))
|
||||
|
||||
@decorators.require_global_admin
|
||||
def POST(self):
|
||||
form = web.input()
|
||||
qr = iredapd_greylist.update_greylist_settings_from_form(account='@.', form=form)
|
||||
|
||||
if qr[0]:
|
||||
raise web.seeother('/system/greylisting?msg=GL_UPDATED')
|
||||
else:
|
||||
raise web.seeother('/system/greylisting?msg=%s' % web.urlquote(qr[1]))
|
||||
|
||||
|
||||
class GreylistingRawTrackingData:
|
||||
@decorators.require_domain_access
|
||||
def GET(self, domain):
|
||||
(_status, _result) = iredapd_greylist.get_domain_tracking_data(domain=domain)
|
||||
if not _status:
|
||||
raise web.seeother('/domains?msg=%s' % web.urlquote(_result))
|
||||
|
||||
return web.render('iredapd/greylisting_tracking_records.html',
|
||||
domain=domain,
|
||||
tracking_records=_result)
|
||||
217
controllers/iredapd/log.py
Normal file
217
controllers/iredapd/log.py
Normal file
@@ -0,0 +1,217 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
from typing import List
|
||||
import web
|
||||
import settings
|
||||
|
||||
from libs.iredapd import log as iredapd_log, wblist_senderscore
|
||||
from libs.iredapd import greylist as lib_greylist
|
||||
|
||||
|
||||
if settings.backend == 'ldap':
|
||||
from libs.ldaplib import decorators
|
||||
else:
|
||||
from libs.sqllib import decorators
|
||||
|
||||
|
||||
session = web.config.get('_session')
|
||||
|
||||
|
||||
def _filter_whitelisted_senderscore_ips(rows=None) -> List:
|
||||
# Get IP addresses of rejected sessions due to senderscore.
|
||||
whitelisted_ips = []
|
||||
|
||||
try:
|
||||
_rejected_ips = [
|
||||
row.client_address
|
||||
for row in rows
|
||||
if row.action == 'REJECT'
|
||||
and row.reason.startswith('Server IP address has bad reputation')
|
||||
]
|
||||
|
||||
if _rejected_ips:
|
||||
_qr = wblist_senderscore.filter_whitelisted_ips(ips=_rejected_ips)
|
||||
if _qr[0]:
|
||||
whitelisted_ips = _qr[1]
|
||||
except:
|
||||
pass
|
||||
|
||||
return whitelisted_ips
|
||||
|
||||
|
||||
def _filter_whitelisted_greylisting_ips(rows=None):
|
||||
# Get IP addresses of rejected sessions due to greylisting.
|
||||
whitelisted_ips = []
|
||||
if not rows:
|
||||
return whitelisted_ips
|
||||
|
||||
try:
|
||||
_rejected_ips = [
|
||||
row.client_address
|
||||
for row in rows
|
||||
if row.action == '451'
|
||||
and row.reason == '4.7.1 Intentional policy rejection, please try again later'
|
||||
]
|
||||
|
||||
if _rejected_ips:
|
||||
_qr = lib_greylist.filter_whitelisted_ips(ips=_rejected_ips)
|
||||
if _qr[0]:
|
||||
whitelisted_ips = _qr[1]
|
||||
except:
|
||||
pass
|
||||
|
||||
return whitelisted_ips
|
||||
|
||||
|
||||
class SMTPSessions:
|
||||
@decorators.require_admin_login
|
||||
def GET(self, page=1, outbound_only=False, rejected_only=False):
|
||||
"""Display log of SMTP rejections."""
|
||||
page = int(page)
|
||||
if page < 1:
|
||||
page = 1
|
||||
|
||||
qr = iredapd_log.get_log_smtp_sessions(
|
||||
outbound_only=outbound_only,
|
||||
rejected_only=rejected_only,
|
||||
offset=settings.PAGE_SIZE_LIMIT * (page - 1),
|
||||
limit=settings.PAGE_SIZE_LIMIT,
|
||||
)
|
||||
|
||||
total = qr['total']
|
||||
rows = qr['rows']
|
||||
|
||||
if outbound_only:
|
||||
tmpl = 'smtp_outbound_sessions.html'
|
||||
else:
|
||||
tmpl = 'smtp_sessions.html'
|
||||
|
||||
num_insecure_outbound = 0
|
||||
insecure_outbound_usernames = []
|
||||
query_insecure_outbound_hours = settings.IREDAPD_QUERY_INSECURE_OUTBOUND_IN_HOURS
|
||||
if outbound_only:
|
||||
# Count insecure outbound connections.
|
||||
_qr = iredapd_log.get_smtp_insecure_outbound(hours=query_insecure_outbound_hours)
|
||||
if _qr[0]:
|
||||
num_insecure_outbound = _qr[1]['total']
|
||||
insecure_outbound_usernames = _qr[1]['usernames']
|
||||
|
||||
# Get IP addresses of rejected sessions due to senderscore.
|
||||
whitelisted_senderscore_ips = []
|
||||
if session.get('is_global_admin') and total > 0:
|
||||
whitelisted_senderscore_ips = _filter_whitelisted_senderscore_ips(rows=rows)
|
||||
|
||||
# Get IP addresses of rejected sessions due to greylisting.
|
||||
whitelisted_greylisting_ips = []
|
||||
if session.get('is_global_admin') and total > 0:
|
||||
whitelisted_greylisting_ips = _filter_whitelisted_greylisting_ips(rows=rows)
|
||||
|
||||
return web.render('iredapd/activities/' + tmpl,
|
||||
total=total,
|
||||
rows=rows,
|
||||
current_page=page,
|
||||
rejected_only=rejected_only,
|
||||
whitelisted_senderscore_ips=whitelisted_senderscore_ips,
|
||||
whitelisted_greylisting_ips=whitelisted_greylisting_ips,
|
||||
query_insecure_outbound_hours=query_insecure_outbound_hours,
|
||||
num_insecure_outbound=num_insecure_outbound,
|
||||
insecure_outbound_usernames=insecure_outbound_usernames,
|
||||
msg=web.input().get('msg'))
|
||||
|
||||
|
||||
class SMTPSessionsPerAccount:
|
||||
@decorators.require_admin_login
|
||||
def GET(self, account_type, account, page=1, outbound_only=False):
|
||||
"""Display log of SMTP authentications."""
|
||||
account_type = account_type.lower()
|
||||
account = account.lower()
|
||||
page = int(page)
|
||||
|
||||
if page < 1:
|
||||
page = 1
|
||||
|
||||
domains = []
|
||||
sasl_usernames = []
|
||||
senders = []
|
||||
recipients = []
|
||||
client_addresses = []
|
||||
encryption_protocols = []
|
||||
|
||||
# Make sure admin has privilege to manage this domain.
|
||||
if account_type == 'sasl_username':
|
||||
sasl_usernames = [account]
|
||||
elif account_type == 'sender':
|
||||
senders = [account]
|
||||
elif account_type == 'recipient':
|
||||
recipients = [account]
|
||||
elif account_type == 'domain':
|
||||
domains = [account]
|
||||
elif account_type == 'client_address':
|
||||
client_addresses = [account]
|
||||
elif account_type == 'encryption_protocol':
|
||||
encryption_protocols = [account]
|
||||
|
||||
qr = iredapd_log.get_log_smtp_sessions(
|
||||
domains=domains,
|
||||
sasl_usernames=sasl_usernames,
|
||||
senders=senders,
|
||||
recipients=recipients,
|
||||
encryption_protocols=encryption_protocols,
|
||||
client_addresses=client_addresses,
|
||||
outbound_only=outbound_only,
|
||||
offset=settings.PAGE_SIZE_LIMIT * (page - 1),
|
||||
limit=settings.PAGE_SIZE_LIMIT,
|
||||
)
|
||||
total = qr['total'] or 0
|
||||
rows = qr['rows']
|
||||
|
||||
if outbound_only:
|
||||
tmpl = 'smtp_outbound_sessions.html'
|
||||
else:
|
||||
tmpl = 'smtp_sessions.html'
|
||||
|
||||
# Get IP addresses of rejected sessions due to senderscore.
|
||||
whitelisted_senderscore_ips = []
|
||||
if session.get('is_global_admin') and total > 0:
|
||||
whitelisted_senderscore_ips = _filter_whitelisted_senderscore_ips(rows=rows)
|
||||
|
||||
# Get IP addresses of rejected sessions due to greylisting.
|
||||
whitelisted_greylisting_ips = []
|
||||
if session.get('is_global_admin') and total > 0:
|
||||
whitelisted_greylisting_ips = _filter_whitelisted_greylisting_ips(rows=rows)
|
||||
|
||||
return web.render(
|
||||
'iredapd/activities/' + tmpl,
|
||||
account_type=account_type,
|
||||
account=account,
|
||||
total=total,
|
||||
rows=rows,
|
||||
whitelisted_senderscore_ips=whitelisted_senderscore_ips,
|
||||
whitelisted_greylisting_ips=whitelisted_greylisting_ips,
|
||||
current_page=page,
|
||||
msg=web.input().get('msg'),
|
||||
)
|
||||
|
||||
|
||||
class SMTPSessionsRejected:
|
||||
@decorators.require_admin_login
|
||||
def GET(self, page=1):
|
||||
c = SMTPSessions()
|
||||
return c.GET(page=page, rejected_only=True)
|
||||
|
||||
|
||||
class SMTPSessionsOutbound:
|
||||
@decorators.require_admin_login
|
||||
def GET(self, page=1):
|
||||
c = SMTPSessions()
|
||||
return c.GET(page=page, outbound_only=True)
|
||||
|
||||
|
||||
class SMTPSessionsOutboundPerAccount:
|
||||
@decorators.require_admin_login
|
||||
def GET(self, account_type, account, page=1):
|
||||
c = SMTPSessionsPerAccount()
|
||||
return c.GET(account_type=account_type,
|
||||
account=account,
|
||||
page=page,
|
||||
outbound_only=True)
|
||||
14
controllers/iredapd/senderscore.py
Normal file
14
controllers/iredapd/senderscore.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from controllers import decorators
|
||||
from controllers.utils import api_render
|
||||
from libs.iredapd import wblist_senderscore
|
||||
|
||||
|
||||
class WhitelistIPForSenderScore:
|
||||
@decorators.require_global_admin
|
||||
def PUT(self, ip):
|
||||
"""Whitelist given IP address for senderscore.
|
||||
|
||||
curl -X PUT -i -b cookie.txt -d "ip=x.x.x.x" https://<server>/api/wblist/senderscore/whitelist/<ip>
|
||||
"""
|
||||
qr = wblist_senderscore.whitelist_ips(ips=[ip])
|
||||
return api_render(qr)
|
||||
45
controllers/iredapd/throttle.py
Normal file
45
controllers/iredapd/throttle.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
import web
|
||||
import settings
|
||||
|
||||
from libs import form_utils
|
||||
from libs.iredapd import throttle as iredapd_throttle
|
||||
|
||||
|
||||
if settings.backend == 'ldap':
|
||||
from libs.ldaplib import decorators
|
||||
else:
|
||||
from libs.sqllib import decorators
|
||||
|
||||
|
||||
# server-wide throttle setting.
|
||||
class GlobalThrottle:
|
||||
@decorators.require_global_admin
|
||||
def GET(self):
|
||||
inbound_setting = iredapd_throttle.get_throttle_setting(account='@.', inout_type='inbound')
|
||||
outbound_setting = iredapd_throttle.get_throttle_setting(account='@.', inout_type='outbound')
|
||||
|
||||
return web.render('iredapd/throttle_global.html',
|
||||
inbound_setting=inbound_setting,
|
||||
outbound_setting=outbound_setting,
|
||||
msg=web.input().get('msg'))
|
||||
|
||||
@decorators.require_global_admin
|
||||
def POST(self):
|
||||
form = web.input(_unicode=False)
|
||||
|
||||
t_account = '@.'
|
||||
|
||||
inbound_setting = form_utils.get_throttle_setting(form, account=t_account, inout_type='inbound')
|
||||
outbound_setting = form_utils.get_throttle_setting(form, account=t_account, inout_type='outbound')
|
||||
|
||||
iredapd_throttle.add_throttle(account=t_account,
|
||||
setting=inbound_setting,
|
||||
inout_type='inbound')
|
||||
|
||||
iredapd_throttle.add_throttle(account=t_account,
|
||||
setting=outbound_setting,
|
||||
inout_type='outbound')
|
||||
|
||||
raise web.seeother('/system/throttle?msg=UPDATED')
|
||||
69
controllers/iredapd/urls.py
Normal file
69
controllers/iredapd/urls.py
Normal file
@@ -0,0 +1,69 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
import settings
|
||||
from libs.regxes import email as e, domain as d, ip
|
||||
|
||||
# fmt: off
|
||||
urls = [
|
||||
# Throttling
|
||||
'/system/throttle', 'controllers.iredapd.throttle.GlobalThrottle',
|
||||
# Greylisting
|
||||
'/system/greylisting', 'controllers.iredapd.greylist.DefaultGreylisting',
|
||||
# Greylisting tracking data
|
||||
'/system/greylisting/tracking/domain/(%s)' % d, 'controllers.iredapd.greylist.GreylistingRawTrackingData',
|
||||
# White/blacklist based on rDNS
|
||||
'/system/wblist/rdns', 'controllers.iredapd.wblist_rdns.WBListRDNS',
|
||||
|
||||
#
|
||||
# Activities
|
||||
#
|
||||
'/activities/smtp/sessions', 'controllers.iredapd.log.SMTPSessions',
|
||||
r'/activities/smtp/sessions/page/(\d+)', 'controllers.iredapd.log.SMTPSessions',
|
||||
'/activities/smtp/sessions/(sasl_username|sender|recipient)/(%s)' % e, 'controllers.iredapd.log.SMTPSessionsPerAccount',
|
||||
r'/activities/smtp/sessions/(sasl_username|sender|recipient)/(%s)/page/(\d+)' % e, 'controllers.iredapd.log.SMTPSessionsPerAccount',
|
||||
'/activities/smtp/sessions/(domain)/(%s)' % d, 'controllers.iredapd.log.SMTPSessionsPerAccount',
|
||||
r'/activities/smtp/sessions/(domain)/(%s)/page/(\d+)' % d, 'controllers.iredapd.log.SMTPSessionsPerAccount',
|
||||
'/activities/smtp/sessions/(client_address)/(%s)' % ip, 'controllers.iredapd.log.SMTPSessionsPerAccount',
|
||||
r'/activities/smtp/sessions/(client_address)/(%s)/page/(\d+)' % ip, 'controllers.iredapd.log.SMTPSessionsPerAccount',
|
||||
r'/activities/smtp/sessions/(encryption_protocol)/([0-9a-zA-Z\.]+)', 'controllers.iredapd.log.SMTPSessionsPerAccount',
|
||||
r'/activities/smtp/sessions/(encryption_protocol)/([0-9a-zA-Z\.]+)/page/(\d+)', 'controllers.iredapd.log.SMTPSessionsPerAccount',
|
||||
|
||||
'/activities/smtp/sessions/rejected', 'controllers.iredapd.log.SMTPSessionsRejected',
|
||||
r'/activities/smtp/sessions/rejected/page/(\d+)', 'controllers.iredapd.log.SMTPSessionsRejected',
|
||||
|
||||
# SMTP Authentications
|
||||
'/activities/smtp/sessions/outbound', 'controllers.iredapd.log.SMTPSessionsOutbound',
|
||||
r'/activities/smtp/sessions/outbound/page/(\d+)', 'controllers.iredapd.log.SMTPSessionsOutbound',
|
||||
'/activities/smtp/sessions/outbound/(sasl_username|sender|recipient)/(%s)' % e, 'controllers.iredapd.log.SMTPSessionsOutboundPerAccount',
|
||||
r'/activities/smtp/sessions/outbound/(sasl_username|sender|recipient)/(%s)/page/(\d+)' % e, 'controllers.iredapd.log.SMTPSessionsOutboundPerAccount',
|
||||
'/activities/smtp/sessions/outbound/(domain)/(%s)' % d, 'controllers.iredapd.log.SMTPSessionsOutboundPerAccount',
|
||||
r'/activities/smtp/sessions/outbound/(domain)/(%s)/page/(\d+)' % d, 'controllers.iredapd.log.SMTPSessionsOutboundPerAccount',
|
||||
'/activities/smtp/sessions/outbound/(client_address)/(%s)' % ip, 'controllers.iredapd.log.SMTPSessionsOutboundPerAccount',
|
||||
r'/activities/smtp/sessions/outbound/(client_address)/(%s)/page/(\d+)' % ip, 'controllers.iredapd.log.SMTPSessionsOutboundPerAccount',
|
||||
r'/activities/smtp/sessions/outbound/(encryption_protocol)/([0-9a-zA-Z\.]+)', 'controllers.iredapd.log.SMTPSessionsOutboundPerAccount',
|
||||
r'/activities/smtp/sessions/outbound/(encryption_protocol)/([0-9a-zA-Z\.]+)/page/(\d+)', 'controllers.iredapd.log.SMTPSessionsOutboundPerAccount',
|
||||
|
||||
# API interfaces used by web ui.
|
||||
'/api/wblist/senderscore/whitelist/(%s)$' % ip, 'controllers.iredapd.senderscore.WhitelistIPForSenderScore',
|
||||
'/api/greylisting/global/whitelist/(%s)$' % ip, 'controllers.iredapd.api_greylist.APIGlobalWhitelist',
|
||||
]
|
||||
|
||||
# API Interfaces
|
||||
if settings.ENABLE_RESTFUL_API:
|
||||
urls += [
|
||||
# Throttling
|
||||
'/api/throttle/global/(inbound|outbound)', 'controllers.iredapd.api_throttle.APIGlobalThrottle',
|
||||
'/api/throttle/(%s)/(inbound|outbound)' % d, 'controllers.iredapd.api_throttle.APIDomainThrottle',
|
||||
'/api/throttle/(%s)/(inbound|outbound)' % e, 'controllers.iredapd.api_throttle.APIUserThrottle',
|
||||
|
||||
# Greylisting
|
||||
'/api/greylisting/all', 'controllers.iredapd.api_greylist.APIAllSettings',
|
||||
'/api/greylisting/global', 'controllers.iredapd.api_greylist.APIGlobalSetting',
|
||||
'/api/greylisting/(%s)' % d, 'controllers.iredapd.api_greylist.APIDomainSetting',
|
||||
'/api/greylisting/(%s)' % e, 'controllers.iredapd.api_greylist.APIUserSetting',
|
||||
'/api/greylisting/global/whitelists', 'controllers.iredapd.api_greylist.APIGlobalWhitelists',
|
||||
'/api/greylisting/(%s)/whitelists' % d, 'controllers.iredapd.api_greylist.APIDomainWhitelists',
|
||||
'/api/greylisting/(%s)/whitelists' % e, 'controllers.iredapd.api_greylist.APIUserWhitelists',
|
||||
'/api/greylisting/whitelist_spf_domains', 'controllers.iredapd.api_greylist.APIWhitelistSPFDomain',
|
||||
]
|
||||
# fmt: on
|
||||
79
controllers/iredapd/wblist_rdns.py
Normal file
79
controllers/iredapd/wblist_rdns.py
Normal file
@@ -0,0 +1,79 @@
|
||||
import web
|
||||
from controllers import decorators
|
||||
|
||||
from libs.iredutils import is_valid_wblist_rdns_domain
|
||||
from libs.iredapd import wblist_rdns, wblist_senderscore
|
||||
|
||||
|
||||
class WBListRDNS:
|
||||
@decorators.require_global_admin
|
||||
def GET(self):
|
||||
# Get wblist records
|
||||
(_status, _result) = wblist_rdns.get_wblist_rdns()
|
||||
if not _status:
|
||||
raise web.seeother('/domains?msg=%s' % web.urlquote(_result))
|
||||
|
||||
whitelists = _result['whitelists']
|
||||
blacklists = _result['blacklists']
|
||||
|
||||
return web.render('iredapd/wblist/rdns.html',
|
||||
whitelists=whitelists,
|
||||
blacklists=blacklists,
|
||||
msg=web.input().get('msg'))
|
||||
|
||||
@decorators.require_global_admin
|
||||
def POST(self):
|
||||
form = web.input()
|
||||
|
||||
whitelists = [str(i).lower()
|
||||
for i in form.get('whitelists', '').splitlines()
|
||||
if is_valid_wblist_rdns_domain(i)]
|
||||
whitelists = list(set(whitelists))
|
||||
|
||||
blacklists = [str(i).lower()
|
||||
for i in form.get('blacklists', '').splitlines()
|
||||
if is_valid_wblist_rdns_domain(i)]
|
||||
blacklists = list(set(blacklists))
|
||||
|
||||
(_status, _result) = wblist_rdns.reset_wblist_rdns(whitelists=whitelists, blacklists=blacklists)
|
||||
if _status:
|
||||
raise web.seeother('/system/wblist/rdns?msg=UPDATED')
|
||||
else:
|
||||
raise web.seeother('/system/wblist/rdns?msg=%s' % web.urlquote(_result))
|
||||
|
||||
|
||||
class WBListSenderScore:
|
||||
@decorators.require_global_admin
|
||||
def GET(self):
|
||||
# Get wblist records
|
||||
(_status, _result) = wblist_senderscore.get_whitelists()
|
||||
if not _status:
|
||||
raise web.seeother('/domains?msg=%s' % web.urlquote(_result))
|
||||
|
||||
total = _result['total']
|
||||
ips = _result['ips']
|
||||
|
||||
return web.render('iredapd/wblist/senderscore.html',
|
||||
total=total,
|
||||
ips=ips,
|
||||
msg=web.input().get('msg'))
|
||||
|
||||
@decorators.require_global_admin
|
||||
def POST(self):
|
||||
form = web.input()
|
||||
|
||||
whitelists = [str(i).lower()
|
||||
for i in form.get('whitelists', '').splitlines()
|
||||
if is_valid_wblist_rdns_domain(i)]
|
||||
whitelists = list(set(whitelists))
|
||||
|
||||
blacklists = [str(i).lower()
|
||||
for i in form.get('blacklists', '').splitlines()
|
||||
if is_valid_wblist_rdns_domain(i)]
|
||||
blacklists = list(set(blacklists))
|
||||
|
||||
(_status, _result) = wblist_rdns.reset_wblist_rdns(whitelists=whitelists, blacklists=blacklists)
|
||||
if _status:
|
||||
raise web.seeother('/system/wblist/senderscore?msg=UPDATED')
|
||||
else:
|
||||
raise web.seeother('/system/wblist/senderscore?msg=%s' % web.urlquote(_result))
|
||||
Reference in New Issue
Block a user