mirror of
https://github.com/marcus-alicia/iRedAdmin-Pro-SQL.git
synced 2026-05-26 23:19:45 +00:00
Add files via upload
This commit is contained in:
19
libs/panel/__init__.py
Normal file
19
libs/panel/__init__.py
Normal file
@@ -0,0 +1,19 @@
|
||||
# Events in admin log. Detailed comments of event names are defined in
|
||||
# templates/default/macros/general.html
|
||||
LOG_EVENTS = [
|
||||
'all',
|
||||
'login',
|
||||
'user_login',
|
||||
'active',
|
||||
'disable',
|
||||
'create',
|
||||
'delete',
|
||||
'update',
|
||||
'grant', # Grant user as domain admin
|
||||
'revoke', # Revoke admin privilege
|
||||
'backup',
|
||||
'delete_mailboxes',
|
||||
'update_wblist',
|
||||
'iredapd', # iRedAPD rejection.
|
||||
'unban', # Unban IP address
|
||||
]
|
||||
429
libs/panel/domain_ownership.py
Normal file
429
libs/panel/domain_ownership.py
Normal file
@@ -0,0 +1,429 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
import time
|
||||
|
||||
from dns import resolver
|
||||
import requests
|
||||
import web
|
||||
|
||||
import settings
|
||||
from libs import iredutils
|
||||
|
||||
if settings.backend == 'ldap':
|
||||
from libs.ldaplib.admin import get_managed_domains
|
||||
else:
|
||||
from libs.sqllib.admin import get_managed_domains
|
||||
|
||||
session = web.config.get('_session', {})
|
||||
|
||||
|
||||
def is_pending_domain(domain, conn=None):
|
||||
if not iredutils.is_domain(domain):
|
||||
return True
|
||||
|
||||
if not conn:
|
||||
conn = web.conn_iredadmin
|
||||
|
||||
try:
|
||||
qr = conn.select('domain_ownership',
|
||||
vars={'domain': domain},
|
||||
where='(domain=$domain OR alias_domain=$domain) AND verified=0',
|
||||
limit=1)
|
||||
if qr:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except:
|
||||
return True
|
||||
|
||||
|
||||
def get_pending_domains(domains=None,
|
||||
domain_name_only=False,
|
||||
conn=None):
|
||||
"""Query `iredadmin.domain_ownership` to get list of pending domains.
|
||||
|
||||
Return list of domain names."""
|
||||
admin = session.get('username')
|
||||
|
||||
if domains:
|
||||
domains = [str(d).lower() for d in domains if iredutils.is_domain(d)]
|
||||
else:
|
||||
if not session.get('is_global_admin'):
|
||||
# Get managed domains
|
||||
if settings.backend == 'ldap':
|
||||
qr = get_managed_domains(admin=admin, conn=None)
|
||||
else:
|
||||
# settings.backend in ['mysql', 'pgsql']
|
||||
qr = get_managed_domains(admin=admin,
|
||||
domain_name_only=True,
|
||||
listed_only=False)
|
||||
|
||||
if qr[0]:
|
||||
domains = qr[1]
|
||||
|
||||
if not domains:
|
||||
return True, []
|
||||
else:
|
||||
raise web.seeother('/domains?msg=%s' % web.urlquote(qr[1]))
|
||||
|
||||
if not conn:
|
||||
conn = web.conn_iredadmin
|
||||
|
||||
try:
|
||||
if session.get('is_global_admin'):
|
||||
qr = conn.select('domain_ownership',
|
||||
where='verified=0')
|
||||
else:
|
||||
qr = conn.select('domain_ownership',
|
||||
vars={'domains': domains, 'admin': admin},
|
||||
where='admin=$admin AND (domain IN $domains OR alias_domain IN $domains) AND verified=0')
|
||||
|
||||
if domain_name_only:
|
||||
pending_domains = set()
|
||||
for r in qr:
|
||||
if r.alias_domain:
|
||||
pending_domains.add(r.alias_domain)
|
||||
else:
|
||||
pending_domains.add(r.domain)
|
||||
|
||||
pending_domains = [str(i).lower() for i in pending_domains if iredutils.is_domain(i)]
|
||||
pending_domains.sort()
|
||||
return True, pending_domains
|
||||
else:
|
||||
return True, list(qr)
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
|
||||
def get_verified_domains(domains=None, conn=None):
|
||||
"""Query `iredadmin.domain_ownership` to get list of verified domains.
|
||||
|
||||
Return list of domain names."""
|
||||
admin = session.get('username')
|
||||
|
||||
if domains:
|
||||
domains = [str(d).lower() for d in domains if iredutils.is_domain(d)]
|
||||
else:
|
||||
if not session.get('is_global_admin'):
|
||||
# Get managed domains
|
||||
if settings.backend == 'ldap':
|
||||
qr = get_managed_domains(admin=admin, conn=None)
|
||||
else:
|
||||
# settings.backend in ['mysql', 'pgsql']
|
||||
qr = get_managed_domains(admin=admin,
|
||||
domain_name_only=True,
|
||||
listed_only=False)
|
||||
|
||||
if qr[0]:
|
||||
domains = qr[1]
|
||||
else:
|
||||
raise web.seeother('/domains?msg=%s' % web.urlquote(qr[1]))
|
||||
|
||||
if not domains:
|
||||
return True, []
|
||||
|
||||
if not conn:
|
||||
conn = web.conn_iredadmin
|
||||
|
||||
try:
|
||||
if session.get('is_global_admin'):
|
||||
qr = conn.select('domain_ownership',
|
||||
what='domain,alias_domain',
|
||||
where='verified=1')
|
||||
else:
|
||||
qr = conn.select('domain_ownership',
|
||||
vars={'domains': domains, 'admin': admin},
|
||||
what='domain,alias_domain',
|
||||
where='admin=$admin AND (domain IN $domains OR alias_domain IN $domains) AND verified=1')
|
||||
|
||||
verified_domains = []
|
||||
for r in qr:
|
||||
if r.alias_domain:
|
||||
verified_domains += [str(r.alias_domain).lower()]
|
||||
else:
|
||||
verified_domains += [str(r.domain).lower()]
|
||||
|
||||
verified_domains.sort()
|
||||
return True, verified_domains
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
|
||||
def remove_pending_domains(domains=None):
|
||||
"""Remove pending domains.
|
||||
|
||||
:param domains: a list/tuple/set of domain names
|
||||
"""
|
||||
if domains:
|
||||
domains = [str(d).lower() for d in domains if iredutils.is_domain(d)]
|
||||
else:
|
||||
return True,
|
||||
|
||||
conn = web.conn_iredadmin
|
||||
|
||||
try:
|
||||
if session.get('is_global_admin'):
|
||||
conn.delete('domain_ownership',
|
||||
vars={'domains': domains},
|
||||
where='(domain IN $domains OR alias_domain IN $domains) AND verified=0')
|
||||
else:
|
||||
conn.delete('domain_ownership',
|
||||
vars={'domains': domains, 'admin': session.get('username')},
|
||||
where='(domain IN $domains OR alias_domain IN $domains) AND admin=$admin AND verified=0')
|
||||
|
||||
return True,
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
|
||||
def _generate_verify_code():
|
||||
"""Generate a random and unique string as verify code."""
|
||||
s = iredutils.generate_random_strings(20)
|
||||
return settings.DOMAIN_OWNERSHIP_VERIFY_CODE_PREFIX + s
|
||||
|
||||
|
||||
def set_verify_code_for_new_domains(primary_domain, alias_domains=None, conn=None):
|
||||
"""Generate new unique verify codes for mail domains.
|
||||
|
||||
primary_domain -- the primary mail domain name
|
||||
alias_domains -- alias domains of primary domain
|
||||
conn -- sql connection cursor (for `iredadmin` database)
|
||||
"""
|
||||
if not settings.REQUIRE_DOMAIN_OWNERSHIP_VERIFICATION:
|
||||
# Bypass domain verification.
|
||||
return True,
|
||||
|
||||
if not iredutils.is_domain(primary_domain):
|
||||
return False, 'INVALID_DOMAIN_NAME'
|
||||
|
||||
if alias_domains:
|
||||
alias_domains = [str(d).lower() for d in alias_domains if iredutils.is_domain(d)]
|
||||
|
||||
if not conn:
|
||||
conn = web.conn_iredadmin
|
||||
|
||||
if session.get('is_global_admin'):
|
||||
admin = ''
|
||||
else:
|
||||
admin = session.get('username')
|
||||
|
||||
try:
|
||||
expire = int(time.time()) + settings.DOMAIN_OWNERSHIP_EXPIRE_DAYS * 24 * 60 * 60
|
||||
|
||||
if alias_domains:
|
||||
for d in alias_domains:
|
||||
try:
|
||||
conn.insert('domain_ownership',
|
||||
admin=admin,
|
||||
domain=primary_domain,
|
||||
alias_domain=d,
|
||||
verify_code=_generate_verify_code(),
|
||||
expire=expire)
|
||||
except Exception as e:
|
||||
if e.__class__.__name__ != 'IntegrityError':
|
||||
return False, repr(e)
|
||||
else:
|
||||
try:
|
||||
conn.insert('domain_ownership',
|
||||
admin=admin,
|
||||
domain=primary_domain,
|
||||
verify_code=_generate_verify_code(),
|
||||
expire=expire)
|
||||
except Exception as e:
|
||||
if e.__class__.__name__ != 'IntegrityError':
|
||||
return False, repr(e)
|
||||
|
||||
return True,
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
|
||||
def mark_ownership_as_verified(rid=None, domain=None, message=None, conn=None):
|
||||
"""Update `iredadmin.domain_ownership` with `verified=1` and
|
||||
`message=<reason>` (optional).
|
||||
|
||||
@rid -- the value of column `domain_ownership.id`
|
||||
@domain -- domain name of `domain_ownership.domain` or `domain_ownership.alias_domain`
|
||||
@message -- the verify message
|
||||
@conn -- sql connection cursor
|
||||
"""
|
||||
if not (rid or domain):
|
||||
return True,
|
||||
|
||||
if domain:
|
||||
if not iredutils.is_domain:
|
||||
return False, 'INVALID_DOMAIN_NAME'
|
||||
|
||||
if not conn:
|
||||
conn = web.conn_iredadmin
|
||||
|
||||
if not message:
|
||||
message = ''
|
||||
|
||||
# Get value of sql column `domain_ownership.id`
|
||||
if domain:
|
||||
try:
|
||||
qr = conn.select('domain_ownership',
|
||||
vars={'domain': domain},
|
||||
what='id',
|
||||
where="(alias_domain=$domain) OR (domain=$domain AND alias_domain='')",
|
||||
limit=1)
|
||||
if qr:
|
||||
rid = qr[0].id
|
||||
else:
|
||||
return True,
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
try:
|
||||
conn.update('domain_ownership',
|
||||
vars={'id': rid},
|
||||
verified=1,
|
||||
message=message,
|
||||
last_verify=web.sqlliteral('NOW()'),
|
||||
where='id=$id')
|
||||
return True,
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
|
||||
def verify_domain_ownership(domains, conn=None):
|
||||
"""Verify domain ownership for given domain names.
|
||||
|
||||
Returned values:
|
||||
|
||||
(True, [(primary_domain, alias_domain), ...]): if some domains were
|
||||
successfully verified.
|
||||
(False, <reason>): if some error happened while verifying.
|
||||
|
||||
Parameters:
|
||||
|
||||
@domains -- a list/tuple/set of domain names
|
||||
@conn -- sql connection cursor (of 'iredadmin' database)
|
||||
"""
|
||||
domains = [str(d).lower() for d in domains if iredutils.is_domain(d)]
|
||||
if not domains:
|
||||
return True, []
|
||||
|
||||
if not conn:
|
||||
conn = web.conn_iredadmin
|
||||
|
||||
# Get verify code of given domains.
|
||||
if session.get('is_global_admin'):
|
||||
qr = conn.select(
|
||||
'domain_ownership',
|
||||
vars={'domains': domains},
|
||||
where="verified=0 AND ((domain IN $domains AND alias_domain='') OR (alias_domain IN $domains))",
|
||||
)
|
||||
else:
|
||||
qr = conn.select(
|
||||
'domain_ownership',
|
||||
vars={'domains': domains, 'admin': session.get('username')},
|
||||
where="verified=0 AND admin=$admin AND ((domain IN $domains AND alias_domain='') OR (alias_domain IN $domains))",
|
||||
)
|
||||
|
||||
if not qr:
|
||||
return True, []
|
||||
|
||||
verified_domains = []
|
||||
expire = int(time.time()) + settings.DOMAIN_OWNERSHIP_EXPIRE_DAYS * 24 * 60 * 60
|
||||
for r in qr:
|
||||
rid = int(r.id)
|
||||
domain = str(r.domain).lower()
|
||||
alias_domain = str(r.alias_domain).lower()
|
||||
verify_code = str(r.verify_code)
|
||||
|
||||
if iredutils.is_domain(alias_domain):
|
||||
verify_domain = alias_domain
|
||||
else:
|
||||
verify_domain = domain
|
||||
|
||||
# web files
|
||||
_web_file = str(verify_domain + '/' + verify_code)
|
||||
|
||||
_verified = False
|
||||
_verified_reason = ''
|
||||
_verify_result = ''
|
||||
|
||||
# Verify web files
|
||||
for _scheme in ['http', 'https']:
|
||||
url = _scheme + '://' + _web_file
|
||||
|
||||
# settings.HTTP_PROXY
|
||||
_proxies = {}
|
||||
if settings.HTTP_PROXY:
|
||||
_proxies = {
|
||||
'http': settings.HTTP_PROXY,
|
||||
'https': settings.HTTP_PROXY,
|
||||
}
|
||||
|
||||
# MAXFILESIZE, 1024) # maximum file size allowed to download, read, fetch
|
||||
# setopt(c.BUFFERSIZE, 1024) # buffer read size: 1024 bytes
|
||||
# _resp_code == 200:
|
||||
try:
|
||||
with requests.get(url,
|
||||
proxies=_proxies,
|
||||
verify=False, # no SSL certificate verifying
|
||||
timeout=settings.DOMAIN_OWNERSHIP_VERIFY_TIMEOUT,
|
||||
stream=True, # defer downloading the response body
|
||||
) as resp:
|
||||
if resp.status_code == 200:
|
||||
pass
|
||||
elif resp.status_code == 404:
|
||||
_verify_result += '%s:// file not found. ' % _scheme
|
||||
else:
|
||||
_verify_result += '%s://, response code must be 200, but got %d. ' % (_scheme, resp.status_code)
|
||||
continue
|
||||
|
||||
try:
|
||||
if int(r.headers['content-length']) < 1024:
|
||||
_body = r.content.strip()
|
||||
|
||||
if _body == verify_code:
|
||||
_verified = True
|
||||
_verified_reason = '%s matches' % _scheme
|
||||
break
|
||||
else:
|
||||
_verify_result += '{}:// file content too long. '.format(_scheme)
|
||||
continue
|
||||
except Exception as e:
|
||||
_verify_result += '{}:// error while reading file content: {}. '.format(_scheme, repr(e))
|
||||
continue
|
||||
except Exception as e:
|
||||
_verify_result += 'Error while verifying {}://: {}. '.format(_scheme, repr(e))
|
||||
|
||||
# Verify TXT type DNS record
|
||||
if not _verified:
|
||||
try:
|
||||
_res = resolver.Resolver()
|
||||
_res.timeout = settings.DOMAIN_OWNERSHIP_VERIFY_TIMEOUT
|
||||
qr_dns = _res.query(domain, 'TXT')
|
||||
for i in qr_dns:
|
||||
_txt = i.to_text().strip('"')
|
||||
if verify_code == _txt:
|
||||
_verified = True
|
||||
_verified_reason = 'DNS record matches'
|
||||
break
|
||||
|
||||
_verify_result += "Verify code is not found as one of TXT type DNS records."
|
||||
except Exception as e:
|
||||
_verify_result += 'Error while querying DNS: %s.' % repr(e)
|
||||
|
||||
if _verified:
|
||||
verified_domains += [(domain, alias_domain)]
|
||||
|
||||
qr = mark_ownership_as_verified(rid=rid, message=_verified_reason, conn=conn)
|
||||
if not qr[0]:
|
||||
return qr
|
||||
else:
|
||||
# Update last verify time, verify result, and expire time
|
||||
try:
|
||||
conn.update('domain_ownership',
|
||||
message=_verify_result,
|
||||
last_verify=web.sqlliteral('NOW()'),
|
||||
expire=expire,
|
||||
where='id=%d' % rid)
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
return True, verified_domains
|
||||
126
libs/panel/log.py
Normal file
126
libs/panel/log.py
Normal file
@@ -0,0 +1,126 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
import web
|
||||
|
||||
import settings
|
||||
from libs import iredutils
|
||||
from libs.panel import LOG_EVENTS
|
||||
|
||||
if settings.backend == 'ldap':
|
||||
from libs.ldaplib.general import is_domain_admin
|
||||
from libs.ldaplib.admin import get_managed_domains
|
||||
else:
|
||||
from libs.sqllib.general import is_domain_admin
|
||||
from libs.sqllib.admin import get_managed_domains
|
||||
|
||||
session = web.config.get('_session')
|
||||
|
||||
|
||||
def list_logs(event='all', domain='all', admin='all', cur_page=1):
|
||||
event = web.safestr(event)
|
||||
domain = web.safestr(domain)
|
||||
admin = web.safestr(admin)
|
||||
cur_page = int(cur_page)
|
||||
|
||||
sql_vars = {}
|
||||
sql_wheres = []
|
||||
sql_where = ''
|
||||
|
||||
if event not in LOG_EVENTS:
|
||||
event = "all"
|
||||
|
||||
if event != 'all':
|
||||
sql_vars['event'] = event
|
||||
sql_wheres += ["event=$event"]
|
||||
|
||||
if iredutils.is_domain(domain):
|
||||
if session.get('is_global_admin') or is_domain_admin(domain=domain, admin=session['username'], conn=None):
|
||||
sql_vars['domain'] = domain
|
||||
sql_wheres += ["domain=$domain"]
|
||||
else:
|
||||
# Get managed domains.
|
||||
if not session.get("is_global_admin"):
|
||||
if settings.backend == 'ldap':
|
||||
qr = get_managed_domains(admin=session["username"],
|
||||
attributes=None,
|
||||
domain_name_only=True,
|
||||
conn=None)
|
||||
|
||||
else:
|
||||
qr = get_managed_domains(admin=session["username"],
|
||||
domain_name_only=True,
|
||||
listed_only=True,
|
||||
conn=None)
|
||||
if qr[0]:
|
||||
sql_vars["managed_domains"] = qr[1]
|
||||
sql_wheres += ["domain IN $managed_domains"]
|
||||
else:
|
||||
return qr
|
||||
|
||||
if iredutils.is_email(admin):
|
||||
if session.get('is_global_admin'):
|
||||
sql_vars['admin'] = admin
|
||||
sql_wheres += ["admin=$admin"]
|
||||
else:
|
||||
sql_vars['admin'] = session.get('username')
|
||||
sql_wheres += ["admin=$admin"]
|
||||
else:
|
||||
if not session.get('is_global_admin'):
|
||||
sql_vars['admin'] = session.get('username')
|
||||
sql_wheres += ["admin=$admin"]
|
||||
|
||||
# Get number of total records.
|
||||
if sql_wheres:
|
||||
sql_where = ' AND '.join(sql_wheres)
|
||||
|
||||
qr = web.conn_iredadmin.select(
|
||||
'log',
|
||||
vars=sql_vars,
|
||||
what='COUNT(id) AS total',
|
||||
where=sql_where,
|
||||
)
|
||||
else:
|
||||
qr = web.conn_iredadmin.select('log', what='COUNT(id) AS total')
|
||||
|
||||
total = qr[0].total or 0
|
||||
|
||||
# Get records.
|
||||
if sql_wheres:
|
||||
qr = web.conn_iredadmin.select(
|
||||
'log',
|
||||
vars=sql_vars,
|
||||
where=sql_where,
|
||||
offset=(cur_page - 1) * settings.PAGE_SIZE_LIMIT,
|
||||
limit=settings.PAGE_SIZE_LIMIT,
|
||||
order='timestamp DESC',
|
||||
)
|
||||
else:
|
||||
# No addition filter.
|
||||
qr = web.conn_iredadmin.select(
|
||||
'log',
|
||||
offset=(cur_page - 1) * settings.PAGE_SIZE_LIMIT,
|
||||
limit=settings.PAGE_SIZE_LIMIT,
|
||||
order='timestamp DESC',
|
||||
)
|
||||
|
||||
return total, list(qr)
|
||||
|
||||
|
||||
def delete_logs(form, delete_all=False):
|
||||
if delete_all:
|
||||
try:
|
||||
web.conn_iredadmin.delete('log', where="1=1")
|
||||
return True,
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
else:
|
||||
ids = form.get('id', [])
|
||||
|
||||
if ids:
|
||||
try:
|
||||
web.conn_iredadmin.delete('log', where="id IN %s" % web.db.sqlquote(ids))
|
||||
return True,
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
return True,
|
||||
Reference in New Issue
Block a user