diff --git a/app/models.py b/app/models.py index 9cc7e66..70bf7e1 100644 --- a/app/models.py +++ b/app/models.py @@ -14,6 +14,7 @@ import dns.name import sys import logging as logger +from ast import literal_eval from datetime import datetime from urllib.parse import urljoin from distutils.util import strtobool @@ -1775,7 +1776,7 @@ class History(db.Model): class Setting(db.Model): id = db.Column(db.Integer, primary_key = True) name = db.Column(db.String(64)) - value = db.Column(db.String(256)) + value = db.Column(db.Text()) view = db.Column(db.String(64)) defaults = { @@ -1821,6 +1822,8 @@ class Setting(db.Model): 'google_token_params': {'scope': 'email profile'}, 'google_authorize_url':'https://accounts.google.com/o/oauth2/auth', 'google_base_url':'https://www.googleapis.com/oauth2/v1/', + 'forward_records_allow_edit': {'A': True, 'AAAA': True, 'AFSDB': False, 'ALIAS': False, 'CAA': True, 'CERT': False, 'CDNSKEY': False, 'CDS': False, 'CNAME': True, 'DNSKEY': False, 'DNAME': False, 'DS': False, 'HINFO': False, 'KEY': False, 'LOC': True, 'MX': True, 'NAPTR': False, 'NS': True, 'NSEC': False, 'NSEC3': False, 'NSEC3PARAM': False, 'OPENPGPKEY': False, 'PTR': True, 'RP': False, 'RRSIG': False, 'SOA': False, 'SPF': True, 'SSHFP': False, 'SRV': True, 'TKEY': False, 'TSIG': False, 'TLSA': False, 'SMIMEA': False, 'TXT': True, 'URI': False}, + 'reverse_records_allow_edit': {'A': False, 'AAAA': False, 'AFSDB': False, 'ALIAS': False, 'CAA': False, 'CERT': False, 'CDNSKEY': False, 'CDS': False, 'CNAME': False, 'DNSKEY': False, 'DNAME': False, 'DS': False, 'HINFO': False, 'KEY': False, 'LOC': True, 'MX': False, 'NAPTR': False, 'NS': True, 'NSEC': False, 'NSEC3': False, 'NSEC3PARAM': False, 'OPENPGPKEY': False, 'PTR': True, 'RP': False, 'RRSIG': False, 'SOA': False, 'SPF': False, 'SSHFP': False, 'SRV': False, 'TKEY': False, 'TSIG': False, 'TLSA': False, 'SMIMEA': False, 'TXT': True, 'URI': False}, } def __init__(self, id=None, name=None, value=None): @@ -1905,6 +1908,17 @@ class Setting(db.Model): else: logging.error('Unknown setting queried: {0}'.format(setting)) + def get_records_allow_to_edit(self): + return list(set(self.get_forward_records_allow_to_edit() + self.get_reverse_records_allow_to_edit())) + + def get_forward_records_allow_to_edit(self): + records = literal_eval(self.get('forward_records_allow_edit')) + return [r for r in records if records[r]] + + def get_reverse_records_allow_to_edit(self): + records = literal_eval(self.get('reverse_records_allow_edit')) + return [r for r in records if records[r]] + def get_view(self, view): r = {} settings = Setting.query.filter(Setting.view == view).all() diff --git a/app/templates/admin_setting_pdns.html b/app/templates/admin_setting_pdns.html index 6405108..5223287 100644 --- a/app/templates/admin_setting_pdns.html +++ b/app/templates/admin_setting_pdns.html @@ -26,7 +26,6 @@
-
{% if not SETTING.get('pdns_api_url') or not SETTING.get('pdns_api_key') or not SETTING.get('pdns_version') %}
diff --git a/app/templates/admin_setting_records.html b/app/templates/admin_setting_records.html new file mode 100644 index 0000000..3d853fd --- /dev/null +++ b/app/templates/admin_setting_records.html @@ -0,0 +1,78 @@ +{% extends "base.html" %} +{% set active_page = "admin_settings" %} +{% block title %} +DNS Records Settings - {{ SITE_NAME }} +{% endblock %} {% block dashboard_stat %} + +
+

