diff --git a/app/models.py b/app/models.py index ff8d97d..9aee967 100644 --- a/app/models.py +++ b/app/models.py @@ -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.+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.+6.arpa)\.?)', reverse_host_address).group('ipname') + else: + for i in range(1,4,1): + address = re.search('((([0-9]+\.){'+ str(i) +'})(?P.+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.+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 - } ] } ] diff --git a/app/templates/dashboard.html b/app/templates/dashboard.html index 8c9a7e0..dee30fc 100644 --- a/app/templates/dashboard.html +++ b/app/templates/dashboard.html @@ -168,7 +168,7 @@ {{ domain.type }} - {{ domain.serial }} + {% if domain.serial == 0 %}{{ domain.notified_serial }}{% else %}{{domain.serial}}{% endif %} {% if domain.master == '[]'%}N/A {% else %}{{ domain.master|display_master_name }}{% endif %} diff --git a/app/templates/domain.html b/app/templates/domain.html index 4b19e7c..eeb4c27 100644 --- a/app/templates/domain.html +++ b/app/templates/domain.html @@ -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 = " \ - \ + \ \ - \ + \ "; } else { var parts = record_data.val().split(" "); var form = " \ - \ + \ \ - \ + \ "; } modal.find('.modal-body p').html(form); diff --git a/app/templates/domain_management.html b/app/templates/domain_management.html index 03cd9b0..a02caa9 100644 --- a/app/templates/domain_management.html +++ b/app/templates/domain_management.html @@ -55,6 +55,22 @@ +
+
+
+
+

Auto PTR creation

+
+
+

+  Allow automatic reverse pointer creation on record updates?{% if + auto_ptr_setting %}
Auto-ptr is enabled globally on the PDA system!{% endif %}

+ +
+
+
+
@@ -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() { diff --git a/app/views.py b/app/views.py index 1a37d53..8cc8761 100644 --- a/app/views.py +++ b/app/views.py @@ -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)) diff --git a/create_db.py b/create_db.py index 8768ab7..41c874d 100755 --- a/create_db.py +++ b/create_db.py @@ -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()