Merge pull request #156 from petersipos/feature/automatic-reverse-domain-creation

Feature/automatic reverse domain creation
This commit is contained in:
Khanh Ngo 2016-12-10 12:38:44 +07:00 committed by GitHub
commit b6ed658cbd
6 changed files with 165 additions and 21 deletions

View File

@ -7,8 +7,11 @@ import urlparse
import itertools import itertools
import traceback import traceback
import pyotp import pyotp
import re
import dns.reversename
from datetime import datetime from datetime import datetime
from distutils.util import strtobool
from distutils.version import StrictVersion from distutils.version import StrictVersion
from flask_login import AnonymousUserMixin from flask_login import AnonymousUserMixin
@ -31,7 +34,6 @@ else:
if 'PRETTY_IPV6_PTR' in app.config.keys(): if 'PRETTY_IPV6_PTR' in app.config.keys():
import dns.inet import dns.inet
import dns.name import dns.name
import dns.reversename
PRETTY_IPV6_PTR = app.config['PRETTY_IPV6_PTR'] PRETTY_IPV6_PTR = app.config['PRETTY_IPV6_PTR']
else: else:
PRETTY_IPV6_PTR = False PRETTY_IPV6_PTR = False
@ -450,7 +452,7 @@ class Domain(db.Model):
db.session.commit() db.session.commit()
return True return True
except Exception, e: except Exception, e:
logging.error('Can not create settting %s for domain %s. %s' % (setting, self.name, str(e))) logging.error('Can not create setting %s for domain %s. %s' % (setting, self.name, str(e)))
return False return False
def get_domains(self): def get_domains(self):
@ -481,8 +483,11 @@ class Domain(db.Model):
""" """
Return domain id Return domain id
""" """
domain = Domain.query.filter(Domain.name==name).first() try:
return domain.id domain = Domain.query.filter(Domain.name==name).first()
return domain.id
except:
return None
def update(self): def update(self):
""" """
@ -507,6 +512,10 @@ class Domain(db.Model):
if domain_user: if domain_user:
domain_user.delete() domain_user.delete()
db.session.commit() db.session.commit()
domain_setting = DomainSetting.query.filter(DomainSetting.domain_id==domain.id)
if domain_setting:
domain_setting.delete()
db.session.commit()
# then remove domain # then remove domain
Domain.query.filter(Domain.name == d).delete() Domain.query.filter(Domain.name == d).delete()
@ -600,6 +609,60 @@ class Domain(db.Model):
logging.debug(str(e)) logging.debug(str(e))
return {'status': 'error', 'msg': 'Cannot add this domain.'} return {'status': 'error', 'msg': 'Cannot add this domain.'}
def create_reverse_domain(self, domain_name, domain_reverse_name):
"""
Check the existing reverse lookup domain,
if not exists create a new one automatically
"""
domain_obj = Domain.query.filter(Domain.name == domain_name).first()
domain_auto_ptr = DomainSetting.query.filter(DomainSetting.domain == domain_obj).filter(DomainSetting.setting == 'auto_ptr').first()
domain_auto_ptr = strtobool(domain_auto_ptr.value) if domain_auto_ptr else False
system_auto_ptr = Setting.query.filter(Setting.name == 'auto_ptr').first()
system_auto_ptr = strtobool(system_auto_ptr.value)
self.name = domain_name
domain_id = self.get_id_by_name(domain_reverse_name)
if None == domain_id and \
(
system_auto_ptr or \
domain_auto_ptr
):
result = self.add(domain_reverse_name, 'Master', 'INCEPTION-INCREMENT', '', '')
self.update()
if result['status'] == 'ok':
history = History(msg='Add reverse lookup domain %s' % domain_reverse_name, detail=str({'domain_type': 'Master', 'domain_master_ips': ''}), created_by='System')
history.add()
else:
return {'status': 'error', 'msg': 'Adding reverse lookup domain failed'}
domain_user_ids = self.get_user()
domain_users = []
u = User()
for uid in domain_user_ids:
u.id = uid
tmp = u.get_user_info_by_id()
domain_users.append(tmp.username)
if 0 != len(domain_users):
self.name = domain_reverse_name
self.grant_privielges(domain_users)
return {'status': 'ok', 'msg': 'New reverse lookup domain created with granted privilages'}
return {'status': 'ok', 'msg': 'New reverse lookup domain created without users'}
return {'status': 'ok', 'msg': 'Reverse lookup domain already exists'}
def get_reverse_domain_name(self, reverse_host_address):
c = 1
if re.search('ip6.arpa', reverse_host_address):
for i in range(1,32,1):
address = re.search('((([a-f0-9]\.){'+ str(i) +'})(?P<ipname>.+6.arpa)\.?)', reverse_host_address)
if None != self.get_id_by_name(address.group('ipname')):
c = i
break
return re.search('((([a-f0-9]\.){'+ str(c) +'})(?P<ipname>.+6.arpa)\.?)', reverse_host_address).group('ipname')
else:
for i in range(1,4,1):
address = re.search('((([0-9]+\.){'+ str(i) +'})(?P<ipname>.+r.arpa)\.?)', reverse_host_address)
if None != self.get_id_by_name(address.group('ipname')):
c = i
break
return re.search('((([0-9]+\.){'+ str(c) +'})(?P<ipname>.+r.arpa)\.?)', reverse_host_address).group('ipname')
def delete(self, domain_name): def delete(self, domain_name):
""" """
@ -769,7 +832,7 @@ class Record(object):
if NEW_SCHEMA: if NEW_SCHEMA:
data = {"rrsets": [ data = {"rrsets": [
{ {
"name": self.name + '.', "name": self.name.rstrip('.') + '.',
"type": self.type, "type": self.type,
"changetype": "REPLACE", "changetype": "REPLACE",
"ttl": self.ttl, "ttl": self.ttl,
@ -861,7 +924,7 @@ class Record(object):
records = [] records = []
for r in deleted_records: for r in deleted_records:
r_name = r['name'] + '.' if NEW_SCHEMA else r['name'] r_name = r['name'].rstrip('.') + '.' if NEW_SCHEMA else r['name']
r_type = r['type'] r_type = r['type']
if PRETTY_IPV6_PTR: # only if activated if PRETTY_IPV6_PTR: # only if activated
if NEW_SCHEMA: # only if new schema if NEW_SCHEMA: # only if new schema
@ -883,7 +946,7 @@ class Record(object):
records = [] records = []
for r in new_records: for r in new_records:
if NEW_SCHEMA: if NEW_SCHEMA:
r_name = r['name'] + '.' r_name = r['name'].rstrip('.') + '.'
r_type = r['type'] r_type = r['type']
if PRETTY_IPV6_PTR: # only if activated if PRETTY_IPV6_PTR: # only if activated
if r_type == 'PTR': # only ptr if r_type == 'PTR': # only ptr
@ -988,12 +1051,54 @@ class Record(object):
logging.debug(jdata2['error']) logging.debug(jdata2['error'])
return {'status': 'error', 'msg': jdata2['error']} return {'status': 'error', 'msg': jdata2['error']}
else: else:
self.auto_ptr(domain, new_records, deleted_records)
logging.info('Record was applied successfully.') logging.info('Record was applied successfully.')
return {'status': 'ok', 'msg': 'Record was applied successfully'} return {'status': 'ok', 'msg': 'Record was applied successfully'}
except Exception, e: except Exception, e:
logging.error("Cannot apply record changes to domain %s. DETAIL: %s" % (str(e), domain)) logging.error("Cannot apply record changes to domain %s. DETAIL: %s" % (str(e), domain))
return {'status': 'error', 'msg': 'There was something wrong, please contact administrator'} return {'status': 'error', 'msg': 'There was something wrong, please contact administrator'}
def auto_ptr(self, domain, new_records, deleted_records):
"""
Add auto-ptr records
"""
domain_obj = Domain.query.filter(Domain.name == domain).first()
domain_auto_ptr = DomainSetting.query.filter(DomainSetting.domain == domain_obj).filter(DomainSetting.setting == 'auto_ptr').first()
domain_auto_ptr = strtobool(domain_auto_ptr.value) if domain_auto_ptr else False
system_auto_ptr = Setting.query.filter(Setting.name == 'auto_ptr').first()
system_auto_ptr = strtobool(system_auto_ptr.value)
if system_auto_ptr or domain_auto_ptr:
try:
d = Domain()
for r in new_records:
if r['type'] in ['A', 'AAAA']:
r_name = r['name'] + '.'
r_content = r['content']
reverse_host_address = dns.reversename.from_address(r_content).to_text()
domain_reverse_name = d.get_reverse_domain_name(reverse_host_address)
d.create_reverse_domain(domain, domain_reverse_name)
self.name = dns.reversename.from_address(r_content).to_text().rstrip('.')
self.type = 'PTR'
self.status = r['disabled']
self.ttl = r['ttl']
self.data = r_name
self.add(domain_reverse_name)
for r in deleted_records:
if r['type'] in ['A', 'AAAA']:
r_name = r['name'] + '.'
r_content = r['content']
reverse_host_address = dns.reversename.from_address(r_content).to_text()
domain_reverse_name = d.get_reverse_domain_name(reverse_host_address)
self.name = reverse_host_address
self.type = 'PTR'
self.data = r_content
self.delete(domain_reverse_name)
return {'status': 'ok', 'msg': 'Auto-PTR record was updated successfully'}
except Exception as e:
logging.error("Cannot update auto-ptr record changes to domain %s. DETAIL: %s" % (str(e), domain))
return {'status': 'error', 'msg': 'Auto-PTR creation failed. There was something wrong, please contact administrator.'}
def delete(self, domain): def delete(self, domain):
""" """
@ -1003,14 +1108,10 @@ class Record(object):
headers['X-API-Key'] = PDNS_API_KEY headers['X-API-Key'] = PDNS_API_KEY
data = {"rrsets": [ data = {"rrsets": [
{ {
"name": self.name, "name": self.name.rstrip('.') + '.',
"type": self.type, "type": self.type,
"changetype": "DELETE", "changetype": "DELETE",
"records": [ "records": [
{
"name": self.name,
"type": self.type
}
] ]
} }
] ]

View File

@ -168,7 +168,7 @@
{{ domain.type }} {{ domain.type }}
</td> </td>
<td> <td>
{{ domain.serial }} {% if domain.serial == 0 %}{{ domain.notified_serial }}{% else %}{{domain.serial}}{% endif %}
</td> </td>
<td> <td>
{% if domain.master == '[]'%}N/A {% else %}{{ domain.master|display_master_name }}{% endif %} {% if domain.master == '[]'%}N/A {% else %}{{ domain.master|display_master_name }}{% endif %}

View File

@ -223,7 +223,8 @@
$("#tbl_records").DataTable().search('').columns().search('').draw(); $("#tbl_records").DataTable().search('').columns().search('').draw();
// add new row // add new row
var nRow = jQuery('#tbl_records').dataTable().fnAddData(['', 'A', 'Active', 3600, '', '', '', '0']); var default_type = records_allow_edit[0]
var nRow = jQuery('#tbl_records').dataTable().fnAddData(['', default_type, 'Active', 3600, '', '', '', '0']);
editRow($("#tbl_records").DataTable(), nRow); editRow($("#tbl_records").DataTable(), nRow);
document.getElementById("edit-row-focus").focus(); document.getElementById("edit-row-focus").focus();
nEditing = nRow; nEditing = nRow;
@ -268,16 +269,16 @@
var modal = $("#modal_custom_record"); var modal = $("#modal_custom_record");
if (record_data.val() == "") { if (record_data.val() == "") {
var form = " <label for=\"mx_priority\">MX Priority</label> \ var form = " <label for=\"mx_priority\">MX Priority</label> \
<input type=\"text\" class=\"form-control\" name=\"mx_priority\" id=\"mx_priority\" placeholder=\"10\"> \ <input type=\"text\" class=\"form-control\" name=\"mx_priority\" id=\"mx_priority\" placeholder=\"eg. 10\"> \
<label for=\"mx_server\">MX Server</label> \ <label for=\"mx_server\">MX Server</label> \
<input type=\"text\" class=\"form-control\" name=\"mx_server\" id=\"mx_server\" placeholder=\"postfix.example.com\"> \ <input type=\"text\" class=\"form-control\" name=\"mx_server\" id=\"mx_server\" placeholder=\"eg. postfix.example.com\"> \
"; ";
} else { } else {
var parts = record_data.val().split(" "); var parts = record_data.val().split(" ");
var form = " <label for=\"mx_priority\">MX Priority</label> \ var form = " <label for=\"mx_priority\">MX Priority</label> \
<input type=\"text\" class=\"form-control\" name=\"mx_priority\" id=\"mx_priority\" placeholder=\"10\" value=\"" + parts[0] + "\"> \ <input type=\"text\" class=\"form-control\" name=\"mx_priority\" id=\"mx_priority\" placeholder=\"eg. 10\" value=\"" + parts[0] + "\"> \
<label for=\"mx_server\">MX Server</label> \ <label for=\"mx_server\">MX Server</label> \
<input type=\"text\" class=\"form-control\" name=\"mx_server\" id=\"mx_server\" placeholder=\"postfix.example.com\" value=\"" + parts[1] + "\"> \ <input type=\"text\" class=\"form-control\" name=\"mx_server\" id=\"mx_server\" placeholder=\"eg. postfix.example.com\" value=\"" + parts[1] + "\"> \
"; ";
} }
modal.find('.modal-body p').html(form); modal.find('.modal-body p').html(form);

View File

@ -55,6 +55,22 @@
</div> </div>
</div> </div>
</div> </div>
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="box-header">
<h3 class="box-title">Auto PTR creation</h3>
</div>
<div class="box-body">
<p><input type="checkbox" id="{{ domain.name }}" class="auto_ptr_toggle"
{% for setting in domain.settings %}{% if setting.setting=='auto_ptr' and setting.value=='True' %}checked{% endif %}{% endfor %} {% if auto_ptr_setting %}disabled="True"{% endif %}>
&nbsp;Allow automatic reverse pointer creation on record updates?{% if
auto_ptr_setting %}</br><code>Auto-ptr is enabled globally on the PDA system!</code>{% endif %}</p>
</div>
</div>
</div>
</div>
<div class="row"> <div class="row">
<div class="col-xs-12"> <div class="col-xs-12">
<div class="box"> <div class="box">
@ -94,6 +110,10 @@ $('.dyndns_on_demand_toggle').iCheck({
checkboxClass : 'icheckbox_square-blue', checkboxClass : 'icheckbox_square-blue',
increaseArea : '20%' // optional increaseArea : '20%' // optional
}); });
$('.auto_ptr_toggle').iCheck({
checkboxClass : 'icheckbox_square-blue',
increaseArea : '20%' // optional
});
$("#domain_multi_user").multiSelect(); $("#domain_multi_user").multiSelect();
@ -110,6 +130,18 @@ $('.dyndns_on_demand_toggle').on('ifToggled', function(event) {
}; };
applyChanges(postdata, $SCRIPT_ROOT + '/domain/' + domain + '/managesetting', true); applyChanges(postdata, $SCRIPT_ROOT + '/domain/' + domain + '/managesetting', true);
}); });
$('.auto_ptr_toggle').on('ifToggled', function(event) {
var is_checked = $(this).prop('checked');
var domain = $(this).prop('id');
postdata = {
'action' : 'set_setting',
'data' : {
'setting' : 'auto_ptr',
'value' : is_checked
}
};
applyChanges(postdata, $SCRIPT_ROOT + '/domain/' + domain + '/managesetting', true);
});
// handle deletion of domain // handle deletion of domain
$(document.body).on('click', '.delete_domain', function() { $(document.body).on('click', '.delete_domain', function() {

View File

@ -2,6 +2,7 @@ import base64
import json import json
import os import os
import traceback import traceback
import re
from distutils.util import strtobool from distutils.util import strtobool
from distutils.version import StrictVersion from distutils.version import StrictVersion
from functools import wraps from functools import wraps
@ -58,6 +59,11 @@ def inject_default_domain_table_size_setting():
default_domain_table_size_setting = Setting.query.filter(Setting.name == 'default_domain_table_size').first() default_domain_table_size_setting = Setting.query.filter(Setting.name == 'default_domain_table_size').first()
return dict(default_domain_table_size_setting=default_domain_table_size_setting.value) return dict(default_domain_table_size_setting=default_domain_table_size_setting.value)
@app.context_processor
def inject_auto_ptr_setting():
auto_ptr_setting = Setting.query.filter(Setting.name == 'auto_ptr').first()
return dict(auto_ptr_setting=strtobool(auto_ptr_setting.value))
# START USER AUTHENTICATION HANDLER # START USER AUTHENTICATION HANDLER
@app.before_request @app.before_request
def before_request(): def before_request():
@ -312,8 +318,11 @@ def domain(domain_name):
if jr['type'] in app.config['RECORDS_ALLOW_EDIT']: if jr['type'] in app.config['RECORDS_ALLOW_EDIT']:
record = Record(name=jr['name'], type=jr['type'], status='Disabled' if jr['disabled'] else 'Active', ttl=jr['ttl'], data=jr['content']) record = Record(name=jr['name'], type=jr['type'], status='Disabled' if jr['disabled'] else 'Active', ttl=jr['ttl'], data=jr['content'])
records.append(record) records.append(record)
if not re.search('ip6\.arpa|in-addr\.arpa$', domain_name):
return render_template('domain.html', domain=domain, records=records, editable_records=app.config['RECORDS_ALLOW_EDIT']) editable_records = app.config['RECORDS_ALLOW_EDIT']
else:
editable_records = ['PTR']
return render_template('domain.html', domain=domain, records=records, editable_records=editable_records)
else: else:
return redirect(url_for('error', code=404)) return redirect(url_for('error', code=404))

View File

@ -77,7 +77,8 @@ def init_records():
Setting('record_helper', 'True'), Setting('record_helper', 'True'),
Setting('login_ldap_first', 'True'), Setting('login_ldap_first', 'True'),
Setting('default_record_table_size', '15'), Setting('default_record_table_size', '15'),
Setting('default_domain_table_size', '10') Setting('default_domain_table_size', '10'),
Setting('auto_ptr','False')
]) ])
db_commit = db.session.commit() db_commit = db.session.commit()