mirror of
https://github.com/cwinfo/powerdns-admin.git
synced 2025-04-17 17:37:00 +00:00
Adjustment to work with Python3
This commit is contained in:
parent
b6ed658cbd
commit
b5b3b77acb
1
.gitignore
vendored
1
.gitignore
vendored
@ -23,6 +23,7 @@ nosetests.xml
|
|||||||
flask
|
flask
|
||||||
config.py
|
config.py
|
||||||
logfile.log
|
logfile.log
|
||||||
|
log.txt
|
||||||
|
|
||||||
db_repository/*
|
db_repository/*
|
||||||
upload/avatar/*
|
upload/avatar/*
|
||||||
|
17
Dockerfile
Normal file
17
Dockerfile
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
FROM ubuntu:latest
|
||||||
|
MAINTAINER Khanh Ngo "ngokhanhit@gmail.com"
|
||||||
|
ARG ENVIRONMENT=development
|
||||||
|
ENV ENVIRONMENT=${ENVIRONMENT}
|
||||||
|
|
||||||
|
WORKDIR /powerdns-admin
|
||||||
|
|
||||||
|
RUN apt-get update -y
|
||||||
|
RUN apt-get install -y python3-pip python3-dev libmysqlclient-dev supervisor
|
||||||
|
RUN apt-get install -y libsasl2-dev libldap2-dev libssl-dev
|
||||||
|
|
||||||
|
COPY ./requirements.txt /powerdns-admin/requirements.txt
|
||||||
|
RUN pip3 install -r requirements.txt
|
||||||
|
|
||||||
|
ADD ./supervisord.conf /etc/supervisord.conf
|
||||||
|
ADD . /powerdns-admin/
|
||||||
|
COPY ./configs/${ENVIRONMENT}.py /powerdns-admin/config.py
|
@ -2,20 +2,21 @@ import re
|
|||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
import requests
|
import requests
|
||||||
import urlparse
|
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
from app import app
|
from app import app
|
||||||
from distutils.version import StrictVersion
|
from distutils.version import StrictVersion
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
if 'TIMEOUT' in app.config.keys():
|
if 'TIMEOUT' in app.config.keys():
|
||||||
TIMEOUT = app.config['TIMEOUT']
|
TIMEOUT = app.config['TIMEOUT']
|
||||||
else:
|
else:
|
||||||
TIMEOUT = 10
|
TIMEOUT = 10
|
||||||
|
|
||||||
|
|
||||||
def auth_from_url(url):
|
def auth_from_url(url):
|
||||||
auth = None
|
auth = None
|
||||||
parsed_url = urlparse.urlparse(url).netloc
|
parsed_url = urlparse(url).netloc
|
||||||
if '@' in parsed_url:
|
if '@' in parsed_url:
|
||||||
auth = parsed_url.split('@')[0].split(':')
|
auth = parsed_url.split('@')[0].split(':')
|
||||||
auth = requests.auth.HTTPBasicAuth(auth[0], auth[1])
|
auth = requests.auth.HTTPBasicAuth(auth[0], auth[1])
|
||||||
@ -55,7 +56,7 @@ def fetch_remote(remote_url, method='GET', data=None, accept=None, params=None,
|
|||||||
if r.status_code not in (200, 400, 422):
|
if r.status_code not in (200, 400, 422):
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise RuntimeError("While fetching " + remote_url + ": " + str(e)), None, sys.exc_info()[2]
|
raise RuntimeError('Error while fetching {0}'.format(remote_url)) from e
|
||||||
|
|
||||||
return r
|
return r
|
||||||
|
|
||||||
@ -72,16 +73,16 @@ def fetch_json(remote_url, method='GET', data=None, params=None, headers=None):
|
|||||||
try:
|
try:
|
||||||
assert('json' in r.headers['content-type'])
|
assert('json' in r.headers['content-type'])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise Exception("While fetching " + remote_url + ": " + str(e)), None, sys.exc_info()[2]
|
raise RuntimeError('Error while fetching {0}'.format(remote_url)) from e
|
||||||
|
|
||||||
# don't use r.json here, as it will read from r.text, which will trigger
|
# don't use r.json here, as it will read from r.text, which will trigger
|
||||||
# content encoding auto-detection in almost all cases, WHICH IS EXTREMELY
|
# content encoding auto-detection in almost all cases, WHICH IS EXTREMELY
|
||||||
# SLOOOOOOOOOOOOOOOOOOOOOOW. just don't.
|
# SLOOOOOOOOOOOOOOOOOOOOOOW. just don't.
|
||||||
data = None
|
data = None
|
||||||
try:
|
try:
|
||||||
data = json.loads(r.content)
|
data = json.loads(r.content.decode('utf-8'))
|
||||||
except UnicodeDecodeError:
|
except Exception as e:
|
||||||
data = json.loads(r.content, 'iso-8859-1')
|
raise RuntimeError('Error while loading JSON data from {0}'.format(remote_url)) from e
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
@ -92,6 +93,7 @@ def display_record_name(data):
|
|||||||
else:
|
else:
|
||||||
return record_name.replace('.'+domain_name, '')
|
return record_name.replace('.'+domain_name, '')
|
||||||
|
|
||||||
|
|
||||||
def display_master_name(data):
|
def display_master_name(data):
|
||||||
"""
|
"""
|
||||||
input data: "[u'127.0.0.1', u'8.8.8.8']"
|
input data: "[u'127.0.0.1', u'8.8.8.8']"
|
||||||
@ -99,6 +101,7 @@ def display_master_name(data):
|
|||||||
matches = re.findall(r'\'(.+?)\'', data)
|
matches = re.findall(r'\'(.+?)\'', data)
|
||||||
return ", ".join(matches)
|
return ", ".join(matches)
|
||||||
|
|
||||||
|
|
||||||
def display_time(amount, units='s', remove_seconds=True):
|
def display_time(amount, units='s', remove_seconds=True):
|
||||||
"""
|
"""
|
||||||
Convert timestamp to normal time format
|
Convert timestamp to normal time format
|
||||||
@ -139,6 +142,7 @@ def display_time(amount, units='s', remove_seconds=True):
|
|||||||
|
|
||||||
return final_string
|
return final_string
|
||||||
|
|
||||||
|
|
||||||
def pdns_api_extended_uri(version):
|
def pdns_api_extended_uri(version):
|
||||||
"""
|
"""
|
||||||
Check the pdns version
|
Check the pdns version
|
||||||
@ -148,14 +152,10 @@ def pdns_api_extended_uri(version):
|
|||||||
else:
|
else:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def email_to_gravatar_url(email, size=100):
|
|
||||||
|
def email_to_gravatar_url(email="", size=100):
|
||||||
"""
|
"""
|
||||||
AD doesn't necessarily have email
|
AD doesn't necessarily have email
|
||||||
"""
|
"""
|
||||||
|
hash_string = hashlib.md5(email.encode('utf-8')).hexdigest()
|
||||||
if not email:
|
return "https://s.gravatar.com/avatar/{0}?s={1}".format(hash_string, size)
|
||||||
email=""
|
|
||||||
|
|
||||||
|
|
||||||
hash_string = hashlib.md5(email).hexdigest()
|
|
||||||
return "https://s.gravatar.com/avatar/%s?s=%s" % (hash_string, size)
|
|
||||||
|
@ -3,7 +3,6 @@ import ldap
|
|||||||
import time
|
import time
|
||||||
import base64
|
import base64
|
||||||
import bcrypt
|
import bcrypt
|
||||||
import urlparse
|
|
||||||
import itertools
|
import itertools
|
||||||
import traceback
|
import traceback
|
||||||
import pyotp
|
import pyotp
|
||||||
@ -11,13 +10,14 @@ import re
|
|||||||
import dns.reversename
|
import dns.reversename
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from urllib.parse import urljoin
|
||||||
from distutils.util import strtobool
|
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
|
||||||
|
|
||||||
from app import app, db
|
from app import app, db
|
||||||
from lib import utils
|
from app.lib import utils
|
||||||
from lib.log import logger
|
from app.lib.log import logger
|
||||||
logging = logger('MODEL', app.config['LOG_LEVEL'], app.config['LOG_FILE']).config()
|
logging = logger('MODEL', app.config['LOG_LEVEL'], app.config['LOG_FILE']).config()
|
||||||
|
|
||||||
if 'LDAP_TYPE' in app.config.keys():
|
if 'LDAP_TYPE' in app.config.keys():
|
||||||
@ -160,7 +160,7 @@ class User(db.Model):
|
|||||||
result_set.append(result_data)
|
result_set.append(result_data)
|
||||||
return result_set
|
return result_set
|
||||||
|
|
||||||
except ldap.LDAPError, e:
|
except ldap.LDAPError as e:
|
||||||
logging.error(e)
|
logging.error(e)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@ -269,6 +269,7 @@ class User(db.Model):
|
|||||||
self.role_id = Role.query.filter_by(name='User').first().id
|
self.role_id = Role.query.filter_by(name='User').first().id
|
||||||
if User.query.count() == 0:
|
if User.query.count() == 0:
|
||||||
self.role_id = Role.query.filter_by(name='Administrator').first().id
|
self.role_id = Role.query.filter_by(name='Administrator').first().id
|
||||||
|
|
||||||
self.password = self.get_hashed_password(self.plain_text_password)
|
self.password = self.get_hashed_password(self.plain_text_password)
|
||||||
|
|
||||||
db.session.add(self)
|
db.session.add(self)
|
||||||
@ -451,8 +452,8 @@ class Domain(db.Model):
|
|||||||
self.settings.append(DomainSetting(setting=setting, value=value))
|
self.settings.append(DomainSetting(setting=setting, value=value))
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return True
|
return True
|
||||||
except Exception, e:
|
except Exception as e:
|
||||||
logging.error('Can not create setting %s for domain %s. %s' % (setting, self.name, str(e)))
|
logging.error('Can not create setting %s for domain %s. %s' % (setting, self.name, e))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_domains(self):
|
def get_domains(self):
|
||||||
@ -476,7 +477,7 @@ class Domain(db.Model):
|
|||||||
"""
|
"""
|
||||||
headers = {}
|
headers = {}
|
||||||
headers['X-API-Key'] = PDNS_API_KEY
|
headers['X-API-Key'] = PDNS_API_KEY
|
||||||
jdata = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones'), headers=headers)
|
jdata = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones'), headers=headers)
|
||||||
return jdata
|
return jdata
|
||||||
|
|
||||||
def get_id_by_name(self, name):
|
def get_id_by_name(self, name):
|
||||||
@ -500,7 +501,7 @@ class Domain(db.Model):
|
|||||||
headers = {}
|
headers = {}
|
||||||
headers['X-API-Key'] = PDNS_API_KEY
|
headers['X-API-Key'] = PDNS_API_KEY
|
||||||
try:
|
try:
|
||||||
jdata = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones'), headers=headers)
|
jdata = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones'), headers=headers)
|
||||||
list_jdomain = [d['name'].rstrip('.') for d in jdata]
|
list_jdomain = [d['name'].rstrip('.') for d in jdata]
|
||||||
try:
|
try:
|
||||||
# domains should remove from db since it doesn't exist in powerdns anymore
|
# domains should remove from db since it doesn't exist in powerdns anymore
|
||||||
@ -564,8 +565,8 @@ class Domain(db.Model):
|
|||||||
except:
|
except:
|
||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
return {'status': 'ok', 'msg': 'Domain table has been updated successfully'}
|
return {'status': 'ok', 'msg': 'Domain table has been updated successfully'}
|
||||||
except Exception, e:
|
except Exception as e:
|
||||||
logging.error('Can not update domain table.' + str(e))
|
logging.error('Can not update domain table. Error: {0}'.format(e))
|
||||||
return {'status': 'error', 'msg': 'Can not update domain table'}
|
return {'status': 'error', 'msg': 'Can not update domain table'}
|
||||||
|
|
||||||
def add(self, domain_name, domain_type, soa_edit_api, domain_ns=[], domain_master_ips=[]):
|
def add(self, domain_name, domain_type, soa_edit_api, domain_ns=[], domain_master_ips=[]):
|
||||||
@ -596,17 +597,17 @@ class Domain(db.Model):
|
|||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
jdata = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones'), headers=headers, method='POST', data=post_data)
|
jdata = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones'), headers=headers, method='POST', data=post_data)
|
||||||
if 'error' in jdata.keys():
|
if 'error' in jdata.keys():
|
||||||
logging.error(jdata['error'])
|
logging.error(jdata['error'])
|
||||||
return {'status': 'error', 'msg': jdata['error']}
|
return {'status': 'error', 'msg': jdata['error']}
|
||||||
else:
|
else:
|
||||||
logging.info('Added domain %s successfully' % domain_name)
|
logging.info('Added domain %s successfully' % domain_name)
|
||||||
return {'status': 'ok', 'msg': 'Added domain successfully'}
|
return {'status': 'ok', 'msg': 'Added domain successfully'}
|
||||||
except Exception, e:
|
except Exception as e:
|
||||||
print traceback.format_exc()
|
traceback.print_exc()
|
||||||
logging.error('Cannot add domain %s' % domain_name)
|
logging.error('Cannot add domain %s' % domain_name)
|
||||||
logging.debug(str(e))
|
logging.debug(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):
|
def create_reverse_domain(self, domain_name, domain_reverse_name):
|
||||||
@ -671,13 +672,13 @@ class Domain(db.Model):
|
|||||||
headers = {}
|
headers = {}
|
||||||
headers['X-API-Key'] = PDNS_API_KEY
|
headers['X-API-Key'] = PDNS_API_KEY
|
||||||
try:
|
try:
|
||||||
jdata = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain_name), headers=headers, method='DELETE')
|
jdata = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain_name), headers=headers, method='DELETE')
|
||||||
logging.info('Delete domain %s successfully' % domain_name)
|
logging.info('Delete domain %s successfully' % domain_name)
|
||||||
return {'status': 'ok', 'msg': 'Delete domain successfully'}
|
return {'status': 'ok', 'msg': 'Delete domain successfully'}
|
||||||
except Exception, e:
|
except Exception as e:
|
||||||
print traceback.format_exc()
|
traceback.print_exc()
|
||||||
logging.error('Cannot delete domain %s' % domain_name)
|
logging.error('Cannot delete domain %s' % domain_name)
|
||||||
logging.debug(str(e))
|
logging.debug(e)
|
||||||
return {'status': 'error', 'msg': 'Cannot delete domain'}
|
return {'status': 'error', 'msg': 'Cannot delete domain'}
|
||||||
|
|
||||||
def get_user(self):
|
def get_user(self):
|
||||||
@ -730,7 +731,7 @@ class Domain(db.Model):
|
|||||||
headers = {}
|
headers = {}
|
||||||
headers['X-API-Key'] = PDNS_API_KEY
|
headers['X-API-Key'] = PDNS_API_KEY
|
||||||
try:
|
try:
|
||||||
jdata = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s/axfr-retrieve' % domain), headers=headers, method='PUT')
|
jdata = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s/axfr-retrieve' % domain), headers=headers, method='PUT')
|
||||||
return {'status': 'ok', 'msg': 'Update from Master successfully'}
|
return {'status': 'ok', 'msg': 'Update from Master successfully'}
|
||||||
except:
|
except:
|
||||||
return {'status': 'error', 'msg': 'There was something wrong, please contact administrator'}
|
return {'status': 'error', 'msg': 'There was something wrong, please contact administrator'}
|
||||||
@ -746,7 +747,7 @@ class Domain(db.Model):
|
|||||||
headers = {}
|
headers = {}
|
||||||
headers['X-API-Key'] = PDNS_API_KEY
|
headers['X-API-Key'] = PDNS_API_KEY
|
||||||
try:
|
try:
|
||||||
jdata = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s/cryptokeys' % domain.name), headers=headers, method='GET')
|
jdata = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s/cryptokeys' % domain.name), headers=headers, method='GET')
|
||||||
if 'error' in jdata:
|
if 'error' in jdata:
|
||||||
return {'status': 'error', 'msg': 'DNSSEC is not enabled for this domain'}
|
return {'status': 'error', 'msg': 'DNSSEC is not enabled for this domain'}
|
||||||
else:
|
else:
|
||||||
@ -791,7 +792,7 @@ class Record(object):
|
|||||||
headers = {}
|
headers = {}
|
||||||
headers['X-API-Key'] = PDNS_API_KEY
|
headers['X-API-Key'] = PDNS_API_KEY
|
||||||
try:
|
try:
|
||||||
jdata = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain), headers=headers)
|
jdata = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain), headers=headers)
|
||||||
except:
|
except:
|
||||||
logging.error("Cannot fetch domain's record data from remote powerdns api")
|
logging.error("Cannot fetch domain's record data from remote powerdns api")
|
||||||
return False
|
return False
|
||||||
@ -865,11 +866,11 @@ class Record(object):
|
|||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
jdata = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain), headers=headers, method='PATCH', data=data)
|
jdata = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain), headers=headers, method='PATCH', data=data)
|
||||||
logging.debug(jdata)
|
logging.debug(jdata)
|
||||||
return {'status': 'ok', 'msg': 'Record was added successfully'}
|
return {'status': 'ok', 'msg': 'Record was added successfully'}
|
||||||
except Exception, e:
|
except Exception as e:
|
||||||
logging.error("Cannot add record %s/%s/%s to domain %s. DETAIL: %s" % (self.name, self.type, self.data, domain, str(e)))
|
logging.error("Cannot add record %s/%s/%s to domain %s. DETAIL: %s" % (self.name, self.type, self.data, domain, e))
|
||||||
return {'status': 'error', 'msg': 'There was something wrong, please contact administrator'}
|
return {'status': 'error', 'msg': 'There was something wrong, please contact administrator'}
|
||||||
|
|
||||||
|
|
||||||
@ -1043,8 +1044,8 @@ class Record(object):
|
|||||||
try:
|
try:
|
||||||
headers = {}
|
headers = {}
|
||||||
headers['X-API-Key'] = PDNS_API_KEY
|
headers['X-API-Key'] = PDNS_API_KEY
|
||||||
jdata1 = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain), headers=headers, method='PATCH', data=postdata_for_delete)
|
jdata1 = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain), headers=headers, method='PATCH', data=postdata_for_delete)
|
||||||
jdata2 = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain), headers=headers, method='PATCH', data=postdata_for_new)
|
jdata2 = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain), headers=headers, method='PATCH', data=postdata_for_new)
|
||||||
|
|
||||||
if 'error' in jdata2.keys():
|
if 'error' in jdata2.keys():
|
||||||
logging.error('Cannot apply record changes.')
|
logging.error('Cannot apply record changes.')
|
||||||
@ -1054,8 +1055,8 @@ class Record(object):
|
|||||||
self.auto_ptr(domain, new_records, deleted_records)
|
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 as 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" % (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):
|
def auto_ptr(self, domain, new_records, deleted_records):
|
||||||
@ -1097,7 +1098,7 @@ class Record(object):
|
|||||||
self.delete(domain_reverse_name)
|
self.delete(domain_reverse_name)
|
||||||
return {'status': 'ok', 'msg': 'Auto-PTR record was updated successfully'}
|
return {'status': 'ok', 'msg': 'Auto-PTR record was updated successfully'}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("Cannot update auto-ptr record changes to domain %s. DETAIL: %s" % (str(e), domain))
|
logging.error("Cannot update auto-ptr record changes to domain %s. DETAIL: %s" % (e, domain))
|
||||||
return {'status': 'error', 'msg': 'Auto-PTR creation failed. There was something wrong, please contact administrator.'}
|
return {'status': 'error', 'msg': 'Auto-PTR creation failed. There was something wrong, please contact administrator.'}
|
||||||
|
|
||||||
def delete(self, domain):
|
def delete(self, domain):
|
||||||
@ -1117,7 +1118,7 @@ class Record(object):
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
jdata = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain), headers=headers, method='PATCH', data=data)
|
jdata = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain), headers=headers, method='PATCH', data=data)
|
||||||
logging.debug(jdata)
|
logging.debug(jdata)
|
||||||
return {'status': 'ok', 'msg': 'Record was removed successfully'}
|
return {'status': 'ok', 'msg': 'Record was removed successfully'}
|
||||||
except:
|
except:
|
||||||
@ -1191,11 +1192,11 @@ class Record(object):
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
jdata = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain), headers=headers, method='PATCH', data=data)
|
jdata = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain), headers=headers, method='PATCH', data=data)
|
||||||
logging.debug("dyndns data: " % data)
|
logging.debug("dyndns data: " % data)
|
||||||
return {'status': 'ok', 'msg': 'Record was updated successfully'}
|
return {'status': 'ok', 'msg': 'Record was updated successfully'}
|
||||||
except Exception, e:
|
except Exception as e:
|
||||||
logging.error("Cannot add record %s/%s/%s to domain %s. DETAIL: %s" % (self.name, self.type, self.data, domain, str(e)))
|
logging.error("Cannot add record %s/%s/%s to domain %s. DETAIL: %s" % (self.name, self.type, self.data, domain, e))
|
||||||
return {'status': 'error', 'msg': 'There was something wrong, please contact administrator'}
|
return {'status': 'error', 'msg': 'There was something wrong, please contact administrator'}
|
||||||
|
|
||||||
|
|
||||||
@ -1217,7 +1218,7 @@ class Server(object):
|
|||||||
headers['X-API-Key'] = PDNS_API_KEY
|
headers['X-API-Key'] = PDNS_API_KEY
|
||||||
|
|
||||||
try:
|
try:
|
||||||
jdata = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/%s/config' % self.server_id), headers=headers, method='GET')
|
jdata = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/%s/config' % self.server_id), headers=headers, method='GET')
|
||||||
return jdata
|
return jdata
|
||||||
except:
|
except:
|
||||||
logging.error("Can not get server configuration.")
|
logging.error("Can not get server configuration.")
|
||||||
@ -1232,7 +1233,7 @@ class Server(object):
|
|||||||
headers['X-API-Key'] = PDNS_API_KEY
|
headers['X-API-Key'] = PDNS_API_KEY
|
||||||
|
|
||||||
try:
|
try:
|
||||||
jdata = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/%s/statistics' % self.server_id), headers=headers, method='GET')
|
jdata = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/%s/statistics' % self.server_id), headers=headers, method='GET')
|
||||||
return jdata
|
return jdata
|
||||||
except:
|
except:
|
||||||
logging.error("Can not get server statistics.")
|
logging.error("Can not get server statistics.")
|
||||||
|
78
app/views.py
78
app/views.py
@ -18,7 +18,7 @@ from werkzeug.security import gen_salt
|
|||||||
|
|
||||||
from .models import User, Domain, Record, Server, History, Anonymous, Setting, DomainSetting
|
from .models import User, Domain, Record, Server, History, Anonymous, Setting, DomainSetting
|
||||||
from app import app, login_manager, github
|
from app import app, login_manager, github
|
||||||
from lib import utils
|
from app.lib import utils
|
||||||
|
|
||||||
|
|
||||||
jinja2.filters.FILTERS['display_record_name'] = utils.display_record_name
|
jinja2.filters.FILTERS['display_record_name'] = utils.display_record_name
|
||||||
@ -34,36 +34,43 @@ if StrictVersion(PDNS_VERSION) >= StrictVersion('4.0.0'):
|
|||||||
else:
|
else:
|
||||||
NEW_SCHEMA = False
|
NEW_SCHEMA = False
|
||||||
|
|
||||||
|
|
||||||
@app.context_processor
|
@app.context_processor
|
||||||
def inject_fullscreen_layout_setting():
|
def inject_fullscreen_layout_setting():
|
||||||
fullscreen_layout_setting = Setting.query.filter(Setting.name == 'fullscreen_layout').first()
|
fullscreen_layout_setting = Setting.query.filter(Setting.name == 'fullscreen_layout').first()
|
||||||
return dict(fullscreen_layout_setting=strtobool(fullscreen_layout_setting.value))
|
return dict(fullscreen_layout_setting=strtobool(fullscreen_layout_setting.value))
|
||||||
|
|
||||||
|
|
||||||
@app.context_processor
|
@app.context_processor
|
||||||
def inject_record_helper_setting():
|
def inject_record_helper_setting():
|
||||||
record_helper_setting = Setting.query.filter(Setting.name == 'record_helper').first()
|
record_helper_setting = Setting.query.filter(Setting.name == 'record_helper').first()
|
||||||
return dict(record_helper_setting=strtobool(record_helper_setting.value))
|
return dict(record_helper_setting=strtobool(record_helper_setting.value))
|
||||||
|
|
||||||
|
|
||||||
@app.context_processor
|
@app.context_processor
|
||||||
def inject_login_ldap_first_setting():
|
def inject_login_ldap_first_setting():
|
||||||
login_ldap_first_setting = Setting.query.filter(Setting.name == 'login_ldap_first').first()
|
login_ldap_first_setting = Setting.query.filter(Setting.name == 'login_ldap_first').first()
|
||||||
return dict(login_ldap_first_setting=strtobool(login_ldap_first_setting.value))
|
return dict(login_ldap_first_setting=strtobool(login_ldap_first_setting.value))
|
||||||
|
|
||||||
|
|
||||||
@app.context_processor
|
@app.context_processor
|
||||||
def inject_default_record_table_size_setting():
|
def inject_default_record_table_size_setting():
|
||||||
default_record_table_size_setting = Setting.query.filter(Setting.name == 'default_record_table_size').first()
|
default_record_table_size_setting = Setting.query.filter(Setting.name == 'default_record_table_size').first()
|
||||||
return dict(default_record_table_size_setting=default_record_table_size_setting.value)
|
return dict(default_record_table_size_setting=default_record_table_size_setting.value)
|
||||||
|
|
||||||
|
|
||||||
@app.context_processor
|
@app.context_processor
|
||||||
def inject_default_domain_table_size_setting():
|
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
|
@app.context_processor
|
||||||
def inject_auto_ptr_setting():
|
def inject_auto_ptr_setting():
|
||||||
auto_ptr_setting = Setting.query.filter(Setting.name == 'auto_ptr').first()
|
auto_ptr_setting = Setting.query.filter(Setting.name == 'auto_ptr').first()
|
||||||
return dict(auto_ptr_setting=strtobool(auto_ptr_setting.value))
|
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():
|
||||||
@ -92,6 +99,7 @@ def dyndns_login_required(f):
|
|||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
return decorated_function
|
return decorated_function
|
||||||
|
|
||||||
|
|
||||||
@login_manager.request_loader
|
@login_manager.request_loader
|
||||||
def login_via_authorization_header(request):
|
def login_via_authorization_header(request):
|
||||||
auth_header = request.headers.get('Authorization')
|
auth_header = request.headers.get('Authorization')
|
||||||
@ -100,8 +108,7 @@ def login_via_authorization_header(request):
|
|||||||
try:
|
try:
|
||||||
auth_header = base64.b64decode(auth_header)
|
auth_header = base64.b64decode(auth_header)
|
||||||
username,password = auth_header.split(":")
|
username,password = auth_header.split(":")
|
||||||
except TypeError, e:
|
except TypeError as e:
|
||||||
error = e.message['desc'] if 'desc' in e.message else e
|
|
||||||
return None
|
return None
|
||||||
user = User(username=username, password=password, plain_text_password=password)
|
user = User(username=username, password=password, plain_text_password=password)
|
||||||
try:
|
try:
|
||||||
@ -111,10 +118,9 @@ def login_via_authorization_header(request):
|
|||||||
else:
|
else:
|
||||||
login_user(user, remember = False)
|
login_user(user, remember = False)
|
||||||
return user
|
return user
|
||||||
except Exception, e:
|
except:
|
||||||
return None
|
return None
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# END USER AUTHENTICATION HANDLER
|
# END USER AUTHENTICATION HANDLER
|
||||||
|
|
||||||
# START CUSTOMIZE DECORATOR
|
# START CUSTOMIZE DECORATOR
|
||||||
@ -132,18 +138,22 @@ def admin_role_required(f):
|
|||||||
def http_bad_request(e):
|
def http_bad_request(e):
|
||||||
return redirect(url_for('error', code=400))
|
return redirect(url_for('error', code=400))
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(401)
|
@app.errorhandler(401)
|
||||||
def http_unauthorized(e):
|
def http_unauthorized(e):
|
||||||
return redirect(url_for('error', code=401))
|
return redirect(url_for('error', code=401))
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(404)
|
@app.errorhandler(404)
|
||||||
def http_internal_server_error(e):
|
def http_internal_server_error(e):
|
||||||
return redirect(url_for('error', code=404))
|
return redirect(url_for('error', code=404))
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(500)
|
@app.errorhandler(500)
|
||||||
def http_page_not_found(e):
|
def http_page_not_found(e):
|
||||||
return redirect(url_for('error', code=500))
|
return redirect(url_for('error', code=500))
|
||||||
|
|
||||||
|
|
||||||
@app.route('/error/<string:code>')
|
@app.route('/error/<string:code>')
|
||||||
def error(code, msg=None):
|
def error(code, msg=None):
|
||||||
supported_code = ('400', '401', '404', '500')
|
supported_code = ('400', '401', '404', '500')
|
||||||
@ -152,6 +162,7 @@ def error(code, msg=None):
|
|||||||
else:
|
else:
|
||||||
return render_template('errors/404.html'), 404
|
return render_template('errors/404.html'), 404
|
||||||
|
|
||||||
|
|
||||||
@app.route('/register', methods=['GET'])
|
@app.route('/register', methods=['GET'])
|
||||||
def register():
|
def register():
|
||||||
SIGNUP_ENABLED = app.config['SIGNUP_ENABLED']
|
SIGNUP_ENABLED = app.config['SIGNUP_ENABLED']
|
||||||
@ -160,12 +171,14 @@ def register():
|
|||||||
else:
|
else:
|
||||||
return render_template('errors/404.html'), 404
|
return render_template('errors/404.html'), 404
|
||||||
|
|
||||||
|
|
||||||
@app.route('/github/login')
|
@app.route('/github/login')
|
||||||
def github_login():
|
def github_login():
|
||||||
if not app.config.get('GITHUB_OAUTH_ENABLE'):
|
if not app.config.get('GITHUB_OAUTH_ENABLE'):
|
||||||
return abort(400)
|
return abort(400)
|
||||||
return github.authorize(callback=url_for('authorized', _external=True))
|
return github.authorize(callback=url_for('authorized', _external=True))
|
||||||
|
|
||||||
|
|
||||||
@app.route('/login', methods=['GET', 'POST'])
|
@app.route('/login', methods=['GET', 'POST'])
|
||||||
@login_manager.unauthorized_handler
|
@login_manager.unauthorized_handler
|
||||||
def login():
|
def login():
|
||||||
@ -224,9 +237,8 @@ def login():
|
|||||||
auth = user.is_validate(method=auth_method)
|
auth = user.is_validate(method=auth_method)
|
||||||
if auth == False:
|
if auth == False:
|
||||||
return render_template('login.html', error='Invalid credentials', ldap_enabled=LDAP_ENABLED, login_title=LOGIN_TITLE, basic_enabled=BASIC_ENABLED, signup_enabled=SIGNUP_ENABLED)
|
return render_template('login.html', error='Invalid credentials', ldap_enabled=LDAP_ENABLED, login_title=LOGIN_TITLE, basic_enabled=BASIC_ENABLED, signup_enabled=SIGNUP_ENABLED)
|
||||||
except Exception, e:
|
except Exception as e:
|
||||||
error = e.message['desc'] if 'desc' in e.message else e
|
return render_template('login.html', error=e, ldap_enabled=LDAP_ENABLED, login_title=LOGIN_TITLE, basic_enabled=BASIC_ENABLED, signup_enabled=SIGNUP_ENABLED)
|
||||||
return render_template('login.html', error=error, ldap_enabled=LDAP_ENABLED, login_title=LOGIN_TITLE, basic_enabled=BASIC_ENABLED, signup_enabled=SIGNUP_ENABLED)
|
|
||||||
|
|
||||||
# check if user enabled OPT authentication
|
# check if user enabled OPT authentication
|
||||||
if user.otp_secret:
|
if user.otp_secret:
|
||||||
@ -255,9 +267,9 @@ def login():
|
|||||||
return render_template('login.html', username=username, password=password, ldap_enabled=LDAP_ENABLED, login_title=LOGIN_TITLE, basic_enabled=BASIC_ENABLED, signup_enabled=SIGNUP_ENABLED)
|
return render_template('login.html', username=username, password=password, ldap_enabled=LDAP_ENABLED, login_title=LOGIN_TITLE, basic_enabled=BASIC_ENABLED, signup_enabled=SIGNUP_ENABLED)
|
||||||
else:
|
else:
|
||||||
return render_template('register.html', error=result)
|
return render_template('register.html', error=result)
|
||||||
except Exception, e:
|
except Exception as e:
|
||||||
error = e.message['desc'] if 'desc' in e.message else e
|
return render_template('register.html', error=e)
|
||||||
return render_template('register.html', error=error)
|
|
||||||
|
|
||||||
@app.route('/logout')
|
@app.route('/logout')
|
||||||
def logout():
|
def logout():
|
||||||
@ -284,7 +296,7 @@ def dashboard():
|
|||||||
server = Server(server_id='localhost')
|
server = Server(server_id='localhost')
|
||||||
statistics = server.get_statistic()
|
statistics = server.get_statistic()
|
||||||
if statistics:
|
if statistics:
|
||||||
uptime = filter(lambda uptime: uptime['name'] == 'uptime', statistics)[0]['value']
|
uptime = list([uptime for uptime in statistics if uptime['name'] == 'uptime'])[0]['value']
|
||||||
else:
|
else:
|
||||||
uptime = 0
|
uptime = 0
|
||||||
return render_template('dashboard.html', domains=domains, domain_count=domain_count, users=users, history_number=history_number, uptime=uptime, histories=history)
|
return render_template('dashboard.html', domains=domains, domain_count=domain_count, users=users, history_number=history_number, uptime=uptime, histories=history)
|
||||||
@ -417,8 +429,7 @@ def record_apply(domain_name):
|
|||||||
"""
|
"""
|
||||||
#TODO: filter removed records / name modified records.
|
#TODO: filter removed records / name modified records.
|
||||||
try:
|
try:
|
||||||
pdata = request.data
|
jdata = request.json
|
||||||
jdata = json.loads(pdata)
|
|
||||||
|
|
||||||
r = Record()
|
r = Record()
|
||||||
result = r.apply(domain_name, jdata)
|
result = r.apply(domain_name, jdata)
|
||||||
@ -429,7 +440,7 @@ def record_apply(domain_name):
|
|||||||
else:
|
else:
|
||||||
return make_response(jsonify( result ), 400)
|
return make_response(jsonify( result ), 400)
|
||||||
except:
|
except:
|
||||||
print traceback.format_exc()
|
traceback.print_exc()
|
||||||
return make_response(jsonify( {'status': 'error', 'msg': 'Error when applying new changes'} ), 500)
|
return make_response(jsonify( {'status': 'error', 'msg': 'Error when applying new changes'} ), 500)
|
||||||
|
|
||||||
|
|
||||||
@ -441,8 +452,7 @@ def record_update(domain_name):
|
|||||||
Pulling the records update from its Master
|
Pulling the records update from its Master
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
pdata = request.data
|
jdata = request.json
|
||||||
jdata = json.loads(pdata)
|
|
||||||
|
|
||||||
domain_name = jdata['domain']
|
domain_name = jdata['domain']
|
||||||
d = Domain()
|
d = Domain()
|
||||||
@ -452,7 +462,7 @@ def record_update(domain_name):
|
|||||||
else:
|
else:
|
||||||
return make_response(jsonify( {'status': 'error', 'msg': result['msg']} ), 500)
|
return make_response(jsonify( {'status': 'error', 'msg': result['msg']} ), 500)
|
||||||
except:
|
except:
|
||||||
print traceback.format_exc()
|
traceback.print_exc()
|
||||||
return make_response(jsonify( {'status': 'error', 'msg': 'Error when applying new changes'} ), 500)
|
return make_response(jsonify( {'status': 'error', 'msg': 'Error when applying new changes'} ), 500)
|
||||||
|
|
||||||
|
|
||||||
@ -464,9 +474,9 @@ def record_delete(domain_name, record_name, record_type):
|
|||||||
r = Record(name=record_name, type=record_type)
|
r = Record(name=record_name, type=record_type)
|
||||||
result = r.delete(domain=domain_name)
|
result = r.delete(domain=domain_name)
|
||||||
if result['status'] == 'error':
|
if result['status'] == 'error':
|
||||||
print result['msg']
|
print(result['msg'])
|
||||||
except:
|
except:
|
||||||
print traceback.format_exc()
|
traceback.print_exc()
|
||||||
return redirect(url_for('error', code=500)), 500
|
return redirect(url_for('error', code=500)), 500
|
||||||
return redirect(url_for('domain', domain_name=domain_name))
|
return redirect(url_for('domain', domain_name=domain_name))
|
||||||
|
|
||||||
@ -478,6 +488,7 @@ def domain_dnssec(domain_name):
|
|||||||
dnssec = domain.get_domain_dnssec(domain_name)
|
dnssec = domain.get_domain_dnssec(domain_name)
|
||||||
return make_response(jsonify(dnssec), 200)
|
return make_response(jsonify(dnssec), 200)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/domain/<string:domain_name>/managesetting', methods=['GET', 'POST'])
|
@app.route('/domain/<string:domain_name>/managesetting', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@admin_role_required
|
@admin_role_required
|
||||||
@ -488,9 +499,9 @@ def admin_setdomainsetting(domain_name):
|
|||||||
# {'action': 'set_setting', 'setting': 'default_action, 'value': 'True'}
|
# {'action': 'set_setting', 'setting': 'default_action, 'value': 'True'}
|
||||||
#
|
#
|
||||||
try:
|
try:
|
||||||
pdata = request.data
|
jdata = request.json
|
||||||
jdata = json.loads(pdata)
|
|
||||||
data = jdata['data']
|
data = jdata['data']
|
||||||
|
|
||||||
if jdata['action'] == 'set_setting':
|
if jdata['action'] == 'set_setting':
|
||||||
new_setting = data['setting']
|
new_setting = data['setting']
|
||||||
new_value = str(data['value'])
|
new_value = str(data['value'])
|
||||||
@ -514,7 +525,7 @@ def admin_setdomainsetting(domain_name):
|
|||||||
else:
|
else:
|
||||||
return make_response(jsonify( { 'status': 'error', 'msg': 'Action not supported.' } ), 400)
|
return make_response(jsonify( { 'status': 'error', 'msg': 'Action not supported.' } ), 400)
|
||||||
except:
|
except:
|
||||||
print traceback.format_exc()
|
traceback.print_exc()
|
||||||
return make_response(jsonify( { 'status': 'error', 'msg': 'There is something wrong, please contact Administrator.' } ), 400)
|
return make_response(jsonify( { 'status': 'error', 'msg': 'There is something wrong, please contact Administrator.' } ), 400)
|
||||||
|
|
||||||
|
|
||||||
@ -531,12 +542,13 @@ def admin():
|
|||||||
history_number = History.query.count()
|
history_number = History.query.count()
|
||||||
|
|
||||||
if statistics:
|
if statistics:
|
||||||
uptime = filter(lambda uptime: uptime['name'] == 'uptime', statistics)[0]['value']
|
uptime = list([uptime for uptime in statistics if uptime['name'] == 'uptime'])[0]['value']
|
||||||
else:
|
else:
|
||||||
uptime = 0
|
uptime = 0
|
||||||
|
|
||||||
return render_template('admin.html', domains=domains, users=users, configs=configs, statistics=statistics, uptime=uptime, history_number=history_number)
|
return render_template('admin.html', domains=domains, users=users, configs=configs, statistics=statistics, uptime=uptime, history_number=history_number)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/admin/user/create', methods=['GET', 'POST'])
|
@app.route('/admin/user/create', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@admin_role_required
|
@admin_role_required
|
||||||
@ -562,6 +574,7 @@ def admin_createuser():
|
|||||||
|
|
||||||
return redirect(url_for('admin_manageuser'))
|
return redirect(url_for('admin_manageuser'))
|
||||||
|
|
||||||
|
|
||||||
@app.route('/admin/manageuser', methods=['GET', 'POST'])
|
@app.route('/admin/manageuser', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@admin_role_required
|
@admin_role_required
|
||||||
@ -576,8 +589,7 @@ def admin_manageuser():
|
|||||||
# {'action': 'delete_user', 'data': 'username'}
|
# {'action': 'delete_user', 'data': 'username'}
|
||||||
#
|
#
|
||||||
try:
|
try:
|
||||||
pdata = request.data
|
jdata = request.json
|
||||||
jdata = json.loads(pdata)
|
|
||||||
data = jdata['data']
|
data = jdata['data']
|
||||||
|
|
||||||
if jdata['action'] == 'delete_user':
|
if jdata['action'] == 'delete_user':
|
||||||
@ -614,7 +626,7 @@ def admin_manageuser():
|
|||||||
else:
|
else:
|
||||||
return make_response(jsonify( { 'status': 'error', 'msg': 'Action not supported.' } ), 400)
|
return make_response(jsonify( { 'status': 'error', 'msg': 'Action not supported.' } ), 400)
|
||||||
except:
|
except:
|
||||||
print traceback.format_exc()
|
traceback.print_exc()
|
||||||
return make_response(jsonify( { 'status': 'error', 'msg': 'There is something wrong, please contact Administrator.' } ), 400)
|
return make_response(jsonify( { 'status': 'error', 'msg': 'There is something wrong, please contact Administrator.' } ), 400)
|
||||||
|
|
||||||
|
|
||||||
@ -637,6 +649,7 @@ def admin_history():
|
|||||||
histories = History.query.all()
|
histories = History.query.all()
|
||||||
return render_template('admin_history.html', histories=histories)
|
return render_template('admin_history.html', histories=histories)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/admin/settings', methods=['GET'])
|
@app.route('/admin/settings', methods=['GET'])
|
||||||
@login_required
|
@login_required
|
||||||
@admin_role_required
|
@admin_role_required
|
||||||
@ -645,6 +658,7 @@ def admin_settings():
|
|||||||
settings = Setting.query.filter(Setting.name != 'maintenance')
|
settings = Setting.query.filter(Setting.name != 'maintenance')
|
||||||
return render_template('admin_settings.html', settings=settings)
|
return render_template('admin_settings.html', settings=settings)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/admin/setting/<string:setting>/toggle', methods=['POST'])
|
@app.route('/admin/setting/<string:setting>/toggle', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@admin_role_required
|
@admin_role_required
|
||||||
@ -655,19 +669,21 @@ def admin_settings_toggle(setting):
|
|||||||
else:
|
else:
|
||||||
return make_response(jsonify( { 'status': 'error', 'msg': 'Unable to toggle setting.' } ), 500)
|
return make_response(jsonify( { 'status': 'error', 'msg': 'Unable to toggle setting.' } ), 500)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/admin/setting/<string:setting>/edit', methods=['POST'])
|
@app.route('/admin/setting/<string:setting>/edit', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@admin_role_required
|
@admin_role_required
|
||||||
def admin_settings_edit(setting):
|
def admin_settings_edit(setting):
|
||||||
pdata = request.data
|
jdata = request.json
|
||||||
jdata = json.loads(pdata)
|
|
||||||
new_value = jdata['value']
|
new_value = jdata['value']
|
||||||
result = Setting().set(setting, new_value)
|
result = Setting().set(setting, new_value)
|
||||||
|
|
||||||
if (result):
|
if (result):
|
||||||
return make_response(jsonify( { 'status': 'ok', 'msg': 'Toggled setting successfully.' } ), 200)
|
return make_response(jsonify( { 'status': 'ok', 'msg': 'Toggled setting successfully.' } ), 200)
|
||||||
else:
|
else:
|
||||||
return make_response(jsonify( { 'status': 'error', 'msg': 'Unable to toggle setting.' } ), 500)
|
return make_response(jsonify( { 'status': 'error', 'msg': 'Unable to toggle setting.' } ), 500)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/user/profile', methods=['GET', 'POST'])
|
@app.route('/user/profile', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def user_profile():
|
def user_profile():
|
||||||
@ -682,7 +698,7 @@ def user_profile():
|
|||||||
|
|
||||||
# json data
|
# json data
|
||||||
if request.data:
|
if request.data:
|
||||||
jdata = json.loads(request.data)
|
jdata = request.json
|
||||||
data = jdata['data']
|
data = jdata['data']
|
||||||
if jdata['action'] == 'enable_otp':
|
if jdata['action'] == 'enable_otp':
|
||||||
enable_otp = data['enable_otp']
|
enable_otp = data['enable_otp']
|
||||||
@ -702,7 +718,6 @@ def user_profile():
|
|||||||
save_file_name = current_user.username + '.' + file_extension
|
save_file_name = current_user.username + '.' + file_extension
|
||||||
file.save(os.path.join(app.config['UPLOAD_DIR'], 'avatar', save_file_name))
|
file.save(os.path.join(app.config['UPLOAD_DIR'], 'avatar', save_file_name))
|
||||||
|
|
||||||
|
|
||||||
# update user profile
|
# update user profile
|
||||||
user = User(username=current_user.username, plain_text_password=new_password, firstname=firstname, lastname=lastname, email=email, avatar=save_file_name, reload_info=False)
|
user = User(username=current_user.username, plain_text_password=new_password, firstname=firstname, lastname=lastname, email=email, avatar=save_file_name, reload_info=False)
|
||||||
user.update_profile()
|
user.update_profile()
|
||||||
@ -737,6 +752,7 @@ def dyndns_checkip():
|
|||||||
# route covers the default ddclient 'web' setting for the checkip service
|
# route covers the default ddclient 'web' setting for the checkip service
|
||||||
return render_template('dyndns.html', response=request.environ.get('HTTP_X_REAL_IP', request.remote_addr))
|
return render_template('dyndns.html', response=request.environ.get('HTTP_X_REAL_IP', request.remote_addr))
|
||||||
|
|
||||||
|
|
||||||
@app.route('/nic/update', methods=['GET', 'POST'])
|
@app.route('/nic/update', methods=['GET', 'POST'])
|
||||||
@dyndns_login_required
|
@dyndns_login_required
|
||||||
def dyndns_update():
|
def dyndns_update():
|
||||||
|
@ -5,7 +5,7 @@ basedir = os.path.abspath(os.path.dirname(__file__))
|
|||||||
WTF_CSRF_ENABLED = True
|
WTF_CSRF_ENABLED = True
|
||||||
SECRET_KEY = 'We are the world'
|
SECRET_KEY = 'We are the world'
|
||||||
BIND_ADDRESS = '127.0.0.1'
|
BIND_ADDRESS = '127.0.0.1'
|
||||||
PORT = 9393
|
PORT = 9191
|
||||||
LOGIN_TITLE = "PDNS"
|
LOGIN_TITLE = "PDNS"
|
||||||
|
|
||||||
# TIMEOUT - for large zones
|
# TIMEOUT - for large zones
|
||||||
|
59
configs/development.py
Normal file
59
configs/development.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import os
|
||||||
|
basedir = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
|
||||||
|
# BASIC APP CONFIG
|
||||||
|
WTF_CSRF_ENABLED = True
|
||||||
|
SECRET_KEY = 'changeme'
|
||||||
|
LOG_LEVEL = 'DEBUG'
|
||||||
|
LOG_FILE = 'log.txt'
|
||||||
|
|
||||||
|
# TIMEOUT - for large zones
|
||||||
|
TIMEOUT = 10
|
||||||
|
|
||||||
|
# UPLOAD DIR
|
||||||
|
UPLOAD_DIR = os.path.join(basedir, 'upload')
|
||||||
|
|
||||||
|
# DATABASE CONFIG FOR MYSQL
|
||||||
|
DB_USER = 'powerdnsadmin'
|
||||||
|
DB_PASSWORD = 'powerdnsadminpassword'
|
||||||
|
DB_HOST = 'docker.for.mac.localhost'
|
||||||
|
DB_NAME = 'powerdnsadmin'
|
||||||
|
|
||||||
|
#MySQL
|
||||||
|
SQLALCHEMY_DATABASE_URI = 'mysql://'+DB_USER+':'+DB_PASSWORD+'@'+DB_HOST+'/'+DB_NAME
|
||||||
|
SQLALCHEMY_MIGRATE_REPO = os.path.join(basedir, 'db_repository')
|
||||||
|
SQLALCHEMY_TRACK_MODIFICATIONS = True
|
||||||
|
|
||||||
|
# AUTHENTICATION CONFIG
|
||||||
|
BASIC_ENABLED = True
|
||||||
|
SIGNUP_ENABLED = True
|
||||||
|
|
||||||
|
## LDAP CONFIG
|
||||||
|
# LDAP_TYPE = 'ldap'
|
||||||
|
# LDAP_URI = 'ldaps://your-ldap-server:636'
|
||||||
|
# LDAP_USERNAME = 'cn=dnsuser,ou=users,ou=services,dc=duykhanh,dc=me'
|
||||||
|
# LDAP_PASSWORD = 'dnsuser'
|
||||||
|
# LDAP_SEARCH_BASE = 'ou=System Admins,ou=People,dc=duykhanh,dc=me'
|
||||||
|
## Additional options only if LDAP_TYPE=ldap
|
||||||
|
# LDAP_USERNAMEFIELD = 'uid'
|
||||||
|
# LDAP_FILTER = '(objectClass=inetorgperson)'
|
||||||
|
|
||||||
|
## GITHUB AUTHENTICATION
|
||||||
|
# GITHUB_OAUTH_ENABLE = False
|
||||||
|
# GITHUB_OAUTH_KEY = 'G0j1Q15aRsn36B3aD6nwKLiYbeirrUPU8nDd1wOC'
|
||||||
|
# GITHUB_OAUTH_SECRET = '0WYrKWePeBDkxlezzhFbDn1PBnCwEa0vCwVFvy6iLtgePlpT7WfUlAa9sZgm'
|
||||||
|
# GITHUB_OAUTH_SCOPE = 'email'
|
||||||
|
# GITHUB_OAUTH_URL = 'http://127.0.0.1:5000/api/v3/'
|
||||||
|
# GITHUB_OAUTH_TOKEN = 'http://127.0.0.1:5000/oauth/token'
|
||||||
|
# GITHUB_OAUTH_AUTHORIZE = 'http://127.0.0.1:5000/oauth/authorize'
|
||||||
|
|
||||||
|
# POWERDNS CONFIG
|
||||||
|
PDNS_STATS_URL = 'http://192.168.100.100:8081/'
|
||||||
|
PDNS_API_KEY = 'changeme'
|
||||||
|
PDNS_VERSION = '4.1.1'
|
||||||
|
|
||||||
|
# RECORDS ALLOWED TO EDIT
|
||||||
|
RECORDS_ALLOW_EDIT = ['A', 'AAAA', 'CNAME', 'SPF', 'PTR', 'MX', 'TXT']
|
||||||
|
|
||||||
|
# EXPERIMENTAL FEATURES
|
||||||
|
PRETTY_IPV6_PTR = False
|
26
create_db.py
26
create_db.py
@ -1,13 +1,16 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import os.path
|
||||||
|
import traceback
|
||||||
|
|
||||||
from migrate.versioning import api
|
from migrate.versioning import api
|
||||||
from config import SQLALCHEMY_DATABASE_URI
|
from config import SQLALCHEMY_DATABASE_URI
|
||||||
from config import SQLALCHEMY_MIGRATE_REPO
|
from config import SQLALCHEMY_MIGRATE_REPO
|
||||||
from app import db
|
from app import db
|
||||||
from app.models import Role, Setting
|
from app.models import Role, Setting
|
||||||
import os.path
|
|
||||||
import time
|
|
||||||
import sys
|
|
||||||
|
|
||||||
def start():
|
def start():
|
||||||
wait_time = get_waittime_from_env()
|
wait_time = get_waittime_from_env()
|
||||||
@ -22,13 +25,14 @@ def get_waittime_from_env():
|
|||||||
return int(os.environ.get('WAITFOR_DB', 1))
|
return int(os.environ.get('WAITFOR_DB', 1))
|
||||||
|
|
||||||
def connect_db(wait_time):
|
def connect_db(wait_time):
|
||||||
for i in xrange(0, wait_time):
|
for i in range(0, wait_time):
|
||||||
print("INFO: Wait for database server")
|
print("INFO: Wait for database server")
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
try:
|
try:
|
||||||
db.create_all()
|
db.create_all()
|
||||||
return True
|
return True
|
||||||
except:
|
except:
|
||||||
|
traceback.print_exc()
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
return False
|
return False
|
||||||
@ -36,14 +40,14 @@ def connect_db(wait_time):
|
|||||||
def init_roles(db, role_names):
|
def init_roles(db, role_names):
|
||||||
|
|
||||||
# Get key name of data
|
# Get key name of data
|
||||||
name_of_roles = map(lambda r: r.name, role_names)
|
name_of_roles = [r.name for r in role_names]
|
||||||
|
|
||||||
# Query to get current data
|
# Query to get current data
|
||||||
rows = db.session.query(Role).filter(Role.name.in_(name_of_roles)).all()
|
rows = db.session.query(Role).filter(Role.name.in_(name_of_roles)).all()
|
||||||
name_of_rows = map(lambda r: r.name, rows)
|
name_of_rows = [r.name for r in rows]
|
||||||
|
|
||||||
# Check which data that need to insert
|
# Check which data that need to insert
|
||||||
roles = filter(lambda r: r.name not in name_of_rows, role_names)
|
roles = [r for r in role_names if r.name not in name_of_rows]
|
||||||
|
|
||||||
# Insert data
|
# Insert data
|
||||||
for role in roles:
|
for role in roles:
|
||||||
@ -52,14 +56,14 @@ def init_roles(db, role_names):
|
|||||||
def init_settings(db, setting_names):
|
def init_settings(db, setting_names):
|
||||||
|
|
||||||
# Get key name of data
|
# Get key name of data
|
||||||
name_of_settings = map(lambda r: r.name, setting_names)
|
name_of_settings = [r.name for r in setting_names]
|
||||||
|
|
||||||
# Query to get current data
|
# Query to get current data
|
||||||
rows = db.session.query(Setting).filter(Setting.name.in_(name_of_settings)).all()
|
rows = db.session.query(Setting).filter(Setting.name.in_(name_of_settings)).all()
|
||||||
|
|
||||||
# Check which data that need to insert
|
# Check which data that need to insert
|
||||||
name_of_rows = map(lambda r: r.name, rows)
|
name_of_rows = [r.name for r in rows]
|
||||||
settings = filter(lambda r: r.name not in name_of_rows, setting_names)
|
settings = [r for r in setting_names if r.name not in name_of_rows]
|
||||||
|
|
||||||
# Insert data
|
# Insert data
|
||||||
for setting in settings:
|
for setting in settings:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
from migrate.versioning import api
|
from migrate.versioning import api
|
||||||
from config import SQLALCHEMY_DATABASE_URI
|
from config import SQLALCHEMY_DATABASE_URI
|
||||||
from config import SQLALCHEMY_MIGRATE_REPO
|
from config import SQLALCHEMY_MIGRATE_REPO
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
import imp
|
import imp
|
||||||
from migrate.versioning import api
|
from migrate.versioning import api
|
||||||
from app import db
|
from app import db
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
from migrate.versioning import api
|
from migrate.versioning import api
|
||||||
from config import SQLALCHEMY_DATABASE_URI
|
from config import SQLALCHEMY_DATABASE_URI
|
||||||
from config import SQLALCHEMY_MIGRATE_REPO
|
from config import SQLALCHEMY_MIGRATE_REPO
|
||||||
|
22
docker-compose.dev.yml
Normal file
22
docker-compose.dev.yml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
version: "2.1"
|
||||||
|
|
||||||
|
services:
|
||||||
|
powerdns-admin:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
image: powerdns-admin
|
||||||
|
container_name: powerdns-admin
|
||||||
|
mem_limit: 256M
|
||||||
|
memswap_limit: 256M
|
||||||
|
tty: true
|
||||||
|
command: /usr/bin/supervisord -c /etc/supervisord.conf
|
||||||
|
ports:
|
||||||
|
- "9191:9191"
|
||||||
|
volumes:
|
||||||
|
- .:/powerdns-admin/
|
||||||
|
- "./configs/development.py:/powerdns-admin/config.py"
|
||||||
|
logging:
|
||||||
|
driver: json-file
|
||||||
|
options:
|
||||||
|
max-size: 50m
|
@ -1,50 +0,0 @@
|
|||||||
version: '2'
|
|
||||||
|
|
||||||
services:
|
|
||||||
|
|
||||||
powerdns-authoritative:
|
|
||||||
image: winggundamth/powerdns-mysql:trusty
|
|
||||||
hostname: powerdns-authoritative
|
|
||||||
depends_on:
|
|
||||||
- powerdns-authoritative-mariadb
|
|
||||||
links:
|
|
||||||
- powerdns-authoritative-mariadb:mysqldb
|
|
||||||
ports:
|
|
||||||
- 172.17.0.1:53:53/udp
|
|
||||||
- 8081:8081
|
|
||||||
environment:
|
|
||||||
- PDNS_DB_HOST=mysqldb
|
|
||||||
- PDNS_DB_USERNAME=root
|
|
||||||
- PDNS_DB_NAME=powerdns
|
|
||||||
- PDNS_DB_PASSWORD=PowerDNSPassword
|
|
||||||
- PDNS_API_KEY=PowerDNSAPIKey
|
|
||||||
|
|
||||||
powerdns-authoritative-mariadb:
|
|
||||||
image: mariadb:10.1.15
|
|
||||||
hostname: powerdns-authoritative-mariadb
|
|
||||||
environment:
|
|
||||||
- MYSQL_DATABASE=powerdns
|
|
||||||
- MYSQL_ROOT_PASSWORD=PowerDNSPassword
|
|
||||||
|
|
||||||
powerdns-admin:
|
|
||||||
image: winggundamth/powerdns-admin:trusty
|
|
||||||
hostname: powerdns-admin
|
|
||||||
depends_on:
|
|
||||||
- powerdns-admin-mariadb
|
|
||||||
- powerdns-authoritative
|
|
||||||
links:
|
|
||||||
- powerdns-admin-mariadb:mysqldb
|
|
||||||
- powerdns-authoritative:powerdns-server
|
|
||||||
volumes:
|
|
||||||
- ./:/home/web/powerdns-admin
|
|
||||||
ports:
|
|
||||||
- 9393:9393
|
|
||||||
environment:
|
|
||||||
- WAITFOR_DB=60
|
|
||||||
|
|
||||||
powerdns-admin-mariadb:
|
|
||||||
image: mariadb:10.1.15
|
|
||||||
hostname: powerdns-admin-mariadb
|
|
||||||
environment:
|
|
||||||
- MYSQL_DATABASE=powerdns-admin
|
|
||||||
- MYSQL_ROOT_PASSWORD=PowerDNSAdminPassword
|
|
@ -1,14 +1,16 @@
|
|||||||
Flask>=0.10
|
Flask==0.12.2
|
||||||
Flask-WTF>=0.11
|
Flask-WTF==0.14.2
|
||||||
Flask-Login>=0.2.11
|
Flask-Login==0.4.1
|
||||||
configobj==5.0.5
|
Flask-OAuthlib==0.9.4
|
||||||
bcrypt==3.1.0
|
Flask-SQLAlchemy==2.3.2
|
||||||
requests==2.7.0
|
SQLAlchemy==1.2.5
|
||||||
python-ldap==2.4.21
|
|
||||||
Flask-SQLAlchemy==2.1
|
|
||||||
SQLAlchemy==1.0.9
|
|
||||||
sqlalchemy-migrate==0.10.0
|
sqlalchemy-migrate==0.10.0
|
||||||
pyotp==2.2.1
|
mysqlclient==1.3.12
|
||||||
qrcode==5.3
|
configobj==5.0.6
|
||||||
Flask-OAuthlib==0.9.3
|
bcrypt==3.1.4
|
||||||
dnspython>=1.12.0
|
requests==2.18.4
|
||||||
|
python-ldap==3.0.0
|
||||||
|
pyotp==2.2.6
|
||||||
|
qrcode==6.0
|
||||||
|
dnspython==1.15.0
|
||||||
|
gunicorn==19.7.1
|
||||||
|
23
supervisord.conf
Normal file
23
supervisord.conf
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
[unix_http_server]
|
||||||
|
file=/tmp/supervisor.sock ; (the path to the socket file)
|
||||||
|
chown=nobody:nogroup ; socket file uid:gid owner
|
||||||
|
|
||||||
|
[supervisord]
|
||||||
|
logfile=/tmp/supervisord.log ; (main log file;default $CWD/supervisord.log)
|
||||||
|
logfile_maxbytes=50MB ; (max main logfile bytes b4 rotation;default 50MB)
|
||||||
|
logfile_backups=10 ; (num of main logfile rotation backups;default 10)
|
||||||
|
loglevel=info ; (log level;default info; others: debug,warn,trace)
|
||||||
|
pidfile=/tmp/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
|
||||||
|
nodaemon=true ; (start in foreground if true;default false)
|
||||||
|
minfds=1024 ; (min. avail startup file descriptors;default 1024)
|
||||||
|
minprocs=200 ; (min. avail process descriptors;default 200)
|
||||||
|
|
||||||
|
[program:powerdns-admin]
|
||||||
|
command=/usr/local/bin/gunicorn -t 120 --workers 4 --bind '0.0.0.0:9191' --log-level info app:app
|
||||||
|
directory=/powerdns-admin
|
||||||
|
autostart=true
|
||||||
|
priority=999
|
||||||
|
user=www-data
|
||||||
|
redirect_stderr=true
|
||||||
|
stdout_logfile=/dev/stdout
|
||||||
|
stdout_logfile_maxbytes=0
|
Loading…
x
Reference in New Issue
Block a user