mirror of
https://github.com/marcus-alicia/iRedAdmin-Pro-SQL.git
synced 2026-05-26 07:08:10 +00:00
Add files via upload
This commit is contained in:
0
controllers/sql/__init__.py
Normal file
0
controllers/sql/__init__.py
Normal file
208
controllers/sql/admin.py
Normal file
208
controllers/sql/admin.py
Normal file
@@ -0,0 +1,208 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
import web
|
||||
import settings
|
||||
from libs import iredutils
|
||||
from libs.l10n import TIMEZONES
|
||||
|
||||
from libs.sqllib import SQLWrap, decorators
|
||||
from libs.sqllib import general as sql_lib_general
|
||||
from libs.sqllib import user as sql_lib_user
|
||||
from libs.sqllib import admin as sql_lib_admin
|
||||
from libs.sqllib import domain as sql_lib_domain
|
||||
from libs.sqllib import utils as sql_lib_utils
|
||||
|
||||
session = web.config.get('_session')
|
||||
|
||||
|
||||
class List:
|
||||
@decorators.require_global_admin
|
||||
def GET(self, cur_page=1):
|
||||
form = web.input()
|
||||
cur_page = int(cur_page)
|
||||
|
||||
if cur_page == 0:
|
||||
cur_page = 1
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
result = sql_lib_admin.get_paged_admins(conn=conn,
|
||||
cur_page=cur_page)
|
||||
|
||||
if result[0]:
|
||||
(total, records) = (result[1]['total'], result[1]['records'])
|
||||
|
||||
# Get list of global admins.
|
||||
all_global_admins = []
|
||||
qr = sql_lib_admin.get_all_global_admins(conn=conn)
|
||||
if qr[0]:
|
||||
all_global_admins = qr[1]
|
||||
|
||||
return web.render(
|
||||
'sql/admin/list.html',
|
||||
cur_page=cur_page,
|
||||
total=total,
|
||||
admins=records,
|
||||
allGlobalAdmins=all_global_admins,
|
||||
msg=form.get('msg', None),
|
||||
)
|
||||
else:
|
||||
raise web.seeother('/domains?msg=%s' % web.urlquote(result[1]))
|
||||
|
||||
@decorators.require_global_admin
|
||||
@decorators.csrf_protected
|
||||
def POST(self):
|
||||
form = web.input(_unicode=False, mail=[])
|
||||
|
||||
accounts = form.get('mail', [])
|
||||
action = form.get('action', None)
|
||||
msg = form.get('msg', None)
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
if action == 'delete':
|
||||
result = sql_lib_admin.delete_admins(mails=accounts,
|
||||
revoke_admin_privilege_from_user=True,
|
||||
conn=conn)
|
||||
msg = 'DELETED'
|
||||
elif action == 'disable':
|
||||
result = sql_lib_utils.set_account_status(conn=conn,
|
||||
accounts=accounts,
|
||||
account_type='admin',
|
||||
enable_account=False)
|
||||
msg = 'DISABLED'
|
||||
elif action == 'enable':
|
||||
result = sql_lib_utils.set_account_status(conn=conn,
|
||||
accounts=accounts,
|
||||
account_type='admin',
|
||||
enable_account=True)
|
||||
msg = 'ENABLED'
|
||||
else:
|
||||
result = (False, 'INVALID_ACTION')
|
||||
|
||||
if result[0]:
|
||||
raise web.seeother('/admins?msg=%s' % msg)
|
||||
else:
|
||||
raise web.seeother('/admins?msg=?' + web.urlquote(result[1]))
|
||||
|
||||
|
||||
class Profile:
|
||||
@decorators.require_admin_login
|
||||
def GET(self, profile_type, mail):
|
||||
mail = str(mail).lower()
|
||||
form = web.input()
|
||||
|
||||
if not (session.get('is_global_admin') or session.get('username') == mail):
|
||||
# Don't allow to view/update others' profile.
|
||||
raise web.seeother('/profile/admin/general/%s?msg=PERMISSION_DENIED' % session.get('username'))
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
is_global_admin = sql_lib_general.is_global_admin(admin=mail, conn=conn)
|
||||
result = sql_lib_admin.get_profile(mail=mail, conn=conn)
|
||||
|
||||
if result[0]:
|
||||
profile = result[1]
|
||||
qr = sql_lib_general.get_admin_settings(admin=mail, conn=conn)
|
||||
if qr[0]:
|
||||
admin_settings = qr[1]
|
||||
else:
|
||||
return qr
|
||||
|
||||
# Get all domains.
|
||||
all_domains = []
|
||||
|
||||
qr_all_domains = sql_lib_domain.get_all_domains(conn=conn)
|
||||
if qr_all_domains[0]:
|
||||
all_domains = qr_all_domains[1]
|
||||
|
||||
# Get managed domains.
|
||||
managed_domains = []
|
||||
|
||||
qr = sql_lib_admin.get_managed_domains(conn=conn,
|
||||
admin=mail,
|
||||
domain_name_only=True,
|
||||
listed_only=True)
|
||||
if qr[0]:
|
||||
managed_domains += qr[1]
|
||||
|
||||
return web.render(
|
||||
'sql/admin/profile.html',
|
||||
mail=mail,
|
||||
profile_type=profile_type,
|
||||
is_global_admin=is_global_admin,
|
||||
profile=profile,
|
||||
admin_settings=admin_settings,
|
||||
languagemaps=iredutils.get_language_maps(),
|
||||
timezones=TIMEZONES,
|
||||
allDomains=all_domains,
|
||||
managedDomains=managed_domains,
|
||||
min_passwd_length=settings.min_passwd_length,
|
||||
max_passwd_length=settings.max_passwd_length,
|
||||
store_password_in_plain_text=settings.STORE_PASSWORD_IN_PLAIN_TEXT,
|
||||
password_policies=iredutils.get_password_policies(),
|
||||
msg=form.get('msg'),
|
||||
)
|
||||
else:
|
||||
# Return to user profile page if admin is a mail user.
|
||||
qr = sql_lib_user.simple_profile(conn=conn,
|
||||
mail=mail,
|
||||
columns=['username'])
|
||||
|
||||
if qr[0]:
|
||||
raise web.seeother('/profile/user/general/' + mail)
|
||||
else:
|
||||
raise web.seeother('/admins?msg=' + web.urlquote(result[1]))
|
||||
|
||||
@decorators.csrf_protected
|
||||
@decorators.require_admin_login
|
||||
def POST(self, profile_type, mail):
|
||||
mail = str(mail).lower()
|
||||
form = web.input(domainName=[])
|
||||
|
||||
if not (session.get('is_global_admin') or session.get('username') == mail):
|
||||
# Don't allow to view/update others' profile.
|
||||
raise web.seeother('/profile/admin/general/%s?msg=PERMISSION_DENIED' % session.get('username'))
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
result = sql_lib_admin.update(mail=mail,
|
||||
profile_type=profile_type,
|
||||
form=form,
|
||||
conn=conn)
|
||||
|
||||
if result[0]:
|
||||
raise web.seeother('/profile/admin/{}/{}?msg=UPDATED'.format(profile_type, mail))
|
||||
else:
|
||||
raise web.seeother('/profile/admin/{}/{}?msg={}'.format(profile_type, mail, web.urlquote(result[1])))
|
||||
|
||||
|
||||
class Create:
|
||||
@decorators.require_global_admin
|
||||
def GET(self):
|
||||
form = web.input()
|
||||
return web.render('sql/admin/create.html',
|
||||
languagemaps=iredutils.get_language_maps(),
|
||||
default_language=settings.default_language,
|
||||
min_passwd_length=settings.min_passwd_length,
|
||||
max_passwd_length=settings.max_passwd_length,
|
||||
password_policies=iredutils.get_password_policies(),
|
||||
msg=form.get('msg'))
|
||||
|
||||
@decorators.require_global_admin
|
||||
@decorators.csrf_protected
|
||||
def POST(self):
|
||||
form = web.input()
|
||||
mail = web.safestr(form.get('mail')).lower()
|
||||
|
||||
qr = sql_lib_admin.add_admin_from_form(form=form, conn=None)
|
||||
|
||||
if qr[0]:
|
||||
# Redirect to assign domains.
|
||||
raise web.seeother('/profile/admin/general/%s?msg=CREATED' % mail)
|
||||
else:
|
||||
raise web.seeother('/create/admin?msg=' + web.urlquote(qr[1]))
|
||||
224
controllers/sql/alias.py
Normal file
224
controllers/sql/alias.py
Normal file
@@ -0,0 +1,224 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
import web
|
||||
|
||||
from libs import iredutils, form_utils
|
||||
from libs.sqllib import SQLWrap, decorators
|
||||
from libs.sqllib import alias as sql_lib_alias
|
||||
from libs.sqllib import admin as sql_lib_admin
|
||||
from libs.sqllib import domain as sql_lib_domain
|
||||
from libs.sqllib import general as sql_lib_general
|
||||
from libs.sqllib import utils as sql_lib_utils
|
||||
|
||||
session = web.config.get('_session')
|
||||
|
||||
|
||||
class List:
|
||||
@decorators.require_domain_access
|
||||
def GET(self, domain, cur_page=1, disabled_only=False):
|
||||
domain = str(domain).lower()
|
||||
cur_page = int(cur_page) or 1
|
||||
|
||||
form = web.input(_unicode=False)
|
||||
|
||||
all_first_chars = []
|
||||
first_char = None
|
||||
if 'starts_with' in form:
|
||||
first_char = form.get('starts_with')[:1].upper()
|
||||
if not iredutils.is_valid_account_first_char(first_char):
|
||||
first_char = None
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
total = sql_lib_alias.num_aliases_under_domain(conn=conn,
|
||||
domain=domain,
|
||||
disabled_only=disabled_only,
|
||||
first_char=first_char)
|
||||
|
||||
records = []
|
||||
if total:
|
||||
_qr = sql_lib_general.get_first_char_of_all_accounts(domain=domain,
|
||||
account_type='alias',
|
||||
conn=conn)
|
||||
if _qr[0]:
|
||||
all_first_chars = _qr[1]
|
||||
|
||||
qr = sql_lib_alias.get_basic_alias_profiles(conn=conn,
|
||||
domain=domain,
|
||||
page=cur_page,
|
||||
first_char=first_char,
|
||||
disabled_only=disabled_only)
|
||||
if qr[0]:
|
||||
records = qr[1]
|
||||
|
||||
return web.render(
|
||||
'sql/alias/list.html',
|
||||
cur_domain=domain,
|
||||
cur_page=cur_page,
|
||||
total=total,
|
||||
aliases=records,
|
||||
all_first_chars=all_first_chars,
|
||||
first_char=first_char,
|
||||
disabled_only=disabled_only,
|
||||
msg=form.get('msg', None),
|
||||
)
|
||||
|
||||
@decorators.csrf_protected
|
||||
@decorators.require_domain_access
|
||||
def POST(self, domain):
|
||||
form = web.input(_unicode=False, mail=[])
|
||||
domain = str(domain).lower()
|
||||
|
||||
accounts = form.get('mail', [])
|
||||
action = form.get('action', None)
|
||||
msg = form.get('msg', None)
|
||||
|
||||
# Filter aliases not under the same domain.
|
||||
accounts = [str(v).lower()
|
||||
for v in accounts
|
||||
if iredutils.is_email(v) and str(v).endswith('@' + domain)]
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
if action == 'delete':
|
||||
result = sql_lib_alias.delete_aliases(conn=conn,
|
||||
accounts=accounts)
|
||||
msg = 'DELETED'
|
||||
elif action == 'disable':
|
||||
result = sql_lib_utils.set_account_status(conn=conn,
|
||||
accounts=accounts,
|
||||
account_type='alias',
|
||||
enable_account=False)
|
||||
msg = 'DISABLED'
|
||||
elif action == 'enable':
|
||||
result = sql_lib_utils.set_account_status(conn=conn,
|
||||
accounts=accounts,
|
||||
account_type='alias',
|
||||
enable_account=True)
|
||||
msg = 'ENABLED'
|
||||
else:
|
||||
result = (False, 'INVALID_ACTION')
|
||||
|
||||
if result[0]:
|
||||
raise web.seeother('/aliases/{}?msg={}'.format(domain, msg))
|
||||
else:
|
||||
raise web.seeother('/aliases/{}?msg={}'.format(domain, web.urlquote(result[1])))
|
||||
|
||||
|
||||
class ListDisabled:
|
||||
@decorators.require_domain_access
|
||||
def GET(self, domain, cur_page=1):
|
||||
_list = List()
|
||||
return _list.GET(domain=domain, cur_page=cur_page, disabled_only=True)
|
||||
|
||||
|
||||
class Create:
|
||||
@decorators.require_domain_access
|
||||
def GET(self, domain):
|
||||
domain = str(domain).lower()
|
||||
|
||||
form = web.input()
|
||||
all_domains = []
|
||||
|
||||
# Get all domains, select the first one.
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
if session.get('is_global_admin'):
|
||||
qr = sql_lib_domain.get_all_domains(conn=conn, name_only=True)
|
||||
else:
|
||||
qr = sql_lib_admin.get_managed_domains(conn=conn,
|
||||
admin=session.get('username'),
|
||||
domain_name_only=True)
|
||||
|
||||
if qr[0]:
|
||||
all_domains = qr[1]
|
||||
|
||||
# Get domain profile.
|
||||
qr_profile = sql_lib_domain.simple_profile(domain=domain, conn=conn)
|
||||
if qr_profile[0]:
|
||||
domain_profile = qr_profile[1]
|
||||
else:
|
||||
raise web.seeother('/domains?msg=%s' % web.urlquote(qr_profile[1]))
|
||||
|
||||
# Cet total number and allocated quota size of existing users under domain.
|
||||
num_aliases_under_domain = sql_lib_alias.num_aliases_under_domain(conn=conn, domain=domain)
|
||||
|
||||
return web.render(
|
||||
'sql/alias/create.html',
|
||||
cur_domain=domain,
|
||||
allDomains=all_domains,
|
||||
profile=domain_profile,
|
||||
num_existing_aliases=num_aliases_under_domain,
|
||||
msg=form.get('msg'),
|
||||
)
|
||||
|
||||
@decorators.require_domain_access
|
||||
@decorators.csrf_protected
|
||||
def POST(self, domain):
|
||||
domain = str(domain).lower()
|
||||
form = web.input()
|
||||
|
||||
domain_in_form = form_utils.get_domain_name(form)
|
||||
|
||||
if domain != domain_in_form:
|
||||
raise web.seeother('/domains?msg=PERMISSION_DENIED')
|
||||
|
||||
listname = form_utils.get_single_value(form, input_name='listname', to_string=True)
|
||||
mail = listname + '@' + domain
|
||||
|
||||
result = sql_lib_alias.add_alias_from_form(domain=domain, form=form)
|
||||
|
||||
if result[0]:
|
||||
raise web.seeother('/profile/alias/general/%s?msg=CREATED' % mail)
|
||||
else:
|
||||
raise web.seeother('/create/alias/{}?msg={}'.format(domain, web.urlquote(result[1])))
|
||||
|
||||
|
||||
class Profile:
|
||||
@decorators.require_domain_access
|
||||
def GET(self, profile_type, mail):
|
||||
if profile_type == 'rename':
|
||||
raise web.seeother('/profile/alias/general/' + mail)
|
||||
|
||||
form = web.input()
|
||||
mail = web.safestr(mail).lower()
|
||||
domain = mail.split('@', 1)[-1]
|
||||
|
||||
if not iredutils.is_email(mail):
|
||||
raise web.seeother('/domains?msg=INVALID_MAIL')
|
||||
|
||||
qr = sql_lib_alias.get_profile(mail=mail,
|
||||
with_members=True,
|
||||
with_moderators=True,
|
||||
conn=None)
|
||||
if qr[0]:
|
||||
profile = qr[1]
|
||||
else:
|
||||
raise web.seeother('/aliases/{}?msg={}'.format(domain, web.urlquote(qr[1])))
|
||||
|
||||
return web.render('sql/alias/profile.html',
|
||||
cur_domain=domain,
|
||||
mail=mail,
|
||||
profile_type=profile_type,
|
||||
profile=profile,
|
||||
msg=form.get('msg'))
|
||||
|
||||
@decorators.csrf_protected
|
||||
@decorators.require_domain_access
|
||||
def POST(self, profile_type, mail):
|
||||
form = web.input()
|
||||
|
||||
result = sql_lib_alias.update(mail=mail,
|
||||
profile_type=profile_type,
|
||||
form=form)
|
||||
|
||||
if profile_type == 'rename':
|
||||
profile_type = 'general'
|
||||
|
||||
if result[0]:
|
||||
raise web.seeother('/profile/alias/{}/{}?msg=UPDATED'.format(profile_type, mail))
|
||||
else:
|
||||
raise web.seeother('/profile/alias/{}/{}?msg={}'.format(profile_type, mail, web.urlquote(result[1])))
|
||||
158
controllers/sql/api_admin.py
Normal file
158
controllers/sql/api_admin.py
Normal file
@@ -0,0 +1,158 @@
|
||||
import web
|
||||
|
||||
from controllers.utils import api_render
|
||||
|
||||
from libs.sqllib import SQLWrap
|
||||
from libs.sqllib import decorators, api_utils
|
||||
from libs.sqllib import admin as sql_lib_admin
|
||||
from libs.sqllib import general as sql_lib_general
|
||||
|
||||
import settings
|
||||
|
||||
|
||||
# Parameter names used in API interface and web form, both POST and PUT.
|
||||
_param_maps = [('maxDomains', 'create_max_domains'),
|
||||
('maxUsers', 'create_max_users'),
|
||||
('maxAliases', 'create_max_aliases'),
|
||||
('maxLists', 'create_max_lists'),
|
||||
('maxQuota', 'create_max_quota'),
|
||||
('quotaUnit', 'create_quota_unit')]
|
||||
|
||||
|
||||
class APIAdmin:
|
||||
@decorators.api_require_global_admin
|
||||
def GET(self, mail):
|
||||
"""Get profile of a standalone domain admin.
|
||||
|
||||
curl -X GET -i -b cookie.txt https://<server>/api/admin/<mail>
|
||||
"""
|
||||
mail = str(mail).lower()
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
qr = sql_lib_admin.get_profile(mail=mail, conn=conn)
|
||||
if qr[0]:
|
||||
profile = api_utils.export_sql_record(record=qr[1],
|
||||
remove_columns=settings.API_HIDDEN_ADMIN_PROFILES)
|
||||
|
||||
profile['isglobaladmin'] = 0
|
||||
if sql_lib_general.is_global_admin(admin=mail, conn=conn):
|
||||
profile['isglobaladmin'] = 1
|
||||
|
||||
_qr = sql_lib_admin.get_managed_domains(admin=mail,
|
||||
domain_name_only=True,
|
||||
listed_only=True,
|
||||
conn=conn)
|
||||
if _qr[0]:
|
||||
profile['managed_domains'] = _qr[1]
|
||||
|
||||
return api_render((True, profile))
|
||||
else:
|
||||
return api_render(qr)
|
||||
|
||||
@decorators.api_require_global_admin
|
||||
def POST(self, mail):
|
||||
"""Create a new domain.
|
||||
|
||||
curl -X POST -i -b cookie.txt -d "var=<value>&var2=value2" https://<server>/api/admin/<mail>
|
||||
|
||||
:param mail: admin email address.
|
||||
|
||||
Form parameters:
|
||||
|
||||
`name`: the display name of this admin
|
||||
`password`: admin's password
|
||||
`accountStatus`: account status (active, disabled)
|
||||
`domainGlobalAdmin`: Mark this admin as global admin (yes, no).
|
||||
`language`: default preferred language for new user.
|
||||
e.g. en_US for English, de_DE for Deutsch.
|
||||
|
||||
Form parameters listed below are used by normal domain admin, so they
|
||||
cannot be set while `domainGlobalAdmin=yes`.
|
||||
|
||||
`maxDomains`: how many mail domains this admin can create.
|
||||
`maxQuota`: how much mailbox quota this admin can create.
|
||||
Quota is shared by all domains created/managed by this
|
||||
admin. Sample: 10, 20, 30. Must be used with @quotaUnit.
|
||||
`quotaUnit`: quota unit of @maxQuota. Must be used with @maxQuota.
|
||||
`maxUsers`: how many mail users this admin can create.
|
||||
It's shared by all domains created/managed by this admin.
|
||||
`maxAliases`: how many mail aliases this admin can create.
|
||||
It's shared by all domains created/managed by this admin.
|
||||
`maxUsers`: how many mailing lists this admin can create.
|
||||
It's shared by all domains created/managed by this admin.
|
||||
"""
|
||||
form = web.input()
|
||||
|
||||
form['mail'] = mail
|
||||
form['cn'] = form.get('name')
|
||||
form['newpw'] = form.get('password')
|
||||
form['confirmpw'] = form.get('password')
|
||||
form['domainGlobalAdmin'] = form.get('isGlobalAdmin')
|
||||
form['preferredLanguage'] = form.get('language')
|
||||
|
||||
for (k_api, k_web) in _param_maps:
|
||||
if k_api in form:
|
||||
form[k_web] = form[k_api]
|
||||
|
||||
# [(api_form_name, web_form_name), ...]
|
||||
for (k_api, k_web) in [('disableViewingMailLog', 'disable_viewing_mail_log'),
|
||||
('disableManagingQuarantinedMails', 'disable_managing_quarantined_mails')]:
|
||||
v = form.get(k_api, '')
|
||||
if v == 'yes':
|
||||
form[k_web] = 'yes'
|
||||
|
||||
qr = sql_lib_admin.add_admin_from_form(form=form)
|
||||
return api_render(qr)
|
||||
|
||||
@decorators.api_require_global_admin
|
||||
def DELETE(self, mail):
|
||||
"""Delete an existing mail domain.
|
||||
|
||||
curl -X DELETE -i -b cookie.txt https://<server>/api/admin/<mail>
|
||||
"""
|
||||
qr = sql_lib_admin.delete_admins(mails=[mail], revoke_admin_privilege_from_user=False)
|
||||
return api_render(qr)
|
||||
|
||||
@decorators.api_require_global_admin
|
||||
def PUT(self, mail):
|
||||
"""Update profile of existing standalone domain admin.
|
||||
|
||||
curl -X PUT -i -b cookie.txt -d "var=<value>" https://<server>/api/domain/<domain>
|
||||
curl -X PUT -i -b cookie.txt -d "var=<value>&var2=<value2>" https://<server>/api/domain/<domain>
|
||||
|
||||
:param mail: full admin email address.
|
||||
|
||||
Form parameters:
|
||||
|
||||
`name`: the display name of this admin
|
||||
`password`: admin's password
|
||||
`accountStatus`: account status (active, disabled)
|
||||
`domainGlobalAdmin`: Mark this admin as global admin (yes, no).
|
||||
`language`: default preferred language for new user.
|
||||
e.g. en_US for English, de_DE for Deutsch.
|
||||
|
||||
Form parameters listed below are used by normal domain admin, so they
|
||||
cannot be set while `domainGlobalAdmin=yes`.
|
||||
|
||||
`maxDomains`: how many mail domains this admin can create.
|
||||
`maxQuota`: how much mailbox quota this admin can create.
|
||||
Quota is shared by all domains created/managed by this
|
||||
admin. Sample: 10, 20, 30. Must be used with @quotaUnit.
|
||||
`quotaUnit`: quota unit of @maxQuota. Must be used with @maxQuota.
|
||||
`maxUsers`: how many mail users this admin can create.
|
||||
It's shared by all domains created/managed by this admin.
|
||||
`maxAliases`: how many mail aliases this admin can create.
|
||||
It's shared by all domains created/managed by this admin.
|
||||
`maxUsers`: how many mailing lists this admin can create.
|
||||
It's shared by all domains created/managed by this admin.
|
||||
"""
|
||||
form = web.input()
|
||||
|
||||
for (k_api, k_web) in _param_maps:
|
||||
if k_api in form:
|
||||
form[k_web] = form[k_api]
|
||||
|
||||
qr = sql_lib_admin.api_update_profile(form=form, mail=mail)
|
||||
return api_render(qr)
|
||||
243
controllers/sql/api_alias.py
Normal file
243
controllers/sql/api_alias.py
Normal file
@@ -0,0 +1,243 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
import web
|
||||
|
||||
from controllers.utils import api_render
|
||||
|
||||
from libs import iredutils, form_utils
|
||||
from libs.logger import log_activity
|
||||
from libs.sqllib import SQLWrap, decorators
|
||||
from libs.sqllib import general as sql_lib_general
|
||||
from libs.sqllib import alias as sql_lib_alias
|
||||
from libs.sqllib import api_utils
|
||||
|
||||
session = web.config.get('_session')
|
||||
|
||||
|
||||
class APIAlias:
|
||||
@decorators.api_require_domain_access
|
||||
def GET(self, mail):
|
||||
"""Export mail alias profile.
|
||||
|
||||
curl -X GET -i -b cookie.txt https://<server>/api/alias/<mail>
|
||||
"""
|
||||
mail = str(mail).lower()
|
||||
qr = sql_lib_alias.get_profile(mail=mail, conn=None)
|
||||
if qr[0]:
|
||||
profile = api_utils.export_sql_record(record=qr[1])
|
||||
return api_render((True, profile))
|
||||
else:
|
||||
return api_render(qr)
|
||||
|
||||
@decorators.api_require_domain_access
|
||||
def POST(self, mail):
|
||||
"""Create a new mail alias account.
|
||||
|
||||
curl -X POST -i -b cookie.txt -d "..." https://<server>/api/alias/<email>
|
||||
|
||||
Optional POST data:
|
||||
|
||||
@name - display name
|
||||
@accessPolicy - access policy
|
||||
@members - members of mail alias
|
||||
"""
|
||||
mail = str(mail).lower()
|
||||
(listname, domain) = mail.split('@', 1)
|
||||
|
||||
form = web.input()
|
||||
|
||||
form['listname'] = listname
|
||||
form['domainName'] = domain
|
||||
|
||||
form['cn'] = form.get('name')
|
||||
|
||||
qr = sql_lib_alias.add_alias_from_form(domain=domain, form=form)
|
||||
|
||||
if qr[0] and 'members' in form:
|
||||
# Update mail forwarding addresses
|
||||
_addresses = form_utils.get_multi_values_from_api(form=form,
|
||||
input_name='members',
|
||||
to_lowercase=False,
|
||||
is_email=True)
|
||||
_qr = sql_lib_alias.reset_members(mail=mail, members=_addresses)
|
||||
return api_render(_qr)
|
||||
|
||||
return api_render(qr)
|
||||
|
||||
# Delete aliases.
|
||||
@decorators.api_require_domain_access
|
||||
def DELETE(self, mail):
|
||||
"""Delete a mail alias account.
|
||||
curl -X DELETE -i -b cookie.txt https://<server>/api/alias/<mail>
|
||||
"""
|
||||
mail = str(mail).lower()
|
||||
qr = sql_lib_alias.delete_aliases(accounts=[mail])
|
||||
return api_render(qr)
|
||||
|
||||
@decorators.api_require_domain_access
|
||||
def PUT(self, mail):
|
||||
"""Update profile of existing mail alias account.
|
||||
|
||||
curl -X PUT -i -b cookie.txt -d "var=<value>" https://<server>/api/alias/<mail>
|
||||
curl -X PUT -i -b cookie.txt -d "var=<value>&var2=<value2>" https://<server>/api/alias/<mail>
|
||||
|
||||
Optional PUT data:
|
||||
|
||||
@name - common name (or, display name)
|
||||
@accountStatus - enable or disable user. possible value is: active, disabled.
|
||||
@accessPolicy - access policy.
|
||||
@members - members of mail alias
|
||||
@addMember - add new members to mailing list
|
||||
@removeMember - remove members from mailing list
|
||||
"""
|
||||
mail = str(mail).lower()
|
||||
domain = mail.split('@', 1)[-1]
|
||||
|
||||
form = web.input()
|
||||
|
||||
params = {}
|
||||
|
||||
# Name
|
||||
kv = form_utils.get_form_dict(form,
|
||||
input_name='name',
|
||||
key_name='name',
|
||||
default_value='')
|
||||
params.update(kv)
|
||||
|
||||
# accountStatus
|
||||
kv = form_utils.get_form_dict(form,
|
||||
input_name='accountStatus',
|
||||
key_name='active',
|
||||
default_value='1')
|
||||
params.update(kv)
|
||||
|
||||
# Access policy
|
||||
kv = form_utils.get_form_dict(form,
|
||||
input_name='accessPolicy',
|
||||
key_name='accesspolicy',
|
||||
default_value='public')
|
||||
params.update(kv)
|
||||
|
||||
# Reset all members
|
||||
_members = []
|
||||
|
||||
# Add new members
|
||||
_new = []
|
||||
|
||||
# Remove existing members
|
||||
_removed = []
|
||||
|
||||
if 'members' in form:
|
||||
# Update mail forwarding addresses
|
||||
_v = form_utils.get_multi_values_from_api(form=form,
|
||||
input_name='members',
|
||||
to_lowercase=False,
|
||||
is_email=True)
|
||||
_members = [iredutils.lower_email_with_upper_ext_address(i) for i in _v]
|
||||
|
||||
else:
|
||||
if 'addMember' in form:
|
||||
_v = form_utils.get_multi_values_from_api(form=form,
|
||||
input_name='addMember',
|
||||
to_lowercase=False,
|
||||
is_email=True)
|
||||
_new = [iredutils.lower_email_with_upper_ext_address(i) for i in _v]
|
||||
|
||||
if 'removeMember' in form:
|
||||
_v = form_utils.get_multi_values_from_api(form=form,
|
||||
input_name='removeMember',
|
||||
to_lowercase=False,
|
||||
is_email=True)
|
||||
_removed = [iredutils.lower_email_with_upper_ext_address(i) for i in _v]
|
||||
|
||||
if not (params or ('members' in form) or _new or _removed):
|
||||
return api_render(True)
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
if not sql_lib_general.is_alias_exists(mail=mail, conn=conn):
|
||||
return api_render((False, 'NO_SUCH_ACCOUNT'))
|
||||
|
||||
if params:
|
||||
try:
|
||||
conn.update('alias',
|
||||
vars={'mail': mail},
|
||||
where='address=$mail',
|
||||
**params)
|
||||
|
||||
log_activity(msg="Update alias profile: {} -> {}".format(mail, ', '.join(params)),
|
||||
admin=session.get('username'),
|
||||
username=mail,
|
||||
domain=domain,
|
||||
event='update')
|
||||
|
||||
except Exception as e:
|
||||
return api_render((False, repr(e)))
|
||||
|
||||
if 'members' in form:
|
||||
qr = sql_lib_alias.reset_members(mail=mail, members=_members, conn=conn)
|
||||
|
||||
if not qr[0]:
|
||||
return api_render(qr)
|
||||
|
||||
if _new or _removed:
|
||||
qr = sql_lib_alias.update_members(mail=mail,
|
||||
new_members=_new,
|
||||
removed_members=_removed,
|
||||
conn=conn)
|
||||
|
||||
if not qr[0]:
|
||||
return api_render(qr)
|
||||
|
||||
return api_render(True)
|
||||
|
||||
|
||||
class APIChangeEmail:
|
||||
@decorators.api_require_domain_access
|
||||
def POST(self, mail, new_mail):
|
||||
"""Change email address of mail alias account.
|
||||
|
||||
curl -X POST -i -b cookie.txt https://<server>/api/alias/<mail>/change_email/<new_mail>
|
||||
"""
|
||||
mail = str(mail).lower()
|
||||
new_mail = str(new_mail).lower()
|
||||
|
||||
qr = sql_lib_alias.change_email(mail=mail, new_mail=new_mail)
|
||||
return api_render(qr)
|
||||
|
||||
|
||||
class APIAliases:
|
||||
@decorators.api_require_domain_access
|
||||
def GET(self, domain):
|
||||
"""List all mail aliases under given domain.
|
||||
|
||||
curl -X GET -i -b cookie.txt https://<server>/api/aliases/<domain>
|
||||
|
||||
Optional parameters:
|
||||
|
||||
@email_only -- return a list of email addresses.
|
||||
if not present, return a list of account profiles
|
||||
(dicts).
|
||||
@disabled_only -- return disabled accounts.
|
||||
"""
|
||||
domain = str(domain).lower()
|
||||
|
||||
form = web.input(_unicode=True)
|
||||
email_only = ('email_only' in form)
|
||||
disabled_only = ('disabled_only' in form)
|
||||
|
||||
qr = sql_lib_alias.get_basic_alias_profiles(domain=domain,
|
||||
email_only=email_only,
|
||||
disabled_only=disabled_only,
|
||||
conn=None)
|
||||
|
||||
if qr[0]:
|
||||
if email_only:
|
||||
emails = qr[1]
|
||||
return api_render((True, emails))
|
||||
else:
|
||||
profiles = api_utils.export_sql_records(records=qr[1])
|
||||
return api_render((True, profiles))
|
||||
else:
|
||||
return api_render(qr)
|
||||
237
controllers/sql/api_domain.py
Normal file
237
controllers/sql/api_domain.py
Normal file
@@ -0,0 +1,237 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
import web
|
||||
|
||||
from controllers.utils import api_render
|
||||
|
||||
from libs.sqllib import SQLWrap, decorators
|
||||
from libs.sqllib import domain as sql_lib_domain
|
||||
from libs.sqllib import general as sql_lib_general
|
||||
from libs.sqllib import api_utils
|
||||
|
||||
session = web.config.get('_session')
|
||||
|
||||
|
||||
class APIDomains:
|
||||
@decorators.api_require_admin_login
|
||||
def GET(self):
|
||||
"""Get all managed domains.
|
||||
|
||||
curl -X GET -i -b cookie.txt https://<server>/api/domains
|
||||
curl -X GET -i -b cookie.txt https://<server>/api/domains?name_only=
|
||||
curl -X GET -i -b cookie.txt https://<server>/api/domains?name_only=&disabled_only=
|
||||
|
||||
Optional parameters:
|
||||
|
||||
@name_only - Return only domain names, no profiles.
|
||||
@disabled_only - Return profiles of disabled domains.
|
||||
|
||||
Values of above 2 parameters don't matter at all, for example, these 2
|
||||
values are the same: `name_only=`, `name_only=yes`.
|
||||
"""
|
||||
name_only = False
|
||||
disabled_only = False
|
||||
|
||||
form = web.input()
|
||||
if 'name_only' in form:
|
||||
name_only = True
|
||||
|
||||
if 'disabled_only' in form:
|
||||
disabled_only = True
|
||||
|
||||
qr = sql_lib_domain.get_all_managed_domains(name_only=name_only, disabled_only=disabled_only)
|
||||
if qr[0]:
|
||||
if name_only:
|
||||
return api_render((True, qr[1]))
|
||||
else:
|
||||
profiles = {}
|
||||
for i in qr[1]:
|
||||
domain = str(i.domain).lower()
|
||||
profiles[domain] = api_utils.export_sql_record(record=i)
|
||||
|
||||
return api_render((True, profiles))
|
||||
else:
|
||||
return api_render(qr)
|
||||
|
||||
|
||||
class APIDomain:
|
||||
@decorators.api_require_domain_access
|
||||
def GET(self, domain):
|
||||
"""Export SQL record of mail domain as a dict.
|
||||
|
||||
curl -X GET -i -b cookie.txt https://<server>/api/domain/<domain>
|
||||
"""
|
||||
domain = str(domain).lower()
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
qr = sql_lib_domain.profile(domain=domain)
|
||||
if qr[0]:
|
||||
profile = api_utils.export_sql_record(record=qr[1])
|
||||
|
||||
#
|
||||
# Get all alias domains
|
||||
#
|
||||
_qr = sql_lib_domain.get_all_alias_domains(domain=domain,
|
||||
name_only=True,
|
||||
conn=conn)
|
||||
if _qr[0]:
|
||||
profile['alias_domains'] = _qr[1]
|
||||
|
||||
#
|
||||
# Get per-domain sender dependent relayhost
|
||||
#
|
||||
(_status, _result) = sql_lib_general.get_sender_relayhost(sender='@' + domain)
|
||||
if _status:
|
||||
profile['relayhost'] = _result
|
||||
|
||||
#
|
||||
# Get allocated domain quota
|
||||
#
|
||||
_quota = sql_lib_domain.get_allocated_domain_quota(domains=[domain])
|
||||
profile['allocated_quota'] = _quota
|
||||
|
||||
return api_render((True, profile))
|
||||
else:
|
||||
return api_render(qr)
|
||||
|
||||
@decorators.api_require_global_admin
|
||||
def POST(self, domain):
|
||||
"""Create a new domain.
|
||||
|
||||
curl -X POST -i -b cookie.txt -d "defaultQuota=1024" https://<server>/api/domain/<new_domain>
|
||||
|
||||
Parameters:
|
||||
|
||||
@name - the short description of this domain name. e.g. company name.
|
||||
@quota - per-domain mailbox quota, in MB.
|
||||
@language - default preferred language for new user.
|
||||
e.g. en_US for English, de_DE for Deutsch.
|
||||
@transport - per-domain transport
|
||||
@defaultQuota - default mailbox quota for new user.
|
||||
@maxUserQuota - Max mailbox quota of a single mail user
|
||||
@numberOfUsers - Max number of mail user accounts
|
||||
@numberOfAliases - Max number of mail alias accounts
|
||||
"""
|
||||
form = web.input()
|
||||
form['domainName'] = domain
|
||||
form['cn'] = form.get('name')
|
||||
|
||||
form['preferredLanguage'] = form.get('language', '')
|
||||
form['mtaTransport'] = form.get('transport', '')
|
||||
|
||||
form['domainQuota'] = form.get('quota')
|
||||
form['domainQuotaUnit'] = 'MB'
|
||||
|
||||
qr = sql_lib_domain.add(form=form)
|
||||
return api_render(qr)
|
||||
|
||||
@decorators.api_require_domain_access
|
||||
def DELETE(self, domain, keep_mailbox_days=0):
|
||||
"""Delete an existing mail domain.
|
||||
|
||||
curl -X DELETE -i -b cookie.txt https://<server>/api/domain/<domain>
|
||||
curl -X DELETE -i -b cookie.txt https://<server>/api/domain/<domain>/keep_mailbox_days/<days>
|
||||
"""
|
||||
qr = sql_lib_domain.delete_domains(domains=[domain], keep_mailbox_days=keep_mailbox_days)
|
||||
return api_render(qr)
|
||||
|
||||
@decorators.api_require_domain_access
|
||||
def PUT(self, domain):
|
||||
"""Update domain profile.
|
||||
|
||||
curl -X PUT -i -b cookie.txt -d "var=<value>" https://<server>/api/domain/<domain>
|
||||
curl -X PUT -i -b cookie.txt -d "var=<value>&var2=<value2>" https://<server>/api/domain/<domain>
|
||||
|
||||
:param domain: domain name.
|
||||
|
||||
Form parameters:
|
||||
|
||||
`name`: the short company/orgnization name
|
||||
`accountStatus`: enable or disable domain. possible value is: active, disabled.
|
||||
`quota`: Per-domain mailbox quota
|
||||
`transport`: Per-domain transport
|
||||
|
||||
`language`: default preferred language for new user.
|
||||
e.g. en_US for English, de_DE for Deutsch.
|
||||
|
||||
`minPasswordLength`: Minimal password length
|
||||
`maxPasswordLength`: Maximum password length
|
||||
|
||||
`defaultQuota`: default mailbox quota for new user.
|
||||
`maxUserQuota`: Max mailbox quota of a single mail user
|
||||
|
||||
`numberOfUsers`: Max number of mail user accounts
|
||||
`numberOfAliases`: Max number of mail alias accounts
|
||||
|
||||
`senderBcc`: set bcc address for outgoing emails
|
||||
`recipientBcc`: set bcc address for incoming emails
|
||||
|
||||
`catchall`: set per-domain catch-all account.
|
||||
catchall account is a list of email address which will
|
||||
receive emails sent to non-existing address under same
|
||||
domain
|
||||
|
||||
`outboundRelay`: relay outgoing emails to specified host
|
||||
|
||||
`addService`: enable new services. Multiple services must be separated by comma.
|
||||
`removeService`: disable existing services. Multiple services must be separated by comma.
|
||||
`services`: reset all services. If empty, all existing services will be removed.
|
||||
|
||||
`disableDomainProfile`: disable given domain profiles. Normal admin
|
||||
cannot view and update disabled profiles in
|
||||
domain profile page.
|
||||
`enableDomainProfile`: enable given domain profiles. Normal admin
|
||||
can view and update disabled profiles in
|
||||
domain profile page.
|
||||
`disableUserProfile`: disable given user profiles. Normal admin
|
||||
cannot view and update disabled profiles in
|
||||
user profile page.
|
||||
`enableUserProfile`: enable given domain profiles. Normal admin
|
||||
can view and update disabled profiles in
|
||||
user profile page.
|
||||
`disableUserPreference`: disable given user preferences in
|
||||
self-service page. Normal mail user cannot
|
||||
view and update disabled preferences.
|
||||
`enableUserPreference`: disable given user preferences in
|
||||
self-service page. Normal mail user can
|
||||
view and update disabled preferences.
|
||||
`aliasDomains`: remove all existing alias domains and add given
|
||||
domains as alias domains. Multiple domains must be
|
||||
separated by comma.
|
||||
`addAliasDomain`: add new alias domains. Multiple domains must be
|
||||
separated by comma.
|
||||
`removeAliasDomain`: remove existing alias domains. Multiple
|
||||
domains must be separated by comma.
|
||||
"""
|
||||
form = web.input()
|
||||
qr = sql_lib_domain.api_update_profile(domain=domain, form=form)
|
||||
return api_render(qr)
|
||||
|
||||
|
||||
class APIDomainAdmin:
|
||||
@decorators.api_require_domain_access
|
||||
def GET(self, domain):
|
||||
"""List all existing domain admins.
|
||||
|
||||
curl -X GET -i -b cookie.txt https://<server>/api/domain/admins/<domain>
|
||||
"""
|
||||
qr = sql_lib_domain.get_domain_admin_addresses(domain=domain)
|
||||
return api_render(qr)
|
||||
|
||||
@decorators.api_require_domain_access
|
||||
def PUT(self, domain):
|
||||
"""Update domain admins.
|
||||
|
||||
curl -X PUT -i -b cookie.txt -d "var=<value>[,<value2>,...]" https://<server>/api/domain/admins/<domain>
|
||||
|
||||
Parameters:
|
||||
|
||||
@addAdmin - Add new domain admin. Multiple admins must be separated by comma.
|
||||
@removeAdmin - Remove existing domain admin. Multiple admins must be separated by comma.
|
||||
@removeAllAdmin - Remove all existing domain admins.
|
||||
"""
|
||||
form = web.input()
|
||||
qr = sql_lib_domain.api_update_domain_admins(domain=domain, form=form)
|
||||
return api_render(qr)
|
||||
45
controllers/sql/api_misc.py
Normal file
45
controllers/sql/api_misc.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import web
|
||||
|
||||
from controllers.utils import api_render
|
||||
from libs import iredpwd
|
||||
|
||||
from libs.sqllib import decorators
|
||||
from libs.sqllib import user as sql_lib_user
|
||||
from libs.sqllib import admin as sql_lib_admin
|
||||
|
||||
|
||||
class APIVerifyPassword:
|
||||
@decorators.api_require_global_admin
|
||||
def POST(self, account_type, mail):
|
||||
"""Verify submitted (plain) password against the one stored in SQL db.
|
||||
|
||||
curl -X POST -i -b cookie.txt -d "var=<value>" https://<server>/api/verify_password/user/<mail>
|
||||
curl -X POST -i -b cookie.txt -d "var=<value>" https://<server>/api/verify_password/admin/<mail>
|
||||
|
||||
Required parameters:
|
||||
|
||||
@password - plain password you want to verify
|
||||
"""
|
||||
mail = str(mail).lower()
|
||||
|
||||
form = web.input()
|
||||
pw = form.get('password', '')
|
||||
|
||||
if not pw:
|
||||
return api_render((False, 'EMPTY_PASSSWORD'))
|
||||
|
||||
try:
|
||||
if account_type == 'admin':
|
||||
qr = sql_lib_admin.get_profile(mail=mail, columns=['password'], conn=None)
|
||||
else:
|
||||
qr = sql_lib_user.simple_profile(mail=mail, columns=['password'])
|
||||
|
||||
if qr[0]:
|
||||
pw_in_db = str(qr[1].password)
|
||||
qr_pw = iredpwd.verify_password_hash(pw_in_db, pw)
|
||||
|
||||
return api_render(qr_pw)
|
||||
else:
|
||||
return api_render(qr)
|
||||
except Exception as e:
|
||||
return api_render((False, repr(e)))
|
||||
123
controllers/sql/api_ml.py
Normal file
123
controllers/sql/api_ml.py
Normal file
@@ -0,0 +1,123 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
import web
|
||||
|
||||
from controllers.utils import api_render
|
||||
from libs.sqllib import decorators
|
||||
from libs.sqllib import ml as sql_lib_ml
|
||||
from libs.sqllib import api_utils
|
||||
|
||||
session = web.config.get('_session')
|
||||
|
||||
|
||||
class APIMLS:
|
||||
@decorators.api_require_domain_access
|
||||
def GET(self, domain):
|
||||
"""List all mailing lists in given domain.
|
||||
|
||||
curl -X GET -i -b cookie.txt https://<server>/api/mls/<domain>
|
||||
|
||||
Optional parameters:
|
||||
|
||||
@email_only -- return a list of mailing list addresses.
|
||||
if not present, return a list of mailing list profiles
|
||||
(dicts).
|
||||
"""
|
||||
domain = str(domain).lower()
|
||||
|
||||
form = web.input(_unicode=True)
|
||||
email_only = ('email_only' in form)
|
||||
|
||||
qr = sql_lib_ml.get_basic_ml_profiles(domain=domain,
|
||||
email_only=email_only,
|
||||
conn=None)
|
||||
|
||||
if qr[0]:
|
||||
if email_only:
|
||||
emails = qr[1]
|
||||
return api_render((True, emails))
|
||||
else:
|
||||
profiles = api_utils.export_sql_records(records=qr[1])
|
||||
return api_render((True, profiles))
|
||||
else:
|
||||
return api_render(qr)
|
||||
|
||||
|
||||
class APIML:
|
||||
@decorators.api_require_domain_access
|
||||
def GET(self, mail):
|
||||
"""Export mailing list profile.
|
||||
|
||||
curl -X GET -i -b cookie.txt https://<server>/api/ml/<mail>
|
||||
|
||||
Optional arguments:
|
||||
|
||||
@with_subscribers -- if set to 'yes', all subscribers will be returned.
|
||||
"""
|
||||
mail = str(mail).lower()
|
||||
|
||||
form = web.input(_unicode=False)
|
||||
with_subscribers = ('with_subscribers' in form)
|
||||
|
||||
qr = sql_lib_ml.get_profile(mail=mail,
|
||||
with_subscribers=with_subscribers,
|
||||
conn=None)
|
||||
|
||||
if qr[0]:
|
||||
profile = api_utils.export_sql_record(record=qr[1])
|
||||
return api_render((True, profile))
|
||||
else:
|
||||
return api_render(qr)
|
||||
|
||||
@decorators.api_require_domain_access
|
||||
def DELETE(self, mail):
|
||||
"""Delete a mailing list.
|
||||
|
||||
curl -X DELETE -i -b cookie.txt https://<server>/api/ml/<mail>
|
||||
"""
|
||||
mail = str(mail).lower()
|
||||
form = web.input()
|
||||
|
||||
keep_archive = True
|
||||
if form.get('keep_archive') == 'no':
|
||||
keep_archive = False
|
||||
|
||||
qr = sql_lib_ml.delete_maillists(accounts=[mail],
|
||||
keep_archive=keep_archive,
|
||||
conn=None)
|
||||
return api_render(qr)
|
||||
|
||||
@decorators.api_require_domain_access
|
||||
def POST(self, mail):
|
||||
"""Create a new mail alias account.
|
||||
|
||||
curl -X POST -i -b cookie.txt -d "..." https://<server>/api/ml/<email>
|
||||
|
||||
Optional POST parameters:
|
||||
|
||||
"""
|
||||
mail = str(mail).lower()
|
||||
(listname, domain) = mail.split('@', 1)
|
||||
|
||||
form = web.input()
|
||||
|
||||
form['listname'] = listname
|
||||
form['domainName'] = domain
|
||||
|
||||
qr = sql_lib_ml.add_ml_from_web_form(domain=domain, form=form, conn=None)
|
||||
return api_render(qr)
|
||||
|
||||
@decorators.api_require_domain_access
|
||||
def PUT(self, mail):
|
||||
"""Update mailing list profile.
|
||||
|
||||
curl -X PUT -i -b cookie.txt -d "var=<value>" https://<server>/api/ml/<mail>
|
||||
curl -X PUT -i -b cookie.txt -d "var=<value>&var2=<value2>" https://<server>/ml/<mail>
|
||||
|
||||
Optional PUT data:
|
||||
|
||||
"""
|
||||
mail = str(mail).lower()
|
||||
form = web.input()
|
||||
qr = sql_lib_ml.api_update_profile(mail=mail, form=form, conn=None)
|
||||
return api_render(qr)
|
||||
340
controllers/sql/api_user.py
Normal file
340
controllers/sql/api_user.py
Normal file
@@ -0,0 +1,340 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
import web
|
||||
|
||||
import settings
|
||||
from controllers.utils import api_render
|
||||
|
||||
from libs import form_utils, iredpwd
|
||||
from libs.logger import log_activity
|
||||
from libs.sqllib import SQLWrap, decorators
|
||||
from libs.sqllib import user as sql_lib_user
|
||||
from libs.sqllib import admin as sql_lib_admin
|
||||
from libs.sqllib import general as sql_lib_general
|
||||
from libs.sqllib import api_utils
|
||||
|
||||
session = web.config.get('_session')
|
||||
|
||||
|
||||
class APIUser:
|
||||
@decorators.api_require_domain_access
|
||||
def GET(self, mail):
|
||||
"""Export SQL record of mail user as a dict.
|
||||
|
||||
curl -X GET -i -b cookie.txt https://<server>/api/user/<mail>
|
||||
"""
|
||||
mail = str(mail).lower()
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
qr = sql_lib_user.profile(mail=mail,
|
||||
with_aliases=True,
|
||||
with_alias_groups=True,
|
||||
with_mailing_lists=True,
|
||||
with_forwardings=True,
|
||||
with_used_quota=True,
|
||||
with_last_login=True,
|
||||
conn=conn)
|
||||
if qr[0]:
|
||||
profile = api_utils.export_sql_record(record=qr[1],
|
||||
remove_columns=settings.API_HIDDEN_USER_PROFILES)
|
||||
|
||||
if profile.get('isadmin') == 1:
|
||||
# Get all managed domains
|
||||
_qr = sql_lib_admin.get_managed_domains(admin=mail,
|
||||
domain_name_only=True,
|
||||
listed_only=True,
|
||||
conn=conn)
|
||||
if _qr[0]:
|
||||
profile['managed_domains'] = _qr[1]
|
||||
|
||||
return api_render((True, profile))
|
||||
else:
|
||||
return api_render(qr)
|
||||
|
||||
@decorators.api_require_domain_access
|
||||
def POST(self, mail):
|
||||
"""Create a new mail user.
|
||||
|
||||
curl -X POST -i -b cookie.txt -d "var=value1&var2=value2&..." https://<server>/api/user/<mail>
|
||||
|
||||
Optional POST data:
|
||||
|
||||
@name - display name
|
||||
@password - password
|
||||
@password_hash - password hash
|
||||
@language - default preferred language for new user. e.g.
|
||||
en_US for English, de_DE for Deutsch.
|
||||
@quota - mailbox quota for this user (in MB).
|
||||
"""
|
||||
mail = str(mail).lower()
|
||||
(username, domain) = mail.split('@', 1)
|
||||
|
||||
if not session.get('is_global_admin'):
|
||||
sql_lib_user.redirect_if_user_is_global_admin(conn=None, mail=mail)
|
||||
|
||||
form = web.input()
|
||||
|
||||
form['username'] = username
|
||||
form['domainName'] = domain
|
||||
|
||||
form['preferredLanguage'] = form.get('language')
|
||||
form['cn'] = form.get('name')
|
||||
|
||||
_pw = form.get('password', '')
|
||||
if _pw:
|
||||
form['newpw'] = _pw
|
||||
form['confirmpw'] = _pw
|
||||
else:
|
||||
_pw_hash = form.get('password_hash', '')
|
||||
form['password_hash'] = _pw_hash
|
||||
|
||||
# Set quota
|
||||
form['mailQuota'] = form.get('quota')
|
||||
|
||||
qr = sql_lib_user.add_user_from_form(domain=domain, form=form)
|
||||
return api_render(qr)
|
||||
|
||||
@decorators.api_require_domain_access
|
||||
def DELETE(self, mail, keep_mailbox_days=0):
|
||||
"""Delete a mail user.
|
||||
|
||||
curl -X DELETE -i -b cookie.txt https://<server>/api/user/<mail>
|
||||
curl -X DELETE -i -b cookie.txt https://<server>/api/user/<mail>/keep_mailbox_days/<days>
|
||||
"""
|
||||
mail = str(mail).lower()
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
if not session.get('is_global_admin'):
|
||||
sql_lib_user.redirect_if_user_is_global_admin(conn=conn, mail=mail)
|
||||
|
||||
qr = sql_lib_user.delete_users(conn=conn, accounts=[mail], keep_mailbox_days=keep_mailbox_days)
|
||||
return api_render(qr)
|
||||
|
||||
@decorators.api_require_domain_access
|
||||
def PUT(self, mail):
|
||||
"""Update user profile.
|
||||
|
||||
curl -X PUT -i -b cookie.txt -d "var=<value>" https://<server>/api/user/<mail>
|
||||
curl -X PUT -i -b cookie.txt -d "var=<value>&var2=<value2>" https://<server>/api/user/<mail>
|
||||
|
||||
Optional PUT data:
|
||||
|
||||
@name - common name (or, display name)
|
||||
@password - set new password for user
|
||||
@password_hash - set new password to given hashed password
|
||||
@quota - mailbox quota for this user (in MB).
|
||||
@accountStatus - enable or disable user. possible value is: active, disabled.
|
||||
@language - set preferred language of web UI
|
||||
@employeeid - set employee id
|
||||
@transport - set per-user transport
|
||||
@isGlobalAdmin -- promote user to be a global admin
|
||||
@forwarding -- set per-user mail forwarding addresseses
|
||||
@addForwarding -- add per-user mail forwarding addresses
|
||||
@removeForwarding -- remove existing per-user mail forwarding addresses
|
||||
@senderBcc -- set per-user bcc for outbound emails
|
||||
@recipientBcc -- set per-user bcc for inbound emails
|
||||
@aliases -- reset per-user alias addresses
|
||||
@addAlias -- add new per-user alias addresses
|
||||
@removeAlias -- remove existing per-user alias addresses
|
||||
@maildir -- full maildir path of the mailbox
|
||||
"""
|
||||
mail = str(mail).lower()
|
||||
form = web.input()
|
||||
qr = sql_lib_user.api_update_profile(mail=mail, form=form, conn=None)
|
||||
return api_render(qr)
|
||||
|
||||
|
||||
class APIUsers:
|
||||
@decorators.api_require_domain_access
|
||||
def GET(self, domain):
|
||||
"""List all mail users in given domain.
|
||||
|
||||
curl -X GET -i -b cookie.txt https://<server>/api/users/<domain>
|
||||
|
||||
Optional parameters:
|
||||
|
||||
@email_only -- return a list of users' email addresses.
|
||||
if not present, return a list of user profiles
|
||||
(dicts).
|
||||
@disabled_only -- return disabled users.
|
||||
"""
|
||||
domain = str(domain).lower()
|
||||
|
||||
form = web.input(_unicode=True)
|
||||
email_only = ('email_only' in form)
|
||||
disabled_only = ('disabled_only' in form)
|
||||
|
||||
qr = sql_lib_user.get_basic_user_profiles(domain=domain,
|
||||
email_only=email_only,
|
||||
disabled_only=disabled_only,
|
||||
with_last_login=True,
|
||||
with_used_quota=True,
|
||||
conn=None)
|
||||
|
||||
if qr[0]:
|
||||
if email_only:
|
||||
emails = qr[1]
|
||||
return api_render((True, emails))
|
||||
else:
|
||||
profiles = api_utils.export_sql_records(records=qr[1])
|
||||
return api_render((True, profiles))
|
||||
else:
|
||||
return api_render(qr)
|
||||
|
||||
@decorators.api_require_domain_access
|
||||
def PUT(self, domain):
|
||||
"""Update profiles of users under domain.
|
||||
|
||||
curl -X PUT -i -b cookie.txt -d "var=<value>" https://<server>/api/users/<domain>
|
||||
curl -X PUT -i -b cookie.txt -d "var=<value>&var2=<value2>" https://<server>/api/users/<domain>
|
||||
|
||||
Optional PUT data:
|
||||
|
||||
@name - common name (or, display name)
|
||||
@accountStatus - enable or disable user. possible value is: active, disabled.
|
||||
@language - set preferred language of web UI
|
||||
@transport - set per-user transport
|
||||
@password - reset all users' password.
|
||||
"""
|
||||
domain = str(domain).lower()
|
||||
|
||||
form = web.input()
|
||||
params = {}
|
||||
|
||||
# Name
|
||||
kv = form_utils.get_form_dict(form,
|
||||
input_name='name',
|
||||
key_name='name')
|
||||
params.update(kv)
|
||||
|
||||
# Account status
|
||||
kv = form_utils.get_form_dict(form,
|
||||
input_name='accountStatus',
|
||||
key_name='active')
|
||||
params.update(kv)
|
||||
|
||||
# Language
|
||||
kv = form_utils.get_form_dict(form,
|
||||
input_name='language',
|
||||
to_string=True)
|
||||
params.update(kv)
|
||||
|
||||
# Transport
|
||||
kv = form_utils.get_form_dict(form,
|
||||
input_name='transport',
|
||||
to_string=True)
|
||||
params.update(kv)
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
# Password
|
||||
if "password" in form:
|
||||
pw = form_utils.get_single_value(form,
|
||||
input_name="password",
|
||||
default_value="",
|
||||
to_string=True)
|
||||
|
||||
if not pw:
|
||||
return api_render((False, "EMPTY_PASSWORD"))
|
||||
|
||||
qr = sql_lib_general.get_domain_settings(domain=domain, conn=conn)
|
||||
if not qr[0]:
|
||||
return api_render(qr)
|
||||
|
||||
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 = iredpwd.verify_new_password(newpw=pw,
|
||||
confirmpw=pw,
|
||||
min_passwd_length=min_passwd_length,
|
||||
max_passwd_length=max_passwd_length)
|
||||
if qr[0]:
|
||||
params["password"] = iredpwd.generate_password_hash(pw)
|
||||
else:
|
||||
return api_render(qr)
|
||||
|
||||
if not params:
|
||||
return api_render(True)
|
||||
|
||||
try:
|
||||
|
||||
conn.update('mailbox',
|
||||
vars={'domain': domain},
|
||||
where='domain=$domain',
|
||||
**params)
|
||||
|
||||
try:
|
||||
# Log updated parameters and values if possible
|
||||
msg = str(params)
|
||||
except:
|
||||
msg = ', '.join(params)
|
||||
|
||||
log_activity(msg="Update profiles of all users under domain: {} -> {}".format(domain, msg),
|
||||
admin=session.get('username'),
|
||||
username=domain,
|
||||
domain=domain,
|
||||
event='update')
|
||||
|
||||
return api_render(True)
|
||||
except Exception as e:
|
||||
return api_render((False, repr(e)))
|
||||
|
||||
|
||||
class APIUsersPassword:
|
||||
@decorators.api_require_domain_access
|
||||
def PUT(self, domain):
|
||||
"""Update password of all users under domain.
|
||||
|
||||
curl -X PUT -i -b cookie.txt -d "var=<value>" https://<server>/api/users/<domain>/password
|
||||
|
||||
Required parameters:
|
||||
|
||||
@password - set new password for user
|
||||
"""
|
||||
domain = str(domain).lower()
|
||||
|
||||
form = web.input()
|
||||
|
||||
qr = api_utils.get_form_password_dict(form=form,
|
||||
domain=domain,
|
||||
input_name='password')
|
||||
if qr[0]:
|
||||
pw_hash = qr[1]['pw_hash']
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
conn.update('mailbox',
|
||||
vars={'domain': domain},
|
||||
where='domain=$domain',
|
||||
password=pw_hash)
|
||||
|
||||
log_activity(msg="Update all users' password under domain: %s" % domain,
|
||||
admin=session.get('username'),
|
||||
username=domain,
|
||||
domain=domain,
|
||||
event='update')
|
||||
|
||||
return api_render(True)
|
||||
else:
|
||||
return api_render(qr)
|
||||
|
||||
|
||||
class APIChangeEmail:
|
||||
@decorators.api_require_domain_access
|
||||
def POST(self, mail, new_mail):
|
||||
"""Change user email address.
|
||||
|
||||
curl -X POST -i -b cookie.txt https://<server>/api/user/<mail>/change_email/<new_mail>
|
||||
"""
|
||||
mail = str(mail).lower()
|
||||
new_mail = str(new_mail).lower()
|
||||
|
||||
qr = sql_lib_user.change_email(mail=mail, new_mail=new_mail)
|
||||
return api_render(qr)
|
||||
501
controllers/sql/basic.py
Normal file
501
controllers/sql/basic.py
Normal file
@@ -0,0 +1,501 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
import web
|
||||
import settings
|
||||
|
||||
from controllers.utils import api_render
|
||||
|
||||
from libs import __version_sql__ as __version__
|
||||
from libs import iredutils, sysinfo, form_utils
|
||||
from libs.logger import logger, log_activity
|
||||
|
||||
from libs.sqllib import SQLWrap, auth, decorators
|
||||
from libs.sqllib import admin as sql_lib_admin
|
||||
from libs.sqllib import domain as sql_lib_domain
|
||||
from libs.sqllib import utils as sql_lib_utils
|
||||
from libs.sqllib import general as sql_lib_general
|
||||
|
||||
if settings.iredapd_enabled:
|
||||
from libs.iredapd import log as iredapd_log
|
||||
|
||||
if settings.fail2ban_enabled:
|
||||
from libs.f2b import log as f2b_log
|
||||
|
||||
if settings.amavisd_enable_quarantine or settings.amavisd_enable_logging:
|
||||
from libs.amavisd import log as lib_amavisd_log
|
||||
|
||||
|
||||
session = web.config.get('_session')
|
||||
|
||||
|
||||
class Login:
|
||||
def GET(self):
|
||||
if not session.get('logged'):
|
||||
form = web.input(_unicode=False)
|
||||
|
||||
if not iredutils.is_allowed_admin_login_ip(client_ip=web.ctx.ip):
|
||||
return web.render('error_without_login.html',
|
||||
error='NOT_ALLOWED_IP')
|
||||
|
||||
# Show login page.
|
||||
return web.render('login.html',
|
||||
languagemaps=iredutils.get_language_maps(),
|
||||
msg=form.get('msg'))
|
||||
else:
|
||||
if session.get('account_is_mail_user'):
|
||||
iredutils.self_service_login_redirect(session['username'])
|
||||
else:
|
||||
if settings.REDIRECT_TO_DOMAIN_LIST_AFTER_LOGIN:
|
||||
raise web.seeother('/domains')
|
||||
else:
|
||||
raise web.seeother('/dashboard')
|
||||
|
||||
def POST(self):
|
||||
# Get username, password.
|
||||
form = web.input(_unicode=False)
|
||||
|
||||
username = form.get('username', '').strip().lower()
|
||||
password = str(form.get('password', '').strip())
|
||||
domain = username.split('@', 1)[-1]
|
||||
|
||||
# Auth as domain admin
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
auth_result = auth.auth(conn=conn,
|
||||
username=username,
|
||||
password=password,
|
||||
account_type='admin')
|
||||
|
||||
if auth_result[0]:
|
||||
log_activity(msg="Admin login success.", domain=domain, event='login')
|
||||
|
||||
# Save selected language
|
||||
selected_language = str(form.get('lang', '')).strip()
|
||||
if selected_language != web.ctx.lang and \
|
||||
selected_language in iredutils.get_language_maps():
|
||||
session['lang'] = selected_language
|
||||
|
||||
account_settings = auth_result[1].get('account_settings', {})
|
||||
if (not session.get('is_global_admin')) and 'create_new_domains' in account_settings:
|
||||
session['create_new_domains'] = True
|
||||
|
||||
for k in ['disable_viewing_mail_log',
|
||||
'disable_managing_quarantined_mails']:
|
||||
if account_settings.get(k) == 'yes':
|
||||
session[k] = True
|
||||
|
||||
if settings.REDIRECT_TO_DOMAIN_LIST_AFTER_LOGIN:
|
||||
raise web.seeother('/domains')
|
||||
else:
|
||||
raise web.seeother('/dashboard?checknew')
|
||||
else:
|
||||
#
|
||||
# User login for self-service
|
||||
#
|
||||
# Check enabled services.
|
||||
qr = sql_lib_domain.get_domain_enabled_services(domain=domain, conn=conn)
|
||||
|
||||
if qr[0]:
|
||||
enabled_services = qr[1]
|
||||
if 'self-service' not in enabled_services:
|
||||
# domain doesn't allow self-service
|
||||
raise web.seeother('/login?msg=INVALID_CREDENTIALS')
|
||||
else:
|
||||
raise web.seeother('/login?msg=INVALID_CREDENTIALS')
|
||||
|
||||
user_auth_result = auth.auth(conn=conn,
|
||||
username=username,
|
||||
password=password,
|
||||
account_type='user')
|
||||
|
||||
if user_auth_result[0]:
|
||||
log_activity(msg="User login success", event='user_login')
|
||||
|
||||
account_settings = user_auth_result[1].get('account_settings', {})
|
||||
if (not session.get('is_global_admin')) and \
|
||||
'create_new_domains' in account_settings:
|
||||
session['create_new_domains'] = True
|
||||
|
||||
iredutils.self_service_login_redirect(session['username'])
|
||||
else:
|
||||
session['failed_times'] += 1
|
||||
logger.warning("Web login failed: client_address={}, username={}".format(web.ctx.ip, username))
|
||||
log_activity(msg="Login failed.", admin=username, event='login', loglevel='error')
|
||||
raise web.seeother('/login?msg=%s' % web.urlquote(auth_result[1]))
|
||||
|
||||
|
||||
class Logout:
|
||||
def GET(self):
|
||||
try:
|
||||
session.kill()
|
||||
except:
|
||||
pass
|
||||
|
||||
raise web.seeother('/login')
|
||||
|
||||
|
||||
class Dashboard:
|
||||
@decorators.require_admin_login
|
||||
def GET(self):
|
||||
form = web.input(_unicode=False)
|
||||
_check_new_version = ('checknew' in form)
|
||||
|
||||
# Check new version.
|
||||
if session.get('is_global_admin') and _check_new_version:
|
||||
(_status, _info) = sysinfo.check_new_version()
|
||||
session['new_version_available'] = _status
|
||||
if _status:
|
||||
session['new_version'] = _info
|
||||
else:
|
||||
session['new_version_check_error'] = _info
|
||||
|
||||
# Get numbers of domains, users, aliases.
|
||||
num_existing_domains = 0
|
||||
num_existing_users = 0
|
||||
num_existing_lists = 0
|
||||
num_existing_aliases = 0
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
try:
|
||||
num_existing_domains = sql_lib_admin.num_managed_domains(conn=conn)
|
||||
num_existing_users = sql_lib_admin.num_managed_users(conn=conn)
|
||||
num_existing_lists = sql_lib_admin.num_managed_lists(conn=conn)
|
||||
num_existing_aliases = sql_lib_admin.num_managed_aliases(conn=conn)
|
||||
except:
|
||||
pass
|
||||
|
||||
#
|
||||
# For normal domain admin
|
||||
#
|
||||
# Get number of max domains/users,aliases. (-1 means no limitation)
|
||||
num_max_domains = -1
|
||||
num_max_users = -1
|
||||
num_max_lists = -1
|
||||
num_max_aliases = -1
|
||||
|
||||
admin = session.get('username')
|
||||
if (not session.get('is_global_admin')) and session.get('create_new_domains'):
|
||||
# Get account settings
|
||||
qr = sql_lib_general.get_admin_settings(admin=admin, conn=conn)
|
||||
|
||||
if qr[0]:
|
||||
account_settings = qr[1]
|
||||
num_max_domains = account_settings.get('create_max_domains', -1)
|
||||
num_max_users = account_settings.get('create_max_users', -1)
|
||||
num_max_lists = account_settings.get('create_max_lists', -1)
|
||||
num_max_aliases = account_settings.get('create_max_aliases', -1)
|
||||
|
||||
# Get numbers of existing messages and quota bytes.
|
||||
# Set None as default, so that it's easy to detect them in Jinja2 template.
|
||||
total_messages = None
|
||||
total_bytes = None
|
||||
if session.get('is_global_admin'):
|
||||
if settings.SHOW_USED_QUOTA:
|
||||
try:
|
||||
qr = sql_lib_admin.sum_all_used_quota(conn=conn)
|
||||
total_messages = qr['messages']
|
||||
total_bytes = qr['bytes']
|
||||
except:
|
||||
pass
|
||||
|
||||
# Get number of incoming/outgoing emails in latest 24 hours.
|
||||
last_hours = settings.STATISTICS_HOURS
|
||||
last_seconds = last_hours * 60 * 60
|
||||
num_incoming_mails = 0
|
||||
num_outgoing_mails = 0
|
||||
num_virus = 0
|
||||
num_quarantined = 0
|
||||
# iRedAPD
|
||||
num_rejected = 0
|
||||
num_smtp_outbound_sessions = 0
|
||||
|
||||
top_senders = []
|
||||
top_recipients = []
|
||||
|
||||
all_reversed_domain_names = []
|
||||
|
||||
if settings.amavisd_enable_logging or settings.amavisd_enable_quarantine:
|
||||
# Get all managed domain names and reversed names.
|
||||
_all_domains = []
|
||||
result_all_domains = sql_lib_admin.get_managed_domains(conn=conn,
|
||||
admin=session.get('username'),
|
||||
domain_name_only=True)
|
||||
if result_all_domains[0]:
|
||||
_all_domains += result_all_domains[1]
|
||||
|
||||
all_reversed_domain_names = iredutils.reverse_amavisd_domain_names(_all_domains)
|
||||
|
||||
if settings.amavisd_enable_logging:
|
||||
num_incoming_mails = lib_amavisd_log.count_incoming_mails(all_reversed_domain_names, last_seconds)
|
||||
num_outgoing_mails = lib_amavisd_log.count_outgoing_mails(all_reversed_domain_names, last_seconds)
|
||||
num_virus = lib_amavisd_log.count_virus_mails(all_reversed_domain_names, last_seconds)
|
||||
|
||||
top_senders = lib_amavisd_log.get_top_users(
|
||||
reversedDomainNames=all_reversed_domain_names,
|
||||
log_type='sent',
|
||||
timeLength=last_seconds,
|
||||
number=settings.NUM_TOP_SENDERS,
|
||||
)
|
||||
|
||||
top_recipients = lib_amavisd_log.get_top_users(
|
||||
reversedDomainNames=all_reversed_domain_names,
|
||||
log_type='received',
|
||||
timeLength=last_seconds,
|
||||
number=settings.NUM_TOP_RECIPIENTS,
|
||||
)
|
||||
|
||||
# Get records of quarantined mails.
|
||||
if settings.amavisd_enable_quarantine:
|
||||
num_quarantined = lib_amavisd_log.count_quarantined(all_reversed_domain_names, last_seconds)
|
||||
|
||||
if settings.iredapd_enabled:
|
||||
num_rejected = iredapd_log.get_num_rejected(hours=last_hours)
|
||||
num_smtp_outbound_sessions = iredapd_log.get_num_smtp_outbound_sessions(
|
||||
hours=last_hours,
|
||||
)
|
||||
|
||||
num_banned = 0
|
||||
if session.get('is_global_admin') and settings.fail2ban_enabled:
|
||||
num_banned = f2b_log.num_banned()
|
||||
|
||||
return web.render(
|
||||
'dashboard.html',
|
||||
version=__version__,
|
||||
iredmail_version=sysinfo.get_iredmail_version(),
|
||||
hostname=sysinfo.get_hostname(),
|
||||
uptime=sysinfo.get_server_uptime(),
|
||||
loadavg=sysinfo.get_system_load_average(),
|
||||
netif_data=sysinfo.get_nic_info(),
|
||||
# number of existing accounts
|
||||
num_existing_domains=num_existing_domains,
|
||||
num_existing_users=num_existing_users,
|
||||
num_existing_lists=num_existing_lists,
|
||||
num_existing_aliases=num_existing_aliases,
|
||||
# number of account limitation
|
||||
num_max_domains=num_max_domains,
|
||||
num_max_users=num_max_users,
|
||||
num_max_lists=num_max_lists,
|
||||
num_max_aliases=num_max_aliases,
|
||||
total_messages=total_messages,
|
||||
total_bytes=total_bytes,
|
||||
# amavisd statistics
|
||||
num_incoming_mails=num_incoming_mails,
|
||||
num_outgoing_mails=num_outgoing_mails,
|
||||
num_virus=num_virus,
|
||||
num_quarantined=num_quarantined,
|
||||
top_senders=top_senders,
|
||||
top_recipients=top_recipients,
|
||||
removeQuarantinedInDays=settings.AMAVISD_REMOVE_QUARANTINED_IN_DAYS,
|
||||
# iRedAPD
|
||||
num_rejected=num_rejected,
|
||||
num_smtp_outbound_sessions=num_smtp_outbound_sessions,
|
||||
# Fail2ban
|
||||
num_banned=num_banned,
|
||||
)
|
||||
|
||||
|
||||
class Search:
|
||||
@decorators.require_admin_login
|
||||
def GET(self):
|
||||
form = web.input()
|
||||
return web.render('sql/search.html', msg=form.get('msg'))
|
||||
|
||||
@decorators.csrf_protected
|
||||
@decorators.require_admin_login
|
||||
def POST(self):
|
||||
form = web.input(account_type=[], accountStatus=[])
|
||||
search_string = form.get('searchString', '').strip()
|
||||
if not search_string:
|
||||
raise web.seeother('/search?msg=EMPTY_STRING')
|
||||
|
||||
account_type = form.get('account_type', [])
|
||||
account_status = form.get('accountStatus', [])
|
||||
|
||||
try:
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
qr = sql_lib_utils.search(conn=conn,
|
||||
search_string=search_string,
|
||||
account_type=account_type,
|
||||
account_status=account_status)
|
||||
if not qr[0]:
|
||||
return web.render('sql/search.html',
|
||||
msg=qr[1],
|
||||
searchString=search_string)
|
||||
except Exception as e:
|
||||
return web.render('sql/search.html',
|
||||
msg=repr(e),
|
||||
searchString=search_string)
|
||||
|
||||
# Group account types.
|
||||
domains = qr[1].get('domain', [])
|
||||
admins = qr[1].get('admin', [])
|
||||
users = qr[1].get('user', [])
|
||||
mls = qr[1].get('ml', [])
|
||||
last_logins = qr[1]['last_logins']
|
||||
user_alias_addresses = qr[1]['user_alias_addresses']
|
||||
user_forwarding_addresses = qr[1]['user_forwarding_addresses']
|
||||
user_assigned_groups = qr[1]['user_assigned_groups']
|
||||
aliases = qr[1].get('alias', [])
|
||||
all_global_admins = qr[1].get('allGlobalAdmins', [])
|
||||
total_results = len(domains) + len(admins) + len(users) + len(aliases) + len(mls)
|
||||
|
||||
if session.get('is_global_admin'):
|
||||
days_to_keep_removed_mailbox = settings.DAYS_TO_KEEP_REMOVED_MAILBOX_FOR_GLOBAL_ADMIN
|
||||
else:
|
||||
days_to_keep_removed_mailbox = settings.DAYS_TO_KEEP_REMOVED_MAILBOX
|
||||
|
||||
return web.render('sql/search.html',
|
||||
searchString=search_string,
|
||||
total_results=total_results,
|
||||
domains=domains,
|
||||
admins=admins,
|
||||
users=users,
|
||||
mls=mls,
|
||||
last_logins=last_logins,
|
||||
user_alias_addresses=user_alias_addresses,
|
||||
user_forwarding_addresses=user_forwarding_addresses,
|
||||
user_assigned_groups=user_assigned_groups,
|
||||
aliases=aliases,
|
||||
allGlobalAdmins=all_global_admins,
|
||||
days_to_keep_removed_mailbox=days_to_keep_removed_mailbox,
|
||||
msg=form.get('msg'))
|
||||
|
||||
|
||||
class OperationsFromSearchPage:
|
||||
@decorators.require_admin_login
|
||||
def GET(self, *args, **kw):
|
||||
raise web.seeother('/search')
|
||||
|
||||
@decorators.csrf_protected
|
||||
@decorators.require_admin_login
|
||||
def POST(self, account_type):
|
||||
account_type = web.safestr(account_type)
|
||||
form = web.input(_unicode=False, mail=[])
|
||||
|
||||
# Get action.
|
||||
action = form.get('action', None)
|
||||
if action not in ['enable', 'disable', 'delete']:
|
||||
raise web.seeother('/search?msg=INVALID_ACTION')
|
||||
|
||||
# Get list of accounts which has valid format.
|
||||
accounts = [web.safestr(v).lower()
|
||||
for v in form.get('mail', [])
|
||||
if iredutils.is_email(web.safestr(v))]
|
||||
|
||||
# Raise earlier to avoid SQL query.
|
||||
if not accounts:
|
||||
raise web.seeother('/search?msg=SUCCESS')
|
||||
|
||||
domains = {v.split('@', 1)[-1] for v in accounts}
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
# Get managed accounts.
|
||||
if not session.get('is_global_admin'):
|
||||
# Get list of managed domains.
|
||||
qr = sql_lib_admin.get_managed_domains(conn=conn,
|
||||
admin=session.get('username'),
|
||||
domain_name_only=True,
|
||||
listed_only=True)
|
||||
if qr[0]:
|
||||
domains = [d for d in domains if d in qr[1]]
|
||||
accounts = [v for v in accounts if v.split('@', 1)[-1] in domains]
|
||||
else:
|
||||
raise web.seeother('/search?msg=%s' % web.urlquote(qr[1]))
|
||||
|
||||
if not accounts:
|
||||
raise web.seeother('/search?msg=SUCCESS')
|
||||
|
||||
if action in ['enable']:
|
||||
qr = sql_lib_utils.set_account_status(conn=conn,
|
||||
accounts=accounts,
|
||||
account_type=account_type,
|
||||
enable_account=True)
|
||||
elif action in ['disable']:
|
||||
qr = sql_lib_utils.set_account_status(conn=conn,
|
||||
accounts=accounts,
|
||||
account_type=account_type,
|
||||
enable_account=False)
|
||||
elif action in ['delete']:
|
||||
keep_mailbox_days = 0 # keep forever
|
||||
if account_type in ['user', 'domain']:
|
||||
keep_mailbox_days = form_utils.get_single_value(form=form,
|
||||
input_name='keep_mailbox_days',
|
||||
default_value=0,
|
||||
is_integer=True)
|
||||
try:
|
||||
keep_mailbox_days = int(keep_mailbox_days)
|
||||
except:
|
||||
if session.get('is_global_admin'):
|
||||
keep_mailbox_days = 0
|
||||
else:
|
||||
_max_days = max(settings.DAYS_TO_KEEP_REMOVED_MAILBOX)
|
||||
if keep_mailbox_days > _max_days:
|
||||
# Get the max days
|
||||
keep_mailbox_days = _max_days
|
||||
|
||||
qr = sql_lib_utils.delete_accounts(accounts=accounts,
|
||||
account_type=account_type,
|
||||
keep_mailbox_days=keep_mailbox_days,
|
||||
conn=conn)
|
||||
else:
|
||||
raise web.seeother("/search?msg=INVALID_ACTION")
|
||||
|
||||
if qr[0]:
|
||||
raise web.seeother('/search?msg=SUCCESS')
|
||||
else:
|
||||
raise web.seeother('/search?msg=%s' % str(qr[1]))
|
||||
|
||||
|
||||
class APILogin:
|
||||
def GET(self):
|
||||
return api_render((False, 'INVALID_HTTP_METHOD'))
|
||||
|
||||
def POST(self):
|
||||
"""Login.
|
||||
|
||||
curl -X POST -c cookie.txt -d "username=<username>&password=<password>" https://<server>/api/login
|
||||
|
||||
Required POST data:
|
||||
|
||||
@username - valid email address of domain admin
|
||||
@password - password of username
|
||||
"""
|
||||
if not iredutils.is_allowed_api_client(web.ctx.ip):
|
||||
return api_render((False, 'NOT_AUTHORIZED'))
|
||||
|
||||
# Get username, password.
|
||||
form = web.input(_unicode=False)
|
||||
|
||||
username = form.get('username', '').strip().lower()
|
||||
password = web.safestr(form.get('password', '').strip())
|
||||
domain = username.split("@", 1)[-1]
|
||||
|
||||
# Auth as domain admin
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
auth_result = auth.auth(conn=conn,
|
||||
username=username,
|
||||
password=password,
|
||||
account_type='admin')
|
||||
|
||||
if auth_result[0]:
|
||||
log_activity(msg="Admin login success.", domain=domain, event='login')
|
||||
|
||||
return api_render(True)
|
||||
else:
|
||||
session['failed_times'] += 1
|
||||
logger.warning("API login failed: client_address={}, username={}".format(web.ctx.ip, username))
|
||||
log_activity(msg="Admin login failed.",
|
||||
admin=username,
|
||||
domain=domain,
|
||||
event='login',
|
||||
loglevel='error')
|
||||
return api_render(auth_result)
|
||||
368
controllers/sql/domain.py
Normal file
368
controllers/sql/domain.py
Normal file
@@ -0,0 +1,368 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
import web
|
||||
import settings
|
||||
|
||||
from libs import iredutils, form_utils
|
||||
from libs.l10n import TIMEZONES
|
||||
|
||||
from libs.sqllib import SQLWrap, decorators, sqlutils
|
||||
from libs.sqllib import alias as sql_lib_alias
|
||||
from libs.sqllib import ml as sql_lib_ml
|
||||
from libs.sqllib import domain as sql_lib_domain
|
||||
from libs.sqllib import admin as sql_lib_admin
|
||||
|
||||
from libs.amavisd import spampolicy as spampolicylib, wblist as lib_wblist
|
||||
|
||||
from libs.panel.domain_ownership import get_pending_domains
|
||||
|
||||
session = web.config.get('_session')
|
||||
|
||||
if settings.iredapd_enabled:
|
||||
from libs.iredapd import throttle as iredapd_throttle
|
||||
from libs.iredapd import greylist as iredapd_greylist
|
||||
|
||||
|
||||
class List:
|
||||
@decorators.require_admin_login
|
||||
def GET(self, cur_page=1, disabled_only=False):
|
||||
"""List paged mail domains."""
|
||||
form = web.input(_unicode=False)
|
||||
cur_page = int(cur_page) or 1
|
||||
|
||||
all_domain_profiles = []
|
||||
domain_used_quota = {}
|
||||
all_first_chars = []
|
||||
|
||||
first_char = None
|
||||
if 'starts_with' in form:
|
||||
first_char = form.get('starts_with')[:1].upper()
|
||||
if not iredutils.is_valid_account_first_char(first_char):
|
||||
first_char = None
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
# Get first characters of all domains - no matter whether it's
|
||||
# requested to list all domains or disabled only.
|
||||
_qr = sql_lib_domain.get_first_char_of_all_domains(conn=conn)
|
||||
if _qr[0]:
|
||||
all_first_chars = _qr[1]
|
||||
|
||||
total = sql_lib_admin.num_managed_domains(conn=conn,
|
||||
disabled_only=disabled_only,
|
||||
first_char=first_char)
|
||||
|
||||
if total:
|
||||
qr = sql_lib_domain.get_paged_domains(cur_page=cur_page,
|
||||
first_char=first_char,
|
||||
disabled_only=disabled_only,
|
||||
conn=conn)
|
||||
if qr[0]:
|
||||
all_domain_profiles = qr[1]
|
||||
|
||||
if settings.SHOW_USED_QUOTA:
|
||||
domains = []
|
||||
for i in all_domain_profiles:
|
||||
domains.append(str(i.domain))
|
||||
|
||||
domain_used_quota = sql_lib_domain.get_domain_used_quota(conn=conn,
|
||||
domains=domains)
|
||||
|
||||
# Get alias domain names.
|
||||
all_domain_names = []
|
||||
all_alias_domains = {}
|
||||
if all_domain_profiles:
|
||||
all_domain_names = [str(d.domain).lower() for d in all_domain_profiles]
|
||||
qr = conn.select('alias_domain',
|
||||
vars={'all_domain_names': all_domain_names},
|
||||
what='alias_domain, target_domain',
|
||||
where='target_domain IN $all_domain_names')
|
||||
|
||||
if qr:
|
||||
for r in qr:
|
||||
td = str(r.target_domain).lower()
|
||||
ad = str(r.alias_domain).lower()
|
||||
|
||||
if td in all_alias_domains:
|
||||
all_alias_domains[td].append(ad)
|
||||
else:
|
||||
all_alias_domains[td] = [ad]
|
||||
|
||||
# Query pending domains which didn't passed ownership verification
|
||||
pending_domains = []
|
||||
if all_domain_names:
|
||||
qr = get_pending_domains(domains=all_domain_names, domain_name_only=True)
|
||||
if qr[0]:
|
||||
pending_domains = qr[1]
|
||||
|
||||
if session.get('is_global_admin'):
|
||||
days_to_keep_removed_mailbox = settings.DAYS_TO_KEEP_REMOVED_MAILBOX_FOR_GLOBAL_ADMIN
|
||||
else:
|
||||
days_to_keep_removed_mailbox = settings.DAYS_TO_KEEP_REMOVED_MAILBOX
|
||||
|
||||
return web.render('sql/domain/list.html',
|
||||
cur_page=cur_page,
|
||||
total=total,
|
||||
all_domain_profiles=all_domain_profiles,
|
||||
all_alias_domains=all_alias_domains,
|
||||
domain_used_quota=domain_used_quota,
|
||||
local_transports=settings.LOCAL_TRANSPORTS,
|
||||
first_char=first_char,
|
||||
all_first_chars=all_first_chars,
|
||||
disabled_only=disabled_only,
|
||||
pending_domains=pending_domains,
|
||||
days_to_keep_removed_mailbox=days_to_keep_removed_mailbox,
|
||||
msg=form.get('msg', None))
|
||||
|
||||
@decorators.require_admin_login
|
||||
@decorators.csrf_protected
|
||||
def POST(self):
|
||||
form = web.input(domainName=[], _unicode=False)
|
||||
domains = form.get('domainName', [])
|
||||
action = form.get('action')
|
||||
|
||||
if action not in ['delete', 'enable', 'disable']:
|
||||
raise web.seeother('/domains?msg=INVALID_ACTION')
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
if not domains:
|
||||
raise web.seeother('/domains?msg=INVALID_DOMAIN_NAME')
|
||||
|
||||
if session.get('is_global_admin') or session.get('create_new_domains'):
|
||||
if action == 'delete':
|
||||
keep_mailbox_days = form_utils.get_single_value(form=form,
|
||||
input_name='keep_mailbox_days',
|
||||
default_value=0,
|
||||
is_integer=True)
|
||||
|
||||
qr = sql_lib_domain.delete_domains(domains=domains,
|
||||
keep_mailbox_days=keep_mailbox_days,
|
||||
conn=conn)
|
||||
msg = 'DELETED'
|
||||
|
||||
if action in ['enable', 'disable']:
|
||||
qr = sql_lib_domain.enable_disable_domains(domains=domains,
|
||||
action=action,
|
||||
conn=conn)
|
||||
|
||||
# msg: ENABLED, DISABLED
|
||||
msg = action.upper() + 'D'
|
||||
|
||||
if qr[0]:
|
||||
raise web.seeother('/domains?msg=%s' % msg)
|
||||
else:
|
||||
raise web.seeother('/domains?msg=' + web.urlquote(qr[1]))
|
||||
|
||||
|
||||
class ListDisabled:
|
||||
"""List disabled mail domains."""
|
||||
@decorators.require_admin_login
|
||||
def GET(self, cur_page=1):
|
||||
lst = List()
|
||||
return lst.GET(cur_page=cur_page, disabled_only=True)
|
||||
|
||||
|
||||
class Profile:
|
||||
@decorators.require_domain_access
|
||||
def GET(self, profile_type, domain):
|
||||
form = web.input()
|
||||
domain = web.safestr(domain.split('/', 1)[0])
|
||||
profile_type = web.safestr(profile_type)
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
result = sql_lib_domain.profile(conn=conn, domain=domain)
|
||||
|
||||
if result[0] is not True:
|
||||
raise web.seeother('/domains?msg=' + web.urlquote(result[1]))
|
||||
|
||||
domain_profile = result[1]
|
||||
|
||||
alias_domains = [] # Get all alias domains.
|
||||
all_alias_accounts = [] # Get all mail alias accounts.
|
||||
all_mailing_lists = []
|
||||
|
||||
# profile_type == 'throttle'
|
||||
# throttle: iRedAPD
|
||||
gl_setting = {}
|
||||
gl_whitelists = []
|
||||
inbound_throttle_setting = {}
|
||||
outbound_throttle_setting = {}
|
||||
|
||||
# Get alias domains.
|
||||
qr = sql_lib_domain.get_all_alias_domains(domain=domain,
|
||||
name_only=True,
|
||||
conn=conn)
|
||||
if qr[0]:
|
||||
alias_domains = qr[1]
|
||||
|
||||
# Get all mail aliases.
|
||||
mails_of_all_alias_accounts = []
|
||||
qr = sql_lib_alias.get_basic_alias_profiles(conn=conn,
|
||||
domain=domain,
|
||||
columns=['name', 'address'])
|
||||
if qr[0]:
|
||||
all_alias_accounts = qr[1]
|
||||
for ali in all_alias_accounts:
|
||||
mails_of_all_alias_accounts += [ali.address]
|
||||
|
||||
# Get all mailing lists.
|
||||
mails_of_all_mailing_lists = []
|
||||
qr = sql_lib_ml.get_basic_ml_profiles(domain=domain,
|
||||
columns=['address', 'name'],
|
||||
conn=conn)
|
||||
if qr[0]:
|
||||
all_mailing_lists = qr[1]
|
||||
for i in all_mailing_lists:
|
||||
mails_of_all_mailing_lists.append(i['address'])
|
||||
|
||||
# Get per-admin settings used by normal admin to create new domains.
|
||||
creation_limits = sql_lib_admin.get_per_admin_domain_creation_limits(admin=session.get('username'), conn=conn)
|
||||
|
||||
# Get sender/recipient throttle data from iRedAPD database.
|
||||
if settings.iredapd_enabled:
|
||||
_account = '@' + domain
|
||||
|
||||
# Greylisting
|
||||
gl_setting = iredapd_greylist.get_greylist_setting(account=_account)
|
||||
gl_whitelists = iredapd_greylist.get_greylist_whitelists(account=_account)
|
||||
|
||||
# Throttling
|
||||
inbound_throttle_setting = iredapd_throttle.get_throttle_setting(account=_account,
|
||||
inout_type='inbound')
|
||||
outbound_throttle_setting = iredapd_throttle.get_throttle_setting(account=_account,
|
||||
inout_type='outbound')
|
||||
|
||||
spampolicy = {}
|
||||
global_spam_score = None
|
||||
if settings.amavisd_enable_policy_lookup:
|
||||
qr = spampolicylib.get_spam_policy(account='@' + domain)
|
||||
if not qr[0]:
|
||||
raise web.seeother('/domains?msg=%s' % web.urlquote(qr[1]))
|
||||
spampolicy = qr[1]
|
||||
|
||||
global_spam_score = spampolicylib.get_global_spam_score()
|
||||
|
||||
# Get per-domain white/blacklists
|
||||
whitelists = []
|
||||
blacklists = []
|
||||
outbound_whitelists = []
|
||||
outbound_blacklists = []
|
||||
|
||||
qr = lib_wblist.get_wblist(account='@' + domain)
|
||||
|
||||
if qr[0]:
|
||||
whitelists = qr[1]['inbound_whitelists']
|
||||
blacklists = qr[1]['inbound_blacklists']
|
||||
outbound_whitelists = qr[1]['outbound_whitelists']
|
||||
outbound_blacklists = qr[1]['outbound_blacklists']
|
||||
|
||||
# Domain ownership verification
|
||||
pending_domains = []
|
||||
qr = get_pending_domains(domains=[domain], domain_name_only=True)
|
||||
if qr[0]:
|
||||
pending_domains = qr[1]
|
||||
|
||||
# Get settings from db.
|
||||
_settings = iredutils.get_settings_from_db(params=['min_passwd_length', 'max_passwd_length'])
|
||||
global_min_passwd_length = _settings['min_passwd_length']
|
||||
global_max_passwd_length = _settings['max_passwd_length']
|
||||
|
||||
return web.render(
|
||||
'sql/domain/profile.html',
|
||||
cur_domain=domain,
|
||||
profile_type=profile_type,
|
||||
profile=domain_profile,
|
||||
default_mta_transport=settings.default_mta_transport,
|
||||
domain_settings=sqlutils.account_settings_string_to_dict(domain_profile['settings']),
|
||||
global_min_passwd_length=global_min_passwd_length,
|
||||
global_max_passwd_length=global_max_passwd_length,
|
||||
alias_domains=alias_domains,
|
||||
all_alias_accounts=all_alias_accounts,
|
||||
mails_of_all_alias_accounts=mails_of_all_alias_accounts,
|
||||
all_mailing_lists=all_mailing_lists,
|
||||
mails_of_all_mailing_lists=mails_of_all_mailing_lists,
|
||||
timezones=TIMEZONES,
|
||||
creation_limits=creation_limits,
|
||||
# iRedAPD
|
||||
gl_setting=gl_setting,
|
||||
gl_whitelists=gl_whitelists,
|
||||
inbound_throttle_setting=inbound_throttle_setting,
|
||||
outbound_throttle_setting=outbound_throttle_setting,
|
||||
# Language
|
||||
languagemaps=iredutils.get_language_maps(),
|
||||
# Spam policy, wblist
|
||||
spampolicy=spampolicy,
|
||||
custom_ban_rules=settings.AMAVISD_BAN_RULES,
|
||||
global_spam_score=global_spam_score,
|
||||
whitelists=whitelists,
|
||||
blacklists=blacklists,
|
||||
outbound_whitelists=outbound_whitelists,
|
||||
outbound_blacklists=outbound_blacklists,
|
||||
# domain ownership verification
|
||||
pending_domains=pending_domains,
|
||||
msg=form.get('msg'),
|
||||
)
|
||||
|
||||
@decorators.csrf_protected
|
||||
@decorators.require_domain_access
|
||||
def POST(self, profile_type, domain):
|
||||
domain = str(domain).lower()
|
||||
|
||||
form = web.input(domainAliasName=[],
|
||||
domainAdmin=[],
|
||||
default_mail_list=[],
|
||||
defaultList=[],
|
||||
enabledService=[],
|
||||
disabledMailService=[],
|
||||
disabledDomainProfile=[],
|
||||
disabledUserProfile=[],
|
||||
disabledUserPreference=[],
|
||||
banned_rulenames=[])
|
||||
|
||||
result = sql_lib_domain.update(profile_type=profile_type,
|
||||
domain=domain,
|
||||
form=form)
|
||||
|
||||
if result[0]:
|
||||
raise web.seeother('/profile/domain/{}/{}?msg=UPDATED'.format(profile_type, domain))
|
||||
else:
|
||||
raise web.seeother('/profile/domain/{}/{}?msg={}'.format(profile_type, domain, web.urlquote(result[1])))
|
||||
|
||||
|
||||
class Create:
|
||||
@decorators.require_permission_create_domain
|
||||
def GET(self):
|
||||
form = web.input()
|
||||
admin = session.get('username')
|
||||
|
||||
# for normal domain admin: check limitations
|
||||
creation_limits = sql_lib_admin.get_per_admin_domain_creation_limits(admin=admin)
|
||||
if creation_limits['error_code']:
|
||||
msg = None
|
||||
else:
|
||||
msg = form.get('msg')
|
||||
|
||||
return web.render('sql/domain/create.html',
|
||||
preferred_language=settings.default_language,
|
||||
languagemaps=iredutils.get_language_maps(),
|
||||
timezones=TIMEZONES,
|
||||
creation_limits=creation_limits,
|
||||
msg=msg)
|
||||
|
||||
@decorators.require_permission_create_domain
|
||||
@decorators.csrf_protected
|
||||
def POST(self):
|
||||
form = web.input()
|
||||
domain = form_utils.get_domain_name(form)
|
||||
|
||||
result = sql_lib_domain.add(form=form)
|
||||
|
||||
if result[0]:
|
||||
raise web.seeother('/profile/domain/general/%s?msg=CREATED' % domain)
|
||||
else:
|
||||
raise web.seeother('/create/domain?msg=%s' % web.urlquote(result[1]))
|
||||
257
controllers/sql/export.py
Normal file
257
controllers/sql/export.py
Normal file
@@ -0,0 +1,257 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
import zipfile
|
||||
import io
|
||||
import csv
|
||||
import web
|
||||
|
||||
from libs.sqllib import SQLWrap, decorators
|
||||
from libs.sqllib import general as sql_lib_general
|
||||
from libs.sqllib import admin as sql_lib_admin
|
||||
|
||||
session = web.config.get('_session')
|
||||
|
||||
|
||||
class ExportManagedAccounts:
|
||||
@decorators.require_admin_login
|
||||
def GET(self, mail):
|
||||
mail = mail.lower()
|
||||
|
||||
# Raise error if normal admin is trying to export accounts managed by
|
||||
# other admin
|
||||
if (not session.get('is_global_admin')) and session.get('username') != mail:
|
||||
raise web.seeother('/domains?msg=PERMISSION_DENIED')
|
||||
|
||||
qr = sql_lib_general.export_managed_accounts(mail=mail, domains=None, conn=None)
|
||||
if not qr[0]:
|
||||
raise web.seeother('/domains?msg=%s' % web.urlquote(qr[1]))
|
||||
|
||||
managed_domains = qr[1]
|
||||
|
||||
# Generate summary
|
||||
content_summary = ['Accounts managed by admin: {}'.format(mail), '------']
|
||||
|
||||
_domains = []
|
||||
_total_domains = 0
|
||||
_total_users = 0
|
||||
_total_lists = 0
|
||||
_total_aliases = 0
|
||||
|
||||
for d in managed_domains:
|
||||
_total_domains += 1
|
||||
_domains += [d['domain']]
|
||||
_total_users += d['total_users']
|
||||
_total_lists += d['total_lists']
|
||||
_total_aliases += d['total_aliases']
|
||||
|
||||
content_summary += ['- Domains: {}'.format(_total_domains)]
|
||||
content_summary += ['- Mailboxes: {}'.format(_total_users)]
|
||||
content_summary += ['- Mailing lists: {}'.format(_total_lists)]
|
||||
content_summary += ['- Mail aliases: {}'.format(_total_aliases)]
|
||||
|
||||
# Generate zip file
|
||||
f = io.BytesIO()
|
||||
try:
|
||||
zf = zipfile.ZipFile(f, mode='w', compression=zipfile.ZIP_DEFLATED)
|
||||
# Summary of all managed accounts
|
||||
zf.writestr('summary.txt', '\n'.join(content_summary))
|
||||
|
||||
_content_domains = ['# Exported domains:']
|
||||
_content_domains += ['# Format: domain name, display name']
|
||||
|
||||
# Generate files for each domain
|
||||
for d in managed_domains:
|
||||
_domain = d['domain']
|
||||
_content_domains += ['{domain}, {name}'.format(**d)]
|
||||
|
||||
for _account_type in ['users', 'lists', 'aliases']:
|
||||
if d['total_' + _account_type] == 0:
|
||||
continue
|
||||
|
||||
if _account_type == 'users':
|
||||
_content = ['# Mailboxes under domain %s' % _domain]
|
||||
elif _account_type == 'lists':
|
||||
_content = ['# Mailing lists under domain %s' % _domain]
|
||||
else:
|
||||
# account_type == 'aliases'
|
||||
_content = ['# Mail aliases under domain %s' % _domain]
|
||||
|
||||
_content += ['# Format: mail address, display name']
|
||||
|
||||
for _account in d[_account_type]:
|
||||
_content += ['{mail}, {name}'.format(**_account)]
|
||||
|
||||
zf.writestr(_domain + '_' + _account_type + '.txt', '\n'.join(_content))
|
||||
|
||||
zf.writestr('domains.txt', '\n'.join(_content_domains))
|
||||
except Exception as e:
|
||||
raise web.seeother('/domains?msg=%s' % web.urlquote(repr(e)))
|
||||
finally:
|
||||
zf.close()
|
||||
|
||||
web.header('Content-Disposition', 'attachment; filename=accounts.zip')
|
||||
return f.getvalue()
|
||||
|
||||
|
||||
class ExportDomainAccounts:
|
||||
@decorators.require_admin_login
|
||||
def GET(self, domain):
|
||||
domain = str(domain).lower()
|
||||
mail = session.get('username')
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
if not sql_lib_general.is_domain_admin(domain=domain, admin=mail, conn=conn):
|
||||
raise web.seeother('/domains?msg=PERMISSION_DENIED')
|
||||
|
||||
qr = sql_lib_general.export_managed_accounts(mail=mail, domains=[domain], conn=conn)
|
||||
if not qr[0]:
|
||||
raise web.seeother('/domains?msg=%s' % web.urlquote(qr[1]))
|
||||
|
||||
managed_domains = qr[1]
|
||||
|
||||
_domains = []
|
||||
_total_domains = 0
|
||||
_total_users = 0
|
||||
_total_lists = 0
|
||||
_total_aliases = 0
|
||||
|
||||
for d in managed_domains:
|
||||
_total_domains += 1
|
||||
_domains += [d['domain']]
|
||||
_total_users += d['total_users']
|
||||
_total_lists += d['total_lists']
|
||||
_total_aliases += d['total_aliases']
|
||||
|
||||
# Generate zip file
|
||||
f = io.BytesIO()
|
||||
with zipfile.ZipFile(f, mode='w', compression=zipfile.ZIP_DEFLATED) as zf:
|
||||
# Summary of all managed accounts
|
||||
content_summary = ['- Exported domains: %d' % _total_domains]
|
||||
content_summary += ['- Mailboxes: %d' % _total_users]
|
||||
content_summary += ['- Mailing lists: %d' % _total_lists]
|
||||
content_summary += ['- Mail aliases: %d' % _total_aliases]
|
||||
zf.writestr('summary.txt', '\n'.join(content_summary))
|
||||
|
||||
_content_domains = ['# All managed domains:']
|
||||
_content_domains += ['# Format: domain name, display name']
|
||||
|
||||
# Generate files for each domain
|
||||
for d in managed_domains:
|
||||
_domain = d['domain']
|
||||
_content_domains += ['{domain}, {name}'.format(**d)]
|
||||
|
||||
for account_type in ['users', 'lists', 'aliases']:
|
||||
if account_type == 'users':
|
||||
_content = ['# Mailboxes under domain %s' % _domain]
|
||||
elif account_type == 'lists':
|
||||
_content = ['# Mailing lists under domain %s' % _domain]
|
||||
else:
|
||||
# account_type == 'aliases'
|
||||
_content = ['# Mail aliases under domain %s' % _domain]
|
||||
|
||||
_content += ['# Format: mail address, display name']
|
||||
|
||||
for _account in d[account_type]:
|
||||
_content += ['{mail}, {name}'.format(**_account)]
|
||||
|
||||
zf.writestr(_domain + '_' + account_type + '.txt', '\n'.join(_content))
|
||||
zf.writestr('domains.txt', '\n'.join(_content_domains))
|
||||
|
||||
web.header('Content-Disposition', 'attachment; filename=accounts.zip')
|
||||
return f.getvalue()
|
||||
|
||||
|
||||
class ExportAdminStatistics:
|
||||
@decorators.require_global_admin
|
||||
def GET(self):
|
||||
"""
|
||||
Admin <email>
|
||||
domain1.com | 12 Mailboxes | 3 Mailinglists
|
||||
domain2.com | 9 Mailboxes | 1 Mailinglist
|
||||
"""
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
# Get all admins
|
||||
qr = sql_lib_admin.get_all_admins(email_only=True, conn=conn)
|
||||
|
||||
if not qr[0]:
|
||||
return qr
|
||||
|
||||
all_admins = qr[1]
|
||||
|
||||
# Get all global admins
|
||||
qr = sql_lib_admin.get_all_global_admins(conn=conn)
|
||||
if not qr[0]:
|
||||
return qr
|
||||
|
||||
global_admins = qr[1]
|
||||
non_global_admins = [i for i in all_admins if i not in global_admins]
|
||||
|
||||
# dict used to store analyzed domain names to avoid duplicate ldap query:
|
||||
# {'<domain>': {'user': 10,
|
||||
# 'aliases': 23,
|
||||
# 'maillists': 2}, ...}
|
||||
_analyzed_domains = {}
|
||||
|
||||
# dict used to store admin and managed domains.
|
||||
# {'<admin-email>': [<domain>, <domain>, ...], ...}
|
||||
# WARNING: it's possible that admin doesn't manage any domains.
|
||||
_admins_and_domains = {}
|
||||
|
||||
# Write statistics in csv file.
|
||||
for _admin in non_global_admins:
|
||||
_qr = sql_lib_admin.get_managed_domains(admin=_admin,
|
||||
domain_name_only=True,
|
||||
listed_only=True,
|
||||
conn=conn)
|
||||
|
||||
if _qr[0]:
|
||||
_domains = _qr[1]
|
||||
_admins_and_domains[_admin] = _domains
|
||||
|
||||
for _domain in _domains:
|
||||
if _domain not in _analyzed_domains:
|
||||
_num_users = sql_lib_general.num_users_under_domain(domain=_domain, conn=conn)
|
||||
_num_aliases = sql_lib_general.num_aliases_under_domain(domain=_domain, conn=conn)
|
||||
_num_ml = sql_lib_general.num_maillists_under_domain(domain=_domain, conn=conn)
|
||||
|
||||
_analyzed_domains[_domain] = {'users': _num_users,
|
||||
'aliases': _num_aliases,
|
||||
'maillists': _num_ml}
|
||||
|
||||
_rows = []
|
||||
|
||||
for _admin in global_admins:
|
||||
_rows.append([_admin, 'ALL'])
|
||||
|
||||
for (_admin, _domains) in list(_admins_and_domains.items()):
|
||||
_rows.append([_admin, len(_domains)])
|
||||
|
||||
_count = 1
|
||||
for _domain in _domains:
|
||||
_num_users = _analyzed_domains[_domain]['users']
|
||||
_num_aliases = _analyzed_domains[_domain]['aliases']
|
||||
_num_maillists = _analyzed_domains[_domain]['maillists']
|
||||
|
||||
_rows.append([_count, _domain, _num_users, _num_aliases, _num_maillists])
|
||||
_count += 1
|
||||
|
||||
try:
|
||||
f = io.StringIO()
|
||||
cw = csv.writer(f)
|
||||
|
||||
# Header row
|
||||
cw.writerow(['Admin', 'Managed Domains', 'Users', 'Aliases', 'Mailing Lists'])
|
||||
|
||||
# Data rows
|
||||
cw.writerows(_rows)
|
||||
|
||||
v = f.getvalue()
|
||||
except Exception as e:
|
||||
raise web.seeother('/domains?msg=%s' % web.urlquote(repr(e)))
|
||||
|
||||
web.header('Content-Disposition', 'attachment; filename=statistics_admins.csv')
|
||||
return v
|
||||
376
controllers/sql/ml.py
Normal file
376
controllers/sql/ml.py
Normal file
@@ -0,0 +1,376 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
import web
|
||||
|
||||
from libs import iredutils, form_utils
|
||||
|
||||
from libs.sqllib import SQLWrap, decorators
|
||||
from libs.sqllib import ml as sql_lib_ml
|
||||
from libs.sqllib import admin as sql_lib_admin
|
||||
from libs.sqllib import domain as sql_lib_domain
|
||||
from libs.sqllib import general as sql_lib_general
|
||||
from libs.sqllib import utils as sql_lib_utils
|
||||
|
||||
|
||||
session = web.config.get('_session')
|
||||
|
||||
|
||||
class List:
|
||||
@decorators.require_domain_access
|
||||
def GET(self, domain, cur_page=1, disabled_only=False):
|
||||
domain = str(domain).lower()
|
||||
cur_page = int(cur_page) or 1
|
||||
|
||||
form = web.input(_unicode=False)
|
||||
|
||||
all_first_chars = []
|
||||
first_char = None
|
||||
if 'starts_with' in form:
|
||||
first_char = form.get('starts_with')[:1].upper()
|
||||
if not iredutils.is_valid_account_first_char(first_char):
|
||||
first_char = None
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
total = sql_lib_ml.num_maillists_under_domain(conn=conn,
|
||||
domain=domain,
|
||||
disabled_only=disabled_only,
|
||||
first_char=first_char)
|
||||
|
||||
records = []
|
||||
if total:
|
||||
_qr = sql_lib_general.get_first_char_of_all_accounts(domain=domain,
|
||||
account_type='ml',
|
||||
conn=conn)
|
||||
if _qr[0]:
|
||||
all_first_chars = _qr[1]
|
||||
|
||||
qr = sql_lib_ml.get_basic_ml_profiles(conn=conn,
|
||||
domain=domain,
|
||||
page=cur_page,
|
||||
first_char=first_char,
|
||||
disabled_only=disabled_only)
|
||||
if qr[0]:
|
||||
records = qr[1]
|
||||
|
||||
return web.render(
|
||||
'sql/ml/list.html',
|
||||
cur_domain=domain,
|
||||
cur_page=cur_page,
|
||||
total=total,
|
||||
maillists=records,
|
||||
all_first_chars=all_first_chars,
|
||||
first_char=first_char,
|
||||
msg=form.get('msg', None),
|
||||
)
|
||||
|
||||
@decorators.csrf_protected
|
||||
@decorators.require_domain_access
|
||||
def POST(self, domain):
|
||||
form = web.input(_unicode=False, mail=[])
|
||||
domain = str(domain).lower()
|
||||
|
||||
accounts = form.get('mail', [])
|
||||
action = form.get('action', None)
|
||||
msg = form.get('msg', None)
|
||||
|
||||
# Filter aliases not under the same domain.
|
||||
accounts = [str(v).lower()
|
||||
for v in accounts
|
||||
if iredutils.is_email(v) and str(v).endswith('@' + domain)]
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
if action == 'delete':
|
||||
result = sql_lib_ml.delete_maillists(accounts=accounts,
|
||||
keep_archive=True,
|
||||
conn=conn)
|
||||
msg = 'DELETED'
|
||||
elif action == 'delete_without_archiving':
|
||||
result = sql_lib_ml.delete_maillists(accounts=accounts,
|
||||
keep_archive=False,
|
||||
conn=conn)
|
||||
msg = 'DELETED'
|
||||
elif action == 'disable':
|
||||
result = sql_lib_utils.set_account_status(conn=conn,
|
||||
accounts=accounts,
|
||||
account_type='maillist',
|
||||
enable_account=False)
|
||||
msg = 'DISABLED'
|
||||
elif action == 'enable':
|
||||
result = sql_lib_utils.set_account_status(conn=conn,
|
||||
accounts=accounts,
|
||||
account_type='maillist',
|
||||
enable_account=True)
|
||||
msg = 'ENABLED'
|
||||
else:
|
||||
result = (False, 'INVALID_ACTION')
|
||||
|
||||
if result[0]:
|
||||
raise web.seeother('/mls/{}?msg={}'.format(domain, msg))
|
||||
else:
|
||||
raise web.seeother('/mls/{}?msg={}'.format(domain, web.urlquote(result[1])))
|
||||
|
||||
|
||||
class Create:
|
||||
@decorators.require_domain_access
|
||||
def GET(self, domain):
|
||||
domain = str(domain).lower()
|
||||
|
||||
form = web.input()
|
||||
all_domains = []
|
||||
|
||||
# Get all domains, select the first one.
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
qr = sql_lib_admin.get_managed_domains(conn=conn,
|
||||
admin=session.get('username'),
|
||||
domain_name_only=True)
|
||||
|
||||
if qr[0]:
|
||||
all_domains = qr[1]
|
||||
|
||||
# Get domain profile.
|
||||
qr_profile = sql_lib_domain.simple_profile(domain=domain, conn=conn)
|
||||
if qr_profile[0]:
|
||||
domain_profile = qr_profile[1]
|
||||
else:
|
||||
raise web.seeother('/domains?msg=%s' % web.urlquote(qr_profile[1]))
|
||||
|
||||
# Get total number and allocated quota size of existing users under domain.
|
||||
num_maillists_under_domain = sql_lib_ml.num_maillists_under_domain(domain=domain, conn=conn)
|
||||
|
||||
# TODO read default creation settings from domain profile.
|
||||
# Default creation settings
|
||||
default_creation_settings = {'only_subscriber_can_post': 'yes'}
|
||||
|
||||
return web.render(
|
||||
'sql/ml/create.html',
|
||||
cur_domain=domain,
|
||||
allDomains=all_domains,
|
||||
profile=domain_profile,
|
||||
num_existing_maillists=num_maillists_under_domain,
|
||||
default_creation_settings=default_creation_settings,
|
||||
msg=form.get('msg'),
|
||||
)
|
||||
|
||||
@decorators.require_domain_access
|
||||
@decorators.csrf_protected
|
||||
def POST(self, domain):
|
||||
domain = str(domain).lower()
|
||||
form = web.input()
|
||||
|
||||
domain_in_form = form_utils.get_domain_name(form)
|
||||
|
||||
if domain != domain_in_form:
|
||||
raise web.seeother('/domains?msg=PERMISSION_DENIED')
|
||||
|
||||
listname = form_utils.get_single_value(form, input_name='listname', to_string=True, to_lowercase=True)
|
||||
mail = listname + '@' + domain
|
||||
|
||||
qr = sql_lib_ml.add_ml_from_web_form(domain=domain, form=form)
|
||||
|
||||
if qr[0]:
|
||||
raise web.seeother('/profile/ml/general/%s?msg=CREATED' % mail)
|
||||
else:
|
||||
raise web.seeother('/create/ml/{}?msg={}'.format(domain, web.urlquote(qr[1])))
|
||||
|
||||
|
||||
class Profile:
|
||||
@decorators.require_domain_access
|
||||
def GET(self, profile_type, mail):
|
||||
form = web.input()
|
||||
mail = str(mail).lower()
|
||||
domain = mail.split('@', 1)[-1]
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
# Get mlmmj account profile
|
||||
qr = sql_lib_ml.get_profile(mail=mail, conn=conn)
|
||||
if qr[0] is not True:
|
||||
raise web.seeother('/mls/{}?msg={}'.format(domain, web.urlquote(qr[1])))
|
||||
|
||||
profile = qr[1]
|
||||
|
||||
# Get per-account alias addresses.
|
||||
qr = sql_lib_ml.get_alias_addresses(mail=mail, conn=conn)
|
||||
if qr[0]:
|
||||
alias_addresses = qr[1]
|
||||
else:
|
||||
raise web.seeother('/mls/{}?msg={}'.format(domain, web.urlquote(qr[1])))
|
||||
|
||||
# Get subscribers
|
||||
subscribers = []
|
||||
|
||||
qr = sql_lib_ml.get_subscribers(mail=mail)
|
||||
if qr[0]:
|
||||
subscribers = qr[1]
|
||||
|
||||
return web.render('sql/ml/profile.html',
|
||||
cur_domain=domain,
|
||||
mail=mail,
|
||||
profile_type=profile_type,
|
||||
profile=profile,
|
||||
alias_addresses=alias_addresses,
|
||||
subscribers=subscribers,
|
||||
msg=form.get('msg'))
|
||||
|
||||
@decorators.csrf_protected
|
||||
@decorators.require_domain_access
|
||||
def POST(self, profile_type, mail):
|
||||
form = web.input(subscriber=[])
|
||||
|
||||
result = sql_lib_ml.update(mail=mail,
|
||||
profile_type=profile_type,
|
||||
form=form)
|
||||
|
||||
if result[0]:
|
||||
raise web.seeother('/profile/ml/{}/{}?msg=UPDATED'.format(profile_type, mail))
|
||||
else:
|
||||
raise web.seeother('/profile/ml/{}/{}?msg={}'.format(profile_type, mail, web.urlquote(result[1])))
|
||||
|
||||
|
||||
class AddSubscribers:
|
||||
@decorators.csrf_protected
|
||||
@decorators.require_domain_access
|
||||
def POST(self, mail):
|
||||
form = web.input(_unicode=False)
|
||||
_require_confirm = 'require_confirm' in form
|
||||
|
||||
qr = sql_lib_ml.add_subscribers(mail=mail, form=form)
|
||||
|
||||
if qr[0]:
|
||||
if _require_confirm:
|
||||
raise web.seeother('/profile/ml/members/%s?msg=CONFIRM_MAIL_SENT' % mail)
|
||||
else:
|
||||
raise web.seeother('/profile/ml/members/%s?msg=MEMBERS_ADDED' % mail)
|
||||
else:
|
||||
raise web.seeother('/profile/ml/members/{}?msg={}'.format(mail, web.urlquote(qr[1])))
|
||||
|
||||
|
||||
class MigrateAliasToML:
|
||||
@decorators.csrf_protected
|
||||
@decorators.require_domain_access
|
||||
def POST(self, mail):
|
||||
mail = str(mail).lower()
|
||||
domain = mail.split('@', 1)[-1]
|
||||
qr = sql_lib_ml.migrate_alias_to_ml(mail=mail)
|
||||
|
||||
if qr[0]:
|
||||
raise web.seeother('/profile/ml/general/%s?msg=MIGRATED' % mail)
|
||||
else:
|
||||
raise web.seeother('/aliases/{}?msg={}'.format(domain, web.urlquote(qr[1])))
|
||||
|
||||
|
||||
# self-service: allow user to manage lists as owner or moderator.
|
||||
class ManagedMls:
|
||||
@decorators.require_preference_access("manageml")
|
||||
def GET(self, cur_page=1):
|
||||
mail = session['username']
|
||||
cur_page = int(cur_page) or 1
|
||||
|
||||
form = web.input(_unicode=False)
|
||||
|
||||
all_first_chars = []
|
||||
first_char = None
|
||||
if 'starts_with' in form:
|
||||
first_char = form.get('starts_with')[:1].upper()
|
||||
if not iredutils.is_valid_account_first_char(first_char):
|
||||
first_char = None
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
# Get managed mailing lists.
|
||||
total = sql_lib_ml.num_maillists_managed_by_user(mail=mail, first_char=first_char, conn=conn)
|
||||
|
||||
rows = []
|
||||
if total:
|
||||
_qr = sql_lib_ml.get_first_char_of_all_managed_mls(mail=mail, conn=conn)
|
||||
if _qr[0]:
|
||||
all_first_chars = _qr[1]
|
||||
|
||||
qr = sql_lib_ml.get_basic_profiles_of_managed_mls(
|
||||
page=cur_page,
|
||||
first_char=first_char,
|
||||
conn=conn,
|
||||
)
|
||||
if qr[0]:
|
||||
rows = qr[1]
|
||||
|
||||
return web.render(
|
||||
'sql/self-service/ml/list.html',
|
||||
cur_page=cur_page,
|
||||
total=total,
|
||||
maillists=rows,
|
||||
all_first_chars=all_first_chars,
|
||||
first_char=first_char,
|
||||
msg=form.get('msg', None),
|
||||
)
|
||||
|
||||
|
||||
class ManagedMlProfile:
|
||||
@decorators.require_preference_access("manageml")
|
||||
@decorators.require_ml_owner_or_moderator
|
||||
def GET(self, profile_type, mail):
|
||||
form = web.input()
|
||||
mail = str(mail).lower()
|
||||
domain = mail.split('@', 1)[-1]
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
# Get account profile
|
||||
qr = sql_lib_ml.get_profile(mail=mail, conn=conn)
|
||||
if not qr[0]:
|
||||
raise web.seeother('/mls/{}?msg={}'.format(domain, web.urlquote(qr[1])))
|
||||
|
||||
profile = qr[1]
|
||||
|
||||
# Get subscribers
|
||||
subscribers = []
|
||||
qr = sql_lib_ml.get_subscribers(mail=mail)
|
||||
if qr[0]:
|
||||
subscribers = qr[1]
|
||||
|
||||
return web.render('sql/self-service/ml/profile.html',
|
||||
mail=mail,
|
||||
profile_type=profile_type,
|
||||
profile=profile,
|
||||
subscribers=subscribers,
|
||||
msg=form.get('msg'))
|
||||
|
||||
@decorators.require_preference_access("manageml")
|
||||
@decorators.csrf_protected
|
||||
@decorators.require_ml_owner_or_moderator
|
||||
def POST(self, profile_type, mail):
|
||||
form = web.input(subscriber=[])
|
||||
|
||||
qr = sql_lib_ml.update(mail=mail,
|
||||
profile_type=profile_type,
|
||||
form=form)
|
||||
|
||||
if qr[0]:
|
||||
raise web.seeother('/self-service/ml/profile/{}/{}?msg=UPDATED'.format(profile_type, mail))
|
||||
else:
|
||||
raise web.seeother('/self-service/ml/profile/{}/{}?msg={}'.format(profile_type, mail, web.urlquote(qr[1])))
|
||||
|
||||
|
||||
# self-service
|
||||
class ManagedMlAddSubscribers:
|
||||
@decorators.require_preference_access("manageml")
|
||||
@decorators.csrf_protected
|
||||
@decorators.require_ml_owner_or_moderator
|
||||
def POST(self, mail):
|
||||
form = web.input(_unicode=False)
|
||||
|
||||
qr = sql_lib_ml.add_subscribers(mail=mail, form=form)
|
||||
|
||||
if qr[0]:
|
||||
raise web.seeother('/self-service/ml/profile/members/%s?msg=MEMBERS_ADDED' % mail)
|
||||
else:
|
||||
raise web.seeother('/self-service/ml/profile/members/{}?msg={}'.format(mail, web.urlquote(qr[1])))
|
||||
169
controllers/sql/urls.py
Normal file
169
controllers/sql/urls.py
Normal file
@@ -0,0 +1,169 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
import settings
|
||||
from libs.regxes import email as e, domain as d
|
||||
|
||||
# fmt: off
|
||||
urls = [
|
||||
# Make url ending with or without '/' going to the same class.
|
||||
'/(.*)/', 'controllers.utils.Redirect',
|
||||
|
||||
'/', 'controllers.sql.basic.Login',
|
||||
'/login', 'controllers.sql.basic.Login',
|
||||
'/logout', 'controllers.sql.basic.Logout',
|
||||
'/dashboard', 'controllers.sql.basic.Dashboard',
|
||||
|
||||
# Search.
|
||||
'/search', 'controllers.sql.basic.Search',
|
||||
|
||||
# Perform some operations from search page.
|
||||
'/action/(user|alias|ml)', 'controllers.sql.basic.OperationsFromSearchPage',
|
||||
|
||||
# Export managed accounts
|
||||
'/export/managed_accounts/(%s$)' % e, 'controllers.sql.export.ExportManagedAccounts',
|
||||
'/export/statistics/admins', 'controllers.sql.export.ExportAdminStatistics',
|
||||
'/export/domain/(%s$)' % d, 'controllers.sql.export.ExportDomainAccounts',
|
||||
|
||||
# Domain related.
|
||||
'/domains', 'controllers.sql.domain.List',
|
||||
r'/domains/page/(\d+)', 'controllers.sql.domain.List',
|
||||
# List disabled accounts.
|
||||
'/domains/disabled', 'controllers.sql.domain.ListDisabled',
|
||||
r'/domains/disabled/page/(\d+)', 'controllers.sql.domain.ListDisabled',
|
||||
# Domain profiles
|
||||
'/profile/domain/(general)/(%s$)' % d, 'controllers.sql.domain.Profile',
|
||||
'/profile/domain/(aliases)/(%s$)' % d, 'controllers.sql.domain.Profile',
|
||||
'/profile/domain/(relay)/(%s$)' % d, 'controllers.sql.domain.Profile',
|
||||
'/profile/domain/(backupmx)/(%s$)' % d, 'controllers.sql.domain.Profile',
|
||||
'/profile/domain/(bcc)/(%s$)' % d, 'controllers.sql.domain.Profile',
|
||||
'/profile/domain/(catchall)/(%s$)' % d, 'controllers.sql.domain.Profile',
|
||||
'/profile/domain/(throttle)/(%s$)' % d, 'controllers.sql.domain.Profile',
|
||||
'/profile/domain/(greylisting)/(%s$)' % d, 'controllers.sql.domain.Profile',
|
||||
'/profile/domain/(wblist)/(%s$)' % d, 'controllers.sql.domain.Profile',
|
||||
'/profile/domain/(spampolicy)/(%s$)' % d, 'controllers.sql.domain.Profile',
|
||||
'/profile/domain/(advanced)/(%s$)' % d, 'controllers.sql.domain.Profile',
|
||||
'/profile/domain/(%s)' % d, 'controllers.sql.domain.Profile',
|
||||
'/create/domain', 'controllers.sql.domain.Create',
|
||||
|
||||
# Admin related.
|
||||
'/admins', 'controllers.sql.admin.List',
|
||||
r'/admins/page/(\d+)', 'controllers.sql.admin.List',
|
||||
'/profile/admin/(general)/(%s$)' % e, 'controllers.sql.admin.Profile',
|
||||
'/profile/admin/(password)/(%s$)' % e, 'controllers.sql.admin.Profile',
|
||||
'/create/admin', 'controllers.sql.admin.Create',
|
||||
|
||||
# Redirect to first mail domain.
|
||||
'/create/(user|ml|alias)', 'controllers.sql.utils.CreateDispatcher',
|
||||
|
||||
# User related.
|
||||
'/users/(%s$)' % d, 'controllers.sql.user.List',
|
||||
r'/users/(%s)/page/(\d+)' % d, 'controllers.sql.user.List',
|
||||
# List disabled accounts.
|
||||
'/users/(%s)/disabled' % d, 'controllers.sql.user.ListDisabled',
|
||||
r'/users/(%s)/disabled/page/(\d+)' % d, 'controllers.sql.user.ListDisabled',
|
||||
# List all last logins.
|
||||
'/users/(%s)/last_logins' % d, 'controllers.sql.user.AllLastLogins',
|
||||
# Create user.
|
||||
'/create/user/(%s$)' % d, 'controllers.sql.user.Create',
|
||||
# Profile pages.
|
||||
'/profile/user/(general)/(%s$)' % e, 'controllers.sql.user.Profile',
|
||||
'/profile/user/(forwarding)/(%s$)' % e, 'controllers.sql.user.Profile',
|
||||
'/profile/user/(bcc)/(%s$)' % e, 'controllers.sql.user.Profile',
|
||||
'/profile/user/(relay)/(%s$)' % e, 'controllers.sql.user.Profile',
|
||||
'/profile/user/(aliases)/(%s$)' % e, 'controllers.sql.user.Profile',
|
||||
'/profile/user/(wblist)/(%s$)' % e, 'controllers.sql.user.Profile',
|
||||
'/profile/user/(spampolicy)/(%s$)' % e, 'controllers.sql.user.Profile',
|
||||
'/profile/user/(password)/(%s$)' % e, 'controllers.sql.user.Profile',
|
||||
'/profile/user/(throttle)/(%s$)' % e, 'controllers.sql.user.Profile',
|
||||
'/profile/user/(greylisting)/(%s$)' % e, 'controllers.sql.user.Profile',
|
||||
'/profile/user/(advanced)/(%s$)' % e, 'controllers.sql.user.Profile',
|
||||
'/profile/user/(rename)/(%s$)' % e, 'controllers.sql.user.Profile',
|
||||
|
||||
'/apiproxy/user/(%s$)' % e, 'controllers.sql.user.APIProxyUser',
|
||||
####################
|
||||
# mlmmj mailing list
|
||||
#
|
||||
'/create/ml/(%s$)' % d, 'controllers.sql.ml.Create',
|
||||
# make it compatible with old (LDAP) mailing list
|
||||
'/create/maillist/(%s$)' % d, 'controllers.sql.ml.Create',
|
||||
'/mls/(%s$)' % d, 'controllers.sql.ml.List',
|
||||
r'/mls/(%s)/page/(\d+)' % d, 'controllers.sql.ml.List',
|
||||
'/profile/ml/(general|aliases|owners|members|newsletter)/(%s$)' % e, 'controllers.sql.ml.Profile',
|
||||
# Add subscribers
|
||||
'/profile/ml/add_subscribers/(%s$)' % e, 'controllers.sql.ml.AddSubscribers',
|
||||
# migrate alias account to mlmmj mailing list.
|
||||
'/migrate/alias_to_ml/(%s$)' % e, 'controllers.sql.ml.MigrateAliasToML',
|
||||
|
||||
# Alias related.
|
||||
'/aliases', 'controllers.sql.alias.List',
|
||||
'/aliases/(%s$)' % d, 'controllers.sql.alias.List',
|
||||
r'/aliases/(%s)/page/(\d+)' % d, 'controllers.sql.alias.List',
|
||||
# List disabled accounts.
|
||||
'/aliases/(%s)/disabled' % d, 'controllers.sql.alias.ListDisabled',
|
||||
r'/aliases/(%s)/disabled/page/(\d+)' % d, 'controllers.sql.alias.ListDisabled',
|
||||
'/profile/alias/(general)/(%s$)' % e, 'controllers.sql.alias.Profile',
|
||||
'/profile/alias/(members)/(%s$)' % e, 'controllers.sql.alias.Profile',
|
||||
'/profile/alias/(rename)/(%s$)' % e, 'controllers.sql.alias.Profile',
|
||||
'/create/alias/(%s$)' % d, 'controllers.sql.alias.Create',
|
||||
|
||||
# User admins
|
||||
'/admins/(%s$)' % d, 'controllers.sql.user.Admin',
|
||||
r'/admins/(%s)/page/(\d+)' % d, 'controllers.sql.user.Admin',
|
||||
|
||||
#
|
||||
# Self-service
|
||||
#
|
||||
'/preferences', 'controllers.sql.user.Preferences',
|
||||
'/preferences/(general)$', 'controllers.sql.user.Preferences',
|
||||
'/preferences/(forwarding)$', 'controllers.sql.user.Preferences',
|
||||
'/preferences/(password)$', 'controllers.sql.user.Preferences',
|
||||
# manage owned or moderated mailing lists
|
||||
'/self-service/mls', 'controllers.sql.ml.ManagedMls',
|
||||
'/self-service/mls/page/(\d+)', 'controllers.sql.ml.ManagedMls',
|
||||
'/self-service/ml/profile/(general|owners|members|newsletter)/(%s$)' % e, 'controllers.sql.ml.ManagedMlProfile',
|
||||
'/self-service/ml/profile/add_subscribers/(%s$)' % e, 'controllers.sql.ml.ManagedMlAddSubscribers',
|
||||
]
|
||||
|
||||
|
||||
# API Interfaces
|
||||
if settings.ENABLE_RESTFUL_API:
|
||||
urls += [
|
||||
# API Interfaces
|
||||
'/api/login', 'controllers.sql.basic.APILogin',
|
||||
|
||||
#
|
||||
# Domain
|
||||
#
|
||||
'/api/domains', 'controllers.sql.api_domain.APIDomains',
|
||||
'/api/domain/(%s$)' % d, 'controllers.sql.api_domain.APIDomain',
|
||||
# Delete domain, and keep mailboxes for given days
|
||||
r'/api/domain/(%s)/keep_mailbox_days/(\d+)' % d, 'controllers.sql.api_domain.APIDomain',
|
||||
'/api/domain/admins/(%s$)' % d, 'controllers.sql.api_domain.APIDomainAdmin',
|
||||
|
||||
# User
|
||||
'/api/user/(%s$)' % e, 'controllers.sql.api_user.APIUser',
|
||||
# Delete user, and keep mailboxes for given days
|
||||
r'/api/user/(%s)/keep_mailbox_days/(\d+)' % e, 'controllers.sql.api_user.APIUser',
|
||||
'/api/user/({})/change_email/({}$)'.format(e, e), 'controllers.sql.api_user.APIChangeEmail',
|
||||
'/api/users/(%s$)' % d, 'controllers.sql.api_user.APIUsers',
|
||||
|
||||
# Alias
|
||||
'/api/alias/(%s$)' % e, 'controllers.sql.api_alias.APIAlias',
|
||||
'/api/alias/({})/change_email/({}$)'.format(e, e), 'controllers.sql.api_alias.APIChangeEmail',
|
||||
'/api/aliases/(%s$)' % d, 'controllers.sql.api_alias.APIAliases',
|
||||
|
||||
# (mlmmj) mailing list
|
||||
'/api/mls/(%s$)' % d, 'controllers.sql.api_ml.APIMLS',
|
||||
'/api/ml/(%s$)' % e, 'controllers.sql.api_ml.APIML',
|
||||
|
||||
# Admin
|
||||
'/api/admin/(%s$)' % e, 'controllers.sql.api_admin.APIAdmin',
|
||||
|
||||
#
|
||||
# Misc
|
||||
#
|
||||
# Verify account password.
|
||||
'/api/verify_password/(user)/(%s$)' % e, 'controllers.sql.api_misc.APIVerifyPassword',
|
||||
'/api/verify_password/(admin)/(%s$)' % e, 'controllers.sql.api_misc.APIVerifyPassword',
|
||||
]
|
||||
# fmt: on
|
||||
834
controllers/sql/user.py
Normal file
834
controllers/sql/user.py
Normal file
@@ -0,0 +1,834 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
import web
|
||||
import settings
|
||||
|
||||
from controllers.utils import api_render
|
||||
|
||||
from libs import iredutils, form_utils
|
||||
from libs.l10n import TIMEZONES
|
||||
|
||||
from libs.sqllib import SQLWrap, decorators, sqlutils
|
||||
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 admin as sql_lib_admin
|
||||
from libs.sqllib import domain as sql_lib_domain
|
||||
from libs.sqllib import utils as sql_lib_utils
|
||||
from libs.sqllib import general as sql_lib_general
|
||||
from libs import mlmmj
|
||||
|
||||
from libs.amavisd import spampolicy as spampolicylib, wblist as lib_wblist
|
||||
|
||||
session = web.config.get('_session')
|
||||
|
||||
if settings.iredapd_enabled:
|
||||
from libs.iredapd import throttle as iredapd_throttle
|
||||
from libs.iredapd import greylist as iredapd_greylist
|
||||
|
||||
|
||||
class List:
|
||||
@decorators.require_domain_access
|
||||
def GET(self, domain, cur_page=1, disabled_only=False):
|
||||
domain = str(domain).lower()
|
||||
cur_page = int(cur_page) or 1
|
||||
|
||||
form = web.input(_unicode=False)
|
||||
order_name = form.get('order_name')
|
||||
order_by_desc = (form.get('order_by', 'asc').lower() == 'desc')
|
||||
|
||||
records = []
|
||||
|
||||
# Real-time used quota.
|
||||
used_quotas = {}
|
||||
# Last login date
|
||||
last_logins = {}
|
||||
|
||||
# Forwardings and per-user alias addresses
|
||||
user_forwardings = {}
|
||||
user_alias_addresses = {}
|
||||
user_assigned_groups = {}
|
||||
|
||||
all_first_chars = []
|
||||
first_char = None
|
||||
if 'starts_with' in form:
|
||||
first_char = form.get('starts_with')[:1].upper()
|
||||
if not iredutils.is_valid_account_first_char(first_char):
|
||||
first_char = None
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
total = sql_lib_user.num_users_under_domains(conn=conn,
|
||||
domains=[domain],
|
||||
disabled_only=disabled_only,
|
||||
first_char=first_char)
|
||||
|
||||
if total:
|
||||
_qr = sql_lib_general.get_first_char_of_all_accounts(domain=domain,
|
||||
account_type='user',
|
||||
conn=conn)
|
||||
if _qr[0]:
|
||||
all_first_chars = _qr[1]
|
||||
|
||||
qr = sql_lib_user.get_paged_users(conn=conn,
|
||||
domain=domain,
|
||||
cur_page=cur_page,
|
||||
order_name=order_name,
|
||||
order_by_desc=order_by_desc,
|
||||
first_char=first_char,
|
||||
disabled_only=disabled_only)
|
||||
|
||||
if qr[0]:
|
||||
records = qr[1]
|
||||
else:
|
||||
raise web.seeother('/domains?msg=%s' % web.urlquote(qr[1]))
|
||||
|
||||
# Get list of email addresses
|
||||
mails = []
|
||||
for r in records:
|
||||
mails += [str(r.get('username')).lower()]
|
||||
|
||||
if mails:
|
||||
# Get real-time mailbox usage
|
||||
if settings.SHOW_USED_QUOTA:
|
||||
try:
|
||||
used_quotas = sql_lib_general.get_account_used_quota(accounts=mails, conn=conn)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Get last login
|
||||
last_logins = sql_lib_general.get_account_last_login(accounts=mails, conn=conn)
|
||||
|
||||
# Get user forwardings
|
||||
(_status, _result) = sql_lib_user.get_bulk_user_forwardings(conn=conn, mails=mails)
|
||||
if _status:
|
||||
user_forwardings = _result
|
||||
else:
|
||||
raise web.seeother('/domains?msg=%s' % web.urlquote(_result))
|
||||
|
||||
# Get user alias addresses
|
||||
(_status, _result) = sql_lib_user.get_bulk_user_alias_addresses(mails=mails, conn=conn)
|
||||
if _status:
|
||||
user_alias_addresses = _result
|
||||
else:
|
||||
raise web.seeother('/domains?msg=%s' % web.urlquote(_result))
|
||||
|
||||
# Get assigned groups
|
||||
(_status, _result) = sql_lib_user.get_bulk_user_assigned_groups(mails=mails, conn=conn)
|
||||
if _status:
|
||||
user_assigned_groups = _result
|
||||
else:
|
||||
raise web.seeother('/domains?msg=%s' % web.urlquote(_result))
|
||||
|
||||
if session.get('is_global_admin'):
|
||||
days_to_keep_removed_mailbox = settings.DAYS_TO_KEEP_REMOVED_MAILBOX_FOR_GLOBAL_ADMIN
|
||||
else:
|
||||
days_to_keep_removed_mailbox = settings.DAYS_TO_KEEP_REMOVED_MAILBOX
|
||||
|
||||
return web.render('sql/user/list.html',
|
||||
cur_domain=domain,
|
||||
cur_page=cur_page,
|
||||
total=total,
|
||||
users=records,
|
||||
user_forwardings=user_forwardings,
|
||||
user_alias_addresses=user_alias_addresses,
|
||||
user_assigned_groups=user_assigned_groups,
|
||||
used_quotas=used_quotas,
|
||||
last_logins=last_logins,
|
||||
order_name=order_name,
|
||||
order_by_desc=order_by_desc,
|
||||
all_first_chars=all_first_chars,
|
||||
first_char=first_char,
|
||||
disabled_only=disabled_only,
|
||||
days_to_keep_removed_mailbox=days_to_keep_removed_mailbox,
|
||||
msg=form.get('msg', None))
|
||||
|
||||
@decorators.csrf_protected
|
||||
@decorators.require_domain_access
|
||||
def POST(self, domain, page=1):
|
||||
form = web.input(_unicode=False, mail=[])
|
||||
page = int(page)
|
||||
if page < 1:
|
||||
page = 1
|
||||
|
||||
domain = str(domain).lower()
|
||||
|
||||
# Filter users not under the same domain.
|
||||
mails = [str(v).strip().lower() for v in form.get("mail", [])]
|
||||
mails = [v for v in mails if iredutils.is_email(v) and v.endswith('@' + domain)]
|
||||
|
||||
action = form.get('action', None)
|
||||
msg = form.get('msg', None)
|
||||
|
||||
redirect_to_admin_list = False
|
||||
if 'redirect_to_admin_list' in form:
|
||||
redirect_to_admin_list = True
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
if action == 'delete':
|
||||
keep_mailbox_days = form_utils.get_single_value(form=form,
|
||||
input_name='keep_mailbox_days',
|
||||
default_value=0,
|
||||
is_integer=True)
|
||||
result = sql_lib_user.delete_users(conn=conn,
|
||||
accounts=mails,
|
||||
keep_mailbox_days=keep_mailbox_days)
|
||||
msg = 'DELETED'
|
||||
elif action == 'disable':
|
||||
result = sql_lib_utils.set_account_status(conn=conn,
|
||||
accounts=mails,
|
||||
account_type='user',
|
||||
enable_account=False)
|
||||
msg = 'DISABLED'
|
||||
elif action == 'enable':
|
||||
result = sql_lib_utils.set_account_status(conn=conn,
|
||||
accounts=mails,
|
||||
account_type='user',
|
||||
enable_account=True)
|
||||
msg = 'ENABLED'
|
||||
elif action == 'markasadmin':
|
||||
result = sql_lib_user.mark_user_as_admin(conn=conn,
|
||||
domain=domain,
|
||||
users=mails,
|
||||
as_normal_admin=True)
|
||||
msg = 'MARKASADMIN'
|
||||
elif action == 'unmarkasadmin':
|
||||
result = sql_lib_user.mark_user_as_admin(conn=conn,
|
||||
domain=domain,
|
||||
users=mails,
|
||||
as_normal_admin=False)
|
||||
msg = 'UNMARKASADMIN'
|
||||
elif action == 'markasglobaladmin':
|
||||
result = sql_lib_user.mark_user_as_admin(conn=conn,
|
||||
domain=domain,
|
||||
users=mails,
|
||||
as_global_admin=True)
|
||||
msg = 'MARKASGLOBALADMIN'
|
||||
elif action == 'unmarkasglobaladmin':
|
||||
result = sql_lib_user.mark_user_as_admin(conn=conn,
|
||||
domain=domain,
|
||||
users=mails,
|
||||
as_global_admin=False)
|
||||
msg = 'UNMARKASGLOBALADMIN'
|
||||
else:
|
||||
result = (False, 'INVALID_ACTION')
|
||||
|
||||
if result[0]:
|
||||
if redirect_to_admin_list:
|
||||
raise web.seeother('/admins/%s/page/%d?msg=%s' % (domain, page, msg))
|
||||
else:
|
||||
raise web.seeother('/users/%s/page/%d?msg=%s' % (domain, page, msg))
|
||||
else:
|
||||
if redirect_to_admin_list:
|
||||
raise web.seeother('/admins/%s/page/%d?msg=%s' % (domain, page, web.urlquote(result[1])))
|
||||
else:
|
||||
raise web.seeother('/users/%s/page/%d?msg=%s' % (domain, page, web.urlquote(result[1])))
|
||||
|
||||
|
||||
class ListDisabled:
|
||||
@decorators.require_domain_access
|
||||
def GET(self, domain, cur_page=1):
|
||||
_instance = List()
|
||||
return _instance.GET(domain=domain, cur_page=cur_page, disabled_only=True)
|
||||
|
||||
|
||||
class Profile:
|
||||
# Don't use decorator `@decorators.require_domain_access` here, because if
|
||||
# domain admin doesn't manage its own domain, it cannot access its own
|
||||
# profile.
|
||||
def GET(self, profile_type, mail):
|
||||
mail = str(mail).lower()
|
||||
domain = mail.split('@', 1)[-1]
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
# - Allow global admin
|
||||
# - normal admin who manages this domain
|
||||
# - allow normal admin who doesn't manage this domain, but is updating its own profile
|
||||
if sql_lib_general.is_domain_admin(domain=domain, admin=session.get('username'), conn=conn) or \
|
||||
(session.get('is_normal_admin') and session.get('username') == mail):
|
||||
pass
|
||||
else:
|
||||
raise web.seeother('/domains?msg=PERMISSION_DENIED')
|
||||
|
||||
if profile_type == 'rename':
|
||||
raise web.seeother('/profile/user/general/' + mail)
|
||||
|
||||
form = web.input()
|
||||
msg = form.get('msg', '')
|
||||
|
||||
discarded_aliases = form.get('discarded_aliases', '')
|
||||
if discarded_aliases:
|
||||
discarded_aliases = [i.strip().lower()
|
||||
for i in discarded_aliases.split(',')]
|
||||
|
||||
# profile_type == 'general'
|
||||
used_quota = {}
|
||||
last_logins = {}
|
||||
|
||||
# profile_type == 'greylisting'
|
||||
# greylisting: iRedAPD
|
||||
gl_setting = {}
|
||||
gl_whitelists = []
|
||||
|
||||
# profile_type == 'throttle'
|
||||
# throttle: iRedAPD
|
||||
inbound_throttle_setting = {}
|
||||
outbound_throttle_setting = {}
|
||||
|
||||
# profile_type == 'advanced'
|
||||
disabled_user_profiles = [] # Per-domain disabled user profiles.
|
||||
|
||||
if mail.startswith('@') and iredutils.is_domain(domain):
|
||||
# Catchall account.
|
||||
raise web.seeother('/profile/domain/catchall/%s' % domain)
|
||||
|
||||
qr = sql_lib_user.profile(mail=mail, conn=conn)
|
||||
if qr[0]:
|
||||
user_profile = qr[1]
|
||||
|
||||
if not session.get('is_global_admin'):
|
||||
sql_lib_user.redirect_if_user_is_global_admin(conn=conn, mail=mail, user_profile=user_profile)
|
||||
else:
|
||||
raise web.seeother('/users/{}?msg={}'.format(domain, web.urlquote(qr[1])))
|
||||
del qr
|
||||
|
||||
# Get mailbox.allow_nets
|
||||
allow_nets = []
|
||||
_allow_nets = user_profile.get('allow_nets')
|
||||
if _allow_nets:
|
||||
allow_nets = _allow_nets.split(',')
|
||||
|
||||
# Get per-user settings
|
||||
user_settings = {}
|
||||
qr = sql_lib_general.get_user_settings(conn=conn,
|
||||
mail=mail,
|
||||
existing_settings=user_profile['settings'])
|
||||
if qr[0]:
|
||||
user_settings = qr[1]
|
||||
del qr
|
||||
|
||||
# Get used quota.
|
||||
if settings.SHOW_USED_QUOTA:
|
||||
used_quota = sql_lib_general.get_account_used_quota(accounts=[mail], conn=conn)
|
||||
|
||||
# Get last login.
|
||||
last_logins = sql_lib_general.get_account_last_login(accounts=[mail], conn=conn)
|
||||
|
||||
# Get basic profile of all mail alias accounts under same domain.
|
||||
all_aliases = []
|
||||
(_status, _result) = sql_lib_alias.get_basic_alias_profiles(domain=domain, conn=conn)
|
||||
if _status:
|
||||
all_aliases = _result
|
||||
|
||||
# Get email addresses of mail alias accounts which has current mail
|
||||
# user as a member
|
||||
assigned_aliases = []
|
||||
(_status, _result) = sql_lib_user.get_assigned_aliases(mail=mail, conn=conn)
|
||||
if _status:
|
||||
assigned_aliases = _result
|
||||
|
||||
# Get per-user alias addresses.
|
||||
user_alias_addresses = []
|
||||
qr = sql_lib_user.get_user_alias_addresses(mail=mail, conn=conn)
|
||||
if qr[0]:
|
||||
user_alias_addresses = qr[1]
|
||||
|
||||
# subscribable mailing lists
|
||||
all_maillist_addresses = []
|
||||
all_subscribed_lists = []
|
||||
|
||||
_qr = sql_lib_ml.get_basic_ml_profiles(domain=domain,
|
||||
columns=['address', 'name'],
|
||||
conn=conn)
|
||||
if _qr[0]:
|
||||
all_maillist_profiles = _qr[1]
|
||||
for i in all_maillist_profiles:
|
||||
all_maillist_addresses.append(i['address'])
|
||||
else:
|
||||
return _qr
|
||||
|
||||
# Get subscribed mailing lists
|
||||
_qr = mlmmj.get_subscribed_lists(mail=mail, query_all_lists=False)
|
||||
if _qr[0]:
|
||||
for i in _qr[1]:
|
||||
all_subscribed_lists.append(i['mail'])
|
||||
|
||||
# Get per-domain disabled user profiles.
|
||||
qr = sql_lib_domain.simple_profile(conn=conn,
|
||||
domain=domain,
|
||||
columns=['settings'])
|
||||
|
||||
if qr[0]:
|
||||
domain_profile = qr[1]
|
||||
domain_settings = sqlutils.account_settings_string_to_dict(domain_profile['settings'])
|
||||
|
||||
disabled_user_profiles = domain_settings.get('disabled_user_profiles', [])
|
||||
|
||||
db_settings = iredutils.get_settings_from_db()
|
||||
_min_passwd_length = db_settings['min_passwd_length']
|
||||
_max_passwd_length = db_settings['max_passwd_length']
|
||||
|
||||
min_passwd_length = domain_settings.get('min_passwd_length', _min_passwd_length)
|
||||
max_passwd_length = domain_settings.get('max_passwd_length', _max_passwd_length)
|
||||
|
||||
# Get sender dependent relayhost
|
||||
relayhost = ''
|
||||
(_status, _result) = sql_lib_general.get_sender_relayhost(sender=mail, conn=conn)
|
||||
if _status:
|
||||
relayhost = _result
|
||||
|
||||
if settings.iredapd_enabled:
|
||||
# Greylisting
|
||||
gl_setting = iredapd_greylist.get_greylist_setting(account=mail)
|
||||
gl_whitelists = iredapd_greylist.get_greylist_whitelists(account=mail)
|
||||
|
||||
# Throttling
|
||||
inbound_throttle_setting = iredapd_throttle.get_throttle_setting(account=mail, inout_type='inbound')
|
||||
outbound_throttle_setting = iredapd_throttle.get_throttle_setting(account=mail, inout_type='outbound')
|
||||
|
||||
# Get managed domains and all domains under control.
|
||||
managed_domains = []
|
||||
all_domains = []
|
||||
|
||||
if session.get('is_global_admin') or session.get('is_normal_admin') or session.get('allowed_to_grant_admin'):
|
||||
qr = sql_lib_admin.get_managed_domains(admin=mail,
|
||||
domain_name_only=True,
|
||||
listed_only=True,
|
||||
conn=conn)
|
||||
if qr[0]:
|
||||
managed_domains += qr[1]
|
||||
|
||||
if session.get('is_global_admin'):
|
||||
qr = sql_lib_domain.get_all_domains(conn=conn,
|
||||
columns=['domain', 'description'])
|
||||
if qr[0]:
|
||||
all_domains = qr[1]
|
||||
else:
|
||||
qr = sql_lib_admin.get_managed_domains(conn=conn,
|
||||
admin=session.username,
|
||||
listed_only=True)
|
||||
if qr[0]:
|
||||
all_domains = qr[1]
|
||||
|
||||
# Get spam policy
|
||||
spampolicy = {}
|
||||
global_spam_score = None
|
||||
if settings.amavisd_enable_policy_lookup:
|
||||
qr = spampolicylib.get_spam_policy(account=mail)
|
||||
if not qr[0]:
|
||||
raise web.seeother('/domains?msg=%s' % web.urlquote(qr[1]))
|
||||
else:
|
||||
spampolicy = qr[1]
|
||||
|
||||
global_spam_score = spampolicylib.get_global_spam_score()
|
||||
|
||||
# Get per-user white/blacklists
|
||||
whitelists = []
|
||||
blacklists = []
|
||||
outbound_whitelists = []
|
||||
outbound_blacklists = []
|
||||
|
||||
qr = lib_wblist.get_wblist(account=mail)
|
||||
|
||||
if qr[0]:
|
||||
whitelists = qr[1]['inbound_whitelists']
|
||||
blacklists = qr[1]['inbound_blacklists']
|
||||
outbound_whitelists = qr[1]['outbound_whitelists']
|
||||
outbound_blacklists = qr[1]['outbound_blacklists']
|
||||
|
||||
return web.render(
|
||||
'sql/user/profile.html',
|
||||
cur_domain=domain,
|
||||
mail=mail,
|
||||
profile_type=profile_type,
|
||||
profile=user_profile,
|
||||
timezones=TIMEZONES,
|
||||
min_passwd_length=min_passwd_length,
|
||||
max_passwd_length=max_passwd_length,
|
||||
store_password_in_plain_text=settings.STORE_PASSWORD_IN_PLAIN_TEXT,
|
||||
password_policies=iredutils.get_password_policies(),
|
||||
user_settings=user_settings,
|
||||
used_quota=used_quota,
|
||||
last_logins=last_logins,
|
||||
all_aliases=all_aliases,
|
||||
assigned_aliases=assigned_aliases,
|
||||
user_alias_addresses=user_alias_addresses,
|
||||
user_alias_cross_all_domains=settings.USER_ALIAS_CROSS_ALL_DOMAINS,
|
||||
all_maillist_profiles=all_maillist_profiles,
|
||||
all_subscribed_lists=all_subscribed_lists,
|
||||
disabled_user_profiles=disabled_user_profiles,
|
||||
allow_nets=allow_nets,
|
||||
managed_domains=managed_domains,
|
||||
all_domains=all_domains,
|
||||
relayhost=relayhost,
|
||||
# iRedAPD
|
||||
gl_setting=gl_setting,
|
||||
gl_whitelists=gl_whitelists,
|
||||
# iRedAPD
|
||||
inbound_throttle_setting=inbound_throttle_setting,
|
||||
outbound_throttle_setting=outbound_throttle_setting,
|
||||
# spam policy, wblist, throttling
|
||||
spampolicy=spampolicy,
|
||||
custom_ban_rules=settings.AMAVISD_BAN_RULES,
|
||||
global_spam_score=global_spam_score,
|
||||
whitelists=whitelists,
|
||||
blacklists=blacklists,
|
||||
outbound_whitelists=outbound_whitelists,
|
||||
outbound_blacklists=outbound_blacklists,
|
||||
languagemaps=iredutils.get_language_maps(),
|
||||
msg=msg,
|
||||
discarded_aliases=discarded_aliases,
|
||||
)
|
||||
|
||||
# Don't use decorator `@decorators.require_domain_access` here, because if
|
||||
# domain admin doesn't manage its own domain, it cannot access its own
|
||||
# profile.
|
||||
@decorators.csrf_protected
|
||||
def POST(self, profile_type, mail):
|
||||
form = web.input(
|
||||
enabledService=[],
|
||||
shadowAddress=[],
|
||||
telephoneNumber=[],
|
||||
subscribed_list=[],
|
||||
memberOfGroup=[],
|
||||
oldMemberOfAlias=[],
|
||||
memberOfAlias=[],
|
||||
domainName=[], # Managed domains
|
||||
banned_rulenames=[],
|
||||
)
|
||||
|
||||
mail = str(mail).lower()
|
||||
domain = mail.split('@', 1)[-1]
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
# - Allow global admin
|
||||
# - normal admin who manages this domain
|
||||
# - allow normal admin who doesn't manage this domain, but is updating its own profile
|
||||
if sql_lib_general.is_domain_admin(domain=domain, admin=session.get('username'), conn=conn) or \
|
||||
(session.get('is_normal_admin') and session.get('username') == mail):
|
||||
pass
|
||||
else:
|
||||
raise web.seeother('/domains?msg=PERMISSION_DENIED')
|
||||
|
||||
result = sql_lib_user.update(conn=conn,
|
||||
mail=mail,
|
||||
profile_type=profile_type,
|
||||
form=form)
|
||||
|
||||
if profile_type == 'rename':
|
||||
profile_type = 'general'
|
||||
|
||||
if result[0]:
|
||||
_discarded_aliases = []
|
||||
if profile_type == 'aliases':
|
||||
# Notify admin the discarded addresses.
|
||||
try:
|
||||
_discarded_aliases = result[1]['discarded_aliases']
|
||||
except:
|
||||
pass
|
||||
|
||||
if _discarded_aliases:
|
||||
raise web.seeother('/profile/user/%s/%s?msg=UPDATED'
|
||||
'&discarded_aliases=%s' % (profile_type, mail, ','.join(_discarded_aliases)))
|
||||
else:
|
||||
raise web.seeother('/profile/user/{}/{}?msg=UPDATED'.format(profile_type, mail))
|
||||
else:
|
||||
raise web.seeother('/profile/user/{}/{}?msg={}'.format(profile_type, mail, web.urlquote(result[1])))
|
||||
|
||||
|
||||
class Create:
|
||||
@decorators.require_domain_access
|
||||
def GET(self, domain):
|
||||
domain = str(domain).lower()
|
||||
|
||||
form = web.input()
|
||||
|
||||
# Get all managed domains.
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
if session.get('is_global_admin'):
|
||||
qr = sql_lib_domain.get_all_domains(conn=conn, name_only=True)
|
||||
else:
|
||||
qr = sql_lib_admin.get_managed_domains(conn=conn,
|
||||
admin=session.get('username'),
|
||||
domain_name_only=True)
|
||||
|
||||
if qr[0]:
|
||||
all_domains = qr[1]
|
||||
else:
|
||||
raise web.seeother('/domains?msg=' + web.urlquote(qr[1]))
|
||||
|
||||
if not all_domains:
|
||||
raise web.seeother('/domains?msg=NO_DOMAIN_AVAILABLE')
|
||||
|
||||
# Get domain profile.
|
||||
qr_profile = sql_lib_domain.simple_profile(domain=domain, conn=conn)
|
||||
if qr_profile[0]:
|
||||
domain_profile = qr_profile[1]
|
||||
domain_settings = sqlutils.account_settings_string_to_dict(domain_profile['settings'])
|
||||
else:
|
||||
raise web.seeother('/domains?msg=%s' % web.urlquote(qr_profile[1]))
|
||||
|
||||
# Cet total number and allocated quota size of existing users under domain.
|
||||
num_users_under_domain = sql_lib_general.num_users_under_domain(domain=domain, conn=conn)
|
||||
used_quota_size = sql_lib_domain.get_allocated_domain_quota(domains=[domain], conn=conn)
|
||||
|
||||
db_settings = iredutils.get_settings_from_db()
|
||||
_min_passwd_length = db_settings['min_passwd_length']
|
||||
_max_passwd_length = db_settings['max_passwd_length']
|
||||
|
||||
min_passwd_length = domain_settings.get('min_passwd_length', _min_passwd_length)
|
||||
max_passwd_length = domain_settings.get('max_passwd_length', _max_passwd_length)
|
||||
|
||||
return web.render(
|
||||
'sql/user/create.html',
|
||||
cur_domain=domain,
|
||||
all_domains=all_domains,
|
||||
profile=domain_profile,
|
||||
domain_settings=domain_settings,
|
||||
min_passwd_length=min_passwd_length,
|
||||
max_passwd_length=max_passwd_length,
|
||||
store_password_in_plain_text=settings.STORE_PASSWORD_IN_PLAIN_TEXT,
|
||||
num_existing_users=num_users_under_domain,
|
||||
usedQuotaSize=used_quota_size,
|
||||
languagemaps=iredutils.get_language_maps(),
|
||||
password_policies=iredutils.get_password_policies(),
|
||||
msg=form.get('msg'),
|
||||
)
|
||||
|
||||
@decorators.csrf_protected
|
||||
@decorators.require_domain_access
|
||||
def POST(self, domain):
|
||||
domain = str(domain).lower()
|
||||
form = web.input()
|
||||
|
||||
domain_in_form = form_utils.get_domain_name(form)
|
||||
if domain != domain_in_form:
|
||||
raise web.seeother('/domains?msg=PERMISSION_DENIED')
|
||||
|
||||
# Get domain name, username, cn.
|
||||
username = form_utils.get_single_value(form,
|
||||
input_name='username',
|
||||
to_string=True)
|
||||
|
||||
qr = sql_lib_user.add_user_from_form(domain=domain, form=form)
|
||||
|
||||
if qr[0]:
|
||||
raise web.seeother('/profile/user/general/{}@{}?msg=CREATED'.format(username, domain))
|
||||
else:
|
||||
raise web.seeother('/create/user/{}?msg={}'.format(domain, web.urlquote(qr[1])))
|
||||
|
||||
|
||||
# Internal domain admins
|
||||
class Admin:
|
||||
@decorators.require_domain_access
|
||||
def GET(self, domain, cur_page=1):
|
||||
domain = str(domain).lower()
|
||||
cur_page = int(cur_page) or 1
|
||||
|
||||
form = web.input(_unicode=False)
|
||||
|
||||
first_char = None
|
||||
if 'starts_with' in form:
|
||||
first_char = form.get('starts_with')[:1].upper()
|
||||
if not iredutils.is_valid_account_first_char(first_char):
|
||||
first_char = None
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
_include_global_admins = settings.SHOW_GLOBAL_ADMINS_IN_PER_DOMAIN_ADMIN_LIST
|
||||
qr = sql_lib_admin.get_paged_domain_admins(conn=conn,
|
||||
domain=domain,
|
||||
include_global_admins=_include_global_admins,
|
||||
current_page=cur_page,
|
||||
first_char=first_char)
|
||||
|
||||
if not qr[0]:
|
||||
raise web.seeother('/domains?msg=%s' % web.urlquote(qr[1]))
|
||||
|
||||
total = qr[1]['total']
|
||||
records = qr[1]['records']
|
||||
|
||||
# Get list of email addresses
|
||||
mails = []
|
||||
for r in records:
|
||||
mails += [str(r.get('username'))]
|
||||
|
||||
# Get real-time used quota.
|
||||
used_quotas = {}
|
||||
|
||||
if settings.SHOW_USED_QUOTA:
|
||||
if mails:
|
||||
try:
|
||||
used_quotas = sql_lib_general.get_account_used_quota(accounts=mails, conn=conn)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Get user forwardings
|
||||
_status, _result = sql_lib_user.get_bulk_user_forwardings(conn=conn, mails=mails)
|
||||
if _status:
|
||||
user_forwardings = _result
|
||||
else:
|
||||
raise web.seeother('/domains?msg=%s' % web.urlquote(_result))
|
||||
|
||||
# Get user alias addresses
|
||||
(_status, _result) = sql_lib_user.get_bulk_user_alias_addresses(mails=mails, conn=conn)
|
||||
if _status:
|
||||
user_alias_addresses = _result
|
||||
else:
|
||||
raise web.seeother('/domains?msg=%s' % web.urlquote(_result))
|
||||
|
||||
# Get assigned groups
|
||||
(_status, _result) = sql_lib_user.get_bulk_user_assigned_groups(mails=mails, conn=conn)
|
||||
if _status:
|
||||
user_assigned_groups = _result
|
||||
else:
|
||||
raise web.seeother('/domains?msg=%s' % web.urlquote(_result))
|
||||
|
||||
if session.get('is_global_admin'):
|
||||
days_to_keep_removed_mailbox = settings.DAYS_TO_KEEP_REMOVED_MAILBOX_FOR_GLOBAL_ADMIN
|
||||
else:
|
||||
days_to_keep_removed_mailbox = settings.DAYS_TO_KEEP_REMOVED_MAILBOX
|
||||
|
||||
return web.render('sql/user/list.html',
|
||||
cur_domain=domain,
|
||||
cur_page=cur_page,
|
||||
total=total,
|
||||
users=records,
|
||||
user_forwardings=user_forwardings,
|
||||
user_alias_addresses=user_alias_addresses,
|
||||
user_assigned_groups=user_assigned_groups,
|
||||
used_quotas=used_quotas,
|
||||
first_char=first_char,
|
||||
days_to_keep_removed_mailbox=days_to_keep_removed_mailbox,
|
||||
all_are_admins=True,
|
||||
msg=web.input().get('msg', None))
|
||||
|
||||
|
||||
# Preferences allowed to be updated by user
|
||||
class Preferences:
|
||||
@decorators.require_user_login
|
||||
def GET(self, profile_type='general'):
|
||||
form = web.input()
|
||||
mail = session['username']
|
||||
domain = mail.split('@', 1)[-1]
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
qr = sql_lib_user.profile(mail=mail, conn=conn)
|
||||
user_profile = qr[1]
|
||||
del qr
|
||||
|
||||
# Get per-user settings
|
||||
user_settings = {}
|
||||
qr = sql_lib_general.get_user_settings(conn=conn,
|
||||
mail=mail,
|
||||
existing_settings=user_profile['settings'])
|
||||
if qr[0]:
|
||||
user_settings = qr[1]
|
||||
del qr
|
||||
|
||||
# Get used quota
|
||||
used_quota_bytes = 0
|
||||
if settings.SHOW_USED_QUOTA:
|
||||
used_quota = sql_lib_general.get_account_used_quota(accounts=[mail], conn=conn)
|
||||
|
||||
used_quota_bytes = used_quota.get(mail, {}).get('bytes', 0)
|
||||
|
||||
# Get per-domain disabled user preferences.
|
||||
qr = sql_lib_domain.simple_profile(conn=conn,
|
||||
domain=domain,
|
||||
columns=['settings'])
|
||||
|
||||
if qr[0]:
|
||||
domain_profile = qr[1]
|
||||
domain_settings = sqlutils.account_settings_string_to_dict(domain_profile['settings'])
|
||||
|
||||
disabled_user_preferences = domain_settings.get('disabled_user_preferences', [])
|
||||
session['disabled_user_preferences'] = disabled_user_preferences
|
||||
|
||||
db_settings = iredutils.get_settings_from_db()
|
||||
_min_passwd_length = db_settings['min_passwd_length']
|
||||
_max_passwd_length = db_settings['max_passwd_length']
|
||||
|
||||
min_passwd_length = domain_settings.get('min_passwd_length', _min_passwd_length)
|
||||
max_passwd_length = domain_settings.get('max_passwd_length', _max_passwd_length)
|
||||
|
||||
password_policies = iredutils.get_password_policies()
|
||||
if min_passwd_length > 0:
|
||||
password_policies['min_passwd_length'] = min_passwd_length
|
||||
|
||||
if max_passwd_length > 0:
|
||||
password_policies['max_passwd_length'] = max_passwd_length
|
||||
|
||||
return web.render(
|
||||
'sql/self-service/user/preferences.html',
|
||||
cur_domain=domain,
|
||||
mail=mail,
|
||||
profile_type=profile_type,
|
||||
profile=user_profile,
|
||||
user_settings=user_settings,
|
||||
used_quota_bytes=used_quota_bytes,
|
||||
disabled_user_preferences=disabled_user_preferences,
|
||||
languagemaps=iredutils.get_language_maps(),
|
||||
timezones=TIMEZONES,
|
||||
min_passwd_length=min_passwd_length,
|
||||
max_passwd_length=max_passwd_length,
|
||||
store_password_in_plain_text=settings.STORE_PASSWORD_IN_PLAIN_TEXT,
|
||||
password_policies=password_policies,
|
||||
msg=form.get('msg'),
|
||||
)
|
||||
|
||||
@decorators.csrf_protected
|
||||
@decorators.require_user_login
|
||||
def POST(self, profile_type='general'):
|
||||
mail = session['username']
|
||||
|
||||
form = web.input(telephoneNumber=[])
|
||||
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
result = sql_lib_user.update_preferences(conn=conn,
|
||||
mail=mail,
|
||||
form=form,
|
||||
profile_type=profile_type)
|
||||
|
||||
if result[0]:
|
||||
raise web.seeother('/preferences?msg=UPDATED')
|
||||
else:
|
||||
raise web.seeother('/preferences?msg=%s' % web.urlquote(result[1]))
|
||||
|
||||
|
||||
# APIProxyUser proxies requests to RESTful API interface without calling
|
||||
# the exposed `/api/` url.
|
||||
class APIProxyUser:
|
||||
@decorators.require_domain_access
|
||||
def PUT(self, mail):
|
||||
form = web.input()
|
||||
qr = sql_lib_user.api_update_profile(mail=mail, form=form, conn=None)
|
||||
return api_render(qr)
|
||||
|
||||
|
||||
class AllLastLogins:
|
||||
@decorators.require_domain_access
|
||||
def GET(self, domain):
|
||||
domain = domain.lower()
|
||||
last_logins = sql_lib_general.get_all_last_logins(domain=domain, conn=None)
|
||||
|
||||
return web.render(
|
||||
'sql/user/all_last_logins.html',
|
||||
cur_domain=domain,
|
||||
last_logins=last_logins,
|
||||
# msg=msg,
|
||||
)
|
||||
33
controllers/sql/utils.py
Normal file
33
controllers/sql/utils.py
Normal file
@@ -0,0 +1,33 @@
|
||||
import web
|
||||
from controllers.decorators import require_admin_login
|
||||
from libs.sqllib import SQLWrap
|
||||
from libs.sqllib import domain as sql_lib_domain
|
||||
from libs.sqllib import admin as sql_lib_admin
|
||||
|
||||
session = web.config.get('_session')
|
||||
|
||||
|
||||
# Get all domains, select the first one.
|
||||
class CreateDispatcher:
|
||||
@require_admin_login
|
||||
def GET(self, account_type):
|
||||
_wrap = SQLWrap()
|
||||
conn = _wrap.conn
|
||||
|
||||
if session.get('is_global_admin'):
|
||||
qr = sql_lib_domain.get_all_domains(conn=conn, name_only=True)
|
||||
else:
|
||||
qr = sql_lib_admin.get_managed_domains(conn=conn,
|
||||
admin=session.get('username'),
|
||||
domain_name_only=True)
|
||||
|
||||
if qr[0]:
|
||||
all_domains = qr[1]
|
||||
|
||||
# Go to first available domain.
|
||||
if all_domains:
|
||||
raise web.seeother('/create/{}/{}'.format(account_type, all_domains[0]))
|
||||
else:
|
||||
raise web.seeother('/domains?msg=NO_DOMAIN_AVAILABLE')
|
||||
else:
|
||||
raise web.seeother('/domains?msg=' + web.urlquote(qr[1]))
|
||||
Reference in New Issue
Block a user