+ Settings PowerDNS-Admin settings +

+ +
+{% endblock %} +{% block content %} +
+
+
+
+
+

DNS record Settings

+
+ + + + +
+ + + + + + + + {% for record in f_records %} + + + + + + + {% endfor %} +
#RecordForward ZoneReverse Zone
{{ loop.index }}{{ record }} + + + +
+
+ + +
+
+
+
+
+

Help

+
+
+

TBD

+
+
+
+
+
+{% endblock %} +{% block extrascripts %} + +{% endblock %} \ No newline at end of file diff --git a/app/templates/base.html b/app/templates/base.html index 4cc88d7..98845ea 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -137,6 +137,7 @@ diff --git a/app/views.py b/app/views.py index db79f1d..67a9c98 100644 --- a/app/views.py +++ b/app/views.py @@ -8,6 +8,7 @@ from distutils.util import strtobool from distutils.version import StrictVersion from functools import wraps from io import BytesIO +from ast import literal_eval import jinja2 import qrcode as qrc @@ -564,29 +565,31 @@ def domain(domain_name): return redirect(url_for('error', code=500)) quick_edit = Setting().get('allow_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() records = [] - #TODO: This should be done in the "model" instead of "view" + if StrictVersion(Setting().get('pdns_version')) >= StrictVersion('4.0.0'): for jr in jrecords: - if jr['type'] in app.config['RECORDS_ALLOW_EDIT']: + if jr['type'] in Setting().get_records_allow_to_edit(): for subrecord in jr['records']: record = Record(name=jr['name'], type=jr['type'], status='Disabled' if subrecord['disabled'] else 'Active', ttl=jr['ttl'], data=subrecord['content']) records.append(record) if not re.search('ip6\.arpa|in-addr\.arpa$', domain_name): - editable_records = app.config['FORWARD_RECORDS_ALLOW_EDIT'] + editable_records = forward_records_allow_to_edit else: - editable_records = app.config['REVERSE_RECORDS_ALLOW_EDIT'] + editable_records = reverse_records_allow_to_edit return render_template('domain.html', domain=domain, records=records, editable_records=editable_records, quick_edit=quick_edit) else: for jr in jrecords: - if jr['type'] in app.config['RECORDS_ALLOW_EDIT']: + if jr['type'] in Setting().get_records_allow_to_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) if not re.search('ip6\.arpa|in-addr\.arpa$', domain_name): - editable_records = app.config['FORWARD_RECORDS_ALLOW_EDIT'] + editable_records = forward_records_allow_to_edit else: - editable_records = app.config['REVERSE_RECORDS_ALLOW_EDIT'] + editable_records = reverse_records_allow_to_edit return render_template('domain.html', domain=domain, records=records, editable_records=editable_records, quick_edit=quick_edit) @@ -980,14 +983,14 @@ def create_template_from_zone(): if StrictVersion(Setting().get('pdns_version')) >= StrictVersion('4.0.0'): for jr in jrecords: - if jr['type'] in app.config['RECORDS_ALLOW_EDIT']: + if jr['type'] in Setting().get_records_allow_to_edit(): name = '@' if jr['name'] == domain_name else re.sub('\.{}$'.format(domain_name), '', jr['name']) for subrecord in jr['records']: record = DomainTemplateRecord(name=name, type=jr['type'], status=True if subrecord['disabled'] else False, ttl=jr['ttl'], data=subrecord['content']) records.append(record) else: for jr in jrecords: - if jr['type'] in app.config['RECORDS_ALLOW_EDIT']: + if jr['type'] in Setting().get_records_allow_to_edit(): name = '@' if jr['name'] == domain_name else re.sub('\.{}$'.format(domain_name), '', jr['name']) record = DomainTemplateRecord(name=name, type=jr['type'], status=True if jr['disabled'] else False, ttl=jr['ttl'], data=jr['content']) records.append(record) @@ -1013,14 +1016,15 @@ def create_template_from_zone(): def edit_template(template): try: t = DomainTemplate.query.filter(DomainTemplate.name == template).first() + records_allow_to_edit = Setting().get_records_allow_to_edit() if t is not None: records = [] for jr in t.records: - if jr.type in app.config['RECORDS_ALLOW_EDIT']: + if jr.type in records_allow_to_edit: record = DomainTemplateRecord(name=jr.name, type=jr.type, status='Disabled' if jr.status else 'Active', ttl=jr.ttl, data=jr.data) records.append(record) - return render_template('template_edit.html', template=t.name, records=records, editable_records=app.config['RECORDS_ALLOW_EDIT']) + return render_template('template_edit.html', template=t.name, records=records, editable_records=records_allow_to_edit) except: logging.error(traceback.print_exc()) return redirect(url_for('error', code=500)) @@ -1374,6 +1378,27 @@ def admin_setting_pdns(): return render_template('admin_setting_pdns.html', pdns_api_url=pdns_api_url, pdns_api_key=pdns_api_key, pdns_version=pdns_version) +@app.route('/admin/setting/dns-records', methods=['GET', 'POST']) +@login_required +@admin_role_required +def admin_setting_records(): + if request.method == 'GET': + f_records = literal_eval(Setting().get('forward_records_allow_edit')) + r_records = literal_eval(Setting().get('reverse_records_allow_edit')) + return render_template('admin_setting_records.html', f_records=f_records, r_records=r_records) + elif request.method == 'POST': + fr = {} + rr = {} + records = Setting().defaults['forward_records_allow_edit'] + for r in records: + fr[r] = True if request.form.get('fr_{0}'.format(r.lower())) else False + rr[r] = True if request.form.get('rr_{0}'.format(r.lower())) else False + + Setting().set('forward_records_allow_edit', str(fr)) + Setting().set('reverse_records_allow_edit', str(rr)) + return redirect(url_for('admin_setting_records')) + + @app.route('/admin/setting/authentication', methods=['GET', 'POST']) @login_required @admin_role_required diff --git a/config_template.py b/config_template.py index f1b7666..10e1e7f 100644 --- a/config_template.py +++ b/config_template.py @@ -11,29 +11,26 @@ PORT = 9191 TIMEOUT = 10 # LOG CONFIG +# - For docker, LOG_FILE='' LOG_LEVEL = 'DEBUG' LOG_FILE = 'logfile.log' -# For Docker, leave empty string -#LOG_FILE = '' -# Upload +# UPLOAD DIRECTORY UPLOAD_DIR = os.path.join(basedir, 'upload') # DATABASE CONFIG -#You'll need MySQL-python -SQLA_DB_USER = 'powerdnsadmin' -SQLA_DB_PASSWORD = 'powerdnsadminpassword' -SQLA_DB_HOST = 'mysqlhostorip' -SQLA_DB_NAME = 'powerdnsadmin' - -#MySQL -#SQLALCHEMY_DATABASE_URI = 'mysql://'+SQLA_DB_USER+':'\ -# +SQLA_DB_PASSWORD+'@'+SQLA_DB_HOST+'/'+SQLA_DB_NAME -#SQLite -SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'pdns.db') -SQLALCHEMY_MIGRATE_REPO = os.path.join(basedir, 'db_repository') +SQLA_DB_USER = 'pda' +SQLA_DB_PASSWORD = 'changeme' +SQLA_DB_HOST = '127.0.0.1' +SQLA_DB_NAME = 'pda' SQLALCHEMY_TRACK_MODIFICATIONS = True +# DATBASE - MySQL +SQLALCHEMY_DATABASE_URI = 'mysql://'+SQLA_DB_USER+':'+SQLA_DB_PASSWORD+'@'+SQLA_DB_HOST+'/'+SQLA_DB_NAME + +# DATABSE - SQLite +#SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'pdns.db') + # SAML Authnetication SAML_ENABLED = False SAML_DEBUG = True @@ -106,17 +103,3 @@ SAML_LOGOUT = False #Configure to redirect to a different url then PowerDNS-Admin login after SAML logout #for example redirect to google.com after successful saml logout #SAML_LOGOUT_URL = 'https://google.com' - -# RECORDS ALLOWED TO EDIT -RECORDS_ALLOW_EDIT = ['A', 'AAAA', 'CAA', 'CNAME', 'MX', 'PTR', 'SPF', 'SRV', 'TXT', 'LOC', 'NS', 'PTR', 'SOA'] -FORWARD_RECORDS_ALLOW_EDIT = ['A', 'AAAA', 'CAA', 'CNAME', 'MX', 'PTR', 'SPF', 'SRV', 'TXT', 'LOC' 'NS'] -REVERSE_RECORDS_ALLOW_EDIT = ['SOA', 'TXT', 'LOC', 'NS', 'PTR'] - -# ALLOW DNSSEC CHANGES FOR ADMINS ONLY -DNSSEC_ADMINS_ONLY = False - -# EXPERIMENTAL FEATURES -PRETTY_IPV6_PTR = False - -# Domain updates in background, for big installations -BG_DOMAIN_UPDATES = False diff --git a/migrations/versions/1274ed462010_change_setting_value_data_type.py b/migrations/versions/1274ed462010_change_setting_value_data_type.py new file mode 100644 index 0000000..91600ca --- /dev/null +++ b/migrations/versions/1274ed462010_change_setting_value_data_type.py @@ -0,0 +1,46 @@ +"""Change setting.value data type + +Revision ID: 1274ed462010 +Revises: 59729e468045 +Create Date: 2018-08-21 17:12:30.058782 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '1274ed462010' +down_revision = '59729e468045' +branch_labels = None +depends_on = None + + +def update_data(): + setting_table = sa.sql.table('setting', + sa.sql.column('id', sa.Integer), + sa.sql.column('name', sa.String), + sa.sql.column('value', sa.String), + sa.sql.column('view', sa.String) + ) + + # add more new settings + op.bulk_insert(setting_table, + [ + {'id': 42, 'name': 'forward_records_allow_edit', 'value': "{'A': True, 'AAAA': True, 'AFSDB': False, 'ALIAS': False, 'CAA': True, 'CERT': False, 'CDNSKEY': False, 'CDS': False, 'CNAME': True, 'DNSKEY': False, 'DNAME': False, 'DS': False, 'HINFO': False, 'KEY': False, 'LOC': True, 'MX': True, 'NAPTR': False, 'NS': True, 'NSEC': False, 'NSEC3': False, 'NSEC3PARAM': False, 'OPENPGPKEY': False, 'PTR': True, 'RP': False, 'RRSIG': False, 'SOA': False, 'SPF': True, 'SSHFP': False, 'SRV': True, 'TKEY': False, 'TSIG': False, 'TLSA': False, 'SMIMEA': False, 'TXT': True, 'URI': False}", 'view': 'records'}, + {'id': 43, 'name': 'reverse_records_allow_edit', 'value': "{'A': False, 'AAAA': False, 'AFSDB': False, 'ALIAS': False, 'CAA': False, 'CERT': False, 'CDNSKEY': False, 'CDS': False, 'CNAME': False, 'DNSKEY': False, 'DNAME': False, 'DS': False, 'HINFO': False, 'KEY': False, 'LOC': True, 'MX': False, 'NAPTR': False, 'NS': True, 'NSEC': False, 'NSEC3': False, 'NSEC3PARAM': False, 'OPENPGPKEY': False, 'PTR': True, 'RP': False, 'RRSIG': False, 'SOA': False, 'SPF': False, 'SSHFP': False, 'SRV': False, 'TKEY': False, 'TSIG': False, 'TLSA': False, 'SMIMEA': False, 'TXT': True, 'URI': False}", 'view': 'records'}, + ] + ) + +def upgrade(): + # change column data type + op.alter_column('setting', 'value', existing_type=sa.String(256), type_=sa.Text()) + # update data for new schema + update_data() + + +def downgrade(): + # delete added records in previous version + op.execute("DELETE FROM setting WHERE id > 41") + # change column data type + op.alter_column('setting', 'value', existing_type=sa.Text(), type_=sa.String(256))