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 traceback
import pyotp
import re
import dns.reversename
from datetime import datetime
from distutils.util import strtobool
from distutils.version import StrictVersion
from flask_login import AnonymousUserMixin
@ -31,7 +34,6 @@ else:
if 'PRETTY_IPV6_PTR' in app.config.keys():
import dns.inet
import dns.name
import dns.reversename
PRETTY_IPV6_PTR = app.config['PRETTY_IPV6_PTR']
else:
PRETTY_IPV6_PTR = False
@ -450,7 +452,7 @@ class Domain(db.Model):
db.session.commit()
return True
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
def get_domains(self):
@ -481,8 +483,11 @@ class Domain(db.Model):
"""
Return domain id
"""
domain = Domain.query.filter(Domain.name==name).first()
return domain.id
try:
domain = Domain.query.filter(Domain.name==name).first()
return domain.id
except:
return None
def update(self):
"""
@ -507,6 +512,10 @@ class Domain(db.Model):
if domain_user:
domain_user.delete()
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
Domain.query.filter(Domain.name == d).delete()
@ -600,6 +609,60 @@ class Domain(db.Model):
logging.debug(str(e))
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):
"""
@ -769,7 +832,7 @@ class Record(object):
if NEW_SCHEMA:
data = {"rrsets": [
{
"name": self.name + '.',
"name": self.name.rstrip('.') + '.',
"type": self.type,
"changetype": "REPLACE",
"ttl": self.ttl,
@ -861,7 +924,7 @@ class Record(object):
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']
if PRETTY_IPV6_PTR: # only if activated
if NEW_SCHEMA: # only if new schema
@ -883,7 +946,7 @@ class Record(object):
records = []
for r in new_records:
if NEW_SCHEMA:
r_name = r['name'] + '.'
r_name = r['name'].rstrip('.') + '.'
r_type = r['type']
if PRETTY_IPV6_PTR: # only if activated
if r_type == 'PTR': # only ptr
@ -988,12 +1051,54 @@ class Record(object):
logging.debug(jdata2['error'])
return {'status': 'error', 'msg': jdata2['error']}
else:
self.auto_ptr(domain, new_records, deleted_records)
logging.info('Record was applied successfully.')
return {'status': 'ok', 'msg': 'Record was applied successfully'}
except Exception, e:
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'}
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):
"""
@ -1003,14 +1108,10 @@ class Record(object):
headers['X-API-Key'] = PDNS_API_KEY
data = {"rrsets": [
{
"name": self.name,
"name": self.name.rstrip('.') + '.',
"type": self.type,
"changetype": "DELETE",
"records": [
{
"name": self.name,
"type": self.type
}
]
}
]

View File

@ -168,7 +168,7 @@
{{ domain.type }}
</td>
<td>
{{ domain.serial }}
{% if domain.serial == 0 %}{{ domain.notified_serial }}{% else %}{{domain.serial}}{% endif %}
</td>
<td>
{% 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();
// 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);
document.getElementById("edit-row-focus").focus();
nEditing = nRow;
@ -268,16 +269,16 @@
var modal = $("#modal_custom_record");
if (record_data.val() == "") {
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> \
<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 {
var parts = record_data.val().split(" ");
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> \
<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);

View File

@ -55,6 +55,22 @@
</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="col-xs-12">
<div class="box">
@ -94,6 +110,10 @@ $('.dyndns_on_demand_toggle').iCheck({
checkboxClass : 'icheckbox_square-blue',
increaseArea : '20%' // optional
});
$('.auto_ptr_toggle').iCheck({
checkboxClass : 'icheckbox_square-blue',
increaseArea : '20%' // optional
});
$("#domain_multi_user").multiSelect();
@ -110,6 +130,18 @@ $('.dyndns_on_demand_toggle').on('ifToggled', function(event) {
};
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
$(document.body).on('click', '.delete_domain', function() {

View File

@ -2,6 +2,7 @@ import base64
import json
import os
import traceback
import re
from distutils.util import strtobool
from distutils.version import StrictVersion
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()
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
@app.before_request
def before_request():
@ -312,8 +318,11 @@ def domain(domain_name):
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'])
records.append(record)
return render_template('domain.html', domain=domain, records=records, editable_records=app.config['RECORDS_ALLOW_EDIT'])
if not re.search('ip6\.arpa|in-addr\.arpa$', domain_name):
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:
return redirect(url_for('error', code=404))

View File

@ -77,7 +77,8 @@ def init_records():
Setting('record_helper', 'True'),
Setting('login_ldap_first', 'True'),
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()