mirror of
https://github.com/marcus-alicia/iRedAdmin-Pro-SQL.git
synced 2026-05-31 17:39:43 +00:00
Add files via upload
This commit is contained in:
66
libs/sqllib/__init__.py
Normal file
66
libs/sqllib/__init__.py
Normal file
@@ -0,0 +1,66 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
import web
|
||||
import settings
|
||||
|
||||
from libs.logger import logger
|
||||
|
||||
|
||||
class MYSQLWrap:
|
||||
def __del__(self):
|
||||
try:
|
||||
self.conn.ctx.db.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def connect(self):
|
||||
conn = web.database(
|
||||
dbn='mysql',
|
||||
host=settings.vmail_db_host,
|
||||
port=int(settings.vmail_db_port),
|
||||
db=settings.vmail_db_name,
|
||||
user=settings.vmail_db_user,
|
||||
pw=settings.vmail_db_password,
|
||||
charset='utf8')
|
||||
|
||||
conn.supports_multiple_insert = True
|
||||
|
||||
return conn
|
||||
|
||||
def __init__(self):
|
||||
try:
|
||||
self.conn = self.connect()
|
||||
except AttributeError:
|
||||
# Reconnect if error raised: MySQL server has gone away.
|
||||
self.conn = self.connect()
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
|
||||
|
||||
class PGSQLWrap:
|
||||
def __del__(self):
|
||||
try:
|
||||
self.conn.ctx.db.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def __init__(self):
|
||||
# Initial DB connection and cursor.
|
||||
try:
|
||||
self.conn = web.database(
|
||||
dbn='postgres',
|
||||
host=settings.vmail_db_host,
|
||||
port=int(settings.vmail_db_port),
|
||||
db=settings.vmail_db_name,
|
||||
user=settings.vmail_db_user,
|
||||
pw=settings.vmail_db_password,
|
||||
)
|
||||
self.conn.supports_multiple_insert = True
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
|
||||
|
||||
if settings.backend == 'mysql':
|
||||
SQLWrap = MYSQLWrap
|
||||
elif settings.backend == 'pgsql':
|
||||
SQLWrap = PGSQLWrap
|
||||
1279
libs/sqllib/admin.py
Normal file
1279
libs/sqllib/admin.py
Normal file
File diff suppressed because it is too large
Load Diff
656
libs/sqllib/alias.py
Normal file
656
libs/sqllib/alias.py
Normal file
@@ -0,0 +1,656 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
import web
|
||||
import settings
|
||||
from libs import iredutils, form_utils
|
||||
|
||||
from libs.logger import logger, log_activity
|
||||
from libs.sqllib import SQLWrap, decorators
|
||||
from libs.sqllib import general as sql_lib_general
|
||||
from libs.sqllib import domain as sql_lib_domain
|
||||
|
||||
session = web.config.get('_session')
|
||||
|
||||
|
||||
@decorators.require_domain_access
|
||||
def change_email(mail, new_mail, conn=None):
|
||||
if not iredutils.is_email(mail):
|
||||
return False, 'INVALID_OLD_EMAIL'
|
||||
|
||||
if not iredutils.is_email(new_mail):
|
||||
return False, 'INVALID_NEW_EMAIL'
|
||||
|
||||
old_domain = mail.split('@', 1)[-1]
|
||||
new_domain = new_mail.split('@', 1)[-1]
|
||||
|
||||
if old_domain != new_domain:
|
||||
return False, 'PERMISSION_DENIED'
|
||||
|
||||
if not conn:
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
if not sql_lib_general.is_email_exists(mail=mail, conn=conn):
|
||||
return False, 'OLD_EMAIL_NOT_EXIST'
|
||||
|
||||
if sql_lib_general.is_email_exists(mail=new_mail, conn=conn):
|
||||
return False, 'NEW_EMAIL_ALREADY_EXISTS'
|
||||
|
||||
# Change email address
|
||||
try:
|
||||
sql_vars = {'mail': mail, 'new_mail': new_mail}
|
||||
|
||||
conn.update('alias',
|
||||
vars=sql_vars,
|
||||
address=new_mail,
|
||||
where='address=$mail')
|
||||
|
||||
# Update per-user mail forwardings, alias memberships
|
||||
conn.update('forwardings',
|
||||
vars=sql_vars,
|
||||
address=new_mail,
|
||||
where='address=$mail')
|
||||
|
||||
conn.update('forwardings',
|
||||
vars=sql_vars,
|
||||
forwarding=new_mail,
|
||||
where='forwarding=$mail')
|
||||
|
||||
# Update moderators
|
||||
conn.update('moderators',
|
||||
vars=sql_vars,
|
||||
address=new_mail,
|
||||
where='address=$mail')
|
||||
|
||||
conn.update('moderators',
|
||||
vars=sql_vars,
|
||||
moderator=new_mail,
|
||||
where='moderator=$mail')
|
||||
|
||||
log_activity(event='update',
|
||||
domain=old_domain,
|
||||
msg="Change alias account email address: {} -> {}.".format(mail, new_mail))
|
||||
|
||||
return True,
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
|
||||
def add_alias_from_form(domain, form, conn=None):
|
||||
# Get domain name, username, cn.
|
||||
form_domain = form_utils.get_domain_name(form)
|
||||
username = web.safestr(form.get('listname')).strip().lower()
|
||||
mail = username + '@' + form_domain
|
||||
|
||||
if domain != form_domain:
|
||||
return False, 'PERMISSION_DENIED'
|
||||
|
||||
if not iredutils.is_domain(domain):
|
||||
return False, 'INVALID_DOMAIN_NAME'
|
||||
|
||||
if not iredutils.is_auth_email(mail):
|
||||
return False, 'INVALID_MAIL'
|
||||
|
||||
if not conn:
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
# Check account existing.
|
||||
if sql_lib_general.is_email_exists(mail=mail, conn=conn):
|
||||
return False, 'ALREADY_EXISTS'
|
||||
|
||||
# Get domain profile.
|
||||
qr_profile = sql_lib_domain.profile(conn=conn, domain=domain)
|
||||
|
||||
if qr_profile[0]:
|
||||
domain_profile = qr_profile[1]
|
||||
else:
|
||||
return qr_profile
|
||||
|
||||
# Check account limit.
|
||||
num_exist = num_aliases_under_domain(conn=conn, domain=domain)
|
||||
|
||||
if domain_profile.aliases == -1:
|
||||
return False, 'NOT_ALLOWED'
|
||||
elif domain_profile.aliases > 0:
|
||||
if domain_profile.aliases <= num_exist:
|
||||
return False, 'EXCEEDED_DOMAIN_ACCOUNT_LIMIT'
|
||||
|
||||
# Define columns and values used to insert.
|
||||
columns = {
|
||||
'address': mail, 'domain': domain,
|
||||
'name': form_utils.get_name(form=form),
|
||||
'created': iredutils.get_gmttime(), 'active': 1,
|
||||
'accesspolicy': form_utils.get_list_access_policy(form=form,
|
||||
input_name='accessPolicy',
|
||||
default_value='public'),
|
||||
}
|
||||
|
||||
# Get access policy
|
||||
|
||||
try:
|
||||
conn.insert('alias', **columns)
|
||||
|
||||
log_activity(msg="Create mail alias: %s." % mail,
|
||||
domain=domain,
|
||||
event='create')
|
||||
return True,
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
|
||||
def delete_aliases(accounts, conn=None):
|
||||
"""Delete alias accounts under same domain."""
|
||||
accounts = [str(i).lower() for i in accounts if iredutils.is_email(i)]
|
||||
if not accounts:
|
||||
return True,
|
||||
|
||||
# Get domain from first account
|
||||
domain = accounts[0].split('@', 1)[-1]
|
||||
if not iredutils.is_domain(domain):
|
||||
return True,
|
||||
|
||||
sql_vars = {'domain': domain, 'accounts': accounts}
|
||||
|
||||
try:
|
||||
if not conn:
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
conn.delete('alias',
|
||||
vars=sql_vars,
|
||||
where='address IN $accounts')
|
||||
|
||||
conn.delete('forwardings',
|
||||
vars=sql_vars,
|
||||
where='address IN $accounts OR forwarding IN $accounts')
|
||||
|
||||
log_activity(event='delete',
|
||||
domain=accounts[0].split('@', 1)[-1],
|
||||
msg="Delete alias: %s." % ', '.join(accounts))
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
# Remove alias from domain.settings: default_groups
|
||||
qr = sql_lib_domain.remove_default_maillists_in_domain_setting(domain=domain,
|
||||
maillists=accounts,
|
||||
conn=conn)
|
||||
if not qr[0]:
|
||||
return qr
|
||||
|
||||
return True,
|
||||
|
||||
|
||||
@decorators.require_domain_access
|
||||
def num_aliases_under_domain(conn, domain, disabled_only=False, first_char=None):
|
||||
if not iredutils.is_domain(domain):
|
||||
return False, 'INVALID_DOMAIN_NAME'
|
||||
|
||||
num = 0
|
||||
sql_vars = {'domain': domain}
|
||||
|
||||
sql_where = ''
|
||||
if disabled_only:
|
||||
sql_where = ' AND active=0'
|
||||
|
||||
if first_char:
|
||||
sql_where += ' AND address LIKE %s' % web.sqlquote(first_char.lower() + '%')
|
||||
|
||||
try:
|
||||
qr = conn.select('alias',
|
||||
vars=sql_vars,
|
||||
what='COUNT(address) AS total',
|
||||
where='domain=$domain %s' % sql_where)
|
||||
num = qr[0].total or 0
|
||||
except:
|
||||
pass
|
||||
|
||||
return num
|
||||
|
||||
|
||||
@decorators.require_domain_access
|
||||
def get_basic_alias_profiles(domain,
|
||||
columns=None,
|
||||
first_char=None,
|
||||
page=0,
|
||||
email_only=False,
|
||||
disabled_only=False,
|
||||
conn=None):
|
||||
"""Get all aliases under domain.
|
||||
|
||||
Return data:
|
||||
(True, [{'mail': 'alias@domain.com',
|
||||
'name': '...',
|
||||
...other profiles in `vmail.alias` table...
|
||||
'members', [...],
|
||||
'moderators', [...]]
|
||||
"""
|
||||
domain = web.safestr(domain).lower()
|
||||
if not iredutils.is_domain(domain):
|
||||
raise web.seeother('/domains?msg=INVALID_DOMAIN_NAME')
|
||||
|
||||
sql_vars = {'domain': domain}
|
||||
|
||||
if columns:
|
||||
sql_what = ','.join(columns)
|
||||
else:
|
||||
if email_only:
|
||||
sql_what = 'address'
|
||||
else:
|
||||
sql_what = '*'
|
||||
|
||||
# Get alias members
|
||||
additional_sql_where = ''
|
||||
if first_char:
|
||||
additional_sql_where = ' AND address LIKE %s' % web.sqlquote(first_char.lower() + '%')
|
||||
|
||||
if disabled_only:
|
||||
additional_sql_where = ' AND active=0'
|
||||
|
||||
# Get basic alias profiles first
|
||||
try:
|
||||
if not conn:
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
if page:
|
||||
qr = conn.select('alias',
|
||||
vars=sql_vars,
|
||||
what=sql_what,
|
||||
where='domain=$domain %s' % additional_sql_where,
|
||||
order='address ASC',
|
||||
limit=settings.PAGE_SIZE_LIMIT,
|
||||
offset=(page - 1) * settings.PAGE_SIZE_LIMIT)
|
||||
else:
|
||||
qr = conn.select('alias',
|
||||
vars=sql_vars,
|
||||
what=sql_what,
|
||||
where='domain=$domain %s' % additional_sql_where,
|
||||
order='address ASC')
|
||||
|
||||
if email_only:
|
||||
emails = []
|
||||
for r in qr:
|
||||
email = str(r.address).lower()
|
||||
emails.append(email)
|
||||
|
||||
emails.sort()
|
||||
return True, emails
|
||||
else:
|
||||
return True, list(qr)
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
|
||||
@decorators.require_domain_access
|
||||
def get_profile(mail,
|
||||
with_members=True,
|
||||
with_moderators=True,
|
||||
conn=None):
|
||||
if not iredutils.is_email(mail):
|
||||
return False, 'INVALID_MAIL'
|
||||
|
||||
try:
|
||||
if not conn:
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
qr = conn.select('alias',
|
||||
vars={'address': mail},
|
||||
where='address=$address',
|
||||
limit=1)
|
||||
|
||||
if qr:
|
||||
profile = list(qr)[0]
|
||||
|
||||
if with_members:
|
||||
_qr = get_member_emails(mail=mail, conn=conn)
|
||||
if _qr[0]:
|
||||
profile['members'] = _qr[1]
|
||||
profile['members'].sort()
|
||||
else:
|
||||
return _qr
|
||||
|
||||
if with_moderators:
|
||||
_qr = get_moderators(mail=mail, conn=conn)
|
||||
if _qr[0]:
|
||||
profile['moderators'] = _qr[1]
|
||||
profile['moderators'].sort()
|
||||
else:
|
||||
return _qr
|
||||
|
||||
return True, profile
|
||||
else:
|
||||
return False, 'NO_SUCH_ACCOUNT'
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
|
||||
@decorators.require_domain_access
|
||||
def update(mail, profile_type, form, conn=None):
|
||||
mail = web.safestr(mail).lower()
|
||||
domain = mail.split('@', 1)[-1]
|
||||
|
||||
if not iredutils.is_email(mail):
|
||||
return False, 'INVALID_MAIL'
|
||||
|
||||
if not conn:
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
# change email address
|
||||
if profile_type == 'rename':
|
||||
# new email address
|
||||
new_mail = web.safestr(form.get('new_mail_username')).strip().lower() + '@' + domain
|
||||
qr = change_email(mail=mail, new_mail=new_mail, conn=conn)
|
||||
if qr[0]:
|
||||
raise web.seeother('/profile/alias/general/%s?msg=EMAIL_CHANGED' % new_mail)
|
||||
else:
|
||||
raise web.seeother('/profile/alias/general/{}?msg={}'.format(new_mail, web.urlquote(qr[1])))
|
||||
|
||||
# Pre-defined.
|
||||
values = {'modified': iredutils.get_gmttime()}
|
||||
|
||||
# Get cn.
|
||||
cn = form.get('cn', '')
|
||||
values['name'] = cn
|
||||
|
||||
# check account status.
|
||||
values['active'] = 0
|
||||
if 'accountStatus' in form:
|
||||
# Enabled.
|
||||
values['active'] = 1
|
||||
|
||||
# Get access policy.
|
||||
access_policy = str(form.get('accessPolicy'))
|
||||
if access_policy in iredutils.MAILLIST_ACCESS_POLICIES:
|
||||
values['accesspolicy'] = access_policy
|
||||
|
||||
# Get members & moderators from web form.
|
||||
_members = form_utils.get_multi_values_from_textarea(form=form,
|
||||
input_name='members',
|
||||
is_email=True)
|
||||
|
||||
_members = list({iredutils.lower_email_with_upper_ext_address(v) for v in _members})
|
||||
|
||||
_moderators = [str(v).strip().lower() for v in form.get('moderators', '').splitlines()]
|
||||
_moderators = list({iredutils.lower_email_with_upper_ext_address(v)
|
||||
for v in _moderators
|
||||
if iredutils.is_email(v) or v.startswith('*@')})
|
||||
_moderators_wildcard = [v for v in _moderators if iredutils.is_domain(v.split('@', 1)[-1])]
|
||||
|
||||
# Remove non-exist accounts in same domain.
|
||||
# Get members & moderators which in same domain.
|
||||
_members_in_domain = [i for i in _members if i.endswith('@' + domain)]
|
||||
_members_not_in_domain = [i for i in _members if not i.endswith('@' + domain)]
|
||||
_moderators_in_domain = [i for i in _moderators if i.endswith('@' + domain) and i not in _moderators_wildcard]
|
||||
_moderators_not_in_domain = [i for i in _moderators if not (i.endswith('@' + domain) or i in _moderators_wildcard)]
|
||||
|
||||
# Verify internal users
|
||||
addresses_in_domain = []
|
||||
_addresses_in_domain = list(set(_members_in_domain + _moderators_in_domain))
|
||||
if _addresses_in_domain:
|
||||
try:
|
||||
# Remove non-existing addresses
|
||||
_qr = sql_lib_general.filter_existing_emails(mails=_addresses_in_domain, conn=conn)
|
||||
addresses_in_domain = _qr['exist']
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
|
||||
members_in_domain = [v for v in _members_in_domain if v in addresses_in_domain]
|
||||
moderators_in_domain = [v for v in _moderators_in_domain if v in addresses_in_domain]
|
||||
|
||||
try:
|
||||
# Update profile
|
||||
conn.update('alias',
|
||||
vars={'address': mail},
|
||||
where='address=$address',
|
||||
**values)
|
||||
|
||||
# Delete all members and moderators first
|
||||
conn.delete('forwardings',
|
||||
vars={'address': mail},
|
||||
where='address=$address')
|
||||
|
||||
conn.delete('moderators',
|
||||
vars={'address': mail},
|
||||
where='address=$address')
|
||||
|
||||
# Add members by inserting new records
|
||||
_all_members = members_in_domain + _members_not_in_domain
|
||||
if _all_members:
|
||||
v = []
|
||||
for _member in _all_members:
|
||||
v += [{'address': mail,
|
||||
'forwarding': _member,
|
||||
'domain': domain,
|
||||
'dest_domain': _member.split('@', 1)[-1],
|
||||
'active': values['active'],
|
||||
'is_list': 1}]
|
||||
|
||||
conn.multiple_insert('forwardings', values=v)
|
||||
|
||||
# Add moderators by inserting new records
|
||||
_all_moderators = moderators_in_domain + _moderators_not_in_domain + _moderators_wildcard
|
||||
if _all_moderators:
|
||||
v = []
|
||||
for _moderator in _all_moderators:
|
||||
v += [{'address': mail,
|
||||
'moderator': _moderator,
|
||||
'domain': domain,
|
||||
'dest_domain': _moderator.split('@', 1)[-1]}]
|
||||
|
||||
conn.multiple_insert('moderators', values=v)
|
||||
|
||||
# Log changes.
|
||||
msg = "Update alias profile (%s)." % mail
|
||||
|
||||
if access_policy:
|
||||
msg += " Access policy: %s." % access_policy
|
||||
|
||||
if _all_members:
|
||||
msg += " Members: %s." % (', '.join(_all_members))
|
||||
else:
|
||||
msg += " No members."
|
||||
|
||||
if _all_moderators:
|
||||
msg += " Moderators: %s." % (', '.join(_all_moderators))
|
||||
else:
|
||||
msg += " No moderators."
|
||||
|
||||
log_activity(msg=msg, username=mail, domain=domain, event='update')
|
||||
|
||||
return True,
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
|
||||
def get_member_emails(mail, conn=None):
|
||||
"""Get members of mail alias account. Return a list of mail addresses.
|
||||
|
||||
Return a list with all members' email addresses."""
|
||||
if not conn:
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
try:
|
||||
qr = conn.select(
|
||||
'forwardings',
|
||||
vars={'mail': mail},
|
||||
what='forwarding',
|
||||
where='address=$mail AND is_list=1',
|
||||
)
|
||||
|
||||
_addresses = [iredutils.lower_email_with_upper_ext_address(i.forwarding)
|
||||
for i in qr if iredutils.is_email(i.forwarding)]
|
||||
_addresses.sort()
|
||||
|
||||
return True, _addresses
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
|
||||
def get_moderators(mail, conn=None):
|
||||
"""Get moderators of given mail alias account.
|
||||
|
||||
Return a list with all moderators' email addresses."""
|
||||
if not conn:
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
try:
|
||||
qr = conn.select('moderators',
|
||||
vars={'mail': mail},
|
||||
what='moderator',
|
||||
where='address=$mail')
|
||||
|
||||
_addresses = [iredutils.lower_email_with_upper_ext_address(i.moderator)
|
||||
for i in qr
|
||||
if iredutils.is_email(i.moderator) or i.moderator.startswith('*@')]
|
||||
_addresses.sort()
|
||||
|
||||
return True, _addresses
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
|
||||
def reset_members(mail, members, conn=None):
|
||||
"""Assign all given addresses specified in `@members` as members."""
|
||||
_addresses = {iredutils.lower_email_with_upper_ext_address(i)
|
||||
for i in members
|
||||
if iredutils.is_email(i)}
|
||||
|
||||
domain = mail.split('@', 1)[-1]
|
||||
|
||||
_addresses_in_domain = [v for v in _addresses if v.endswith('@' + domain) and v != mail]
|
||||
_addresses_not_in_domain = [v for v in _addresses if not v.endswith('@' + domain)]
|
||||
del _addresses
|
||||
|
||||
if not conn:
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
# Verify existence of addresses in same domain
|
||||
if _addresses_in_domain:
|
||||
try:
|
||||
# Remove non-existing addresses
|
||||
qr = sql_lib_general.filter_existing_emails(mails=_addresses_in_domain, conn=conn)
|
||||
_addresses_in_domain = qr['exist']
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
|
||||
try:
|
||||
# Delete all existing members first
|
||||
conn.delete('forwardings',
|
||||
vars={'mail': mail},
|
||||
where='address=$mail AND is_list=1')
|
||||
|
||||
# Add member by inserting new record
|
||||
_all_addresses = _addresses_in_domain + _addresses_not_in_domain
|
||||
if _all_addresses:
|
||||
v = []
|
||||
for i in _all_addresses:
|
||||
v += [{'address': mail,
|
||||
'forwarding': i,
|
||||
'domain': domain,
|
||||
'dest_domain': i.split('@', 1)[-1],
|
||||
'is_list': 1}]
|
||||
|
||||
conn.multiple_insert('forwardings', values=v)
|
||||
|
||||
log_activity(msg='Reset alias ({}) members to: {}'.format(mail, ', '.join(_all_addresses)),
|
||||
admin=session.get('username'),
|
||||
username=mail,
|
||||
domain=domain,
|
||||
event='update')
|
||||
|
||||
return True,
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
|
||||
def update_members(mail,
|
||||
new_members=None,
|
||||
removed_members=None,
|
||||
conn=None):
|
||||
"""Add new members to mail alias account, and remove removed_members."""
|
||||
_new = []
|
||||
if new_members:
|
||||
_new = [iredutils.lower_email_with_upper_ext_address(i)
|
||||
for i in new_members if iredutils.is_email(i)]
|
||||
|
||||
_removed = []
|
||||
if removed_members:
|
||||
_removed = [iredutils.lower_email_with_upper_ext_address(i)
|
||||
for i in removed_members if iredutils.is_email(i)]
|
||||
|
||||
if not (_new or _removed):
|
||||
return True, 'NO_VALID_MEMBERS'
|
||||
|
||||
domain = mail.split('@', 1)[-1]
|
||||
|
||||
if not conn:
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
# Verify existence of addresses in same domain
|
||||
_new_in_domain = set()
|
||||
_new_not_in_domain = set()
|
||||
if _new:
|
||||
for i in _new:
|
||||
if i.endswith('@' + domain):
|
||||
_new_in_domain.add(i)
|
||||
else:
|
||||
_new_not_in_domain.add(i)
|
||||
|
||||
# remove self
|
||||
_new_in_domain.discard(mail)
|
||||
|
||||
if _new_in_domain:
|
||||
try:
|
||||
# Remove non-existing addresses
|
||||
qr = sql_lib_general.filter_existing_emails(mails=_new_in_domain, conn=conn)
|
||||
_new_in_domain = qr['exist']
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
|
||||
# Get existing members
|
||||
qr = get_member_emails(mail=mail, conn=conn)
|
||||
if qr[0]:
|
||||
_old_members = qr[1]
|
||||
else:
|
||||
return qr
|
||||
|
||||
# Add new, remove removed
|
||||
_members = set(_old_members)
|
||||
_members.update(_new_in_domain)
|
||||
_members.update(_new_not_in_domain)
|
||||
_members -= set(_removed)
|
||||
|
||||
try:
|
||||
# Delete all existing members first
|
||||
conn.delete('forwardings',
|
||||
vars={'mail': mail},
|
||||
where='address=$mail AND is_list=1')
|
||||
|
||||
# Add member by inserting new record
|
||||
if _members:
|
||||
v = []
|
||||
for i in _members:
|
||||
v += [{'address': mail,
|
||||
'forwarding': i,
|
||||
'domain': domain,
|
||||
'dest_domain': i.split('@', 1)[-1],
|
||||
'is_list': 1}]
|
||||
|
||||
conn.multiple_insert('forwardings', values=v)
|
||||
|
||||
log_activity(msg='Update alias ({}) members to: {}'.format(mail, ', '.join(_members)),
|
||||
admin=session.get('username'),
|
||||
username=mail,
|
||||
domain=domain,
|
||||
event='update')
|
||||
|
||||
return True,
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
73
libs/sqllib/api_utils.py
Normal file
73
libs/sqllib/api_utils.py
Normal file
@@ -0,0 +1,73 @@
|
||||
import datetime
|
||||
from libs import form_utils
|
||||
|
||||
from libs.sqllib import general as sql_lib_general
|
||||
from libs.sqllib import sqlutils
|
||||
|
||||
import settings
|
||||
|
||||
|
||||
def get_form_password_dict(form,
|
||||
domain,
|
||||
input_name='password',
|
||||
min_passwd_length=None,
|
||||
max_passwd_length=None):
|
||||
"""Extract password from form, verify it, return both plain and hashed password.
|
||||
|
||||
>>> get_form_password_dict(form=form,
|
||||
domain='domain.tld',
|
||||
input_name='password',
|
||||
min_passwd_length=None,
|
||||
max_passwd_length=None)
|
||||
(True, {'pw_plain', '123456',
|
||||
'pw_hash', '{SSHA512}....'})
|
||||
"""
|
||||
if input_name not in form:
|
||||
return False, 'NO_PASSWORD'
|
||||
|
||||
# Get min/max password length from domain profile
|
||||
if not (min_passwd_length or max_passwd_length):
|
||||
qr = sql_lib_general.get_domain_settings(domain=domain)
|
||||
|
||||
if qr[0]:
|
||||
ds = qr[1]
|
||||
min_passwd_length = ds.get('min_passwd_length', settings.min_passwd_length)
|
||||
max_passwd_length = ds.get('max_passwd_length', settings.max_passwd_length)
|
||||
|
||||
qr = form_utils.get_password(form=form,
|
||||
input_name=input_name,
|
||||
confirm_pw_input_name=input_name,
|
||||
min_passwd_length=min_passwd_length,
|
||||
max_passwd_length=max_passwd_length)
|
||||
|
||||
return qr
|
||||
|
||||
|
||||
def export_sql_record(record, remove_columns=None):
|
||||
"""Convert some values in SQL format to general string.
|
||||
|
||||
- datetime
|
||||
- settings
|
||||
"""
|
||||
for (k, v) in list(record.items()):
|
||||
if remove_columns:
|
||||
if k in remove_columns:
|
||||
record.pop(k)
|
||||
continue
|
||||
|
||||
if isinstance(v, datetime.datetime):
|
||||
record[k] = v.isoformat()
|
||||
elif k == 'settings':
|
||||
record[k] = sqlutils.account_settings_string_to_dict(v)
|
||||
|
||||
return record
|
||||
|
||||
|
||||
def export_sql_records(records, remove_columns=None):
|
||||
new_records = []
|
||||
|
||||
for rcd in records:
|
||||
new_rcd = export_sql_record(record=rcd, remove_columns=remove_columns)
|
||||
new_records.append(new_rcd)
|
||||
|
||||
return new_records
|
||||
138
libs/sqllib/auth.py
Normal file
138
libs/sqllib/auth.py
Normal file
@@ -0,0 +1,138 @@
|
||||
import web
|
||||
import settings
|
||||
from libs import iredutils, iredpwd
|
||||
from libs.l10n import TIMEZONES
|
||||
from libs.sqllib import sqlutils
|
||||
|
||||
session = web.config.get('_session', {})
|
||||
|
||||
|
||||
def auth(conn,
|
||||
username,
|
||||
password,
|
||||
account_type='admin',
|
||||
verify_password=False):
|
||||
if not iredutils.is_email(username):
|
||||
return False, 'INVALID_USERNAME'
|
||||
|
||||
if not password:
|
||||
return False, 'EMPTY_PASSWORD'
|
||||
|
||||
username = str(username).lower()
|
||||
password = str(password)
|
||||
domain = username.split('@', 1)[-1]
|
||||
|
||||
# Query account from SQL database.
|
||||
if account_type == 'admin':
|
||||
# separate admin accounts
|
||||
result = conn.select('admin',
|
||||
vars={'username': username},
|
||||
where="username=$username AND active=1",
|
||||
what='password, language, settings',
|
||||
limit=1)
|
||||
|
||||
# mail user marked as domain admin
|
||||
if not result:
|
||||
result = conn.select(
|
||||
["mailbox", "domain"],
|
||||
vars={'username': username},
|
||||
where="mailbox.username=$username AND mailbox.active=1 AND (mailbox.isadmin=1 OR mailbox.isglobaladmin=1) AND mailbox.domain=domain.domain and domain.active=1",
|
||||
what='mailbox.password, mailbox.language, mailbox.isadmin, mailbox.isglobaladmin, mailbox.settings',
|
||||
limit=1,
|
||||
)
|
||||
|
||||
if result:
|
||||
session['admin_is_mail_user'] = True
|
||||
elif account_type == 'user':
|
||||
result = conn.select('mailbox',
|
||||
vars={'username': username},
|
||||
what='password, language, isadmin, isglobaladmin, settings',
|
||||
where="username=$username AND active=1",
|
||||
limit=1)
|
||||
else:
|
||||
return False, 'INVALID_ACCOUNT_TYPE'
|
||||
|
||||
if not result:
|
||||
# Account not found.
|
||||
# Do NOT return msg like 'Account does not ***EXIST***', crackers
|
||||
# can use it to verify valid accounts.
|
||||
return False, 'INVALID_CREDENTIALS'
|
||||
|
||||
record = result[0]
|
||||
password_sql = str(record.password)
|
||||
account_settings = sqlutils.account_settings_string_to_dict(str(record.settings))
|
||||
|
||||
# Verify password
|
||||
if not iredpwd.verify_password_hash(password_sql, password):
|
||||
return False, 'INVALID_CREDENTIALS'
|
||||
|
||||
if not verify_password:
|
||||
session['username'] = username
|
||||
|
||||
if account_type == 'user':
|
||||
session['account_is_mail_user'] = True
|
||||
|
||||
# Set preferred language.
|
||||
session['lang'] = web.safestr(record.get('language', settings.default_language))
|
||||
|
||||
# Set timezone (GMT-XX:XX).
|
||||
# Priority: per-user timezone > per-domain > global setting
|
||||
timezone = settings.LOCAL_TIMEZONE
|
||||
|
||||
if 'timezone' in account_settings:
|
||||
tz_name = account_settings['timezone']
|
||||
if tz_name in TIMEZONES:
|
||||
timezone = TIMEZONES[tz_name]
|
||||
else:
|
||||
# Get per-domain timezone
|
||||
qr_domain = conn.select('domain',
|
||||
vars={'domain': domain},
|
||||
what='settings',
|
||||
where='domain=$domain',
|
||||
limit=1)
|
||||
if qr_domain:
|
||||
domain_settings = sqlutils.account_settings_string_to_dict(str(qr_domain[0]['settings']))
|
||||
if 'timezone' in domain_settings:
|
||||
tz_name = domain_settings['timezone']
|
||||
if tz_name in TIMEZONES:
|
||||
timezone = TIMEZONES[tz_name]
|
||||
|
||||
session['timezone'] = timezone
|
||||
|
||||
# Set session['is_global_admin']
|
||||
if session.get('admin_is_mail_user'):
|
||||
if record.get('isglobaladmin', 0) == 1:
|
||||
session['is_global_admin'] = True
|
||||
else:
|
||||
session['is_normal_admin'] = True
|
||||
|
||||
# Set session['allowed_to_grant_admin']
|
||||
if 'grant_admin' in account_settings:
|
||||
session['allowed_to_grant_admin'] = True
|
||||
else:
|
||||
try:
|
||||
result = conn.select('domain_admins',
|
||||
vars={'username': username, 'domain': 'ALL'},
|
||||
what='domain',
|
||||
where='username=$username AND domain=$domain',
|
||||
limit=1)
|
||||
if result:
|
||||
session['is_global_admin'] = True
|
||||
else:
|
||||
if account_type == 'admin':
|
||||
session['is_normal_admin'] = True
|
||||
except:
|
||||
pass
|
||||
|
||||
if session['is_global_admin']:
|
||||
if not iredutils.is_allowed_global_admin_login_ip(client_ip=web.ctx.ip):
|
||||
session.kill()
|
||||
raise web.seeother('/login?msg=NOT_ALLOWED_IP')
|
||||
|
||||
session['logged'] = True
|
||||
|
||||
web.config.session_parameters['cookie_name'] = 'iRedAdmin-Pro'
|
||||
web.config.session_parameters['ignore_change_ip'] = settings.SESSION_IGNORE_CHANGE_IP
|
||||
web.config.session_parameters['ignore_expiry'] = False
|
||||
|
||||
return True, {'account_settings': account_settings}
|
||||
201
libs/sqllib/decorators.py
Normal file
201
libs/sqllib/decorators.py
Normal file
@@ -0,0 +1,201 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
import web
|
||||
|
||||
import settings
|
||||
from controllers import decorators as base_decorators
|
||||
from controllers.utils import api_render
|
||||
from libs import iredutils
|
||||
from libs.logger import logger
|
||||
from libs.sqllib import general as sql_lib_general
|
||||
|
||||
session = web.config.get('_session', {})
|
||||
|
||||
# require_api_auth_token = base_decorators.require_api_auth_token
|
||||
require_login = base_decorators.require_login
|
||||
require_admin_login = base_decorators.require_admin_login
|
||||
require_global_admin = base_decorators.require_global_admin
|
||||
csrf_protected = base_decorators.csrf_protected
|
||||
require_permission_create_domain = base_decorators.require_permission_create_domain
|
||||
require_preference_access = base_decorators.require_preference_access
|
||||
|
||||
api_require_admin_login = base_decorators.api_require_admin_login
|
||||
api_require_global_admin = base_decorators.api_require_global_admin
|
||||
|
||||
|
||||
def require_domain_access(func):
|
||||
def proxyfunc(*args, **kw):
|
||||
if not session.get('username'):
|
||||
raise web.seeother('/login?msg=LOGIN_REQUIRED')
|
||||
|
||||
# Check domain global admin.
|
||||
if session.get('is_global_admin'):
|
||||
return func(*args, **kw)
|
||||
else:
|
||||
username = session.get('username')
|
||||
# admin/user is viewing its own data
|
||||
if username == kw.get('mail') \
|
||||
or username.endswith('@' + kw.get('domain', 'NONE')):
|
||||
return func(*args, **kw)
|
||||
|
||||
if 'domain' in kw and iredutils.is_domain(kw.get('domain')):
|
||||
domain = web.safestr(kw['domain'])
|
||||
elif 'mail' in kw and iredutils.is_email(kw.get('mail')):
|
||||
domain = web.safestr(kw['mail']).split('@')[-1]
|
||||
elif 'admin' in kw and iredutils.is_email(kw.get('admin')):
|
||||
domain = web.safestr(kw['admin']).split('@')[-1]
|
||||
else:
|
||||
domain = None
|
||||
# Try to use the first valid domain name or email address as
|
||||
# key, it's passed from controllers/*.
|
||||
for arg in args:
|
||||
if iredutils.is_domain(arg):
|
||||
domain = arg
|
||||
break
|
||||
elif iredutils.is_email(arg):
|
||||
domain = arg.split('@', 1)[-1]
|
||||
break
|
||||
|
||||
if not domain:
|
||||
if settings.LOG_PERMISSION_DENIED:
|
||||
logger.error("PERMISSION_DENIED (1) raised in "
|
||||
"@require_domain_access, triggered in module: "
|
||||
"%s.py, function: %s(). No target domain for "
|
||||
"accessing." % (func.__module__, func.__name__))
|
||||
|
||||
raise web.seeother('/domains?msg=PERMISSION_DENIED')
|
||||
|
||||
# Check whether is domain admin.
|
||||
is_admin = sql_lib_general.is_domain_admin(domain=domain,
|
||||
admin=username)
|
||||
if is_admin:
|
||||
return func(*args, **kw)
|
||||
else:
|
||||
if settings.LOG_PERMISSION_DENIED:
|
||||
logger.error("PERMISSION_DENIED (2) raised in "
|
||||
"@require_domain_access, triggered in module: %s.py, "
|
||||
"function: %s(), accessing data: admin=%s, "
|
||||
"domain=%s" % (func.__module__, func.__name__, username, domain))
|
||||
|
||||
raise web.seeother('/domains?msg=PERMISSION_DENIED')
|
||||
return proxyfunc
|
||||
|
||||
|
||||
def require_user_login(func):
|
||||
def proxyfunc(self, *args, **kw):
|
||||
if session.get('account_is_mail_user'):
|
||||
return func(self, *args, **kw)
|
||||
|
||||
"""
|
||||
elif session.get('is_normal_admin') and session.get('admin_is_mail_user'):
|
||||
# Admin manages other domains but not self domain.
|
||||
# <admin>@<domain.com> doesn't manage <domain.com>
|
||||
admin = session.get('username')
|
||||
domain = admin.split('@', 1)[-1]
|
||||
if not sql_lib_general.is_domain_admin(domain=domain, admin=admin):
|
||||
return func(self, *args, **kw)
|
||||
"""
|
||||
|
||||
session.kill()
|
||||
raise web.seeother('/login?msg=LOGIN_REQUIRED')
|
||||
return proxyfunc
|
||||
|
||||
|
||||
# self-service.
|
||||
def require_ml_owner_or_moderator(func):
|
||||
def proxyfunc(*args, **kw):
|
||||
username = session.get('username')
|
||||
if not username:
|
||||
raise web.seeother('/login?msg=LOGIN_REQUIRED')
|
||||
|
||||
mail = None
|
||||
if 'mail' in kw:
|
||||
# the mailing list
|
||||
mail = kw['mail']
|
||||
if not iredutils.is_email(mail):
|
||||
raise web.seeother("/self-service/mls?msg=INVALID_MAILLIST")
|
||||
else:
|
||||
for i in args:
|
||||
if iredutils.is_email(i):
|
||||
mail = i
|
||||
break
|
||||
|
||||
if not mail:
|
||||
raise web.seeother("/self-service/mls?msg=INVALID_MAILLIST")
|
||||
|
||||
# Check whether user is an owner or moderator.
|
||||
_is_owner_or_moderator = sql_lib_general.is_ml_owner_or_moderator(ml=mail, user=username, conn=None)
|
||||
if _is_owner_or_moderator:
|
||||
return func(*args, **kw)
|
||||
else:
|
||||
if settings.LOG_PERMISSION_DENIED:
|
||||
logger.error("PERMISSION_DENIED (2) raised in "
|
||||
"@require_ml_owner_or_moderator, triggered in module: %s.py, "
|
||||
"function: %s(), accessing data: user=%s, "
|
||||
"maillist=%s" % (func.__module__, func.__name__, username, mail))
|
||||
|
||||
raise web.seeother('/self-service/mls?msg=PERMISSION_DENIED')
|
||||
|
||||
return proxyfunc
|
||||
|
||||
|
||||
def api_require_domain_access(func):
|
||||
def proxyfunc(*args, **kw):
|
||||
if not iredutils.is_allowed_api_client(web.ctx.ip):
|
||||
return api_render((False, 'NOT_AUTHORIZED'))
|
||||
|
||||
if not session.get('username'):
|
||||
return api_render((False, 'LOGIN_REQUIRED'))
|
||||
|
||||
# Check domain global admin.
|
||||
if session.get('is_global_admin'):
|
||||
return func(*args, **kw)
|
||||
else:
|
||||
username = session.get('username')
|
||||
# admin/user is viewing its own data
|
||||
if username == kw.get('mail') \
|
||||
or username.endswith('@' + kw.get('domain', 'NONE')):
|
||||
return func(*args, **kw)
|
||||
|
||||
if 'domain' in kw and iredutils.is_domain(kw.get('domain')):
|
||||
domain = web.safestr(kw['domain'])
|
||||
elif 'mail' in kw and iredutils.is_email(kw.get('mail')):
|
||||
domain = web.safestr(kw['mail']).split('@')[-1]
|
||||
elif 'admin' in kw and iredutils.is_email(kw.get('admin')):
|
||||
domain = web.safestr(kw['admin']).split('@')[-1]
|
||||
else:
|
||||
domain = None
|
||||
# Try to use the first valid domain name or email address as
|
||||
# key, it's passed from controllers/*.
|
||||
for arg in args:
|
||||
if iredutils.is_domain(arg):
|
||||
domain = arg
|
||||
break
|
||||
elif iredutils.is_email(arg):
|
||||
domain = arg.split('@', 1)[-1]
|
||||
break
|
||||
|
||||
if not domain:
|
||||
if settings.LOG_PERMISSION_DENIED:
|
||||
logger.error("PERMISSION_DENIED (1) raised in "
|
||||
"@require_domain_access: module=%s.py, "
|
||||
"function=%s(), admin=%s. "
|
||||
"No target domain for accessing." % (func.__module__, func.__name__, username))
|
||||
|
||||
return api_render((False, 'PERMISSION_DENIED'))
|
||||
|
||||
# Check whether is domain admin.
|
||||
is_admin = sql_lib_general.is_domain_admin(domain=domain,
|
||||
admin=username)
|
||||
if is_admin:
|
||||
return func(*args, **kw)
|
||||
else:
|
||||
if settings.LOG_PERMISSION_DENIED:
|
||||
logger.error("PERMISSION_DENIED (2) raised in "
|
||||
"@require_domain_access: module=%s.py, "
|
||||
"function=%s(), "
|
||||
"admin=%s, "
|
||||
"domain=%s" % (func.__module__, func.__name__, username, domain))
|
||||
|
||||
return api_render((False, 'PERMISSION_DENIED'))
|
||||
return proxyfunc
|
||||
2705
libs/sqllib/domain.py
Normal file
2705
libs/sqllib/domain.py
Normal file
File diff suppressed because it is too large
Load Diff
1084
libs/sqllib/general.py
Normal file
1084
libs/sqllib/general.py
Normal file
File diff suppressed because it is too large
Load Diff
1108
libs/sqllib/ml.py
Normal file
1108
libs/sqllib/ml.py
Normal file
File diff suppressed because it is too large
Load Diff
90
libs/sqllib/sqlutils.py
Normal file
90
libs/sqllib/sqlutils.py
Normal file
@@ -0,0 +1,90 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
from typing import Dict
|
||||
|
||||
|
||||
def account_settings_dict_to_string(account_settings: Dict) -> str:
|
||||
# Convert account setting dict to string.
|
||||
# - dict: {'var': 'value', 'var2: value2', ...}
|
||||
# - string: 'var:value;var2:value2;...'
|
||||
if not account_settings or not isinstance(account_settings, dict):
|
||||
return ''
|
||||
|
||||
for (k, v) in list(account_settings.items()):
|
||||
if k in ['default_groups',
|
||||
'default_mailing_lists',
|
||||
'enabled_services',
|
||||
'disabled_mail_services',
|
||||
'disabled_domain_profiles',
|
||||
'disabled_user_profiles',
|
||||
'disabled_user_preferences']:
|
||||
if isinstance(v, (list, tuple, set)):
|
||||
if isinstance(v, list):
|
||||
v.sort()
|
||||
elif isinstance(v, set):
|
||||
v = list(v)
|
||||
v.sort()
|
||||
|
||||
account_settings[k] = ','.join(v)
|
||||
else:
|
||||
# Remove item if value is not a list/tuple/set
|
||||
account_settings.pop(k)
|
||||
|
||||
new_settings = ';'.join(['{}:{}'.format(str(i), j) for (i, j) in list(account_settings.items()) if j])
|
||||
|
||||
if new_settings:
|
||||
new_settings += ';'
|
||||
|
||||
return new_settings
|
||||
|
||||
|
||||
def account_settings_string_to_dict(account_settings: str) -> Dict:
|
||||
# Convert account setting (string, format 'var:value;var2:value2;...', used
|
||||
# in MySQL/PGSQL backends) to dict.
|
||||
# - domain.settings
|
||||
# - mailbox.settings
|
||||
# Original setting must be a string
|
||||
if not account_settings:
|
||||
return {}
|
||||
|
||||
new_settings = {}
|
||||
|
||||
items = [st for st in account_settings.split(';') if ':' in st]
|
||||
for item in items:
|
||||
if item:
|
||||
(k, v) = item.split(':')
|
||||
if v:
|
||||
new_settings[k] = v
|
||||
|
||||
# Convert value to proper format (int, string, ...), default is string.
|
||||
# It will be useful to compare values with converted values.
|
||||
# If original value is not stored in proper format, key:value pair will
|
||||
# be removed.
|
||||
for key in new_settings:
|
||||
# integer
|
||||
if key in ['default_user_quota',
|
||||
'max_user_quota',
|
||||
'min_passwd_length',
|
||||
'max_passwd_length',
|
||||
# settings used to create new domains.
|
||||
'create_max_domains',
|
||||
'create_max_users',
|
||||
'create_max_lists',
|
||||
'create_max_aliases',
|
||||
'create_max_quota']:
|
||||
try:
|
||||
new_settings[key] = int(new_settings[key])
|
||||
except:
|
||||
new_settings.pop(key)
|
||||
|
||||
# list
|
||||
if key in ['enabled_services',
|
||||
'disabled_mail_services',
|
||||
'default_groups',
|
||||
'default_mailing_lists',
|
||||
'disabled_domain_profiles',
|
||||
'disabled_user_profiles',
|
||||
'disabled_user_preferences']:
|
||||
new_settings[key] = [str(i) for i in new_settings[key].split(',') if i]
|
||||
|
||||
return new_settings
|
||||
2626
libs/sqllib/user.py
Normal file
2626
libs/sqllib/user.py
Normal file
File diff suppressed because it is too large
Load Diff
344
libs/sqllib/utils.py
Normal file
344
libs/sqllib/utils.py
Normal file
@@ -0,0 +1,344 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
import web
|
||||
|
||||
from libs import iredutils
|
||||
from libs.logger import log_activity
|
||||
from libs.sqllib import SQLWrap
|
||||
from libs.sqllib import domain as sql_lib_domain
|
||||
from libs.sqllib import admin as sql_lib_admin
|
||||
from libs.sqllib import user as sql_lib_user
|
||||
from libs.sqllib import alias as sql_lib_alias
|
||||
from libs.sqllib import ml as sql_lib_ml
|
||||
from libs.sqllib import general as sql_lib_general
|
||||
|
||||
session = web.config.get('_session', {})
|
||||
|
||||
|
||||
def set_account_status(conn,
|
||||
accounts,
|
||||
account_type,
|
||||
enable_account=False):
|
||||
"""Set account status.
|
||||
|
||||
accounts -- an iterable object (list/tuple) filled with accounts.
|
||||
account_type -- possible value: domain, admin, user, alias, ml
|
||||
enable_account -- possible value: True, False
|
||||
"""
|
||||
if account_type in ['admin', 'user', 'alias', 'maillist', 'ml']:
|
||||
# email
|
||||
accounts = [str(v).lower() for v in accounts if iredutils.is_email(v)]
|
||||
else:
|
||||
# domain name
|
||||
accounts = [str(v).lower() for v in accounts if iredutils.is_domain(v)]
|
||||
|
||||
if not accounts:
|
||||
return True,
|
||||
|
||||
# 0: disable, 1: enable
|
||||
account_status = 0
|
||||
action = 'disable'
|
||||
if enable_account:
|
||||
account_status = 1
|
||||
action = 'active'
|
||||
|
||||
if account_type == 'domain':
|
||||
# handle with function which handles admin privilege
|
||||
qr = sql_lib_domain.enable_disable_domains(domains=accounts,
|
||||
action=action)
|
||||
return qr
|
||||
elif account_type == 'admin':
|
||||
# [(<table>, <column-used-for-query>), ...]
|
||||
table_column_maps = [("admin", "username")]
|
||||
elif account_type == 'alias':
|
||||
table_column_maps = [
|
||||
("alias", "address"),
|
||||
("forwardings", "address"),
|
||||
]
|
||||
elif account_type in ['maillist', 'ml']:
|
||||
table_column_maps = [("maillists", "address")]
|
||||
else:
|
||||
# account_type == 'user'
|
||||
table_column_maps = [
|
||||
("mailbox", "username"),
|
||||
("forwardings", "address"),
|
||||
]
|
||||
|
||||
for (_table, _column) in table_column_maps:
|
||||
sql_where = '{} IN {}'.format(_column, web.sqlquote(accounts))
|
||||
try:
|
||||
conn.update(_table,
|
||||
where=sql_where,
|
||||
active=account_status)
|
||||
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
log_activity(event=action,
|
||||
msg="{} {}: {}.".format(action.title(), account_type, ', '.join(accounts)))
|
||||
return True,
|
||||
|
||||
|
||||
def delete_accounts(accounts,
|
||||
account_type,
|
||||
keep_mailbox_days=0,
|
||||
conn=None):
|
||||
# accounts must be a list/tuple.
|
||||
# account_type in ['domain', 'user', 'admin', 'alias', 'ml']
|
||||
if not accounts:
|
||||
return True,
|
||||
|
||||
if not conn:
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
if account_type == 'domain':
|
||||
qr = sql_lib_domain.delete_domains(domains=accounts,
|
||||
keep_mailbox_days=keep_mailbox_days,
|
||||
conn=conn)
|
||||
return qr
|
||||
elif account_type == 'user':
|
||||
sql_lib_user.delete_users(accounts=accounts,
|
||||
keep_mailbox_days=keep_mailbox_days,
|
||||
conn=conn)
|
||||
elif account_type == 'admin':
|
||||
sql_lib_admin.delete_admins(mails=accounts, conn=conn)
|
||||
elif account_type == 'alias':
|
||||
sql_lib_alias.delete_aliases(conn=conn, accounts=accounts)
|
||||
elif account_type == 'ml':
|
||||
sql_lib_ml.delete_maillists(conn=conn, accounts=accounts)
|
||||
|
||||
return True,
|
||||
|
||||
|
||||
# Search accounts with display name, email.
|
||||
def search(search_string,
|
||||
account_type=None,
|
||||
account_status=None,
|
||||
conn=None):
|
||||
"""Return search result in dict.
|
||||
|
||||
(True, {
|
||||
'domain': sql_query_result,
|
||||
'user': sql_query_result,
|
||||
...
|
||||
}
|
||||
)
|
||||
"""
|
||||
sql_vars = {
|
||||
'search_str': '%%' + search_string + '%%',
|
||||
'search_str_exclude_domain': '%%' + search_string + '%%@%%',
|
||||
}
|
||||
|
||||
if not account_type:
|
||||
account_type = ['domain', 'user', 'alias', 'ml', 'admin']
|
||||
|
||||
if not account_status:
|
||||
account_status = ['active', 'disabled']
|
||||
|
||||
sql_where_domain_status = ''
|
||||
sql_where_admin_status = ''
|
||||
sql_where_user_status = ''
|
||||
sql_where_ml_status = ''
|
||||
sql_where_alias_status = ''
|
||||
sql_where_user_domain = ''
|
||||
sql_where_alias_domain = ''
|
||||
sql_where_ml_domain = ''
|
||||
|
||||
if 'active' in account_status and 'disabled' in account_status:
|
||||
pass
|
||||
elif 'active' in account_status:
|
||||
sql_where_domain_status = ' AND domain.active=1'
|
||||
sql_where_admin_status = ' AND domain.active=1'
|
||||
sql_where_user_status = ' AND mailbox.active=1'
|
||||
sql_where_alias_status = ' AND alias.active=1'
|
||||
sql_where_ml_status = ' AND maillists.active=1'
|
||||
elif 'disabled' in account_status:
|
||||
sql_where_domain_status = ' AND domain.active=0'
|
||||
sql_where_admin_status = ' AND domain.active=0'
|
||||
sql_where_user_status = ' AND mailbox.active=0'
|
||||
sql_where_alias_status = ' AND alias.active=0'
|
||||
sql_where_ml_status = ' AND maillists.active=0'
|
||||
|
||||
if not conn:
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
# Get managed domains.
|
||||
if not session.get('is_global_admin'):
|
||||
qr = sql_lib_admin.get_managed_domains(admin=session.get('username'),
|
||||
domain_name_only=True,
|
||||
listed_only=True,
|
||||
conn=conn)
|
||||
|
||||
if qr[0]:
|
||||
managed_domains = qr[1]
|
||||
sql_where_user_domain = ' AND mailbox.domain IN %s' % web.sqlquote(managed_domains)
|
||||
sql_where_alias_domain = ' AND alias.domain IN %s' % web.sqlquote(managed_domains)
|
||||
sql_where_ml_domain = ' AND maillists.domain IN %s' % web.sqlquote(managed_domains)
|
||||
else:
|
||||
raise web.seeother('/search?msg=%s' % web.urlquote(qr[1]))
|
||||
|
||||
result = {
|
||||
'domain': [],
|
||||
'admin': [],
|
||||
'user': [],
|
||||
'ml': [],
|
||||
'last_logins': {},
|
||||
'user_alias_addresses': {},
|
||||
'user_forwarding_addresses': {},
|
||||
'user_assigned_groups': {},
|
||||
'alias': [],
|
||||
# List of email addresses of global admins.
|
||||
'allGlobalAdmins': [],
|
||||
}
|
||||
|
||||
if session.get('is_global_admin'):
|
||||
if 'domain' in account_type:
|
||||
qr_domain = conn.select(
|
||||
'domain',
|
||||
vars=sql_vars,
|
||||
what='domain,description,aliases,mailboxes,maxquota,active',
|
||||
where='(domain LIKE $search_str OR description LIKE $search_str) %s' % sql_where_domain_status,
|
||||
order='domain',
|
||||
)
|
||||
|
||||
if qr_domain:
|
||||
result['domain'] = iredutils.bytes2str(qr_domain)
|
||||
|
||||
if 'admin' in account_type:
|
||||
qr_admin = conn.select(
|
||||
'admin',
|
||||
vars=sql_vars,
|
||||
what='username,name,active',
|
||||
where='(username LIKE $search_str OR name LIKE $search_str) %s' % sql_where_admin_status,
|
||||
order='username',
|
||||
)
|
||||
|
||||
if qr_admin:
|
||||
result['admin'] = iredutils.bytes2str(qr_admin) or []
|
||||
|
||||
# Get all global admin accounts.
|
||||
qr = sql_lib_admin.get_all_global_admins(conn=conn)
|
||||
if qr[0]:
|
||||
result['allGlobalAdmins'] = qr[1]
|
||||
|
||||
# Search user accounts.
|
||||
if 'user' in account_type:
|
||||
search_str_user = sql_vars['search_str_exclude_domain']
|
||||
if '@' in sql_vars['search_str']:
|
||||
search_str_user = sql_vars['search_str']
|
||||
sql_vars['search_str_user'] = search_str_user
|
||||
|
||||
# Query users by email address or display name
|
||||
qr_user = conn.select(
|
||||
'mailbox',
|
||||
vars=sql_vars,
|
||||
what='username,name,quota,employeeid,active',
|
||||
where='(username LIKE $search_str_user OR name LIKE $search_str) {} {}'.format(sql_where_user_status, sql_where_user_domain),
|
||||
order='username')
|
||||
|
||||
# Query users by per-user alias address
|
||||
qr_user_alias = conn.select(
|
||||
['forwardings', 'mailbox'],
|
||||
vars=sql_vars,
|
||||
what='mailbox.username, mailbox.name, mailbox.quota, mailbox.employeeid, mailbox.active',
|
||||
where='(forwardings.address LIKE $search_str_user) AND forwardings.forwarding=mailbox.username AND forwardings.is_alias=1 {} {}'.format(sql_where_user_status, sql_where_user_domain),
|
||||
group='mailbox.username, mailbox.name, mailbox.quota, mailbox.employeeid, mailbox.active',
|
||||
order='mailbox.username')
|
||||
|
||||
# Query users by mail forwarding address
|
||||
qr_user_forwarding = conn.select(
|
||||
['forwardings', 'mailbox'],
|
||||
vars=sql_vars,
|
||||
what='mailbox.username, mailbox.name, mailbox.quota, mailbox.employeeid, mailbox.active',
|
||||
where='(forwardings.forwarding LIKE $search_str_user) AND forwardings.address=mailbox.username AND forwardings.is_forwarding=1 {} {}'.format(sql_where_user_status, sql_where_user_domain),
|
||||
group='mailbox.username, mailbox.name, mailbox.quota, mailbox.employeeid, mailbox.active',
|
||||
order='mailbox.username')
|
||||
|
||||
if qr_user:
|
||||
result['user'] += iredutils.bytes2str(qr_user)
|
||||
|
||||
if qr_user_alias:
|
||||
_records = iredutils.bytes2str(qr_user_alias)
|
||||
|
||||
# Add new, remove duplicate records.
|
||||
for i in _records:
|
||||
if not (i in result['user']):
|
||||
result['user'] += [i]
|
||||
|
||||
if qr_user_forwarding:
|
||||
_records = iredutils.bytes2str(qr_user_forwarding)
|
||||
|
||||
# Add new, remove duplicate records.
|
||||
for i in _records:
|
||||
if not (i in result['user']):
|
||||
result['user'] += [i]
|
||||
|
||||
# Get email addresses of returned user accounts
|
||||
_user_emails = []
|
||||
for i in result['user']:
|
||||
_user_emails.append(str(i['username']).lower())
|
||||
_user_emails.sort()
|
||||
|
||||
# Get per-user alias and mail forwarding addresses
|
||||
if _user_emails:
|
||||
(_status, _result) = sql_lib_user.get_bulk_user_alias_addresses(mails=_user_emails, conn=conn)
|
||||
if _status:
|
||||
result['user_alias_addresses'] = _result
|
||||
|
||||
(_status, _result) = sql_lib_user.get_bulk_user_forwardings(mails=_user_emails, conn=conn)
|
||||
if _status:
|
||||
result['user_forwarding_addresses'] = _result
|
||||
|
||||
(_status, _result) = sql_lib_user.get_bulk_user_assigned_groups(mails=_user_emails, conn=conn)
|
||||
if _status:
|
||||
result['user_assigned_groups'] = _result
|
||||
|
||||
# Get user last login
|
||||
result['last_logins'] = sql_lib_general.get_account_last_login(accounts=_user_emails, conn=conn)
|
||||
|
||||
# Search alias accounts.
|
||||
if 'alias' in account_type:
|
||||
search_str_alias = sql_vars['search_str_exclude_domain']
|
||||
if '@' in sql_vars['search_str']:
|
||||
search_str_alias = sql_vars['search_str']
|
||||
sql_vars['search_str_alias'] = search_str_alias
|
||||
|
||||
qr_alias = conn.select(
|
||||
'alias',
|
||||
vars=sql_vars,
|
||||
what='address,name,accesspolicy,domain,active',
|
||||
where='(address LIKE $search_str_alias OR name LIKE $search_str) {} {}'.format(
|
||||
sql_where_alias_status, sql_where_alias_domain,
|
||||
),
|
||||
order='address',
|
||||
)
|
||||
|
||||
if qr_alias:
|
||||
result['alias'] = iredutils.bytes2str(qr_alias) or []
|
||||
|
||||
# Search mailing list accounts.
|
||||
if 'ml' in account_type:
|
||||
search_str_ml = sql_vars['search_str_exclude_domain']
|
||||
if '@' in sql_vars['search_str']:
|
||||
search_str_ml = sql_vars['search_str']
|
||||
sql_vars['search_str_ml'] = search_str_ml
|
||||
|
||||
qr_ml = conn.select(
|
||||
'maillists',
|
||||
vars=sql_vars,
|
||||
what='address,name,accesspolicy,domain,active',
|
||||
where='(address LIKE $search_str_alias OR name LIKE $search_str) {} {}'.format(
|
||||
sql_where_ml_status, sql_where_ml_domain,
|
||||
),
|
||||
order='address',
|
||||
)
|
||||
|
||||
if qr_ml:
|
||||
result['ml'] = iredutils.bytes2str(qr_ml) or []
|
||||
|
||||
if result:
|
||||
return True, result
|
||||
else:
|
||||
return False, []
|
||||
Reference in New Issue
Block a user