952 lines
26 KiB
Python
952 lines
26 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
domain_service.py
|
|
"""
|
|
from __future__ import print_function, unicode_literals, absolute_import, division
|
|
|
|
import io
|
|
import time
|
|
import traceback
|
|
from datetime import datetime, timedelta
|
|
|
|
from peewee import chunked, fn
|
|
from playhouse.shortcuts import model_to_dict
|
|
|
|
from domain_admin.enums.role_enum import RoleEnum
|
|
from domain_admin.enums.source_enum import SourceEnum
|
|
from domain_admin.log import logger
|
|
from domain_admin.model import domain_model
|
|
from domain_admin.model.address_model import AddressModel
|
|
from domain_admin.model.domain_info_model import DomainInfoModel
|
|
from domain_admin.model.domain_model import DomainModel
|
|
from domain_admin.model.group_model import GroupModel
|
|
from domain_admin.model.group_user_model import GroupUserModel
|
|
from domain_admin.model.user_model import UserModel
|
|
from domain_admin.service import file_service, async_task_service
|
|
from domain_admin.service import render_service, group_service
|
|
from domain_admin.utils import datetime_util, cert_util, file_util, json_util, time_util
|
|
from domain_admin.utils import domain_util
|
|
from domain_admin.config import SHODAN_API_KEY
|
|
from domain_admin.utils.cert_util import cert_socket_v2, cert_openssl_v2, cert_ftp
|
|
from domain_admin.utils.flask_ext.app_exception import ForbiddenAppException
|
|
from domain_admin.utils.open_api import certspotter_api, shodan_api, crtsh_api
|
|
|
|
|
|
def update_domain_host_list(domain_row):
|
|
"""
|
|
更新ip信息
|
|
:param domain_row: DomainModel
|
|
:return:
|
|
@since v1.2.24
|
|
"""
|
|
domain_host_list = []
|
|
|
|
try:
|
|
domain_host_list = cert_socket_v2.get_domain_host_list(
|
|
domain=domain_row.domain,
|
|
port=domain_row.port
|
|
)
|
|
except Exception as e:
|
|
logger.error(traceback.format_exc())
|
|
|
|
lst = [
|
|
{
|
|
'domain_id': domain_row.id,
|
|
'host': domain_host
|
|
} for domain_host in domain_host_list]
|
|
|
|
logger.info(lst)
|
|
|
|
AddressModel.insert_many(lst).on_conflict_ignore().execute()
|
|
|
|
|
|
def update_domain_address_list_cert(domain_row):
|
|
"""
|
|
更新证书信息
|
|
:param domain_row: DomainModel
|
|
:return:
|
|
"""
|
|
# logger.info("%s", model_to_dict(domain_row))
|
|
|
|
lst = AddressModel.select().where(
|
|
AddressModel.domain_id == domain_row.id
|
|
)
|
|
|
|
err = ''
|
|
for address_row in lst:
|
|
err = update_address_row_info_wrap(address_row, domain_row)
|
|
|
|
sync_address_info_to_domain_info(domain_row)
|
|
return err
|
|
|
|
|
|
def update_address_row_info_wrap(address_row, domain_row):
|
|
"""
|
|
更新单个地址信息 的代理方法 增加重试次数
|
|
:param address_row:
|
|
:param domain_row:
|
|
:return: error
|
|
"""
|
|
# 最大重试次数
|
|
MAX_RETRY_COUNT = 3
|
|
retry_count = 0
|
|
err = ''
|
|
|
|
while True:
|
|
retry_count += 1
|
|
logger.info("retry_count: %s", retry_count)
|
|
|
|
err = update_address_row_info(address_row, domain_row)
|
|
|
|
if not err or retry_count >= MAX_RETRY_COUNT:
|
|
break
|
|
|
|
if _is_unreachable_handshake_error(err):
|
|
break
|
|
|
|
# sleep
|
|
time.sleep(0.5)
|
|
|
|
return err
|
|
|
|
|
|
def _is_unreachable_handshake_error(err_msg):
|
|
if not err_msg:
|
|
return False
|
|
|
|
err_msg = err_msg.lower()
|
|
|
|
if 'domain not verified' in err_msg:
|
|
return False
|
|
|
|
unreachable_keywords = [
|
|
'timed out',
|
|
'timeout',
|
|
'name or service not known',
|
|
'temporary failure in name resolution',
|
|
'nodename nor servname provided',
|
|
'connection refused',
|
|
'connection reset',
|
|
'network is unreachable',
|
|
'no route to host',
|
|
'host unreachable',
|
|
'errno 111',
|
|
'errno 113',
|
|
'errno -2',
|
|
]
|
|
|
|
return any(keyword in err_msg for keyword in unreachable_keywords)
|
|
|
|
|
|
|
|
def _get_latest_certspotter_cert_info(domain):
|
|
if domain_util.is_ipv4(domain):
|
|
return {}
|
|
|
|
try:
|
|
rows = certspotter_api.search(domain)
|
|
except Exception:
|
|
logger.error(traceback.format_exc())
|
|
return {}
|
|
|
|
if not rows:
|
|
return {}
|
|
|
|
candidates = []
|
|
|
|
for row in rows:
|
|
names = [item.strip() for item in row.get('dns_names', []) if item and item.strip()]
|
|
|
|
matched = False
|
|
for name in names:
|
|
if domain_util.verify_cert_common_name(name, domain):
|
|
matched = True
|
|
break
|
|
|
|
if not matched:
|
|
continue
|
|
|
|
not_before = row.get('not_before')
|
|
not_after = row.get('not_after')
|
|
if not not_before or not not_after:
|
|
continue
|
|
|
|
try:
|
|
start_date = time_util.parse_time(not_before)
|
|
expire_date = time_util.parse_time(not_after)
|
|
|
|
entry_timestamp = row.get('entry_timestamp')
|
|
if entry_timestamp:
|
|
entry_time = time_util.parse_time(entry_timestamp)
|
|
else:
|
|
entry_time = expire_date
|
|
|
|
row_id = row.get('id') or 0
|
|
candidates.append({
|
|
'start_date': start_date,
|
|
'expire_date': expire_date,
|
|
'entry_time': entry_time,
|
|
'row_id': int(row_id),
|
|
})
|
|
|
|
except Exception:
|
|
logger.error(traceback.format_exc())
|
|
|
|
if not candidates:
|
|
return {}
|
|
|
|
latest = max(
|
|
candidates,
|
|
key=lambda item: (
|
|
item['entry_time'],
|
|
item['start_date'],
|
|
item['expire_date'],
|
|
item['row_id'],
|
|
)
|
|
)
|
|
|
|
return {
|
|
'start_date': latest['start_date'],
|
|
'expire_date': latest['expire_date'],
|
|
}
|
|
|
|
|
|
|
|
def _get_latest_shodan_cert_info(domain):
|
|
if domain_util.is_ipv4(domain):
|
|
return {}
|
|
|
|
query_domains = [domain]
|
|
root_domain = domain_util.get_root_domain(domain)
|
|
if root_domain and root_domain not in query_domains:
|
|
query_domains.append(root_domain)
|
|
|
|
candidates = []
|
|
|
|
for query_domain in query_domains:
|
|
rows = shodan_api.search_domain_certificates(domain=query_domain, api_key=SHODAN_API_KEY)
|
|
if not rows:
|
|
continue
|
|
|
|
for row in rows:
|
|
subdomain = row.get('subdomain')
|
|
if subdomain and subdomain != '@':
|
|
record_domain = '{subdomain}.{domain}'.format(subdomain=subdomain, domain=query_domain)
|
|
else:
|
|
record_domain = query_domain
|
|
|
|
if not domain_util.verify_cert_common_name(record_domain, domain):
|
|
continue
|
|
|
|
value = row.get('value') or ''
|
|
value = value.strip().lower()
|
|
if len(value) != 40:
|
|
continue
|
|
|
|
cert_detail = shodan_api.get_certificate_by_sha1(value, SHODAN_API_KEY)
|
|
if not cert_detail:
|
|
continue
|
|
|
|
cert_name = cert_detail.get('subject', {}).get('CN')
|
|
if cert_name and not domain_util.verify_cert_common_name(cert_name, domain):
|
|
continue
|
|
|
|
not_before = cert_detail.get('issued') or cert_detail.get('validity', {}).get('start')
|
|
not_after = cert_detail.get('expires') or cert_detail.get('validity', {}).get('end')
|
|
if not not_before or not not_after:
|
|
continue
|
|
|
|
try:
|
|
start_date = time_util.parse_time(not_before)
|
|
expire_date = time_util.parse_time(not_after)
|
|
candidates.append({
|
|
'start_date': start_date,
|
|
'expire_date': expire_date,
|
|
'fingerprint': value,
|
|
})
|
|
except Exception:
|
|
logger.error(traceback.format_exc())
|
|
|
|
if not candidates:
|
|
return {}
|
|
|
|
latest = max(
|
|
candidates,
|
|
key=lambda item: (
|
|
item['start_date'],
|
|
item['expire_date'],
|
|
item['fingerprint'],
|
|
)
|
|
)
|
|
|
|
return {
|
|
'start_date': latest['start_date'],
|
|
'expire_date': latest['expire_date'],
|
|
}
|
|
|
|
|
|
|
|
def _get_latest_fallback_cert_info(domain):
|
|
cert_info = _get_latest_certspotter_cert_info(domain)
|
|
if cert_info:
|
|
return cert_info
|
|
|
|
return _get_latest_shodan_cert_info(domain)
|
|
|
|
|
|
|
|
def update_address_row_info(address_row, domain_row):
|
|
"""
|
|
更新单个地址信息
|
|
:param domain_row:
|
|
:param address_row:
|
|
:return: error
|
|
"""
|
|
|
|
# 获取证书信息
|
|
cert_info = {}
|
|
|
|
err = ''
|
|
try:
|
|
if domain_row.port == 21:
|
|
cert_info = cert_ftp.get_ftp_cert_by_ftplib(
|
|
domain=domain_row.domain,
|
|
host=address_row.host,
|
|
port=domain_row.port,
|
|
)
|
|
|
|
else:
|
|
cert_info = cert_openssl_v2.get_ssl_cert_by_openssl(
|
|
domain=domain_row.domain,
|
|
host=address_row.host,
|
|
port=domain_row.port,
|
|
ssl_type=domain_row.ssl_type
|
|
)
|
|
except Exception as e:
|
|
err = e.__str__()
|
|
logger.error(traceback.format_exc())
|
|
|
|
fallback_applied = False
|
|
|
|
if not cert_info and _is_unreachable_handshake_error(err):
|
|
cert_info = _get_latest_fallback_cert_info(domain_row.domain)
|
|
fallback_applied = True
|
|
|
|
logger.info(cert_info)
|
|
|
|
if fallback_applied and not cert_info:
|
|
err = ''
|
|
|
|
update_time = datetime_util.get_datetime()
|
|
update_data = {
|
|
'update_time': update_time,
|
|
}
|
|
|
|
if cert_info.get('expire_date'):
|
|
address = AddressModel()
|
|
address.ssl_start_time = cert_info.get('start_date')
|
|
address.ssl_expire_time = cert_info.get('expire_date')
|
|
|
|
update_data.update({
|
|
'ssl_start_time': address.ssl_start_time,
|
|
'ssl_expire_time': address.ssl_expire_time,
|
|
'ssl_expire_days': address.real_time_ssl_expire_days,
|
|
})
|
|
|
|
AddressModel.update(
|
|
**update_data
|
|
).where(
|
|
AddressModel.id == address_row.id
|
|
).execute()
|
|
|
|
if fallback_applied and cert_info.get('expire_date'):
|
|
AddressModel.update(
|
|
ssl_start_time=update_data.get('ssl_start_time'),
|
|
ssl_expire_time=update_data.get('ssl_expire_time'),
|
|
ssl_expire_days=update_data.get('ssl_expire_days'),
|
|
update_time=update_time,
|
|
).where(
|
|
AddressModel.domain_id == address_row.domain_id,
|
|
AddressModel.id != address_row.id,
|
|
AddressModel.ssl_expire_time.is_null(False),
|
|
AddressModel.ssl_expire_time < update_data.get('ssl_expire_time')
|
|
).execute()
|
|
|
|
return err
|
|
|
|
|
|
def update_address_row_info_with_sync_domain_row(address_id):
|
|
"""
|
|
更新主机信息并同步到与名表
|
|
:param address_id: int
|
|
:return:
|
|
"""
|
|
address_row = AddressModel.get_by_id(address_id)
|
|
|
|
domain_row = DomainModel.get_by_id(address_row.domain_id)
|
|
|
|
update_address_row_info_wrap(address_row, domain_row)
|
|
|
|
sync_address_info_to_domain_info(domain_row)
|
|
|
|
|
|
def sync_address_info_to_domain_info(domain_row):
|
|
"""
|
|
同步主机信息到域名信息表
|
|
:param domain_row: DomainModel
|
|
:return:
|
|
"""
|
|
first_address_row = AddressModel.select().where(
|
|
AddressModel.domain_id == domain_row.id,
|
|
AddressModel.ssl_expire_time.is_null(False)
|
|
).order_by(
|
|
AddressModel.ssl_expire_days.asc()
|
|
).first()
|
|
|
|
update_data = {
|
|
'connect_status': False,
|
|
'update_time': datetime_util.get_datetime(),
|
|
'version': DomainModel.version + 1
|
|
}
|
|
|
|
if first_address_row is not None:
|
|
if first_address_row.real_time_ssl_expire_days > 0:
|
|
update_data['connect_status'] = True
|
|
|
|
update_data.update({
|
|
'start_time': first_address_row.ssl_start_time,
|
|
'expire_time': first_address_row.ssl_expire_time,
|
|
'expire_days': first_address_row.real_time_ssl_expire_days,
|
|
})
|
|
|
|
DomainModel.update(
|
|
**update_data
|
|
).where(
|
|
DomainModel.id == domain_row.id
|
|
).execute()
|
|
|
|
|
|
def update_domain_row(domain_row):
|
|
"""
|
|
更新域名相关数据
|
|
:param domain_row: DomainModel
|
|
:return:
|
|
"""
|
|
# fix old data update root domain
|
|
if not domain_row.root_domain:
|
|
DomainModel.update(
|
|
root_domain=domain_util.get_root_domain(domain_row.domain)
|
|
).where(
|
|
DomainModel.id == domain_row.id
|
|
).execute()
|
|
|
|
# 动态主机ip,需要先删除所有主机地址
|
|
if domain_row.is_dynamic_host:
|
|
pass
|
|
|
|
# 移除动态主机行为,都清空自动添加的数据再获取
|
|
AddressModel.delete().where(
|
|
AddressModel.domain_id == domain_row.id,
|
|
AddressModel.source == SourceEnum.AUTO
|
|
).execute()
|
|
|
|
# 主机ip信息
|
|
update_domain_host_list(domain_row)
|
|
|
|
# 证书信息
|
|
update_domain_address_list_cert(domain_row)
|
|
|
|
|
|
def get_cert_info(domain):
|
|
"""
|
|
:param domain: str
|
|
:return:
|
|
"""
|
|
now = datetime.now()
|
|
info = {}
|
|
expire_days = 0
|
|
total_days = 0
|
|
connect_status = True
|
|
|
|
try:
|
|
info = cert_util.get_cert_info(domain)
|
|
|
|
except Exception:
|
|
logger.error(traceback.format_exc())
|
|
connect_status = False
|
|
|
|
start_date = info.get('start_date')
|
|
expire_date = info.get('expire_date')
|
|
|
|
if start_date and expire_date:
|
|
start_time = datetime_util.parse_datetime(start_date)
|
|
expire_time = datetime_util.parse_datetime(expire_date)
|
|
|
|
expire_days = (expire_time - now).days
|
|
total_days = (expire_time - start_time).days
|
|
|
|
return {
|
|
'start_date': start_date,
|
|
'expire_date': expire_date,
|
|
'expire_days': expire_days,
|
|
'total_days': total_days,
|
|
'connect_status': connect_status,
|
|
# 'ip': info.get('ip', ''),
|
|
'info': info,
|
|
}
|
|
|
|
|
|
def update_all_domain_cert_info():
|
|
"""
|
|
更新所有域名信息
|
|
:return:
|
|
"""
|
|
rows = DomainModel.select().where(
|
|
DomainModel.auto_update == True
|
|
).order_by(DomainModel.expire_days.asc())
|
|
|
|
for row in rows:
|
|
try:
|
|
update_domain_row(row)
|
|
except Exception as e:
|
|
logger.error(traceback.format_exc())
|
|
|
|
|
|
@async_task_service.async_task_decorator("更新证书信息")
|
|
def update_all_domain_cert_info_of_user(user_id):
|
|
"""
|
|
更新用户的所有证书信息
|
|
:return:
|
|
"""
|
|
rows = DomainModel.select().where(
|
|
DomainModel.user_id == user_id,
|
|
DomainModel.auto_update == True
|
|
)
|
|
|
|
for row in rows:
|
|
update_domain_row(row)
|
|
|
|
# key = f'update_domain_status:{user_id}'
|
|
# global_data_service.set_value(key, False)
|
|
|
|
|
|
def get_domain_info_list(user_id=None):
|
|
query = DomainModel.select()
|
|
|
|
user_row = UserModel.get_by_id(user_id)
|
|
|
|
query = query.where(
|
|
DomainModel.user_id == user_id,
|
|
DomainModel.is_monitor == True,
|
|
DomainModel.expire_days < user_row.before_expire_days
|
|
)
|
|
|
|
query = query.order_by(
|
|
DomainModel.expire_days.asc(),
|
|
DomainModel.id.desc()
|
|
)
|
|
|
|
lst = list(map(lambda m: model_to_dict(
|
|
model=m,
|
|
# exclude=[DomainModel.detail_raw],
|
|
extra_attrs=[
|
|
'start_date',
|
|
'expire_date',
|
|
# 'real_time_domain_expire_days',
|
|
'real_time_expire_days',
|
|
# 'expire_days',
|
|
]
|
|
), query))
|
|
|
|
# def compare(a, b):
|
|
# if a['expire_days'] and b['expire_days']:
|
|
# return a['expire_days'] - b['expire_days']
|
|
# else:
|
|
# if a['expire_days']:
|
|
# return a['expire_days']
|
|
# else:
|
|
# return -b['expire_days']
|
|
|
|
# lst = sorted(lst, key=cmp_to_key(compare))
|
|
|
|
return lst
|
|
|
|
|
|
def check_permission_and_get_row(domain_id, user_id):
|
|
"""
|
|
权限检查
|
|
:param domain_id:
|
|
:param user_id:
|
|
:return:
|
|
"""
|
|
row = DomainModel.get_by_id(domain_id)
|
|
if row.user_id != user_id:
|
|
raise ForbiddenAppException()
|
|
|
|
return row
|
|
|
|
|
|
@async_task_service.async_task_decorator("自动批量导入子域名证书")
|
|
def auto_import_from_domain_batch_async(rows, user_id):
|
|
# type: (list[DomainInfoModel], int)->None
|
|
"""
|
|
自动批量导入子域名证书
|
|
:param rows:
|
|
:param user_id:
|
|
:return:
|
|
"""
|
|
for row in rows:
|
|
auto_import_from_domain(root_domain=row.domain, group_id=row.group_id, user_id=user_id)
|
|
|
|
init_domain_cert_info_of_user(user_id=user_id)
|
|
|
|
|
|
@async_task_service.async_task_decorator("自动导入子域名证书")
|
|
def auto_import_from_domain_async(root_domain, group_id=0, user_id=0):
|
|
# type: (str, int, int)->None
|
|
auto_import_from_domain(root_domain=root_domain, group_id=group_id, user_id=user_id)
|
|
|
|
init_domain_cert_info_of_user(user_id=user_id)
|
|
|
|
|
|
def auto_import_from_domain(root_domain, group_id=0, user_id=0):
|
|
"""
|
|
自动导入顶级域名下包含的子域名到证书列表
|
|
:param root_domain: str
|
|
:param group_id: int
|
|
:param user_id: int
|
|
:return:
|
|
"""
|
|
logger.info("domain: %s", root_domain)
|
|
|
|
lst = crtsh_api.search(root_domain)
|
|
|
|
domain_set = set()
|
|
|
|
for domain in lst:
|
|
common_name = domain['common_name']
|
|
|
|
# 过滤空域名
|
|
if not common_name:
|
|
continue
|
|
|
|
# 过滤非主域名下子域
|
|
if not common_name.endswith(root_domain):
|
|
continue
|
|
|
|
# 过滤邮箱数据
|
|
if '@' in common_name:
|
|
continue
|
|
|
|
# 识别通配符 *.music.163.com -> music.163.com
|
|
if common_name.startswith('*.'):
|
|
common_name = common_name[2:]
|
|
|
|
domain_set.add(common_name)
|
|
|
|
data = [
|
|
{
|
|
'domain': domain,
|
|
'root_domain': root_domain,
|
|
'port': 443,
|
|
'alias': '',
|
|
'user_id': user_id,
|
|
'group_id': group_id,
|
|
} for domain in domain_set
|
|
]
|
|
|
|
for batch in chunked(data, 500):
|
|
DomainModel.insert_many(batch).on_conflict_ignore().execute()
|
|
|
|
|
|
def add_domain_from_file(filename, user_id):
|
|
logger.info('user_id: %s, filename: %s', user_id, filename)
|
|
|
|
lst = list(domain_util.parse_domain_from_file(filename, domain_model.FIELD_MAPPING))
|
|
|
|
# 导入分组
|
|
group_name_list = [item.get('group_name') for item in lst if item.get('group_name')]
|
|
if group_name_list:
|
|
group_map = group_service.get_or_create_group_map(group_name_list, user_id)
|
|
else:
|
|
group_map = {}
|
|
|
|
items = [
|
|
{
|
|
'domain': item['domain'],
|
|
'root_domain': domain_util.get_root_domain(item['domain']),
|
|
'port': item.get('port'),
|
|
'alias': item.get('alias') or '',
|
|
'user_id': user_id,
|
|
'group_id': group_map.get(item.get('group_name'), 0),
|
|
} for item in lst
|
|
]
|
|
|
|
# print(json_util.json_dump(items))
|
|
|
|
# fix: peewee.OperationalError: too many SQL variables
|
|
# https://github.com/mouday/domain-admin/issues/63
|
|
for batch in chunked(items, 500):
|
|
DomainModel.insert_many(batch).on_conflict_ignore().execute()
|
|
|
|
|
|
def export_domain_to_file(rows, ext):
|
|
"""
|
|
导出域名到文件
|
|
:param rows:
|
|
:return:
|
|
"""
|
|
|
|
# content = render_service.render_template('cert-export.csv', {'list': rows})
|
|
|
|
filename = datetime.now().strftime("cert_%Y%m%d%H%M%S") + '.' + ext
|
|
temp_filename = file_service.resolve_temp_file(filename)
|
|
|
|
if ext == 'txt':
|
|
lst = [row['domain'] for row in rows]
|
|
else:
|
|
lst = file_util.convert_to_export(rows, domain_model.FIELD_MAPPING)
|
|
|
|
file_util.write_data_to_file(temp_filename, lst)
|
|
|
|
# temp_filename = file_service.resolve_temp_file(filename)
|
|
# # print(temp_filename)
|
|
# with io.open(temp_filename, 'w', encoding='utf-8') as f:
|
|
# f.write(content)
|
|
|
|
return filename
|
|
|
|
|
|
def load_domain_expire_days(lst):
|
|
"""
|
|
加载域名过期时间字段 Number or None
|
|
:param lst: List[DomainModel dict]
|
|
:return:
|
|
"""
|
|
|
|
root_domains = [row['root_domain'] for row in lst]
|
|
|
|
domain_info_rows = DomainInfoModel.select().where(
|
|
DomainInfoModel.domain.in_(root_domains)
|
|
)
|
|
|
|
domain_info_map = {
|
|
row.domain: row.real_domain_expire_days
|
|
for row in domain_info_rows
|
|
}
|
|
|
|
for row in lst:
|
|
row['domain_expire_days'] = domain_info_map.get(row['root_domain'])
|
|
|
|
return lst
|
|
|
|
|
|
def load_address_count(lst):
|
|
"""
|
|
加载主机数量字段
|
|
:param lst: List
|
|
:return:
|
|
"""
|
|
row_ids = [row['id'] for row in lst]
|
|
|
|
# 主机数量
|
|
address_groups = AddressModel.select(
|
|
AddressModel.domain_id,
|
|
fn.COUNT(AddressModel.id).alias('count')
|
|
).where(
|
|
AddressModel.domain_id.in_(row_ids)
|
|
).group_by(AddressModel.domain_id)
|
|
|
|
address_group_map = {
|
|
str(row.domain_id): row.count
|
|
for row in address_groups
|
|
}
|
|
|
|
for row in lst:
|
|
row['address_count'] = address_group_map.get(str(row['id']), 0)
|
|
|
|
return lst
|
|
|
|
|
|
def get_domain_list_query(keyword, group_id, group_ids, expire_days, user_id, role):
|
|
user_group_ids = None
|
|
|
|
if role == RoleEnum.ADMIN:
|
|
pass
|
|
|
|
else:
|
|
# 所在分组
|
|
group_user_rows = GroupUserModel.select().where(
|
|
GroupUserModel.user_id == user_id
|
|
)
|
|
|
|
group_user_list = list(group_user_rows)
|
|
user_group_ids = [row.group_id for row in group_user_list]
|
|
|
|
query = DomainModel.select()
|
|
|
|
if isinstance(group_id, int):
|
|
query = query.where(DomainModel.group_id == group_id)
|
|
|
|
if keyword:
|
|
query = query.where(DomainModel.domain.contains(keyword))
|
|
|
|
if group_ids:
|
|
query = query.where(DomainModel.group_id.in_(group_ids))
|
|
else:
|
|
if role == RoleEnum.ADMIN:
|
|
pass
|
|
|
|
elif user_group_ids:
|
|
query = query.where(
|
|
(DomainModel.user_id == user_id)
|
|
| (DomainModel.group_id.in_(user_group_ids))
|
|
)
|
|
else:
|
|
query = query.where(DomainModel.user_id == user_id)
|
|
|
|
if expire_days is not None:
|
|
if expire_days[0] is None:
|
|
max_expire_time = datetime.now() + timedelta(days=expire_days[1])
|
|
query = query.where(
|
|
DomainModel.expire_time.is_null(False),
|
|
DomainModel.expire_time <= max_expire_time
|
|
)
|
|
elif expire_days[1] is None:
|
|
min_expire_time = datetime.now() + timedelta(days=expire_days[0])
|
|
query = query.where(DomainModel.expire_time >= min_expire_time)
|
|
else:
|
|
min_expire_time = datetime.now() + timedelta(days=expire_days[0])
|
|
max_expire_time = datetime.now() + timedelta(days=expire_days[1])
|
|
|
|
query = query.where(DomainModel.expire_time.between(min_expire_time, max_expire_time))
|
|
|
|
return query
|
|
|
|
|
|
def get_domain_ordering(order_prop='expire_days', order_type='ascending'):
|
|
ordering = []
|
|
|
|
# order by expire_days
|
|
if order_prop == 'expire_days':
|
|
if order_type == 'descending':
|
|
ordering.append(DomainModel.expire_time.desc())
|
|
else:
|
|
ordering.append(DomainModel.expire_time.asc())
|
|
|
|
# order by connect_status
|
|
elif order_prop == 'connect_status':
|
|
if order_type == 'descending':
|
|
ordering.append(DomainModel.connect_status.desc())
|
|
else:
|
|
ordering.append(DomainModel.connect_status.asc())
|
|
|
|
# order by domain
|
|
elif order_prop == 'domain':
|
|
if order_type == 'descending':
|
|
ordering.append(DomainModel.domain.desc())
|
|
else:
|
|
ordering.append(DomainModel.domain.asc())
|
|
|
|
# order by group_id
|
|
elif order_prop == 'group_name':
|
|
if order_type == 'descending':
|
|
ordering.append(DomainModel.group_id.desc())
|
|
else:
|
|
ordering.append(DomainModel.group_id.asc())
|
|
|
|
# order by port
|
|
elif order_prop == 'port':
|
|
if order_type == 'descending':
|
|
ordering.append(DomainModel.port.desc())
|
|
else:
|
|
ordering.append(DomainModel.port.asc())
|
|
|
|
# order by update_time
|
|
elif order_prop == 'update_time':
|
|
if order_type == 'descending':
|
|
ordering.append(DomainModel.update_time.desc())
|
|
else:
|
|
ordering.append(DomainModel.update_time.asc())
|
|
|
|
# order by domain_expire_monitor
|
|
elif order_prop == 'domain_expire_monitor':
|
|
if order_type == 'descending':
|
|
ordering.append(DomainModel.domain_expire_monitor.desc())
|
|
else:
|
|
ordering.append(DomainModel.domain_expire_monitor.asc())
|
|
|
|
# order by auto_update
|
|
elif order_prop == 'auto_update':
|
|
if order_type == 'descending':
|
|
ordering.append(DomainModel.auto_update.desc())
|
|
else:
|
|
ordering.append(DomainModel.auto_update.asc())
|
|
# order by is_monitor
|
|
elif order_prop == 'is_monitor':
|
|
if order_type == 'descending':
|
|
ordering.append(DomainModel.is_monitor.desc())
|
|
else:
|
|
ordering.append(DomainModel.is_monitor.asc())
|
|
|
|
ordering.append(DomainModel.id.desc())
|
|
|
|
return ordering
|
|
|
|
|
|
def init_domain_cert_info_of_user(user_id):
|
|
"""
|
|
初始化证书信息
|
|
:param user_id:
|
|
:return:
|
|
"""
|
|
# 更新插入的证书
|
|
rows = DomainModel.select().where(
|
|
DomainModel.version == 0,
|
|
DomainModel.user_id == user_id,
|
|
)
|
|
|
|
for row in rows:
|
|
update_domain_row(row)
|
|
|
|
|
|
def has_read_permission(domain_row, user_id):
|
|
"""
|
|
读取权限
|
|
"""
|
|
# 1、域名所有人
|
|
if domain_row.user_id == user_id:
|
|
return True
|
|
|
|
# 2、分组成员
|
|
group_user_row = GroupUserModel.select().where(
|
|
GroupUserModel.group_id == domain_row.group_id,
|
|
GroupUserModel.user_id == user_id
|
|
).first()
|
|
|
|
if group_user_row:
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
def has_edit_permission(domain_row, user_id):
|
|
"""
|
|
编辑权限
|
|
"""
|
|
# 1、域名所有人
|
|
if domain_row.user_id == user_id:
|
|
return True
|
|
|
|
# 2、分组成员 并且拥有编辑权限
|
|
group_user_row = GroupUserModel.select().where(
|
|
GroupUserModel.group_id == domain_row.group_id,
|
|
GroupUserModel.user_id == user_id,
|
|
GroupUserModel.has_edit_permission == True
|
|
).first()
|
|
|
|
if group_user_row:
|
|
return True
|
|
|
|
return False
|