2019-12-02 03:32:03 +00:00
|
|
|
import re
|
|
|
|
import json
|
2019-12-18 08:25:20 +00:00
|
|
|
import datetime
|
2019-12-02 03:32:03 +00:00
|
|
|
import traceback
|
2019-12-14 16:13:55 +00:00
|
|
|
import dns.name
|
|
|
|
import dns.reversename
|
2019-12-02 03:32:03 +00:00
|
|
|
from distutils.version import StrictVersion
|
2019-12-18 08:25:20 +00:00
|
|
|
from flask import Blueprint, render_template, make_response, url_for, current_app, request, redirect, abort, jsonify, g, session
|
|
|
|
from flask_login import login_required, current_user, login_manager
|
2019-12-02 03:32:03 +00:00
|
|
|
|
2021-03-16 18:37:05 +00:00
|
|
|
from ..lib.utils import pretty_domain_name
|
2019-12-14 07:47:21 +00:00
|
|
|
from ..lib.utils import pretty_json
|
2021-10-30 19:21:45 +00:00
|
|
|
from ..decorators import can_create_domain, operator_role_required, can_access_domain, can_configure_dnssec, can_remove_domain
|
2019-12-18 08:25:20 +00:00
|
|
|
from ..models.user import User, Anonymous
|
2019-12-02 03:32:03 +00:00
|
|
|
from ..models.account import Account
|
|
|
|
from ..models.setting import Setting
|
|
|
|
from ..models.history import History
|
|
|
|
from ..models.domain import Domain
|
|
|
|
from ..models.record import Record
|
|
|
|
from ..models.record_entry import RecordEntry
|
|
|
|
from ..models.domain_template import DomainTemplate
|
|
|
|
from ..models.domain_template_record import DomainTemplateRecord
|
|
|
|
from ..models.domain_setting import DomainSetting
|
2021-10-30 19:21:45 +00:00
|
|
|
from ..models.base import db
|
|
|
|
from ..models.domain_user import DomainUser
|
|
|
|
from ..models.account_user import AccountUser
|
2021-11-30 09:02:37 +00:00
|
|
|
from .admin import extract_changelogs_from_a_history_entry
|
|
|
|
from ..decorators import history_access_required
|
2019-12-02 03:32:03 +00:00
|
|
|
domain_bp = Blueprint('domain',
|
|
|
|
__name__,
|
|
|
|
template_folder='templates',
|
|
|
|
url_prefix='/domain')
|
|
|
|
|
|
|
|
|
2019-12-18 08:25:20 +00:00
|
|
|
@domain_bp.before_request
|
|
|
|
def before_request():
|
|
|
|
# Check if user is anonymous
|
|
|
|
g.user = current_user
|
|
|
|
login_manager.anonymous_user = Anonymous
|
|
|
|
|
|
|
|
# Check site is in maintenance mode
|
|
|
|
maintenance = Setting().get('maintenance')
|
|
|
|
if maintenance and current_user.is_authenticated and current_user.role.name not in [
|
|
|
|
'Administrator', 'Operator'
|
|
|
|
]:
|
|
|
|
return render_template('maintenance.html')
|
|
|
|
|
|
|
|
# Manage session timeout
|
|
|
|
session.permanent = True
|
|
|
|
current_app.permanent_session_lifetime = datetime.timedelta(
|
|
|
|
minutes=int(Setting().get('session_timeout')))
|
|
|
|
session.modified = True
|
|
|
|
|
|
|
|
|
2019-12-02 03:32:03 +00:00
|
|
|
@domain_bp.route('/<path:domain_name>', methods=['GET'])
|
|
|
|
@login_required
|
|
|
|
@can_access_domain
|
|
|
|
def domain(domain_name):
|
2019-12-14 07:47:21 +00:00
|
|
|
# Validate the domain existing in the local DB
|
2019-12-02 03:32:03 +00:00
|
|
|
domain = Domain.query.filter(Domain.name == domain_name).first()
|
|
|
|
if not domain:
|
|
|
|
abort(404)
|
|
|
|
|
2019-12-14 07:47:21 +00:00
|
|
|
# Query domain's rrsets from PowerDNS API
|
|
|
|
rrsets = Record().get_rrsets(domain.name)
|
2022-05-18 22:53:35 +00:00
|
|
|
current_app.logger.debug("Fetched rrsets: \n{}".format(pretty_json(rrsets)))
|
2019-12-14 07:47:21 +00:00
|
|
|
|
|
|
|
# API server might be down, misconfigured
|
2020-05-02 02:41:01 +00:00
|
|
|
if not rrsets and domain.type != 'Slave':
|
2019-12-02 03:32:03 +00:00
|
|
|
abort(500)
|
|
|
|
|
|
|
|
quick_edit = Setting().get('record_quick_edit')
|
|
|
|
records_allow_to_edit = Setting().get_records_allow_to_edit()
|
|
|
|
forward_records_allow_to_edit = Setting(
|
|
|
|
).get_forward_records_allow_to_edit()
|
|
|
|
reverse_records_allow_to_edit = Setting(
|
|
|
|
).get_reverse_records_allow_to_edit()
|
|
|
|
ttl_options = Setting().get_ttl_options()
|
|
|
|
records = []
|
|
|
|
|
2019-12-14 07:47:21 +00:00
|
|
|
# Render the "records" to display in HTML datatable
|
|
|
|
#
|
|
|
|
# BUG: If we have multiple records with the same name
|
|
|
|
# and each record has its own comment, the display of
|
|
|
|
# [record-comment] may not consistent because PDNS API
|
|
|
|
# returns the rrsets (records, comments) has different
|
|
|
|
# order than its database records.
|
|
|
|
# TODO:
|
|
|
|
# - Find a way to make it consistent, or
|
|
|
|
# - Only allow one comment for that case
|
2019-12-02 03:32:03 +00:00
|
|
|
if StrictVersion(Setting().get('pdns_version')) >= StrictVersion('4.0.0'):
|
2019-12-14 07:47:21 +00:00
|
|
|
for r in rrsets:
|
|
|
|
if r['type'] in records_allow_to_edit:
|
2019-12-14 16:13:55 +00:00
|
|
|
r_name = r['name'].rstrip('.')
|
|
|
|
|
|
|
|
# If it is reverse zone and pretty_ipv6_ptr setting
|
|
|
|
# is enabled, we reformat the name for ipv6 records.
|
|
|
|
if Setting().get('pretty_ipv6_ptr') and r[
|
2020-09-09 18:18:07 +00:00
|
|
|
'type'] == 'PTR' and 'ip6.arpa' in r_name and '*' not in r_name:
|
2019-12-14 16:13:55 +00:00
|
|
|
r_name = dns.reversename.to_address(
|
|
|
|
dns.name.from_text(r_name))
|
|
|
|
|
|
|
|
# Create the list of records in format that
|
|
|
|
# PDA jinja2 template can understand.
|
2019-12-14 07:47:21 +00:00
|
|
|
index = 0
|
|
|
|
for record in r['records']:
|
2020-05-20 04:21:28 +00:00
|
|
|
if (len(r['comments'])>index):
|
|
|
|
c=r['comments'][index]['content']
|
|
|
|
else:
|
|
|
|
c=''
|
2019-12-14 07:47:21 +00:00
|
|
|
record_entry = RecordEntry(
|
2019-12-14 16:13:55 +00:00
|
|
|
name=r_name,
|
2019-12-14 07:47:21 +00:00
|
|
|
type=r['type'],
|
|
|
|
status='Disabled' if record['disabled'] else 'Active',
|
|
|
|
ttl=r['ttl'],
|
|
|
|
data=record['content'],
|
2020-05-20 04:21:28 +00:00
|
|
|
comment=c,
|
2019-12-14 07:47:21 +00:00
|
|
|
is_allowed_edit=True)
|
|
|
|
index += 1
|
|
|
|
records.append(record_entry)
|
2019-12-02 03:32:03 +00:00
|
|
|
else:
|
2019-12-14 07:47:21 +00:00
|
|
|
# Unsupported version
|
|
|
|
abort(500)
|
|
|
|
|
2019-12-14 16:13:55 +00:00
|
|
|
if not re.search(r'ip6\.arpa|in-addr\.arpa$', domain_name):
|
2019-12-02 03:32:03 +00:00
|
|
|
editable_records = forward_records_allow_to_edit
|
|
|
|
else:
|
|
|
|
editable_records = reverse_records_allow_to_edit
|
2019-12-14 07:47:21 +00:00
|
|
|
|
2019-12-02 03:32:03 +00:00
|
|
|
return render_template('domain.html',
|
|
|
|
domain=domain,
|
|
|
|
records=records,
|
|
|
|
editable_records=editable_records,
|
|
|
|
quick_edit=quick_edit,
|
2021-11-30 09:02:37 +00:00
|
|
|
ttl_options=ttl_options,
|
|
|
|
current_user=current_user)
|
2019-12-02 03:32:03 +00:00
|
|
|
|
|
|
|
|
2021-10-30 19:21:45 +00:00
|
|
|
@domain_bp.route('/remove', methods=['GET', 'POST'])
|
|
|
|
@login_required
|
|
|
|
@can_remove_domain
|
|
|
|
def remove():
|
|
|
|
# domains is a list of all the domains a User may access
|
|
|
|
# Admins may access all
|
|
|
|
# Regular users only if they are associated with the domain
|
|
|
|
if current_user.role.name in ['Administrator', 'Operator']:
|
|
|
|
domains = Domain.query.order_by(Domain.name).all()
|
|
|
|
else:
|
|
|
|
# Get query for domain to which the user has access permission.
|
|
|
|
# This includes direct domain permission AND permission through
|
|
|
|
# account membership
|
|
|
|
domains = db.session.query(Domain) \
|
|
|
|
.outerjoin(DomainUser, Domain.id == DomainUser.domain_id) \
|
|
|
|
.outerjoin(Account, Domain.account_id == Account.id) \
|
|
|
|
.outerjoin(AccountUser, Account.id == AccountUser.account_id) \
|
|
|
|
.filter(
|
|
|
|
db.or_(
|
|
|
|
DomainUser.user_id == current_user.id,
|
|
|
|
AccountUser.user_id == current_user.id
|
|
|
|
)).order_by(Domain.name)
|
|
|
|
|
|
|
|
if request.method == 'POST':
|
|
|
|
# TODO Change name from 'domainid' to something else, its confusing
|
|
|
|
domain_name = request.form['domainid']
|
|
|
|
|
|
|
|
# Get domain from Database, might be None
|
|
|
|
domain = Domain.query.filter(Domain.name == domain_name).first()
|
|
|
|
|
|
|
|
# Check if the domain is in domains before removal
|
|
|
|
if domain not in domains:
|
|
|
|
abort(403)
|
|
|
|
|
|
|
|
# Delete
|
|
|
|
d = Domain()
|
|
|
|
result = d.delete(domain_name)
|
|
|
|
|
|
|
|
if result['status'] == 'error':
|
|
|
|
abort(500)
|
|
|
|
|
|
|
|
history = History(msg='Delete domain {0}'.format(
|
|
|
|
pretty_domain_name(domain_name)),
|
|
|
|
created_by=current_user.username)
|
|
|
|
history.add()
|
|
|
|
|
|
|
|
return redirect(url_for('dashboard.dashboard'))
|
|
|
|
|
|
|
|
else:
|
|
|
|
# On GET return the domains we got earlier
|
|
|
|
return render_template('domain_remove.html',
|
|
|
|
domainss=domains)
|
|
|
|
|
2021-11-30 09:02:37 +00:00
|
|
|
@domain_bp.route('/<path:domain_name>/changelog', methods=['GET'])
|
|
|
|
@login_required
|
|
|
|
@can_access_domain
|
|
|
|
@history_access_required
|
|
|
|
def changelog(domain_name):
|
|
|
|
g.user = current_user
|
|
|
|
login_manager.anonymous_user = Anonymous
|
|
|
|
domain = Domain.query.filter(Domain.name == domain_name).first()
|
|
|
|
if not domain:
|
|
|
|
abort(404)
|
|
|
|
|
|
|
|
# Query domain's rrsets from PowerDNS API
|
|
|
|
rrsets = Record().get_rrsets(domain.name)
|
2022-05-18 22:53:35 +00:00
|
|
|
current_app.logger.debug("Fetched rrsets: \n{}".format(pretty_json(rrsets)))
|
2021-11-30 09:02:37 +00:00
|
|
|
|
|
|
|
# API server might be down, misconfigured
|
|
|
|
if not rrsets and domain.type != 'Slave':
|
|
|
|
abort(500)
|
|
|
|
|
|
|
|
records_allow_to_edit = Setting().get_records_allow_to_edit()
|
|
|
|
records = []
|
|
|
|
|
|
|
|
# get all changelogs for this domain, in descening order
|
|
|
|
if current_user.role.name in [ 'Administrator', 'Operator' ]:
|
|
|
|
histories = History.query.filter(History.domain_id == domain.id).order_by(History.created_on.desc()).all()
|
|
|
|
else:
|
|
|
|
# if the user isn't an administrator or operator,
|
|
|
|
# allow_user_view_history must be enabled to get here,
|
|
|
|
# so include history for the domains for the user
|
|
|
|
histories = db.session.query(History) \
|
|
|
|
.join(Domain, History.domain_id == Domain.id) \
|
|
|
|
.outerjoin(DomainUser, Domain.id == DomainUser.domain_id) \
|
|
|
|
.outerjoin(Account, Domain.account_id == Account.id) \
|
|
|
|
.outerjoin(AccountUser, Account.id == AccountUser.account_id) \
|
|
|
|
.order_by(History.created_on.desc()) \
|
|
|
|
.filter(
|
|
|
|
db.and_(db.or_(
|
|
|
|
DomainUser.user_id == current_user.id,
|
|
|
|
AccountUser.user_id == current_user.id
|
|
|
|
),
|
|
|
|
History.domain_id == domain.id
|
|
|
|
)
|
|
|
|
).all()
|
|
|
|
|
|
|
|
if StrictVersion(Setting().get('pdns_version')) >= StrictVersion('4.0.0'):
|
|
|
|
for r in rrsets:
|
|
|
|
if r['type'] in records_allow_to_edit:
|
|
|
|
r_name = r['name'].rstrip('.')
|
|
|
|
|
|
|
|
# If it is reverse zone and pretty_ipv6_ptr setting
|
|
|
|
# is enabled, we reformat the name for ipv6 records.
|
|
|
|
if Setting().get('pretty_ipv6_ptr') and r[
|
|
|
|
'type'] == 'PTR' and 'ip6.arpa' in r_name and '*' not in r_name:
|
|
|
|
r_name = dns.reversename.to_address(
|
|
|
|
dns.name.from_text(r_name))
|
|
|
|
|
|
|
|
# Create the list of records in format that
|
|
|
|
# PDA jinja2 template can understand.
|
|
|
|
index = 0
|
|
|
|
for record in r['records']:
|
|
|
|
if (len(r['comments'])>index):
|
|
|
|
c=r['comments'][index]['content']
|
|
|
|
else:
|
|
|
|
c=''
|
|
|
|
record_entry = RecordEntry(
|
|
|
|
name=r_name,
|
|
|
|
type=r['type'],
|
|
|
|
status='Disabled' if record['disabled'] else 'Active',
|
|
|
|
ttl=r['ttl'],
|
|
|
|
data=record['content'],
|
|
|
|
comment=c,
|
|
|
|
is_allowed_edit=True)
|
|
|
|
index += 1
|
|
|
|
records.append(record_entry)
|
|
|
|
else:
|
|
|
|
# Unsupported version
|
|
|
|
abort(500)
|
|
|
|
|
|
|
|
changes_set = dict()
|
|
|
|
for i in range(len(histories)):
|
|
|
|
extract_changelogs_from_a_history_entry(changes_set, histories[i], i)
|
|
|
|
if i in changes_set and len(changes_set[i]) == 0: # if empty, then remove the key
|
|
|
|
changes_set.pop(i)
|
|
|
|
return render_template('domain_changelog.html', domain=domain, allHistoryChanges=changes_set)
|
|
|
|
|
|
|
|
"""
|
|
|
|
Returns a changelog for a specific pair of (record_name, record_type)
|
|
|
|
"""
|
|
|
|
@domain_bp.route('/<path:domain_name>/changelog/<path:record_name>-<path:record_type>', methods=['GET'])
|
|
|
|
@login_required
|
|
|
|
@can_access_domain
|
|
|
|
@history_access_required
|
|
|
|
def record_changelog(domain_name, record_name, record_type):
|
|
|
|
|
|
|
|
g.user = current_user
|
|
|
|
login_manager.anonymous_user = Anonymous
|
|
|
|
domain = Domain.query.filter(Domain.name == domain_name).first()
|
|
|
|
if not domain:
|
|
|
|
abort(404)
|
|
|
|
# Query domain's rrsets from PowerDNS API
|
|
|
|
rrsets = Record().get_rrsets(domain.name)
|
2022-05-18 22:53:35 +00:00
|
|
|
current_app.logger.debug("Fetched rrsets: \n{}".format(pretty_json(rrsets)))
|
2021-11-30 09:02:37 +00:00
|
|
|
|
|
|
|
# API server might be down, misconfigured
|
|
|
|
if not rrsets and domain.type != 'Slave':
|
|
|
|
abort(500)
|
|
|
|
|
|
|
|
# get all changelogs for this domain, in descening order
|
|
|
|
if current_user.role.name in [ 'Administrator', 'Operator' ]:
|
|
|
|
histories = History.query.filter(History.domain_id == domain.id).order_by(History.created_on.desc()).all()
|
|
|
|
else:
|
|
|
|
# if the user isn't an administrator or operator,
|
|
|
|
# allow_user_view_history must be enabled to get here,
|
|
|
|
# so include history for the domains for the user
|
|
|
|
histories = db.session.query(History) \
|
|
|
|
.join(Domain, History.domain_id == Domain.id) \
|
|
|
|
.outerjoin(DomainUser, Domain.id == DomainUser.domain_id) \
|
|
|
|
.outerjoin(Account, Domain.account_id == Account.id) \
|
|
|
|
.outerjoin(AccountUser, Account.id == AccountUser.account_id) \
|
|
|
|
.order_by(History.created_on.desc()) \
|
|
|
|
.filter(
|
|
|
|
db.and_(db.or_(
|
|
|
|
DomainUser.user_id == current_user.id,
|
|
|
|
AccountUser.user_id == current_user.id
|
|
|
|
),
|
|
|
|
History.domain_id == domain.id
|
|
|
|
)
|
|
|
|
).all()
|
|
|
|
|
|
|
|
changes_set_of_record = dict()
|
|
|
|
for i in range(len(histories)):
|
|
|
|
extract_changelogs_from_a_history_entry(changes_set_of_record, histories[i], i, record_name, record_type)
|
|
|
|
if i in changes_set_of_record and len(changes_set_of_record[i]) == 0: # if empty, then remove the key
|
|
|
|
changes_set_of_record.pop(i)
|
|
|
|
|
|
|
|
indexes_to_pop = []
|
|
|
|
for change_num in changes_set_of_record:
|
|
|
|
changes_i = changes_set_of_record[change_num]
|
|
|
|
for hre in changes_i: # for each history record entry in changes_i
|
2022-05-18 22:53:35 +00:00
|
|
|
if 'type' in hre.add_rrset and hre.add_rrset['name'] == record_name and hre.add_rrset['type'] == record_type:
|
2021-11-30 09:02:37 +00:00
|
|
|
continue
|
2022-05-18 22:53:35 +00:00
|
|
|
elif 'type' in hre.del_rrset and hre.del_rrset['name'] == record_name and hre.del_rrset['type'] == record_type:
|
2021-11-30 09:02:37 +00:00
|
|
|
continue
|
|
|
|
else:
|
|
|
|
changes_set_of_record[change_num].remove(hre)
|
|
|
|
if change_num in changes_set_of_record and len(changes_set_of_record[change_num]) == 0: # if empty, then remove the key
|
|
|
|
indexes_to_pop.append(change_num)
|
|
|
|
|
|
|
|
for i in indexes_to_pop:
|
|
|
|
changes_set_of_record.pop(i)
|
|
|
|
|
2021-12-03 14:17:39 +00:00
|
|
|
return render_template('domain_changelog.html', domain=domain, allHistoryChanges=changes_set_of_record,
|
|
|
|
record_name = record_name, record_type = record_type)
|
2021-11-30 09:02:37 +00:00
|
|
|
|
|
|
|
|
2021-10-30 19:21:45 +00:00
|
|
|
|
2019-12-02 03:32:03 +00:00
|
|
|
@domain_bp.route('/add', methods=['GET', 'POST'])
|
|
|
|
@login_required
|
|
|
|
@can_create_domain
|
|
|
|
def add():
|
|
|
|
templates = DomainTemplate.query.all()
|
|
|
|
if request.method == 'POST':
|
|
|
|
try:
|
|
|
|
domain_name = request.form.getlist('domain_name')[0]
|
|
|
|
domain_type = request.form.getlist('radio_type')[0]
|
|
|
|
domain_template = request.form.getlist('domain_template')[0]
|
|
|
|
soa_edit_api = request.form.getlist('radio_type_soa_edit_api')[0]
|
|
|
|
account_id = request.form.getlist('accountid')[0]
|
|
|
|
|
|
|
|
if ' ' in domain_name or not domain_name or not domain_type:
|
2019-12-09 10:50:48 +00:00
|
|
|
return render_template(
|
|
|
|
'errors/400.html',
|
|
|
|
msg="Please enter a valid domain name"), 400
|
2019-12-02 03:32:03 +00:00
|
|
|
|
2021-08-05 17:42:58 +00:00
|
|
|
# If User creates the domain, check some additional stuff
|
|
|
|
if current_user.role.name not in ['Administrator', 'Operator']:
|
|
|
|
# Get all the account_ids of the user
|
|
|
|
user_accounts_ids = current_user.get_accounts()
|
|
|
|
user_accounts_ids = [x.id for x in user_accounts_ids]
|
|
|
|
# User may not create domains without Account
|
|
|
|
if int(account_id) == 0 or int(account_id) not in user_accounts_ids:
|
|
|
|
return render_template(
|
|
|
|
'errors/400.html',
|
|
|
|
msg="Please use a valid Account"), 400
|
|
|
|
|
|
|
|
|
2019-12-11 09:02:36 +00:00
|
|
|
#TODO: Validate ip addresses input
|
2021-03-16 18:37:05 +00:00
|
|
|
|
|
|
|
# Encode domain name into punycode (IDN)
|
|
|
|
try:
|
|
|
|
domain_name = domain_name.encode('idna').decode()
|
|
|
|
except:
|
|
|
|
current_app.logger.error("Cannot encode the domain name {}".format(domain_name))
|
|
|
|
current_app.logger.debug(traceback.format_exc())
|
|
|
|
return render_template(
|
|
|
|
'errors/400.html',
|
|
|
|
msg="Please enter a valid domain name"), 400
|
|
|
|
|
2019-12-02 03:32:03 +00:00
|
|
|
if domain_type == 'slave':
|
|
|
|
if request.form.getlist('domain_master_address'):
|
|
|
|
domain_master_string = request.form.getlist(
|
|
|
|
'domain_master_address')[0]
|
|
|
|
domain_master_string = domain_master_string.replace(
|
|
|
|
' ', '')
|
|
|
|
domain_master_ips = domain_master_string.split(',')
|
|
|
|
else:
|
|
|
|
domain_master_ips = []
|
|
|
|
|
|
|
|
account_name = Account().get_name_by_id(account_id)
|
|
|
|
|
|
|
|
d = Domain()
|
|
|
|
result = d.add(domain_name=domain_name,
|
|
|
|
domain_type=domain_type,
|
|
|
|
soa_edit_api=soa_edit_api,
|
|
|
|
domain_master_ips=domain_master_ips,
|
|
|
|
account_name=account_name)
|
|
|
|
if result['status'] == 'ok':
|
2021-03-27 18:33:11 +00:00
|
|
|
domain_id = Domain().get_id_by_name(domain_name)
|
2021-03-16 18:37:05 +00:00
|
|
|
history = History(msg='Add domain {0}'.format(
|
|
|
|
pretty_domain_name(domain_name)),
|
2022-04-26 07:11:05 +00:00
|
|
|
detail = json.dumps({
|
2019-12-02 03:32:03 +00:00
|
|
|
'domain_type': domain_type,
|
|
|
|
'domain_master_ips': domain_master_ips,
|
|
|
|
'account_id': account_id
|
2022-04-26 07:11:05 +00:00
|
|
|
}),
|
2021-03-27 18:33:11 +00:00
|
|
|
created_by=current_user.username,
|
|
|
|
domain_id=domain_id)
|
2019-12-02 03:32:03 +00:00
|
|
|
history.add()
|
|
|
|
|
|
|
|
# grant user access to the domain
|
2019-12-09 10:50:48 +00:00
|
|
|
Domain(name=domain_name).grant_privileges([current_user.id])
|
2019-12-02 03:32:03 +00:00
|
|
|
|
|
|
|
# apply template if needed
|
|
|
|
if domain_template != '0':
|
|
|
|
template = DomainTemplate.query.filter(
|
|
|
|
DomainTemplate.id == domain_template).first()
|
|
|
|
template_records = DomainTemplateRecord.query.filter(
|
|
|
|
DomainTemplateRecord.template_id ==
|
|
|
|
domain_template).all()
|
|
|
|
record_data = []
|
|
|
|
for template_record in template_records:
|
|
|
|
record_row = {
|
|
|
|
'record_data': template_record.data,
|
|
|
|
'record_name': template_record.name,
|
2019-12-16 07:23:17 +00:00
|
|
|
'record_status': 'Active' if template_record.status else 'Disabled',
|
2019-12-02 03:32:03 +00:00
|
|
|
'record_ttl': template_record.ttl,
|
2019-12-09 10:50:48 +00:00
|
|
|
'record_type': template_record.type,
|
|
|
|
'comment_data': [{'content': template_record.comment, 'account': ''}]
|
2019-12-02 03:32:03 +00:00
|
|
|
}
|
|
|
|
record_data.append(record_row)
|
|
|
|
r = Record()
|
|
|
|
result = r.apply(domain_name, record_data)
|
|
|
|
if result['status'] == 'ok':
|
|
|
|
history = History(
|
2019-12-19 14:43:53 +00:00
|
|
|
msg='Applying template {0} to {1} successfully.'.
|
|
|
|
format(template.name, domain_name),
|
2022-04-26 07:11:05 +00:00
|
|
|
detail = json.dumps({
|
|
|
|
'domain':
|
2019-12-19 14:43:53 +00:00
|
|
|
domain_name,
|
2022-04-26 07:11:05 +00:00
|
|
|
'template':
|
2019-12-19 14:43:53 +00:00
|
|
|
template.name,
|
2022-05-18 22:53:35 +00:00
|
|
|
'add_rrsets':
|
2019-12-19 14:43:53 +00:00
|
|
|
result['data'][0]['rrsets'],
|
2022-05-18 22:53:35 +00:00
|
|
|
'del_rrsets':
|
2019-12-19 14:43:53 +00:00
|
|
|
result['data'][1]['rrsets']
|
2022-04-26 07:11:05 +00:00
|
|
|
}),
|
2021-03-27 18:33:11 +00:00
|
|
|
created_by=current_user.username,
|
|
|
|
domain_id=domain_id)
|
2019-12-02 03:32:03 +00:00
|
|
|
history.add()
|
|
|
|
else:
|
|
|
|
history = History(
|
|
|
|
msg=
|
2019-12-19 14:43:53 +00:00
|
|
|
'Failed to apply template {0} to {1}.'
|
2019-12-02 03:32:03 +00:00
|
|
|
.format(template.name, domain_name),
|
2022-04-26 07:11:05 +00:00
|
|
|
detail = json.dumps(result),
|
2019-12-02 03:32:03 +00:00
|
|
|
created_by=current_user.username)
|
|
|
|
history.add()
|
|
|
|
return redirect(url_for('dashboard.dashboard'))
|
|
|
|
else:
|
|
|
|
return render_template('errors/400.html',
|
|
|
|
msg=result['msg']), 400
|
|
|
|
except Exception as e:
|
|
|
|
current_app.logger.error('Cannot add domain. Error: {0}'.format(e))
|
|
|
|
current_app.logger.debug(traceback.format_exc())
|
|
|
|
abort(500)
|
|
|
|
|
2021-08-05 17:42:58 +00:00
|
|
|
# Get
|
2019-12-02 03:32:03 +00:00
|
|
|
else:
|
2021-08-05 17:42:58 +00:00
|
|
|
# Admins and Operators can set to any account
|
|
|
|
if current_user.role.name in ['Administrator', 'Operator']:
|
|
|
|
accounts = Account.query.order_by(Account.name).all()
|
|
|
|
else:
|
|
|
|
accounts = current_user.get_accounts()
|
2019-12-02 03:32:03 +00:00
|
|
|
return render_template('domain_add.html',
|
|
|
|
templates=templates,
|
|
|
|
accounts=accounts)
|
|
|
|
|
|
|
|
|
2021-08-05 17:42:58 +00:00
|
|
|
|
2019-12-02 03:32:03 +00:00
|
|
|
@domain_bp.route('/setting/<path:domain_name>/delete', methods=['POST'])
|
|
|
|
@login_required
|
|
|
|
@operator_role_required
|
|
|
|
def delete(domain_name):
|
|
|
|
d = Domain()
|
|
|
|
result = d.delete(domain_name)
|
|
|
|
|
|
|
|
if result['status'] == 'error':
|
|
|
|
abort(500)
|
|
|
|
|
2021-03-16 18:37:05 +00:00
|
|
|
history = History(msg='Delete domain {0}'.format(
|
|
|
|
pretty_domain_name(domain_name)),
|
2019-12-02 03:32:03 +00:00
|
|
|
created_by=current_user.username)
|
|
|
|
history.add()
|
|
|
|
|
|
|
|
return redirect(url_for('dashboard.dashboard'))
|
|
|
|
|
|
|
|
|
|
|
|
@domain_bp.route('/setting/<path:domain_name>/manage', methods=['GET', 'POST'])
|
|
|
|
@login_required
|
|
|
|
@operator_role_required
|
|
|
|
def setting(domain_name):
|
|
|
|
if request.method == 'GET':
|
|
|
|
domain = Domain.query.filter(Domain.name == domain_name).first()
|
|
|
|
if not domain:
|
|
|
|
abort(404)
|
|
|
|
users = User.query.all()
|
2020-06-27 23:59:12 +00:00
|
|
|
accounts = Account.query.order_by(Account.name).all()
|
2019-12-02 03:32:03 +00:00
|
|
|
|
|
|
|
# get list of user ids to initialize selection data
|
|
|
|
d = Domain(name=domain_name)
|
|
|
|
domain_user_ids = d.get_user()
|
|
|
|
account = d.get_account()
|
|
|
|
|
|
|
|
return render_template('domain_setting.html',
|
|
|
|
domain=domain,
|
|
|
|
users=users,
|
|
|
|
domain_user_ids=domain_user_ids,
|
|
|
|
accounts=accounts,
|
|
|
|
domain_account=account)
|
|
|
|
|
|
|
|
if request.method == 'POST':
|
|
|
|
# username in right column
|
|
|
|
new_user_list = request.form.getlist('domain_multi_user[]')
|
|
|
|
new_user_ids = [
|
|
|
|
user.id for user in User.query.filter(
|
|
|
|
User.username.in_(new_user_list)).all() if user
|
|
|
|
]
|
|
|
|
|
|
|
|
# grant/revoke user privileges
|
|
|
|
d = Domain(name=domain_name)
|
|
|
|
d.grant_privileges(new_user_ids)
|
|
|
|
|
|
|
|
history = History(
|
2021-03-16 18:37:05 +00:00
|
|
|
msg='Change domain {0} access control'.format(
|
|
|
|
pretty_domain_name(domain_name)),
|
2022-05-06 08:17:03 +00:00
|
|
|
detail=json.dumps({'user_has_access': new_user_list}),
|
2021-03-27 18:33:11 +00:00
|
|
|
created_by=current_user.username,
|
|
|
|
domain_id=d.id)
|
2019-12-02 03:32:03 +00:00
|
|
|
history.add()
|
|
|
|
|
|
|
|
return redirect(url_for('domain.setting', domain_name=domain_name))
|
|
|
|
|
|
|
|
|
2019-12-11 09:02:36 +00:00
|
|
|
@domain_bp.route('/setting/<path:domain_name>/change_type',
|
|
|
|
methods=['POST'])
|
|
|
|
@login_required
|
|
|
|
@operator_role_required
|
|
|
|
def change_type(domain_name):
|
|
|
|
domain = Domain.query.filter(Domain.name == domain_name).first()
|
|
|
|
if not domain:
|
|
|
|
abort(404)
|
|
|
|
domain_type = request.form.get('domain_type')
|
|
|
|
if domain_type is None:
|
|
|
|
abort(500)
|
|
|
|
if domain_type == '0':
|
|
|
|
return redirect(url_for('domain.setting', domain_name=domain_name))
|
|
|
|
|
|
|
|
#TODO: Validate ip addresses input
|
|
|
|
domain_master_ips = []
|
|
|
|
if domain_type == 'slave' and request.form.getlist('domain_master_address'):
|
2019-12-14 07:47:21 +00:00
|
|
|
domain_master_string = request.form.getlist(
|
|
|
|
'domain_master_address')[0]
|
|
|
|
domain_master_string = domain_master_string.replace(
|
|
|
|
' ', '')
|
|
|
|
domain_master_ips = domain_master_string.split(',')
|
2019-12-11 09:02:36 +00:00
|
|
|
|
|
|
|
d = Domain()
|
|
|
|
status = d.update_kind(domain_name=domain_name,
|
|
|
|
kind=domain_type,
|
|
|
|
masters=domain_master_ips)
|
|
|
|
if status['status'] == 'ok':
|
2021-03-16 18:37:05 +00:00
|
|
|
history = History(msg='Update type for domain {0}'.format(
|
|
|
|
pretty_domain_name(domain_name)),
|
2022-05-06 08:17:03 +00:00
|
|
|
detail=json.dumps({
|
2019-12-11 09:02:36 +00:00
|
|
|
"domain": domain_name,
|
|
|
|
"type": domain_type,
|
|
|
|
"masters": domain_master_ips
|
|
|
|
}),
|
2021-03-27 18:33:11 +00:00
|
|
|
created_by=current_user.username,
|
|
|
|
domain_id=Domain().get_id_by_name(domain_name))
|
2019-12-11 09:02:36 +00:00
|
|
|
history.add()
|
|
|
|
return redirect(url_for('domain.setting', domain_name = domain_name))
|
|
|
|
else:
|
|
|
|
abort(500)
|
|
|
|
|
|
|
|
|
2019-12-02 03:32:03 +00:00
|
|
|
@domain_bp.route('/setting/<path:domain_name>/change_soa_setting',
|
|
|
|
methods=['POST'])
|
|
|
|
@login_required
|
|
|
|
@operator_role_required
|
|
|
|
def change_soa_edit_api(domain_name):
|
|
|
|
domain = Domain.query.filter(Domain.name == domain_name).first()
|
|
|
|
if not domain:
|
|
|
|
abort(404)
|
|
|
|
new_setting = request.form.get('soa_edit_api')
|
|
|
|
if new_setting is None:
|
|
|
|
abort(500)
|
|
|
|
if new_setting == '0':
|
|
|
|
return redirect(url_for('domain.setting', domain_name=domain_name))
|
|
|
|
|
|
|
|
d = Domain()
|
|
|
|
status = d.update_soa_setting(domain_name=domain_name,
|
|
|
|
soa_edit_api=new_setting)
|
2019-12-11 09:02:36 +00:00
|
|
|
if status['status'] == 'ok':
|
|
|
|
history = History(
|
2021-03-16 18:37:05 +00:00
|
|
|
msg='Update soa_edit_api for domain {0}'.format(
|
|
|
|
pretty_domain_name(domain_name)),
|
2022-04-26 07:11:05 +00:00
|
|
|
detail = json.dumps({
|
|
|
|
'domain': domain_name,
|
|
|
|
'soa_edit_api': new_setting
|
|
|
|
}),
|
2021-03-27 18:33:11 +00:00
|
|
|
created_by=current_user.username,
|
|
|
|
domain_id=d.get_id_by_name(domain_name))
|
2019-12-11 09:02:36 +00:00
|
|
|
history.add()
|
|
|
|
return redirect(url_for('domain.setting', domain_name = domain_name))
|
2019-12-02 03:32:03 +00:00
|
|
|
else:
|
|
|
|
abort(500)
|
|
|
|
|
|
|
|
|
|
|
|
@domain_bp.route('/setting/<path:domain_name>/change_account',
|
|
|
|
methods=['POST'])
|
|
|
|
@login_required
|
|
|
|
@operator_role_required
|
|
|
|
def change_account(domain_name):
|
|
|
|
domain = Domain.query.filter(Domain.name == domain_name).first()
|
|
|
|
if not domain:
|
|
|
|
abort(404)
|
|
|
|
|
|
|
|
account_id = request.form.get('accountid')
|
|
|
|
status = Domain(name=domain.name).assoc_account(account_id)
|
|
|
|
if status['status']:
|
|
|
|
return redirect(url_for('domain.setting', domain_name=domain.name))
|
|
|
|
else:
|
|
|
|
abort(500)
|
|
|
|
|
|
|
|
|
|
|
|
@domain_bp.route('/<path:domain_name>/apply',
|
|
|
|
methods=['POST'],
|
|
|
|
strict_slashes=False)
|
|
|
|
@login_required
|
|
|
|
@can_access_domain
|
|
|
|
def record_apply(domain_name):
|
|
|
|
try:
|
|
|
|
jdata = request.json
|
|
|
|
submitted_serial = jdata['serial']
|
|
|
|
submitted_record = jdata['record']
|
|
|
|
domain = Domain.query.filter(Domain.name == domain_name).first()
|
|
|
|
|
|
|
|
if domain:
|
2019-12-14 07:47:21 +00:00
|
|
|
current_app.logger.debug('Current domain serial: {0}'.format(
|
|
|
|
domain.serial))
|
|
|
|
|
2019-12-02 03:32:03 +00:00
|
|
|
if int(submitted_serial) != domain.serial:
|
|
|
|
return make_response(
|
|
|
|
jsonify({
|
|
|
|
'status':
|
|
|
|
'error',
|
|
|
|
'msg':
|
|
|
|
'The zone has been changed by another session or user. Please refresh this web page to load updated records.'
|
|
|
|
}), 500)
|
|
|
|
else:
|
|
|
|
return make_response(
|
|
|
|
jsonify({
|
|
|
|
'status':
|
|
|
|
'error',
|
|
|
|
'msg':
|
2021-03-16 18:37:05 +00:00
|
|
|
'Domain name {0} does not exist'.format(pretty_domain_name(domain_name))
|
2019-12-02 03:32:03 +00:00
|
|
|
}), 404)
|
|
|
|
|
|
|
|
r = Record()
|
|
|
|
result = r.apply(domain_name, submitted_record)
|
|
|
|
if result['status'] == 'ok':
|
|
|
|
history = History(
|
2021-03-16 18:37:05 +00:00
|
|
|
msg='Apply record changes to domain {0}'.format(pretty_domain_name(domain_name)),
|
2022-04-26 07:11:05 +00:00
|
|
|
detail = json.dumps({
|
|
|
|
'domain': domain_name,
|
2022-05-18 22:53:35 +00:00
|
|
|
'add_rrsets': result['data'][0]['rrsets'],
|
|
|
|
'del_rrsets': result['data'][1]['rrsets']
|
2022-04-26 07:11:05 +00:00
|
|
|
}),
|
2021-03-27 18:33:11 +00:00
|
|
|
created_by=current_user.username,
|
|
|
|
domain_id=domain.id)
|
2019-12-02 03:32:03 +00:00
|
|
|
history.add()
|
|
|
|
return make_response(jsonify(result), 200)
|
|
|
|
else:
|
2020-06-20 02:51:30 +00:00
|
|
|
history = History(
|
2021-03-16 18:37:05 +00:00
|
|
|
msg='Failed to apply record changes to domain {0}'.format(
|
|
|
|
pretty_domain_name(domain_name)),
|
2022-04-26 07:11:05 +00:00
|
|
|
detail = json.dumps({
|
|
|
|
'domain': domain_name,
|
|
|
|
'msg': result['msg'],
|
|
|
|
}),
|
2020-06-20 02:51:30 +00:00
|
|
|
created_by=current_user.username)
|
|
|
|
history.add()
|
2019-12-02 03:32:03 +00:00
|
|
|
return make_response(jsonify(result), 400)
|
|
|
|
except Exception as e:
|
|
|
|
current_app.logger.error(
|
|
|
|
'Cannot apply record changes. Error: {0}'.format(e))
|
|
|
|
current_app.logger.debug(traceback.format_exc())
|
|
|
|
return make_response(
|
|
|
|
jsonify({
|
|
|
|
'status': 'error',
|
|
|
|
'msg': 'Error when applying new changes'
|
|
|
|
}), 500)
|
|
|
|
|
|
|
|
|
|
|
|
@domain_bp.route('/<path:domain_name>/update',
|
|
|
|
methods=['POST'],
|
|
|
|
strict_slashes=False)
|
|
|
|
@login_required
|
|
|
|
@can_access_domain
|
|
|
|
def record_update(domain_name):
|
|
|
|
"""
|
|
|
|
This route is used for domain work as Slave Zone only
|
|
|
|
Pulling the records update from its Master
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
jdata = request.json
|
|
|
|
|
|
|
|
domain_name = jdata['domain']
|
|
|
|
d = Domain()
|
|
|
|
result = d.update_from_master(domain_name)
|
|
|
|
if result['status'] == 'ok':
|
|
|
|
return make_response(
|
|
|
|
jsonify({
|
|
|
|
'status': 'ok',
|
|
|
|
'msg': result['msg']
|
|
|
|
}), 200)
|
|
|
|
else:
|
|
|
|
return make_response(
|
|
|
|
jsonify({
|
|
|
|
'status': 'error',
|
|
|
|
'msg': result['msg']
|
|
|
|
}), 500)
|
|
|
|
except Exception as e:
|
|
|
|
current_app.logger.error('Cannot update record. Error: {0}'.format(e))
|
|
|
|
current_app.logger.debug(traceback.format_exc())
|
|
|
|
return make_response(
|
|
|
|
jsonify({
|
|
|
|
'status': 'error',
|
|
|
|
'msg': 'Error when applying new changes'
|
|
|
|
}), 500)
|
|
|
|
|
|
|
|
|
|
|
|
@domain_bp.route('/<path:domain_name>/info', methods=['GET'])
|
|
|
|
@login_required
|
|
|
|
@can_access_domain
|
|
|
|
def info(domain_name):
|
|
|
|
domain = Domain()
|
|
|
|
domain_info = domain.get_domain_info(domain_name)
|
|
|
|
return make_response(jsonify(domain_info), 200)
|
|
|
|
|
|
|
|
|
|
|
|
@domain_bp.route('/<path:domain_name>/dnssec', methods=['GET'])
|
|
|
|
@login_required
|
|
|
|
@can_access_domain
|
|
|
|
def dnssec(domain_name):
|
|
|
|
domain = Domain()
|
|
|
|
dnssec = domain.get_domain_dnssec(domain_name)
|
|
|
|
return make_response(jsonify(dnssec), 200)
|
|
|
|
|
|
|
|
|
|
|
|
@domain_bp.route('/<path:domain_name>/dnssec/enable', methods=['POST'])
|
|
|
|
@login_required
|
|
|
|
@can_access_domain
|
|
|
|
@can_configure_dnssec
|
|
|
|
def dnssec_enable(domain_name):
|
|
|
|
domain = Domain()
|
|
|
|
dnssec = domain.enable_domain_dnssec(domain_name)
|
2022-02-17 15:40:48 +00:00
|
|
|
domain_object = Domain.query.filter(domain_name == Domain.name).first()
|
|
|
|
history = History(
|
|
|
|
msg='DNSSEC was enabled for domain ' + domain_name ,
|
|
|
|
created_by=current_user.username,
|
|
|
|
domain_id=domain_object.id)
|
|
|
|
history.add()
|
2019-12-02 03:32:03 +00:00
|
|
|
return make_response(jsonify(dnssec), 200)
|
|
|
|
|
|
|
|
|
|
|
|
@domain_bp.route('/<path:domain_name>/dnssec/disable', methods=['POST'])
|
|
|
|
@login_required
|
|
|
|
@can_access_domain
|
|
|
|
@can_configure_dnssec
|
|
|
|
def dnssec_disable(domain_name):
|
|
|
|
domain = Domain()
|
|
|
|
dnssec = domain.get_domain_dnssec(domain_name)
|
|
|
|
|
|
|
|
for key in dnssec['dnssec']:
|
|
|
|
domain.delete_dnssec_key(domain_name, key['id'])
|
2022-02-17 15:40:48 +00:00
|
|
|
domain_object = Domain.query.filter(domain_name == Domain.name).first()
|
|
|
|
history = History(
|
|
|
|
msg='DNSSEC was disabled for domain ' + domain_name ,
|
|
|
|
created_by=current_user.username,
|
|
|
|
domain_id=domain_object.id)
|
|
|
|
history.add()
|
2019-12-02 03:32:03 +00:00
|
|
|
return make_response(jsonify({'status': 'ok', 'msg': 'DNSSEC removed.'}))
|
|
|
|
|
|
|
|
|
|
|
|
@domain_bp.route('/<path:domain_name>/manage-setting', methods=['GET', 'POST'])
|
|
|
|
@login_required
|
|
|
|
@operator_role_required
|
|
|
|
def admin_setdomainsetting(domain_name):
|
|
|
|
if request.method == 'POST':
|
|
|
|
#
|
|
|
|
# post data should in format
|
|
|
|
# {'action': 'set_setting', 'setting': 'default_action, 'value': 'True'}
|
|
|
|
#
|
|
|
|
try:
|
|
|
|
jdata = request.json
|
|
|
|
data = jdata['data']
|
|
|
|
|
|
|
|
if jdata['action'] == 'set_setting':
|
|
|
|
new_setting = data['setting']
|
|
|
|
new_value = str(data['value'])
|
|
|
|
domain = Domain.query.filter(
|
|
|
|
Domain.name == domain_name).first()
|
|
|
|
setting = DomainSetting.query.filter(
|
|
|
|
DomainSetting.domain == domain).filter(
|
|
|
|
DomainSetting.setting == new_setting).first()
|
|
|
|
|
|
|
|
if setting:
|
|
|
|
if setting.set(new_value):
|
|
|
|
history = History(
|
|
|
|
msg='Setting {0} changed value to {1} for {2}'.
|
2021-03-27 18:33:11 +00:00
|
|
|
format(new_setting, new_value,
|
|
|
|
pretty_domain_name(domain_name)),
|
|
|
|
created_by=current_user.username,
|
|
|
|
domain_id=domain.id)
|
2019-12-02 03:32:03 +00:00
|
|
|
history.add()
|
|
|
|
return make_response(
|
|
|
|
jsonify({
|
|
|
|
'status': 'ok',
|
|
|
|
'msg': 'Setting updated.'
|
|
|
|
}))
|
|
|
|
else:
|
|
|
|
return make_response(
|
|
|
|
jsonify({
|
|
|
|
'status': 'error',
|
|
|
|
'msg': 'Unable to set value of setting.'
|
|
|
|
}))
|
|
|
|
else:
|
|
|
|
if domain.add_setting(new_setting, new_value):
|
|
|
|
history = History(
|
|
|
|
msg=
|
|
|
|
'New setting {0} with value {1} for {2} has been created'
|
2021-03-16 18:37:05 +00:00
|
|
|
.format(new_setting, new_value, pretty_domain_name(domain_name)),
|
2021-03-27 18:33:11 +00:00
|
|
|
created_by=current_user.username,
|
|
|
|
domain_id=domain.id)
|
2019-12-02 03:32:03 +00:00
|
|
|
history.add()
|
|
|
|
return make_response(
|
|
|
|
jsonify({
|
|
|
|
'status': 'ok',
|
|
|
|
'msg': 'New setting created and updated.'
|
|
|
|
}))
|
|
|
|
else:
|
|
|
|
return make_response(
|
|
|
|
jsonify({
|
|
|
|
'status': 'error',
|
|
|
|
'msg': 'Unable to create new setting.'
|
|
|
|
}))
|
|
|
|
else:
|
|
|
|
return make_response(
|
|
|
|
jsonify({
|
|
|
|
'status': 'error',
|
|
|
|
'msg': 'Action not supported.'
|
|
|
|
}), 400)
|
|
|
|
except Exception as e:
|
|
|
|
current_app.logger.error(
|
|
|
|
'Cannot change domain setting. Error: {0}'.format(e))
|
|
|
|
current_app.logger.debug(traceback.format_exc())
|
|
|
|
return make_response(
|
|
|
|
jsonify({
|
|
|
|
'status':
|
|
|
|
'error',
|
|
|
|
'msg':
|
|
|
|
'There is something wrong, please contact Administrator.'
|
|
|
|
}), 400)
|