mirror of
https://github.com/cwinfo/powerdns-admin.git
synced 2025-01-07 10:55:40 +00:00
Merge pull request #229 from ngoduykhanh/development
Merge development branch to master
This commit is contained in:
commit
6e07017361
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
|
53
README.md
53
README.md
@ -1,13 +1,14 @@
|
|||||||
# PowerDNS-Admin
|
# PowerDNS-Admin
|
||||||
PowerDNS Web-GUI - Built by Flask
|
A PowerDNS web interface with advanced features.
|
||||||
|
|
||||||
#### Features:
|
#### Features:
|
||||||
- Multiple domain management
|
- Multiple domain management
|
||||||
- Local / LDAP user authentication
|
- Domain template
|
||||||
- Support Two-factor authentication (TOTP)
|
|
||||||
- User management
|
- User management
|
||||||
- User access management based on domain
|
- User access management based on domain
|
||||||
- User activity logging
|
- User activity logging
|
||||||
|
- Local DB / LDAP / Active Directory user authentication
|
||||||
|
- Support Two-factor authentication (TOTP)
|
||||||
- Dashboard and pdns service statistics
|
- Dashboard and pdns service statistics
|
||||||
- DynDNS 2 protocol support
|
- DynDNS 2 protocol support
|
||||||
- Edit IPv6 PTRs using IPv6 addresses directly (no more editing of literal addresses!)
|
- Edit IPv6 PTRs using IPv6 addresses directly (no more editing of literal addresses!)
|
||||||
@ -18,7 +19,7 @@ PowerDNS Web-GUI - Built by Flask
|
|||||||
PowerDNS-Admin supports PowerDNS autoritative server versions **3.4.2** and higher.
|
PowerDNS-Admin supports PowerDNS autoritative server versions **3.4.2** and higher.
|
||||||
|
|
||||||
### pdns Service
|
### pdns Service
|
||||||
I assume that you have already installed powerdns service. Make sure that your `/etc/pdns/pdns.conf` has these contents
|
I assume that you have already installed pdns service. Make sure that your `pdns.conf` config file has these contents
|
||||||
|
|
||||||
PowerDNS 4.0.0 and later
|
PowerDNS 4.0.0 and later
|
||||||
```
|
```
|
||||||
@ -34,55 +35,35 @@ experimental-api-key=your-powerdns-api-key
|
|||||||
webserver=yes
|
webserver=yes
|
||||||
```
|
```
|
||||||
|
|
||||||
This will enable API access in PowerDNS so PowerDNS-Admin can intergrate with PowerDNS.
|
This will enable API access in pdns service so PowerDNS-Admin can intergrate with it.
|
||||||
|
|
||||||
### Create Database
|
### Create Database
|
||||||
We will create a database which used by this web application. Please note that this database is difference from pdns database itself.
|
We will create a database which used by this web application. Please note that this database is difference from pdns database itself.
|
||||||
|
|
||||||
You could use any database that SQLAlchemy supports. For example MySQL (you will need to `pip install MySQL-python` to use MySQL backend):
|
PowerDNS-Admin supports MySQL server, Maria DB, PostgresQL and SQL Lite.
|
||||||
|
|
||||||
```
|
```
|
||||||
MariaDB [(none)]> CREATE DATABASE powerdnsadmin;
|
MariaDB [(none)]> CREATE DATABASE powerdnsadmin;
|
||||||
|
|
||||||
MariaDB [(none)]> GRANT ALL PRIVILEGES ON powerdnsadmin.* TO powerdnsadmin@'%' IDENTIFIED BY 'your-password';
|
MariaDB [(none)]> GRANT ALL PRIVILEGES ON powerdnsadmin.* TO powerdnsadmin@'%' IDENTIFIED BY 'your-password';
|
||||||
```
|
```
|
||||||
For testing purpose, you could also use SQLite as backend. This way you do not have to install `MySQL-python` dependency.
|
|
||||||
|
|
||||||
|
### Running PowerDNS-Admin
|
||||||
|
There are several ways to run PowerDNS-Admin. Following is a simple way to start PowerDNS-Admin with docker in development environment.
|
||||||
|
|
||||||
### PowerDNS-Admin
|
Firstly, let's edit `configs/developments.py` configuration file.
|
||||||
|
|
||||||
In this installation guide, I am using CentOS 7 and run my python stuffs with *virtualenv*. If you don't have it, lets install it:
|
Secondly, build the docker image of PowerDNS-Admin
|
||||||
```
|
|
||||||
$ sudo yum install python-pip
|
|
||||||
$ sudo pip install virtualenv
|
|
||||||
```
|
|
||||||
|
|
||||||
In your python web app directory, create a `flask` directory via `virtualenv`
|
``` $docker-compose -f docker-compose.dev.yml build```
|
||||||
```
|
|
||||||
$ virtualenv flask
|
|
||||||
```
|
|
||||||
|
|
||||||
Enable virtualenv and install python 3rd libraries
|
Finally, start it
|
||||||
```
|
|
||||||
$ source ./flask/bin/activate
|
|
||||||
(flask)$ pip install -r requirements.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
Web application configuration is stored in `config.py` file. Let's clone it from `config_template.py` file and then edit it
|
```$ docker-compose -f docker-compose.dev.yml up```
|
||||||
```
|
|
||||||
(flask)$ cp config_template.py config.py
|
|
||||||
(flask)$ vim config.py
|
|
||||||
```
|
|
||||||
|
|
||||||
Create database after having proper configs
|
You can now access PowerDNS-Admin at url http://localhost:9191
|
||||||
```
|
|
||||||
(flask)% ./create_db.py
|
|
||||||
```
|
|
||||||
|
|
||||||
|
NOTE: For other methods to run PowerDNS-Admin, please take look at WIKI pages.
|
||||||
Run the application and enjoy!
|
|
||||||
```
|
|
||||||
(flask)$ ./run.py
|
|
||||||
```
|
|
||||||
|
|
||||||
### Screenshots
|
### Screenshots
|
||||||
![login page](https://github.com/ngoduykhanh/PowerDNS-Admin/wiki/images/readme_screenshots/fullscreen-login.png?raw=true)
|
![login page](https://github.com/ngoduykhanh/PowerDNS-Admin/wiki/images/readme_screenshots/fullscreen-login.png?raw=true)
|
||||||
|
@ -11,6 +11,7 @@ login_manager = LoginManager()
|
|||||||
login_manager.init_app(app)
|
login_manager.init_app(app)
|
||||||
db = SQLAlchemy(app)
|
db = SQLAlchemy(app)
|
||||||
|
|
||||||
|
|
||||||
def enable_github_oauth(GITHUB_ENABLE):
|
def enable_github_oauth(GITHUB_ENABLE):
|
||||||
if not GITHUB_ENABLE:
|
if not GITHUB_ENABLE:
|
||||||
return None, None
|
return None, None
|
||||||
@ -46,7 +47,47 @@ def enable_github_oauth(GITHUB_ENABLE):
|
|||||||
|
|
||||||
return oauth, github
|
return oauth, github
|
||||||
|
|
||||||
|
|
||||||
oauth, github = enable_github_oauth(app.config.get('GITHUB_OAUTH_ENABLE'))
|
oauth, github = enable_github_oauth(app.config.get('GITHUB_OAUTH_ENABLE'))
|
||||||
|
|
||||||
|
|
||||||
|
def enable_google_oauth(GOOGLE_ENABLE):
|
||||||
|
if not GOOGLE_ENABLE:
|
||||||
|
return None
|
||||||
|
from flask_oauthlib.client import OAuth
|
||||||
|
oauth = OAuth(app)
|
||||||
|
|
||||||
|
google = oauth.remote_app(
|
||||||
|
'google',
|
||||||
|
consumer_key=app.config['GOOGLE_OAUTH_CLIENT_ID'],
|
||||||
|
consumer_secret=app.config['GOOGLE_OAUTH_CLIENT_SECRET'],
|
||||||
|
request_token_params=app.config['GOOGLE_TOKEN_PARAMS'],
|
||||||
|
base_url=app.config['GOOGLE_BASE_URL'],
|
||||||
|
request_token_url=None,
|
||||||
|
access_token_method='POST',
|
||||||
|
access_token_url=app.config['GOOGLE_TOKEN_URL'],
|
||||||
|
authorize_url=app.config['GOOGLE_AUTHORIZE_URL'],
|
||||||
|
)
|
||||||
|
|
||||||
|
@app.route('/user/authorized')
|
||||||
|
def authorized():
|
||||||
|
resp = google.authorized_response()
|
||||||
|
if resp is None:
|
||||||
|
return 'Access denied: reason=%s error=%s' % (
|
||||||
|
request.args['error_reason'],
|
||||||
|
request.args['error_description']
|
||||||
|
)
|
||||||
|
session['google_token'] = (resp['access_token'], '')
|
||||||
|
return redirect(url_for('.login'))
|
||||||
|
|
||||||
|
@google.tokengetter
|
||||||
|
def get_google_oauth_token():
|
||||||
|
return session.get('google_token')
|
||||||
|
|
||||||
|
return google
|
||||||
|
|
||||||
|
|
||||||
|
google = enable_google_oauth(app.config.get('GOOGLE_OAUTH_ENABLE'))
|
||||||
|
|
||||||
|
|
||||||
from app import views, models
|
from app import views, models
|
||||||
|
28
app/decorators.py
Normal file
28
app/decorators.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
from functools import wraps
|
||||||
|
from flask import g, request, redirect, url_for
|
||||||
|
|
||||||
|
from app import app
|
||||||
|
from app.models import Role
|
||||||
|
|
||||||
|
|
||||||
|
def admin_role_required(f):
|
||||||
|
@wraps(f)
|
||||||
|
def decorated_function(*args, **kwargs):
|
||||||
|
if g.user.role.name != 'Administrator':
|
||||||
|
return redirect(url_for('error', code=401))
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
return decorated_function
|
||||||
|
|
||||||
|
|
||||||
|
def can_access_domain(f):
|
||||||
|
@wraps(f)
|
||||||
|
def decorated_function(*args, **kwargs):
|
||||||
|
if g.user.role.name != 'Administrator':
|
||||||
|
domain_name = kwargs.get('domain_name')
|
||||||
|
user_domain = [d.name for d in g.user.get_domain()]
|
||||||
|
|
||||||
|
if domain_name not in user_domain:
|
||||||
|
return redirect(url_for('error', code=401))
|
||||||
|
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
return decorated_function
|
@ -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)
|
|
||||||
|
335
app/models.py
335
app/models.py
@ -1,33 +1,49 @@
|
|||||||
import os
|
import os
|
||||||
import ldap
|
import ldap
|
||||||
|
import ldap.filter
|
||||||
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
|
||||||
import re
|
import re
|
||||||
import dns.reversename
|
import dns.reversename
|
||||||
|
import sys
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
# LOG CONFIGS
|
||||||
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():
|
||||||
LDAP_URI = app.config['LDAP_URI']
|
LDAP_URI = app.config['LDAP_URI']
|
||||||
LDAP_USERNAME = app.config['LDAP_USERNAME']
|
|
||||||
LDAP_PASSWORD = app.config['LDAP_PASSWORD']
|
if 'LDAP_USERNAME' in app.config.keys() and 'LDAP_PASSWORD' in app.config.keys(): #backward compatability
|
||||||
|
LDAP_BIND_TYPE = 'search'
|
||||||
|
if 'LDAP_BIND_TYPE' in app.config.keys():
|
||||||
|
LDAP_BIND_TYPE = app.config['LDAP_BIND_TYPE']
|
||||||
|
if LDAP_BIND_TYPE == 'search':
|
||||||
|
LDAP_USERNAME = app.config['LDAP_USERNAME']
|
||||||
|
LDAP_PASSWORD = app.config['LDAP_PASSWORD']
|
||||||
|
|
||||||
LDAP_SEARCH_BASE = app.config['LDAP_SEARCH_BASE']
|
LDAP_SEARCH_BASE = app.config['LDAP_SEARCH_BASE']
|
||||||
LDAP_TYPE = app.config['LDAP_TYPE']
|
LDAP_TYPE = app.config['LDAP_TYPE']
|
||||||
LDAP_FILTER = app.config['LDAP_FILTER']
|
LDAP_FILTER = app.config['LDAP_FILTER']
|
||||||
LDAP_USERNAMEFIELD = app.config['LDAP_USERNAMEFIELD']
|
LDAP_USERNAMEFIELD = app.config['LDAP_USERNAMEFIELD']
|
||||||
|
|
||||||
|
LDAP_GROUP_SECURITY = app.config.get('LDAP_GROUP_SECURITY')
|
||||||
|
if LDAP_GROUP_SECURITY == True:
|
||||||
|
LDAP_ADMIN_GROUP = app.config['LDAP_ADMIN_GROUP']
|
||||||
|
LDAP_USER_GROUP = app.config['LDAP_USER_GROUP']
|
||||||
else:
|
else:
|
||||||
LDAP_TYPE = False
|
LDAP_TYPE = False
|
||||||
|
|
||||||
@ -107,10 +123,10 @@ class User(db.Model):
|
|||||||
return str(self.id) # python 3
|
return str(self.id) # python 3
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<User %r>' % (self.username)
|
return '<User {0}>'.format(self.username)
|
||||||
|
|
||||||
def get_totp_uri(self):
|
def get_totp_uri(self):
|
||||||
return 'otpauth://totp/PowerDNS-Admin:%s?secret=%s&issuer=PowerDNS-Admin' % (self.username, self.otp_secret)
|
return "otpauth://totp/PowerDNS-Admin:{0}?secret={1}&issuer=PowerDNS-Admin".format(self.username, self.otp_secret)
|
||||||
|
|
||||||
def verify_totp(self, token):
|
def verify_totp(self, token):
|
||||||
totp = pyotp.TOTP(self.otp_secret)
|
totp = pyotp.TOTP(self.otp_secret)
|
||||||
@ -141,12 +157,16 @@ class User(db.Model):
|
|||||||
try:
|
try:
|
||||||
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
|
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
|
||||||
l = ldap.initialize(LDAP_URI)
|
l = ldap.initialize(LDAP_URI)
|
||||||
l.set_option(ldap.OPT_REFERRALS, 0)
|
l.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF)
|
||||||
l.set_option(ldap.OPT_PROTOCOL_VERSION, 3)
|
l.set_option(ldap.OPT_PROTOCOL_VERSION, 3)
|
||||||
l.set_option(ldap.OPT_X_TLS,ldap.OPT_X_TLS_DEMAND)
|
l.set_option(ldap.OPT_X_TLS,ldap.OPT_X_TLS_DEMAND)
|
||||||
l.set_option( ldap.OPT_X_TLS_DEMAND, True )
|
l.set_option( ldap.OPT_X_TLS_DEMAND, True )
|
||||||
l.set_option( ldap.OPT_DEBUG_LEVEL, 255 )
|
l.set_option( ldap.OPT_DEBUG_LEVEL, 255 )
|
||||||
l.protocol_version = ldap.VERSION3
|
l.protocol_version = ldap.VERSION3
|
||||||
|
if LDAP_BIND_TYPE == "direct":
|
||||||
|
global LDAP_USERNAME; LDAP_USERNAME = self.username
|
||||||
|
global LDAP_PASSWORD; LDAP_PASSWORD = self.password
|
||||||
|
|
||||||
|
|
||||||
l.simple_bind_s(LDAP_USERNAME, LDAP_PASSWORD)
|
l.simple_bind_s(LDAP_USERNAME, LDAP_PASSWORD)
|
||||||
ldap_result_id = l.search(baseDN, searchScope, searchFilter, retrieveAttributes)
|
ldap_result_id = l.search(baseDN, searchScope, searchFilter, retrieveAttributes)
|
||||||
@ -160,7 +180,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
|
||||||
|
|
||||||
@ -173,66 +193,88 @@ class User(db.Model):
|
|||||||
|
|
||||||
if user_info:
|
if user_info:
|
||||||
if user_info.password and self.check_password(user_info.password):
|
if user_info.password and self.check_password(user_info.password):
|
||||||
logging.info('User "%s" logged in successfully' % self.username)
|
logging.info('User "{0}" logged in successfully'.format(self.username))
|
||||||
return True
|
return True
|
||||||
logging.error('User "%s" input a wrong password' % self.username)
|
logging.error('User "{0}" input a wrong password'.format(self.username))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
logging.warning('User "%s" does not exist' % self.username)
|
logging.warning('User "{0}" does not exist'.format(self.username))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if method == 'LDAP':
|
if method == 'LDAP':
|
||||||
|
allowedlogin = False
|
||||||
|
isadmin = False
|
||||||
if not LDAP_TYPE:
|
if not LDAP_TYPE:
|
||||||
logging.error('LDAP authentication is disabled')
|
logging.error('LDAP authentication is disabled')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
searchFilter = "(&(objectcategory=person)(samaccountname=%s))" % self.username
|
|
||||||
if LDAP_TYPE == 'ldap':
|
if LDAP_TYPE == 'ldap':
|
||||||
searchFilter = "(&(%s=%s)%s)" % (LDAP_USERNAMEFIELD, self.username, LDAP_FILTER)
|
searchFilter = "(&({0}={1}){2})".format(LDAP_USERNAMEFIELD, self.username, LDAP_FILTER)
|
||||||
logging.info('Ldap searchFilter "%s"' % searchFilter)
|
logging.info('Ldap searchFilter "{0}"'.format(searchFilter))
|
||||||
|
elif LDAP_TYPE == 'ad':
|
||||||
|
searchFilter = "(&(objectcategory=person)({0}={1}){2})".format(LDAP_USERNAMEFIELD, self.username, LDAP_FILTER)
|
||||||
|
|
||||||
result = self.ldap_search(searchFilter, LDAP_SEARCH_BASE)
|
result = self.ldap_search(searchFilter, LDAP_SEARCH_BASE)
|
||||||
if not result:
|
if not result:
|
||||||
logging.warning('User "%s" does not exist' % self.username)
|
logging.warning('LDAP User "{0}" does not exist'.format(self.username))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
|
|
||||||
l = ldap.initialize(LDAP_URI)
|
|
||||||
l.set_option(ldap.OPT_REFERRALS, 0)
|
|
||||||
l.set_option(ldap.OPT_PROTOCOL_VERSION, 3)
|
|
||||||
l.set_option(ldap.OPT_X_TLS,ldap.OPT_X_TLS_DEMAND)
|
|
||||||
l.set_option( ldap.OPT_X_TLS_DEMAND, True )
|
|
||||||
l.set_option( ldap.OPT_DEBUG_LEVEL, 255 )
|
|
||||||
l.protocol_version = ldap.VERSION3
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ldap_username = result[0][0][0]
|
ldap_username = ldap.filter.escape_filter_chars(result[0][0][0])
|
||||||
l.simple_bind_s(ldap_username, self.password)
|
if LDAP_GROUP_SECURITY:
|
||||||
logging.info('User "%s" logged in successfully' % self.username)
|
try:
|
||||||
except Exception:
|
if LDAP_TYPE == 'ldap':
|
||||||
logging.error('User "%s" input a wrong password' % self.username)
|
ldap_user_dn = ldap.filter.escape_filter_chars(result[0][0][0])
|
||||||
|
logging.info(result[0][0][0])
|
||||||
|
if (self.ldap_search('(member={0})'.format(ldap_user_dn) ,LDAP_ADMIN_GROUP)):
|
||||||
|
allowedlogin = True
|
||||||
|
isadmin = True
|
||||||
|
logging.info('User {0} is part of the "{1}" group that allows admin access to PowerDNS-Admin'.format(self.username,LDAP_ADMIN_GROUP))
|
||||||
|
if (self.ldap_search('(member={0})'.format(ldap_user_dn) ,LDAP_USER_GROUP)):
|
||||||
|
#if (group == LDAP_USER_GROUP):
|
||||||
|
allowedlogin = True
|
||||||
|
logging.info('User {0} is part of the "{1}" group that allows user access to PowerDNS-Admin'.format(self.username,LDAP_USER_GROUP))
|
||||||
|
if allowedlogin == False:
|
||||||
|
logging.error('User {0} is not part of the "{1}" or "{2}" groups that allow access to PowerDNS-Admin'.format(self.username,LDAP_ADMIN_GROUP,LDAP_USER_GROUP))
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
logging.error('LDAP group lookup for user "{0}" has failed'.format(e))
|
||||||
|
return False
|
||||||
|
logging.info('User "{0}" logged in successfully'.format(self.username))
|
||||||
|
except Exception as e:
|
||||||
|
logging.error('User "{0}" input a wrong LDAP password'.format(e))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# create user if not exist in the db
|
# create user if not exist in the db
|
||||||
if not User.query.filter(User.username == self.username).first():
|
if not User.query.filter(User.username == self.username).first():
|
||||||
|
self.firstname = self.username
|
||||||
|
self.lastname = ''
|
||||||
try:
|
try:
|
||||||
# try to get user's firstname & lastname from LDAP
|
# try to get user's firstname & lastname from LDAP
|
||||||
# this might be changed in the future
|
# this might be changed in the future
|
||||||
self.firstname = result[0][0][1]['givenName'][0]
|
self.firstname = result[0][0][1]['givenName']
|
||||||
self.lastname = result[0][0][1]['sn'][0]
|
self.lastname = result[0][0][1]['sn']
|
||||||
self.email = result[0][0][1]['mail'][0]
|
self.email = result[0][0][1]['mail']
|
||||||
except Exception:
|
except Exception as e:
|
||||||
self.firstname = self.username
|
logging.info("reading ldap data threw an exception {0}".format(e))
|
||||||
self.lastname = ''
|
|
||||||
|
|
||||||
# first register user will be in Administrator role
|
# first register user will be in Administrator role
|
||||||
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.create_user()
|
# user will be in Administrator role if part of LDAP Admin group
|
||||||
logging.info('Created user "%s" in the DB' % self.username)
|
if LDAP_GROUP_SECURITY:
|
||||||
|
if isadmin == True:
|
||||||
|
self.role_id = Role.query.filter_by(name='Administrator').first().id
|
||||||
|
|
||||||
|
self.create_user()
|
||||||
|
logging.info('Created user "{0}" in the DB'.format(self.username))
|
||||||
|
|
||||||
|
# user already exists in database, set their admin status based on group membership (if enabled)
|
||||||
|
if LDAP_GROUP_SECURITY:
|
||||||
|
self.set_admin(isadmin)
|
||||||
|
self.update_profile()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
logging.error('Unsupported authentication method')
|
logging.error('Unsupported authentication method')
|
||||||
@ -258,22 +300,23 @@ class User(db.Model):
|
|||||||
# check if username existed
|
# check if username existed
|
||||||
user = User.query.filter(User.username == self.username).first()
|
user = User.query.filter(User.username == self.username).first()
|
||||||
if user:
|
if user:
|
||||||
return 'Username already existed'
|
return {'status': False, 'msg': 'Username is already in use'}
|
||||||
|
|
||||||
# check if email existed
|
# check if email existed
|
||||||
user = User.query.filter(User.email == self.email).first()
|
user = User.query.filter(User.email == self.email).first()
|
||||||
if user:
|
if user:
|
||||||
return 'Email already existed'
|
return {'status': False, 'msg': 'Email address is already in use'}
|
||||||
|
|
||||||
# first register user will be in Administrator role
|
# first register user will be in Administrator role
|
||||||
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)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return True
|
return {'status': True, 'msg': 'Created user successfully'}
|
||||||
|
|
||||||
def update_profile(self, enable_otp=None):
|
def update_profile(self, enable_otp=None):
|
||||||
"""
|
"""
|
||||||
@ -303,16 +346,18 @@ class User(db.Model):
|
|||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def get_domain_query(self):
|
||||||
|
return db.session.query(User, DomainUser, Domain) \
|
||||||
|
.filter(User.id == self.id) \
|
||||||
|
.filter(User.id == DomainUser.user_id) \
|
||||||
|
.filter(Domain.id == DomainUser.domain_id)
|
||||||
|
|
||||||
def get_domain(self):
|
def get_domain(self):
|
||||||
"""
|
"""
|
||||||
Get domains which user has permission to
|
Get domains which user has permission to
|
||||||
access
|
access
|
||||||
"""
|
"""
|
||||||
user_domains = []
|
return [q[2] for q in self.get_domain_query()]
|
||||||
query = db.session.query(User, DomainUser, Domain).filter(User.id==self.id).filter(User.id==DomainUser.user_id).filter(Domain.id==DomainUser.domain_id).all()
|
|
||||||
for q in query:
|
|
||||||
user_domains.append(q[2])
|
|
||||||
return user_domains
|
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
"""
|
"""
|
||||||
@ -327,7 +372,7 @@ class User(db.Model):
|
|||||||
return True
|
return True
|
||||||
except:
|
except:
|
||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
logging.error('Cannot delete user %s from DB' % self.username)
|
logging.error('Cannot delete user {0} from DB'.format(self.username))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def revoke_privilege(self):
|
def revoke_privilege(self):
|
||||||
@ -344,7 +389,7 @@ class User(db.Model):
|
|||||||
return True
|
return True
|
||||||
except:
|
except:
|
||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
logging.error('Cannot revoke user %s privielges.' % self.username)
|
logging.error('Cannot revoke user {0} privielges'.format(self.username))
|
||||||
return False
|
return False
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -390,7 +435,7 @@ class Role(db.Model):
|
|||||||
self.description = description
|
self.description = description
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<Role %r>' % (self.name)
|
return '<Role {0}r>'.format(self.name)
|
||||||
|
|
||||||
class DomainSetting(db.Model):
|
class DomainSetting(db.Model):
|
||||||
__tablename__ = 'domain_setting'
|
__tablename__ = 'domain_setting'
|
||||||
@ -406,7 +451,7 @@ class DomainSetting(db.Model):
|
|||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<DomainSetting %r for $d>' % (setting, self.domain.name)
|
return '<DomainSetting {0} for {1}>'.format(setting, self.domain.name)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return self.setting == other.setting
|
return self.setting == other.setting
|
||||||
@ -444,15 +489,15 @@ class Domain(db.Model):
|
|||||||
self.dnssec = dnssec
|
self.dnssec = dnssec
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<Domain %r>' % (self.name)
|
return '<Domain {0}>'.format(self.name)
|
||||||
|
|
||||||
def add_setting(self, setting, value):
|
def add_setting(self, setting, value):
|
||||||
try:
|
try:
|
||||||
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 {0} for domain {1}. {2}'.format(setting, self.name, e))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_domains(self):
|
def get_domains(self):
|
||||||
@ -476,7 +521,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 +545,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 +609,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 +641,16 @@ 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 {0} successfully'.format(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()
|
logging.error('Cannot add domain {0}'.format(domain_name))
|
||||||
logging.error('Cannot add domain %s' % domain_name)
|
logging.debug(traceback.print_exc())
|
||||||
logging.debug(str(e))
|
|
||||||
return {'status': 'error', 'msg': 'Cannot add this domain.'}
|
return {'status': 'error', 'msg': 'Cannot add this domain.'}
|
||||||
|
|
||||||
def create_reverse_domain(self, domain_name, domain_reverse_name):
|
def create_reverse_domain(self, domain_name, domain_reverse_name):
|
||||||
@ -629,7 +673,7 @@ class Domain(db.Model):
|
|||||||
result = self.add(domain_reverse_name, 'Master', 'INCEPTION-INCREMENT', '', '')
|
result = self.add(domain_reverse_name, 'Master', 'INCEPTION-INCREMENT', '', '')
|
||||||
self.update()
|
self.update()
|
||||||
if result['status'] == 'ok':
|
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 = History(msg='Add reverse lookup domain {0}'.format(domain_reverse_name), detail=str({'domain_type': 'Master', 'domain_master_ips': ''}), created_by='System')
|
||||||
history.add()
|
history.add()
|
||||||
else:
|
else:
|
||||||
return {'status': 'error', 'msg': 'Adding reverse lookup domain failed'}
|
return {'status': 'error', 'msg': 'Adding reverse lookup domain failed'}
|
||||||
@ -671,13 +715,12 @@ 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/{0}'.format(domain_name)), headers=headers, method='DELETE')
|
||||||
logging.info('Delete domain %s successfully' % domain_name)
|
logging.info('Delete domain {0} successfully'.format(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()
|
logging.error('Cannot delete domain {0}'.format(domain_name))
|
||||||
logging.error('Cannot delete domain %s' % domain_name)
|
logging.debug(traceback.print_exc())
|
||||||
logging.debug(str(e))
|
|
||||||
return {'status': 'error', 'msg': 'Cannot delete domain'}
|
return {'status': 'error', 'msg': 'Cannot delete domain'}
|
||||||
|
|
||||||
def get_user(self):
|
def get_user(self):
|
||||||
@ -709,7 +752,7 @@ class Domain(db.Model):
|
|||||||
db.session.commit()
|
db.session.commit()
|
||||||
except:
|
except:
|
||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
logging.error('Cannot revoke user privielges on domain %s' % self.name)
|
logging.error('Cannot revoke user privielges on domain {0}'.format(self.name))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
for uid in added_ids:
|
for uid in added_ids:
|
||||||
@ -718,7 +761,7 @@ class Domain(db.Model):
|
|||||||
db.session.commit()
|
db.session.commit()
|
||||||
except:
|
except:
|
||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
logging.error('Cannot grant user privielges to domain %s' % self.name)
|
logging.error('Cannot grant user privielges to domain {0}'.format(self.name))
|
||||||
|
|
||||||
|
|
||||||
def update_from_master(self, domain_name):
|
def update_from_master(self, domain_name):
|
||||||
@ -730,7 +773,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/{0}/axfr-retrieve'.format(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 +789,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/{0}/cryptokeys'.format(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:
|
||||||
@ -768,7 +811,7 @@ class DomainUser(db.Model):
|
|||||||
self.user_id = user_id
|
self.user_id = user_id
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<Domain_User %r %r>' % (self.domain_id, self.user_id)
|
return '<Domain_User {0} {1}>'.format(self.domain_id, self.user_id)
|
||||||
|
|
||||||
|
|
||||||
class Record(object):
|
class Record(object):
|
||||||
@ -791,7 +834,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/{0}'.format(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 +908,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/{0}'.format(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 {0}/{1}/{2} to domain {3}. DETAIL: {4}".format(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'}
|
||||||
|
|
||||||
|
|
||||||
@ -891,7 +934,7 @@ class Record(object):
|
|||||||
list_deleted_records = [x for x in list_current_records if x not in list_new_records]
|
list_deleted_records = [x for x in list_current_records if x not in list_new_records]
|
||||||
|
|
||||||
# convert back to list of hash
|
# convert back to list of hash
|
||||||
deleted_records = [x for x in current_records if [x['name'],x['type']] in list_deleted_records and x['type'] in app.config['RECORDS_ALLOW_EDIT']]
|
deleted_records = [x for x in current_records if [x['name'],x['type']] in list_deleted_records and (x['type'] in app.config['RECORDS_ALLOW_EDIT'] and x['type'] != 'SOA')]
|
||||||
|
|
||||||
# return a tuple
|
# return a tuple
|
||||||
return deleted_records, new_records
|
return deleted_records, new_records
|
||||||
@ -1039,12 +1082,14 @@ class Record(object):
|
|||||||
})
|
})
|
||||||
|
|
||||||
postdata_for_new = {"rrsets": final_records}
|
postdata_for_new = {"rrsets": final_records}
|
||||||
|
logging.info(postdata_for_new)
|
||||||
|
logging.info(postdata_for_delete)
|
||||||
|
logging.info(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/{0}'.format(domain)))
|
||||||
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/{0}'.format(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/{0}'.format(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 +1099,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 {0}. DETAIL: {1}".format(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 +1142,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 {0}. DETAIL: {1}".format(domain, e))
|
||||||
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,19 +1162,25 @@ 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/{0}'.format(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:
|
||||||
logging.error("Cannot remove record %s/%s/%s from domain %s" % (self.name, self.type, self.data, domain))
|
logging.error("Cannot remove record {0}/{1}/{2} from domain {3}".format(self.name, self.type, self.data, domain))
|
||||||
return {'status': 'error', 'msg': 'There was something wrong, please contact administrator'}
|
return {'status': 'error', 'msg': 'There was something wrong, please contact administrator'}
|
||||||
|
|
||||||
def is_allowed(self):
|
def is_allowed_edit(self):
|
||||||
"""
|
"""
|
||||||
Check if record is allowed to edit/removed
|
Check if record is allowed to edit
|
||||||
"""
|
"""
|
||||||
return self.type in app.config['RECORDS_ALLOW_EDIT']
|
return self.type in app.config['RECORDS_ALLOW_EDIT']
|
||||||
|
|
||||||
|
def is_allowed_delete(self):
|
||||||
|
"""
|
||||||
|
Check if record is allowed to removed
|
||||||
|
"""
|
||||||
|
return (self.type in app.config['RECORDS_ALLOW_EDIT'] and self.type != 'SOA')
|
||||||
|
|
||||||
def exists(self, domain):
|
def exists(self, domain):
|
||||||
"""
|
"""
|
||||||
Check if record is present within domain records, and if it's present set self to found record
|
Check if record is present within domain records, and if it's present set self to found record
|
||||||
@ -1191,11 +1242,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/{0}'.format(domain)), headers=headers, method='PATCH', data=data)
|
||||||
logging.debug("dyndns data: " % data)
|
logging.debug("dyndns data: {0}".format(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 {0}/{1}/{2} to domain {3}. DETAIL: {4}".format(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 +1268,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/{0}/config'.format(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 +1283,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/{0}/statistics'.format(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.")
|
||||||
@ -1254,7 +1305,7 @@ class History(db.Model):
|
|||||||
self.created_by = created_by
|
self.created_by = created_by
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<History %r>' % (self.msg)
|
return '<History {0}>'.format(self.msg)
|
||||||
|
|
||||||
def add(self):
|
def add(self):
|
||||||
"""
|
"""
|
||||||
@ -1316,7 +1367,7 @@ class Setting(db.Model):
|
|||||||
db.session.commit()
|
db.session.commit()
|
||||||
return True
|
return True
|
||||||
except:
|
except:
|
||||||
logging.error('Cannot set maintenance to %s' % mode)
|
logging.error('Cannot set maintenance to {0}'.format(mode))
|
||||||
logging.debug(traceback.format_exc())
|
logging.debug(traceback.format_exc())
|
||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
return False
|
return False
|
||||||
@ -1333,10 +1384,10 @@ class Setting(db.Model):
|
|||||||
db.session.commit()
|
db.session.commit()
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
logging.error('Setting %s does not exist' % setting)
|
logging.error('Setting {0} does not exist'.format(setting))
|
||||||
return False
|
return False
|
||||||
except:
|
except:
|
||||||
logging.error('Cannot toggle setting %s' % setting)
|
logging.error('Cannot toggle setting {0}'.format(setting))
|
||||||
logging.debug(traceback.format_exec())
|
logging.debug(traceback.format_exec())
|
||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
return False
|
return False
|
||||||
@ -1351,10 +1402,90 @@ class Setting(db.Model):
|
|||||||
db.session.commit()
|
db.session.commit()
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
logging.error('Setting %s does not exist' % setting)
|
logging.error('Setting {0} does not exist'.format(setting))
|
||||||
return False
|
return False
|
||||||
except:
|
except:
|
||||||
logging.error('Cannot edit setting %s' % setting)
|
logging.error('Cannot edit setting {0}'.format(setting))
|
||||||
logging.debug(traceback.format_exec())
|
logging.debug(traceback.format_exec())
|
||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class DomainTemplate(db.Model):
|
||||||
|
__tablename__ = "domain_template"
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
name = db.Column(db.String(255), index=True, unique=True)
|
||||||
|
description = db.Column(db.String(255))
|
||||||
|
records = db.relationship('DomainTemplateRecord', back_populates='template', cascade="all, delete-orphan")
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<DomainTemplate {0}>'.format(self.name)
|
||||||
|
|
||||||
|
def __init__(self, name=None, description=None):
|
||||||
|
self.id = None
|
||||||
|
self.name = name
|
||||||
|
self.description = description
|
||||||
|
|
||||||
|
def replace_records(self, records):
|
||||||
|
try:
|
||||||
|
self.records = []
|
||||||
|
for record in records:
|
||||||
|
self.records.append(record)
|
||||||
|
db.session.commit()
|
||||||
|
return {'status': 'ok', 'msg': 'Template records have been modified'}
|
||||||
|
except Exception as e:
|
||||||
|
logging.error('Cannot create template records Error: {0}'.format(e))
|
||||||
|
db.session.rollback()
|
||||||
|
return {'status': 'error', 'msg': 'Can not create template records'}
|
||||||
|
|
||||||
|
def create(self):
|
||||||
|
try:
|
||||||
|
db.session.add(self)
|
||||||
|
db.session.commit()
|
||||||
|
return {'status': 'ok', 'msg': 'Template has been created'}
|
||||||
|
except Exception as e:
|
||||||
|
logging.error('Can not update domain template table. Error: {0}'.format(e))
|
||||||
|
db.session.rollback()
|
||||||
|
return {'status': 'error', 'msg': 'Can not update domain template table'}
|
||||||
|
|
||||||
|
def delete_template(self):
|
||||||
|
try:
|
||||||
|
self.records = []
|
||||||
|
db.session.delete(self)
|
||||||
|
db.session.commit()
|
||||||
|
return {'status': 'ok', 'msg': 'Template has been deleted'}
|
||||||
|
except Exception as e:
|
||||||
|
logging.error('Can not delete domain template. Error: {0}'.format(e))
|
||||||
|
db.session.rollback()
|
||||||
|
return {'status': 'error', 'msg': 'Can not delete domain template'}
|
||||||
|
|
||||||
|
|
||||||
|
class DomainTemplateRecord(db.Model):
|
||||||
|
__tablename__ = "domain_template_record"
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
name = db.Column(db.String(255))
|
||||||
|
type = db.Column(db.String(64))
|
||||||
|
ttl = db.Column(db.Integer)
|
||||||
|
data = db.Column(db.String(255))
|
||||||
|
status = db.Column(db.Boolean)
|
||||||
|
template_id = db.Column(db.Integer, db.ForeignKey('domain_template.id'))
|
||||||
|
template = db.relationship('DomainTemplate', back_populates='records')
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<DomainTemplateRecord {0}>'.format(self.id)
|
||||||
|
|
||||||
|
def __init__(self, id=None, name=None, type=None, ttl=None, data=None, status=None):
|
||||||
|
self.id = id
|
||||||
|
self.name = name
|
||||||
|
self.type = type
|
||||||
|
self.ttl = ttl
|
||||||
|
self.data = data
|
||||||
|
self.status = status
|
||||||
|
|
||||||
|
def apply(self):
|
||||||
|
try:
|
||||||
|
db.session.commit()
|
||||||
|
except Exception as e:
|
||||||
|
logging.error('Can not update domain template table. Error: {0}'.format(e))
|
||||||
|
db.session.rollback()
|
||||||
|
return {'status': 'error', 'msg': 'Can not update domain template table'}
|
||||||
|
@ -167,7 +167,7 @@ json_library = {
|
|||||||
return r + (pEnd || '');
|
return r + (pEnd || '');
|
||||||
},
|
},
|
||||||
prettyPrint: function(obj) {
|
prettyPrint: function(obj) {
|
||||||
obj = obj.replace(/u'/g, "\'").replace(/'/g, "\"").replace(/(False|None)/g, "\"$1\"");
|
obj = obj.replace(/"/g, "\\\"").replace(/u'/g, "\'").replace(/'/g, "\"").replace(/(False|None)/g, "\"$1\"");
|
||||||
var jsonData = JSON.parse(obj);
|
var jsonData = JSON.parse(obj);
|
||||||
var jsonLine = /^( *)("[\w]+": )?("[^"]*"|[\w.+-]*)?([,[{])?$/mg;
|
var jsonLine = /^( *)("[\w]+": )?("[^"]*"|[\w.+-]*)?([,[{])?$/mg;
|
||||||
return JSON.stringify(jsonData, null, 3)
|
return JSON.stringify(jsonData, null, 3)
|
||||||
@ -175,4 +175,4 @@ json_library = {
|
|||||||
.replace(/</g, '<').replace(/>/g, '>')
|
.replace(/</g, '<').replace(/>/g, '>')
|
||||||
.replace(jsonLine, json_library.replacer);
|
.replace(jsonLine, json_library.replacer);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -28,6 +28,14 @@
|
|||||||
<!-- form start -->
|
<!-- form start -->
|
||||||
<form role="form" method="post" action="{{ url_for('admin_createuser') }}">
|
<form role="form" method="post" action="{{ url_for('admin_createuser') }}">
|
||||||
<div class="box-body">
|
<div class="box-body">
|
||||||
|
{% if error %}
|
||||||
|
<div class="alert alert-danger alert-dismissible">
|
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||||
|
<h4><i class="icon fa fa-ban"></i> Error!</h4>
|
||||||
|
{{ error }}
|
||||||
|
</div>
|
||||||
|
<span class="help-block">{{ error }}</span>
|
||||||
|
{% endif %}
|
||||||
<div class="form-group has-feedback">
|
<div class="form-group has-feedback">
|
||||||
<label class="control-label" for="firstname">First Name</label>
|
<label class="control-label" for="firstname">First Name</label>
|
||||||
<input type="text" class="form-control" placeholder="First Name"
|
<input type="text" class="form-control" placeholder="First Name"
|
||||||
@ -40,24 +48,18 @@
|
|||||||
name="lastname" {% if user %}value={{ user.lastname }}{% endif %}> <span
|
name="lastname" {% if user %}value={{ user.lastname }}{% endif %}> <span
|
||||||
class="glyphicon glyphicon-user form-control-feedback"></span>
|
class="glyphicon glyphicon-user form-control-feedback"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group has-feedback {% if duplicate_email %}has-error{% endif %}">
|
<div class="form-group has-feedback">
|
||||||
<label class="control-label" for="email">E-mail address</label>
|
<label class="control-label" for="email">E-mail address</label>
|
||||||
<input type="email" class="form-control" placeholder="Email"
|
<input type="email" class="form-control" placeholder="Email"
|
||||||
name="email" id="email" {% if user %}value={{ user.email }}{% endif %}> <span
|
name="email" id="email" {% if user %}value={{ user.email }}{% endif %}> <span
|
||||||
class="glyphicon glyphicon-envelope form-control-feedback"></span>
|
class="glyphicon glyphicon-envelope form-control-feedback"></span>
|
||||||
{% if duplicate_email %}
|
|
||||||
<span class="help-block">This e-mail address is already in use.</span>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
<p class="login-box-msg">Enter the account details below</p>
|
<p class="login-box-msg">Enter the account details below</p>
|
||||||
<div class="form-group has-feedback {% if duplicate_username %}has-error{% endif %}">
|
<div class="form-group has-feedback">
|
||||||
<label class="control-label" for="username">Username</label>
|
<label class="control-label" for="username">Username</label>
|
||||||
<input type="text" class="form-control" placeholder="Username"
|
<input type="text" class="form-control" placeholder="Username"
|
||||||
name="username" {% if user %}value={{ user.username }}{% endif %}> <span
|
name="username" {% if user %}value={{ user.username }}{% endif %}> <span
|
||||||
class="glyphicon glyphicon-user form-control-feedback"></span>
|
class="glyphicon glyphicon-user form-control-feedback"></span>
|
||||||
{% if duplicate_username %}
|
|
||||||
<span class="help-block">This username is already in use.</span>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group has-feedback {% if blank_password %}has-error{% endif %}">
|
<div class="form-group has-feedback {% if blank_password %}has-error{% endif %}">
|
||||||
<label class="control-label" for="username">Password</label>
|
<label class="control-label" for="username">Password</label>
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
<th>Username</th>
|
<th>Username</th>
|
||||||
<th>First Name</th>
|
<th>First Name</th>
|
||||||
<th>Last Name</th>
|
<th>Last Name</th>
|
||||||
|
<th>Email</th>
|
||||||
<th>Admin</th>
|
<th>Admin</th>
|
||||||
<th>Privileges</th>
|
<th>Privileges</th>
|
||||||
<th>Deletion</th>
|
<th>Deletion</th>
|
||||||
@ -44,6 +45,7 @@
|
|||||||
<td>{{ user.username }}</td>
|
<td>{{ user.username }}</td>
|
||||||
<td>{{ user.firstname }}</td>
|
<td>{{ user.firstname }}</td>
|
||||||
<td>{{ user.lastname }}</td>
|
<td>{{ user.lastname }}</td>
|
||||||
|
<td>{{ user.email }}</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="checkbox" id="{{ user.username }}" class="admin_toggle" {% if user.role.name=='Administrator' %}checked{% endif %}>
|
<input type="checkbox" id="{{ user.username }}" class="admin_toggle" {% if user.role.name=='Administrator' %}checked{% endif %}>
|
||||||
</td>
|
</td>
|
||||||
@ -76,13 +78,24 @@
|
|||||||
// set up user data table
|
// set up user data table
|
||||||
$("#tbl_users").DataTable({
|
$("#tbl_users").DataTable({
|
||||||
"paging" : true,
|
"paging" : true,
|
||||||
"lengthChange" : false,
|
"lengthChange" : true,
|
||||||
"searching" : true,
|
"searching" : true,
|
||||||
"ordering" : true,
|
"ordering" : true,
|
||||||
"info" : true,
|
"info" : false,
|
||||||
"autoWidth" : false
|
"autoWidth" : false,
|
||||||
|
"lengthMenu": [ [10, 25, 50, 100, -1],
|
||||||
|
[10, 25, 50, 100, "All"]],
|
||||||
|
"pageLength": 10
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// avoid losing icheck box style when database refreshed
|
||||||
|
$('#tbl_users').on('draw.dt', function () {
|
||||||
|
$('.admin_toggle').iCheck({
|
||||||
|
handle: 'checkbox',
|
||||||
|
checkboxClass: 'icheckbox_square-blue'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// handle revocation of privileges
|
// handle revocation of privileges
|
||||||
$(document.body).on('click', '.button_revoke', function() {
|
$(document.body).on('click', '.button_revoke', function() {
|
||||||
var modal = $("#modal_revoke");
|
var modal = $("#modal_revoke");
|
||||||
|
@ -127,6 +127,7 @@
|
|||||||
<li><a href="{{ url_for('domain_add') }}"><i class="fa fa-plus"></i> <span>New Domain</span></a></li>
|
<li><a href="{{ url_for('domain_add') }}"><i class="fa fa-plus"></i> <span>New Domain</span></a></li>
|
||||||
<li class="header">ADMINISTRATION</li>
|
<li class="header">ADMINISTRATION</li>
|
||||||
<li><a href="{{ url_for('admin') }}"><i class="fa fa-wrench"></i> <span>Admin Console</span></a></li>
|
<li><a href="{{ url_for('admin') }}"><i class="fa fa-wrench"></i> <span>Admin Console</span></a></li>
|
||||||
|
<li><a href="{{ url_for('templates') }}"><i class="fa fa-clone"></i> <span>Domain Templates</span></a></li>
|
||||||
<li><a href="{{ url_for('admin_manageuser') }}"><i class="fa fa-users"></i> <span>Users</span></a></li>
|
<li><a href="{{ url_for('admin_manageuser') }}"><i class="fa fa-users"></i> <span>Users</span></a></li>
|
||||||
<li><a href="{{ url_for('admin_history') }}"><i class="fa fa-calendar"></i> <span>History</span></a></li>
|
<li><a href="{{ url_for('admin_history') }}"><i class="fa fa-calendar"></i> <span>History</span></a></li>
|
||||||
<li><a href="{{ url_for('admin_settings') }}"><i class="fa fa-cog"></i> <span>Settings</span></a></li>
|
<li><a href="{{ url_for('admin_settings') }}"><i class="fa fa-cog"></i> <span>Settings</span></a></li>
|
||||||
|
@ -125,16 +125,6 @@
|
|||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
<h3 class="box-title">Hosted Domains</h3>
|
<h3 class="box-title">Hosted Domains</h3>
|
||||||
</div>
|
</div>
|
||||||
<!-- /.box-header -->
|
|
||||||
<!--
|
|
||||||
{% if current_user.role.name == 'Administrator' %}
|
|
||||||
<div class="box-body">
|
|
||||||
<button type="button" class="btn btn-flat btn-primary" onclick="window.location.href='{{ url_for('domain_add') }}'">
|
|
||||||
New Domain <i class="fa fa-plus"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
-->
|
|
||||||
<div class="box-body">
|
<div class="box-body">
|
||||||
<table id="tbl_domain_list" class="table table-bordered table-striped">
|
<table id="tbl_domain_list" class="table table-bordered table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
@ -144,53 +134,11 @@
|
|||||||
<th>Type</th>
|
<th>Type</th>
|
||||||
<th>Serial</th>
|
<th>Serial</th>
|
||||||
<th>Master</th>
|
<th>Master</th>
|
||||||
<th>Action</th>
|
<th {% if current_user.role.name !='Administrator' %}width="6%"{% else %}width="25%"{% endif %}>Action</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for domain in domains %}
|
<!-- Content loaded via AJAX. -->
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<a href="{{ url_for('domain', domain_name=domain.name) }}"><strong>{{ domain.name }}</strong></a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{% if domain.dnssec %}
|
|
||||||
<button type="button" class="btn btn-flat dnssec btn-success button_dnssec" id="{{ domain.name }}" style="width:100%;">
|
|
||||||
<i class="fa fa-lock"></i> Enabled
|
|
||||||
</button>
|
|
||||||
{% else %}
|
|
||||||
<button type="button" class="btn btn-flat dnssec button_dnssec" id="{{ domain.name }}" style="width:100%;">
|
|
||||||
<i class="fa fa-unlock-alt"></i> Disabled
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ domain.type }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{% 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 %}
|
|
||||||
</td>
|
|
||||||
{% if current_user.role.name !='Administrator' %}
|
|
||||||
<td width="6%">
|
|
||||||
<button type="button" class="btn btn-flat btn-success" onclick="window.location.href='{{ url_for('domain', domain_name=domain.name) }}'">
|
|
||||||
Manage <i class="fa fa-cog"></i>
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
{% else %}
|
|
||||||
<td width="20%">
|
|
||||||
<button type="button" class="btn btn-flat btn-success" onclick="window.location.href='{{ url_for('domain', domain_name=domain.name) }}'">
|
|
||||||
Manage <i class="fa fa-cog"></i>
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn btn-flat btn-danger" onclick="window.location.href='{{ url_for('domain_management', domain_name=domain.name) }}'">
|
|
||||||
Admin <i class="fa fa-cog"></i>
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
{% endif %}
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@ -214,23 +162,26 @@
|
|||||||
"ordering" : false,
|
"ordering" : false,
|
||||||
"info" : false,
|
"info" : false,
|
||||||
"autoWidth" : false
|
"autoWidth" : false
|
||||||
});
|
});
|
||||||
// set up domain list
|
// set up domain list
|
||||||
$("#tbl_domain_list").DataTable({
|
$("#tbl_domain_list").DataTable({
|
||||||
"paging" : true,
|
"paging" : true,
|
||||||
"lengthChange" : true,
|
"lengthChange" : true,
|
||||||
"searching" : true,
|
"searching" : true,
|
||||||
"ordering" : true,
|
"ordering" : true,
|
||||||
|
"processing" : true,
|
||||||
|
"serverSide" : true,
|
||||||
|
"ajax" : "{{ url_for('dashboard_domains') }}",
|
||||||
"info" : false,
|
"info" : false,
|
||||||
"autoWidth" : false,
|
"autoWidth" : false,
|
||||||
{% if default_domain_table_size_setting in ['10','25','50','100'] %}
|
{% if default_domain_table_size_setting in ['10','25','50','100'] %}
|
||||||
"lengthMenu": [ [10, 25, 50, 100, -1],
|
"lengthMenu": [ [10, 25, 50, 100, -1],
|
||||||
[10, 25, 50, 100, "All"]],
|
[10, 25, 50, 100, "All"]],
|
||||||
{% else %}
|
{% else %}
|
||||||
"lengthMenu": [ [10, 25, 50, 100, {{ default_domain_table_size_setting }}, -1],
|
"lengthMenu": [ [10, 25, 50, 100, {{ default_domain_table_size_setting }}, -1],
|
||||||
[10, 25, 50, 100, {{ default_domain_table_size_setting }}, "All"]],
|
[10, 25, 50, 100, {{ default_domain_table_size_setting }}, "All"]],
|
||||||
{% endif %}
|
{% endif %}
|
||||||
"pageLength": {{ default_domain_table_size_setting }}
|
"pageLength": {{ default_domain_table_size_setting }}
|
||||||
});
|
});
|
||||||
$(document.body).on('click', '.history-info-button', function() {
|
$(document.body).on('click', '.history-info-button', function() {
|
||||||
var modal = $("#modal_history_info");
|
var modal = $("#modal_history_info");
|
||||||
@ -238,9 +189,30 @@
|
|||||||
$('#modal-code-content').html(json_library.prettyPrint(info));
|
$('#modal-code-content').html(json_library.prettyPrint(info));
|
||||||
modal.modal('show');
|
modal.modal('show');
|
||||||
});
|
});
|
||||||
$(document.body).on("click", ".button_dnssec", function() {
|
|
||||||
|
$(document.body).on("click", ".button_template", function (e) {
|
||||||
|
var modal = $("#modal_template");
|
||||||
var domain = $(this).prop('id');
|
var domain = $(this).prop('id');
|
||||||
getdnssec($SCRIPT_ROOT + '/domain/' + domain + '/dnssec');
|
var form = " <label for=\"template_name\">Template name</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"template_name\" id=\"template_name\" placeholder=\"Enter a valid template name (required)\"> \
|
||||||
|
<label for=\"template_description\">Template description</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"template_description\" id=\"template_description\" placeholder=\"Enter a template description (optional)\"> \
|
||||||
|
<input id=\"domain\" name=\"domain\" type=\"hidden\" value=\""+domain+"\"> \
|
||||||
|
";
|
||||||
|
modal.find('.modal-body p').html(form);
|
||||||
|
modal.find('#button_save').click(function() {
|
||||||
|
var data = {};
|
||||||
|
data['name'] = modal.find('#template_name').val();
|
||||||
|
data['description'] = modal.find('#template_description').val();
|
||||||
|
data['domain'] = modal.find('#domain').val();
|
||||||
|
applyChanges(data, $SCRIPT_ROOT + "{{ url_for('create_template_from_zone') }}", true);
|
||||||
|
modal.modal('hide');
|
||||||
|
})
|
||||||
|
modal.find('#button_close').click(function() {
|
||||||
|
modal.modal('hide');
|
||||||
|
})
|
||||||
|
|
||||||
|
modal.modal('show');
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -267,8 +239,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- /.modal-dialog -->
|
<!-- /.modal-dialog -->
|
||||||
</div>
|
</div>
|
||||||
<!-- /.modal -->
|
<div class="modal fade modal-primary" id="modal_template">
|
||||||
<div class="modal fade" id="modal_dnssec_info">
|
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
@ -276,19 +247,19 @@
|
|||||||
aria-label="Close">
|
aria-label="Close">
|
||||||
<span aria-hidden="true">×</span>
|
<span aria-hidden="true">×</span>
|
||||||
</button>
|
</button>
|
||||||
<h4 class="modal-title">DNSSEC</h4>
|
<h4 class="modal-title">Clone to template</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p></p>
|
<p></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-flat btn-default pull-right"
|
<button type="button" class="btn btn-flat btn-default pull-left"
|
||||||
data-dismiss="modal">Close</button>
|
id="button_close" data-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-flat btn-primary" id="button_save">Save</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- /.modal-content -->
|
<!-- /.modal-content -->
|
||||||
</div>
|
</div>
|
||||||
<!-- /.modal-dialog -->
|
<!-- /.modal-dialog -->
|
||||||
</div>
|
</div>
|
||||||
<!-- /.modal -->
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
45
app/templates/dashboard_domain.html
Normal file
45
app/templates/dashboard_domain.html
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
{% macro name(domain) %}
|
||||||
|
<a href="{{ url_for('domain', domain_name=domain.name) }}"><strong>{{ domain.name }}</strong></a>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro dnssec(domain) %}
|
||||||
|
{% if domain.dnssec %}
|
||||||
|
<td><span class="label label-success"><i class="fa fa-lock-alt"></i> Enabled</span></td>
|
||||||
|
{% else %}
|
||||||
|
<td><span class="label label-primary"><i class="fa fa-unlock-alt"></i> Disabled</span></td>
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro type(domain) %}
|
||||||
|
{{ domain.type }}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro serial(domain) %}
|
||||||
|
{% if domain.serial == 0 %}{{ domain.notified_serial }}{% else %}{{domain.serial}}{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro master(domain) %}
|
||||||
|
{% if domain.master == '[]'%}N/A{% else %}{{ domain.master|display_master_name }}{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro actions(domain) %}
|
||||||
|
{% if current_user.role.name =='Administrator' %}
|
||||||
|
<td width="25%">
|
||||||
|
<button type="button" class="btn btn-flat btn-success button_template" id="{{ domain.name }}">
|
||||||
|
Template <i class="fa fa-clone"></i>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-flat btn-success" onclick="window.location.href='{{ url_for('domain', domain_name=domain.name) }}'">
|
||||||
|
Manage <i class="fa fa-cog"></i>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-flat btn-danger" onclick="window.location.href='{{ url_for('domain_management', domain_name=domain.name) }}'">
|
||||||
|
Admin <i class="fa fa-cog"></i>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
{% else %}
|
||||||
|
<td width="6%">
|
||||||
|
<button type="button" class="btn btn-flat btn-success" onclick="window.location.href='{{ url_for('domain', domain_name=domain.name) }}'">
|
||||||
|
Manage <i class="fa fa-cog"></i>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
@ -1,10 +1,10 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}<title>DNS Control Panel - DOMAIN</title>{% endblock %}
|
{% block title %}<title>{{ domain.name }} - DNS Control Panel</title>{% endblock %}
|
||||||
|
|
||||||
{% block dashboard_stat %}
|
{% block dashboard_stat %}
|
||||||
<section class="content-header">
|
<section class="content-header">
|
||||||
<h1>
|
<h1>
|
||||||
Manage domain <small>{{ domain.name }}</small>
|
Manage domain: <b>{{ domain.name }}</b>
|
||||||
</h1>
|
</h1>
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="{{ url_for('dashboard') }}"><i
|
<li><a href="{{ url_for('dashboard') }}"><i
|
||||||
@ -20,9 +20,6 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12">
|
<div class="col-xs-12">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header">
|
|
||||||
<h3 class="box-title">Manage Records for {{ domain.name }}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="box-body">
|
<div class="box-body">
|
||||||
{% if domain.type != 'Slave' %}
|
{% if domain.type != 'Slave' %}
|
||||||
<button type="button" class="btn btn-flat btn-primary pull-left button_add_record" id="{{ domain.name }}">
|
<button type="button" class="btn btn-flat btn-primary pull-left button_add_record" id="{{ domain.name }}">
|
||||||
@ -70,25 +67,23 @@
|
|||||||
</td>
|
</td>
|
||||||
{% if domain.type != 'Slave' %}
|
{% if domain.type != 'Slave' %}
|
||||||
<td width="6%">
|
<td width="6%">
|
||||||
{% if record.is_allowed() %}
|
{% if record.is_allowed_edit() %}
|
||||||
<button type="button" class="btn btn-flat btn-warning button_edit" id="{{ (record.name,domain.name)|display_record_name }}">Edit <i class="fa fa-edit"></i></button>
|
<button type="button" class="btn btn-flat btn-warning button_edit" id="{{ (record.name,domain.name)|display_record_name }}">Edit <i class="fa fa-edit"></i></button>
|
||||||
{% else %}
|
{% else %}
|
||||||
<button type="button" class="btn btn-flat btn-warning""> <i class="fa fa-exclamation-circle"></i> </button>
|
<button type="button" class="btn btn-flat btn-warning""> <i class="fa fa-exclamation-circle"></i> </button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td width="6%">
|
<td width="6%">
|
||||||
{% if record.is_allowed() %}
|
{% if record.is_allowed_delete() %}
|
||||||
<button type="button" class="btn btn-flat btn-danger button_delete" id="{{ (record.name,domain.name)|display_record_name }}">Delete <i class="fa fa-trash"></i></button>
|
<button type="button" class="btn btn-flat btn-danger button_delete" id="{{ (record.name,domain.name)|display_record_name }}">Delete <i class="fa fa-trash"></i></button>
|
||||||
{% else %}
|
|
||||||
<button type="button" class="btn btn-flat btn-warning""> <i class="fa fa-exclamation-circle"></i> </button>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<td width="6%">
|
<td width="6%">
|
||||||
<button type="button" class="btn btn-flat btn-warning""> <i class="fa fa-exclamation-circle"></i> </button>
|
<button type="button" class="btn btn-flat btn-warning"> <i class="fa fa-exclamation-circle"></i> </button>
|
||||||
</td>
|
</td>
|
||||||
<td width="6%">
|
<td width="6%">
|
||||||
<button type="button" class="btn btn-flat btn-warning""> <i class="fa fa-exclamation-circle"></i> </button>
|
<button type="button" class="btn btn-flat btn-warning"> <i class="fa fa-exclamation-circle"></i> </button>
|
||||||
</td>
|
</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<!-- hidden column that we can sort on -->
|
<!-- hidden column that we can sort on -->
|
||||||
@ -265,7 +260,37 @@
|
|||||||
$(document.body).on("focus", "#current_edit_record_data", function (e) {
|
$(document.body).on("focus", "#current_edit_record_data", function (e) {
|
||||||
var record_type = $(this).parents("tr").find('#record_type').val();
|
var record_type = $(this).parents("tr").find('#record_type').val();
|
||||||
var record_data = $(this);
|
var record_data = $(this);
|
||||||
if (record_type == "MX") {
|
if (record_type == "CAA") {
|
||||||
|
var modal = $("#modal_custom_record");
|
||||||
|
if (record_data.val() == "") {
|
||||||
|
var form = " <label for=\"caa_flag\">CAA Flag</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"caa_flag\" id=\"caa_flag\" placeholder=\"0\"> \
|
||||||
|
<label for=\"caa_tag\">CAA Tag</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"caa_tag\" id=\"caa_tag\" placeholder=\"issue\"> \
|
||||||
|
<label for=\"caa_value\">CAA Value</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"caa_value\" id=\"caa_value\" placeholder=\"eg. letsencrypt.org\"> \
|
||||||
|
";
|
||||||
|
} else {
|
||||||
|
var parts = record_data.val().split(" ");
|
||||||
|
var form = " <label for=\"caa_flag\">CAA Flag</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"caa_flag\" id=\"caa_flag\" placeholder=\"0\" value=\"" + parts[0] + "\"> \
|
||||||
|
<label for=\"caa_tag\">CAA Tag</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"caa_tag\" id=\"caa_tag\" placeholder=\"issue\" value=\"" + parts[1] + "\"> \
|
||||||
|
<label for=\"caa_value\">CAA Value</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"caa_value\" id=\"caa_value\" placeholder=\"eg. letsencrypt.org\" value=\"" + parts[2] + "\"> \
|
||||||
|
";
|
||||||
|
}
|
||||||
|
modal.find('.modal-body p').html(form);
|
||||||
|
modal.find('#button_save').click(function() {
|
||||||
|
caa_flag = modal.find('#caa_flag').val();
|
||||||
|
caa_tag = modal.find('#caa_tag').val();
|
||||||
|
caa_value = modal.find('#caa_value').val();
|
||||||
|
data = caa_flag + " " + caa_tag + " " + '"' + caa_value + '"';
|
||||||
|
record_data.val(data);
|
||||||
|
modal.modal('hide');
|
||||||
|
})
|
||||||
|
modal.modal('show');
|
||||||
|
} else if (record_type == "MX") {
|
||||||
var modal = $("#modal_custom_record");
|
var modal = $("#modal_custom_record");
|
||||||
if (record_data.val() == "") {
|
if (record_data.val() == "") {
|
||||||
var form = " <label for=\"mx_priority\">MX Priority</label> \
|
var form = " <label for=\"mx_priority\">MX Priority</label> \
|
||||||
@ -324,6 +349,7 @@
|
|||||||
record_data.val(data);
|
record_data.val(data);
|
||||||
modal.modal('hide');
|
modal.modal('hide');
|
||||||
})
|
})
|
||||||
|
modal.modal('show');
|
||||||
} else if (record_type == "SOA") {
|
} else if (record_type == "SOA") {
|
||||||
var modal = $("#modal_custom_record");
|
var modal = $("#modal_custom_record");
|
||||||
if (record_data.val() == "") {
|
if (record_data.val() == "") {
|
||||||
|
@ -47,6 +47,15 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Select a template</label>
|
||||||
|
<select class="form-control" id="domain_template" name="domain_template">
|
||||||
|
<option value="0">No template</option>
|
||||||
|
{% for template in templates %}
|
||||||
|
<option value="{{ template.id }}">{{ template.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div class="form-group" style="display: none;" id="domain_master_address_div">
|
<div class="form-group" style="display: none;" id="domain_master_address_div">
|
||||||
<input type="text" class="form-control" name="domain_master_address" id="domain_master_address" placeholder="Enter valid master ip addresses (separated by commas)">
|
<input type="text" class="form-control" name="domain_master_address" id="domain_master_address" placeholder="Enter valid master ip addresses (separated by commas)">
|
||||||
</div>
|
</div>
|
||||||
@ -93,7 +102,7 @@
|
|||||||
|
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
<button type="submit" class="btn btn-flat btn-primary">Submit</button>
|
<button type="submit" class="btn btn-flat btn-primary">Submit</button>
|
||||||
<button type="submit" class="btn btn-flat btn-default" onclick="window.location.href='{{ url_for('dashboard') }}'">Cancel</button>
|
<button type="button" class="btn btn-flat btn-default" onclick="window.location.href='{{ url_for('dashboard') }}'">Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -98,6 +98,9 @@
|
|||||||
<!-- /.col -->
|
<!-- /.col -->
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
{% if google_enabled %}
|
||||||
|
<a href="{{ url_for('google_login') }}">Google oauth login</a>
|
||||||
|
{% endif %}
|
||||||
{% if github_enabled %}
|
{% if github_enabled %}
|
||||||
<a href="{{ url_for('github_login') }}">Github oauth login</a>
|
<a href="{{ url_for('github_login') }}">Github oauth login</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
116
app/templates/template.html
Normal file
116
app/templates/template.html
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}<title>DNS Control Panel - Templates</title>{% endblock %}
|
||||||
|
|
||||||
|
{% block dashboard_stat %}
|
||||||
|
<!-- Content Header (Page header) -->
|
||||||
|
<section class="content-header">
|
||||||
|
<h1>
|
||||||
|
Templates
|
||||||
|
<small>List</small>
|
||||||
|
</h1>
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li><a href="{{ url_for('templates') }}"><i class="fa fa-dashboard"></i> Templates</a></li>
|
||||||
|
<li class="active">List</li>
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
|
{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<!-- Main content -->
|
||||||
|
<section class="content">
|
||||||
|
{% with errors = get_flashed_messages(category_filter=["error"]) %} {% if errors %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="alert alert-danger alert-dismissible">
|
||||||
|
<button type="button" class="close" data-dismiss="alert"
|
||||||
|
aria-hidden="true">×</button>
|
||||||
|
<h4>
|
||||||
|
<i class="icon fa fa-ban"></i> Error!
|
||||||
|
</h4>
|
||||||
|
<div class="alert-message block-message error">
|
||||||
|
<a class="close" href="#">x</a>
|
||||||
|
<ul>
|
||||||
|
{%- for msg in errors %}
|
||||||
|
<li>{{ msg }}</li> {% endfor -%}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %} {% endwith %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<div class="box">
|
||||||
|
<div class="box-header">
|
||||||
|
<h3 class="box-title">Templates</h3>
|
||||||
|
</div>
|
||||||
|
<div class="box-body">
|
||||||
|
<a href="{{ url_for('create_template') }}">
|
||||||
|
<button type="button" class="btn btn-flat btn-primary pull-left">
|
||||||
|
Create Template <i class="fa fa-plus"></i>
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="box-body">
|
||||||
|
<table id="tbl_template_list" class="table table-bordered table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Number of Records</th>
|
||||||
|
<th width="20%">Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for template in templates %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('edit_template', template=template.name) }}"><strong>{{ template.name }}</strong></a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ template.description }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ template.records|count }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('edit_template', template=template.name) }}">
|
||||||
|
<button type="button" class="btn btn-flat btn-warning button_edit" id="">
|
||||||
|
Edit <i class="fa fa-edit"></i>
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
<a href="{{ url_for('delete_template', template=template.name) }}">
|
||||||
|
<button type="button" class="btn btn-flat btn-danger button_delete" id="">
|
||||||
|
Delete <i class="fa fa-trash"></i>
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<!-- /.box-body -->
|
||||||
|
</div>
|
||||||
|
<!-- /.box -->
|
||||||
|
</div>
|
||||||
|
<!-- /.col -->
|
||||||
|
</div>
|
||||||
|
<!-- /.row -->
|
||||||
|
</section>
|
||||||
|
<!-- /.content -->
|
||||||
|
{% endblock %}
|
||||||
|
{% block extrascripts %}
|
||||||
|
<script>
|
||||||
|
// set up history data table
|
||||||
|
$("#tbl_template_list").DataTable({
|
||||||
|
"paging" : true,
|
||||||
|
"lengthChange" : true,
|
||||||
|
"searching" : true,
|
||||||
|
"ordering" : true,
|
||||||
|
"info" : false,
|
||||||
|
"autoWidth" : false
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
{% block modals %}
|
||||||
|
{% endblock %}
|
104
app/templates/template_add.html
Normal file
104
app/templates/template_add.html
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}<title>DNS Control Panel - Create Template</title>{% endblock %}
|
||||||
|
|
||||||
|
{% block dashboard_stat %}
|
||||||
|
<!-- Content Header (Page header) -->
|
||||||
|
<section class="content-header">
|
||||||
|
<h1>
|
||||||
|
Template
|
||||||
|
<small>Create</small>
|
||||||
|
</h1>
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li><a href="{{ url_for('templates') }}"><i class="fa fa-dashboard"></i> Templates</a></li>
|
||||||
|
<li class="active">Create</li>
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<section class="content">
|
||||||
|
{% with errors = get_flashed_messages(category_filter=["error"]) %} {%
|
||||||
|
if errors %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="alert alert-danger alert-dismissible">
|
||||||
|
<button type="button" class="close" data-dismiss="alert"
|
||||||
|
aria-hidden="true">×</button>
|
||||||
|
<h4>
|
||||||
|
<i class="icon fa fa-ban"></i> Error!
|
||||||
|
</h4>
|
||||||
|
<div class="alert-message block-message error">
|
||||||
|
<a class="close" href="#">x</a>
|
||||||
|
<ul>
|
||||||
|
{%- for msg in errors %}
|
||||||
|
<li>{{ msg }}</li> {% endfor -%}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %} {% endwith %}
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="box box-primary">
|
||||||
|
<div class="box-header with-border">
|
||||||
|
<h3 class="box-title">Create new template</h3>
|
||||||
|
</div>
|
||||||
|
<!-- /.box-header -->
|
||||||
|
<!-- form start -->
|
||||||
|
<form role="form" method="post"
|
||||||
|
action="{{ url_for('create_template') }}">
|
||||||
|
<div class="box-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control" name="name" id="name"
|
||||||
|
placeholder="Enter a valid template name (required)">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control" name="description"
|
||||||
|
id="description"
|
||||||
|
placeholder="Enter a template description (optional)">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box-footer">
|
||||||
|
<button type="submit" class="btn btn-flat btn-primary">Submit</button>
|
||||||
|
<button type="button" class="btn btn-flat btn-default"
|
||||||
|
onclick="window.location.href='{{ url_for('templates') }}'">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<!-- /.box -->
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="box box-primary">
|
||||||
|
<div class="box-header with-border">
|
||||||
|
<h3 class="box-title">Help with creating a new template</h3>
|
||||||
|
</div>
|
||||||
|
<div class="box-body">
|
||||||
|
<dl class="dl-horizontal">
|
||||||
|
<dt>Template name</dt>
|
||||||
|
<dd>Enter your template name, this is the name of the template that
|
||||||
|
will be shown to users. The name should not have any spaces but
|
||||||
|
can have symbols.</dd>
|
||||||
|
<dt>Template description</dt>
|
||||||
|
<dd>Enter your template description, this is to help better
|
||||||
|
identify the template.</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endblock %}
|
||||||
|
{% block extrascripts %}
|
||||||
|
<script>
|
||||||
|
$("input[name=radio_type]").change(function() {
|
||||||
|
var type = $(this).val();
|
||||||
|
if (type == "slave") {
|
||||||
|
$("#domain_master_address_div").show();
|
||||||
|
} else {
|
||||||
|
$("#domain_master_address_div").hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
415
app/templates/template_edit.html
Normal file
415
app/templates/template_edit.html
Normal file
@ -0,0 +1,415 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}<title>DNS Control Panel - Edit Template</title>{% endblock %}
|
||||||
|
|
||||||
|
{% block dashboard_stat %}
|
||||||
|
<section class="content-header">
|
||||||
|
<h1>
|
||||||
|
Edit template <small>{{ template }}</small>
|
||||||
|
</h1>
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li><a href="{{ url_for('dashboard') }}"><i
|
||||||
|
class="fa fa-dashboard"></i> Home</a></li>
|
||||||
|
<li>Templates</li>
|
||||||
|
<li class="active">{{ template }}</li>
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<section class="content">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<div class="box">
|
||||||
|
<div class="box-header">
|
||||||
|
<h3 class="box-title">Manage Template Records for {{ template }}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="box-body">
|
||||||
|
<button type="button" class="btn btn-flat btn-primary pull-left button_add_record" id="{{ template }}">
|
||||||
|
Add Record <i class="fa fa-plus"></i>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-flat btn-primary pull-right button_apply_changes" id="{{ template }}">
|
||||||
|
Apply Changes <i class="fa fa-floppy-o"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="box-body">
|
||||||
|
<table id="tbl_records" class="table table-bordered table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>TTL</th>
|
||||||
|
<th>Data</th>
|
||||||
|
<th>Edit</th>
|
||||||
|
<th>Delete</th>
|
||||||
|
<th>ID</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for record in records %}
|
||||||
|
<tr class="odd row_record" id="{{ record.name }}">
|
||||||
|
<td>
|
||||||
|
{{ record.name }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ record.type }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ record.status }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ record.ttl }}
|
||||||
|
</td>
|
||||||
|
<td class="length-break">
|
||||||
|
{{ record.data }}
|
||||||
|
</td>
|
||||||
|
<td width="6%">
|
||||||
|
<button type="button" class="btn btn-flat btn-warning button_edit" id="{{ record.name }}">
|
||||||
|
Edit <i class="fa fa-edit"></i>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
<td width="6%">
|
||||||
|
<button type="button" class="btn btn-flat btn-danger button_delete" id="{{ record.name }}">
|
||||||
|
Delete <i class="fa fa-trash"></i>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ record.id }}
|
||||||
|
</td>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endblock %}
|
||||||
|
{% block extrascripts %}
|
||||||
|
<script>
|
||||||
|
// superglobals
|
||||||
|
window.records_allow_edit = {{ editable_records|tojson }};
|
||||||
|
window.nEditing = null;
|
||||||
|
window.nNew = false;
|
||||||
|
|
||||||
|
// set up user data table
|
||||||
|
$("#tbl_records").DataTable({
|
||||||
|
"paging" : true,
|
||||||
|
"lengthChange" : true,
|
||||||
|
"searching" : true,
|
||||||
|
"ordering" : true,
|
||||||
|
"info" : true,
|
||||||
|
"autoWidth" : false,
|
||||||
|
{% if default_record_table_size_setting in ['5','15','20'] %}
|
||||||
|
"lengthMenu": [ [5, 15, 20, -1],
|
||||||
|
[5, 15, 20, "All"]],
|
||||||
|
{% else %}
|
||||||
|
"lengthMenu": [ [5, 15, 20, {{ default_record_table_size_setting }}, -1],
|
||||||
|
[5, 15, 20, {{ default_record_table_size_setting }}, "All"]],
|
||||||
|
{% endif %}
|
||||||
|
"pageLength": {{ default_record_table_size_setting }},
|
||||||
|
"language": {
|
||||||
|
"lengthMenu": " _MENU_ records"
|
||||||
|
},
|
||||||
|
"retrieve" : true,
|
||||||
|
"columnDefs": [{
|
||||||
|
"targets": [ 7 ],
|
||||||
|
"visible": false,
|
||||||
|
"searchable": false
|
||||||
|
}]
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// handle delete button
|
||||||
|
$(document.body).on("click", ".button_delete", function(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
var modal = $("#modal_delete");
|
||||||
|
var table = $("#tbl_records").DataTable();
|
||||||
|
var record = $(this).prop('id');
|
||||||
|
var nRow = $(this).parents('tr')[0];
|
||||||
|
var info = "Are you sure you want to delete " + record + "?";
|
||||||
|
modal.find('.modal-body p').text(info);
|
||||||
|
modal.find('#button_delete_confirm').click(function() {
|
||||||
|
table.row(nRow).remove().draw();
|
||||||
|
modal.modal('hide');
|
||||||
|
})
|
||||||
|
modal.modal('show');
|
||||||
|
|
||||||
|
});
|
||||||
|
// handle edit button
|
||||||
|
$(document.body).on("click", ".button_edit, .row_record", function(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if ($(this).is('tr')) {
|
||||||
|
var nRow = $(this)[0];
|
||||||
|
} else {
|
||||||
|
var nRow = $(this).parents('tr')[0];
|
||||||
|
}
|
||||||
|
var table = $("#tbl_records").DataTable();
|
||||||
|
|
||||||
|
if (nEditing == nRow) {
|
||||||
|
/* click on row already being edited, do nothing */
|
||||||
|
} else if (nEditing !== null && nEditing != nRow && nNew == false) {
|
||||||
|
/* Currently editing - but not this row - restore the old before continuing to edit mode */
|
||||||
|
restoreRow(table, nEditing);
|
||||||
|
editRow(table, nRow);
|
||||||
|
nEditing = nRow;
|
||||||
|
} else if (nNew == true) {
|
||||||
|
/* adding a new row, delete it and start editing */
|
||||||
|
table.row(nEditing).remove().draw();
|
||||||
|
nNew = false;
|
||||||
|
editRow(table, nRow);
|
||||||
|
nEditing = nRow;
|
||||||
|
} else {
|
||||||
|
/* No edit in progress - let's start one */
|
||||||
|
editRow(table, nRow);
|
||||||
|
nEditing = nRow;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// handle apply changes button
|
||||||
|
$(document.body).on("click",".button_apply_changes", function() {
|
||||||
|
var modal = $("#modal_apply_changes");
|
||||||
|
var table = $("#tbl_records").DataTable();
|
||||||
|
var template = $(this).prop('id');
|
||||||
|
var info = "Are you sure you want to apply your changes?";
|
||||||
|
modal.find('.modal-body p').text(info);
|
||||||
|
modal.find('#button_apply_confirm').click(function() {
|
||||||
|
var data = getTableData(table);
|
||||||
|
applyChanges(data, '/template/' + template + '/apply', true);
|
||||||
|
modal.modal('hide');
|
||||||
|
})
|
||||||
|
modal.modal('show');
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// handle add record button
|
||||||
|
$(document.body).on("click", ".button_add_record", function (e) {
|
||||||
|
if (nNew || nEditing) {
|
||||||
|
// TODO: replace this alert with modal
|
||||||
|
alert("Previous record not saved. Please save it before adding more record.")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var table = $("#tbl_records").DataTable();
|
||||||
|
|
||||||
|
var aiNew = table.row.add(['', 'A', 'Active', 3600, '', '', '', '']).draw();
|
||||||
|
var nRow = aiNew.index();
|
||||||
|
editRow(table, nRow);
|
||||||
|
nEditing = nRow;
|
||||||
|
nNew = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
//handle cancel button
|
||||||
|
$(document.body).on("click", ".button_cancel", function (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
var oTable = $("#tbl_records").DataTable();
|
||||||
|
if (nNew) {
|
||||||
|
oTable.row(nEditing).remove().draw();
|
||||||
|
nEditing = null;
|
||||||
|
nNew = false;
|
||||||
|
} else {
|
||||||
|
restoreRow(oTable, nEditing);
|
||||||
|
nEditing = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//handle save button
|
||||||
|
$(document.body).on("click", ".button_save", function (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
var table = $("#tbl_records").DataTable();
|
||||||
|
saveRow(table, nEditing);
|
||||||
|
nEditing = null;
|
||||||
|
nNew = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
{% if record_helper_setting %}
|
||||||
|
//handle wacky record types
|
||||||
|
$(document.body).on("focus", "#current_edit_record_data", function (e) {
|
||||||
|
var record_type = $(this).parents("tr").find('#record_type').val();
|
||||||
|
var record_data = $(this);
|
||||||
|
if (record_type == "MX") {
|
||||||
|
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\"> \
|
||||||
|
<label for=\"mx_server\">MX Server</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"mx_server\" id=\"mx_server\" placeholder=\"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] + "\"> \
|
||||||
|
<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] + "\"> \
|
||||||
|
";
|
||||||
|
}
|
||||||
|
modal.find('.modal-body p').html(form);
|
||||||
|
modal.find('#button_save').click(function() {
|
||||||
|
mx_server = modal.find('#mx_server').val();
|
||||||
|
mx_priority = modal.find('#mx_priority').val();
|
||||||
|
data = mx_priority + " " + mx_server;
|
||||||
|
record_data.val(data);
|
||||||
|
modal.modal('hide');
|
||||||
|
})
|
||||||
|
modal.modal('show');
|
||||||
|
} else if (record_type == "SRV") {
|
||||||
|
var modal = $("#modal_custom_record");
|
||||||
|
if (record_data.val() == "") {
|
||||||
|
var form = " <label for=\"srv_priority\">SRV Priority</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"srv_priority\" id=\"srv_priority\" placeholder=\"0\"> \
|
||||||
|
<label for=\"srv_weight\">SRV Weight</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"srv_weight\" id=\"srv_weight\" placeholder=\"10\"> \
|
||||||
|
<label for=\"srv_port\">SRV Port</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"srv_port\" id=\"srv_port\" placeholder=\"5060\"> \
|
||||||
|
<label for=\"srv_target\">SRV Target</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"srv_target\" id=\"srv_target\" placeholder=\"sip.example.com\"> \
|
||||||
|
";
|
||||||
|
} else {
|
||||||
|
var parts = record_data.val().split(" ");
|
||||||
|
var form = " <label for=\"srv_priority\">SRV Priority</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"srv_priority\" id=\"srv_priority\" placeholder=\"0\" value=\"" + parts[0] + "\"> \
|
||||||
|
<label for=\"srv_weight\">SRV Weight</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"srv_weight\" id=\"srv_weight\" placeholder=\"10\" value=\"" + parts[1] + "\"> \
|
||||||
|
<label for=\"srv_port\">SRV Port</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"srv_port\" id=\"srv_port\" placeholder=\"5060\" value=\"" + parts[2] + "\"> \
|
||||||
|
<label for=\"srv_target\">SRV Target</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"srv_target\" id=\"srv_target\" placeholder=\"sip.example.com\" value=\"" + parts[3] + "\"> \
|
||||||
|
";
|
||||||
|
}
|
||||||
|
modal.find('.modal-body p').html(form);
|
||||||
|
modal.find('#button_save').click(function() {
|
||||||
|
srv_priority = modal.find('#srv_priority').val();
|
||||||
|
srv_weight = modal.find('#srv_weight').val();
|
||||||
|
srv_port = modal.find('#srv_port').val();
|
||||||
|
srv_target = modal.find('#srv_target').val();
|
||||||
|
data = srv_priority + " " + srv_weight + " " + srv_port + " " + srv_target;
|
||||||
|
record_data.val(data);
|
||||||
|
modal.modal('hide');
|
||||||
|
})
|
||||||
|
modal.modal('show');
|
||||||
|
} else if (record_type == "SOA") {
|
||||||
|
var modal = $("#modal_custom_record");
|
||||||
|
if (record_data.val() == "") {
|
||||||
|
var form = " <label for=\"soa_primaryns\">Primary Name Server</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"soa_primaryns\" id=\"soa_primaryns\" placeholder=\"ns1.example.com\"> \
|
||||||
|
<label for=\"soa_adminemail\">Primary Contact</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"soa_adminemail\" id=\"soa_adminemail\" placeholder=\"admin.example.com\"> \
|
||||||
|
<label for=\"soa_serial\">Serial</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"soa_serial\" id=\"soa_serial\" placeholder=\"2016010101\"> \
|
||||||
|
<label for=\"soa_zonerefresh\">Zone refresh timer</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"soa_zonerefresh\" id=\"soa_zonerefresh\" placeholder=\"86400\"> \
|
||||||
|
<label for=\"soa_failedzonerefresh\">Failed refresh retry timer</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"soa_failedzonerefresh\" id=\"soa_failedzonerefresh\" placeholder=\"7200\"> \
|
||||||
|
<label for=\"soa_zoneexpiry\">Zone expiry timer</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"soa_zoneexpiry\" id=\"soa_zoneexpiry\" placeholder=\"604800\"> \
|
||||||
|
<label for=\"soa_minimumttl\">Minimum TTL</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"soa_minimumttl\" id=\"soa_minimumttl\" placeholder=\"300\"> \
|
||||||
|
";
|
||||||
|
} else {
|
||||||
|
var parts = record_data.val().split(" ");
|
||||||
|
var form = " <label for=\"soa_primaryns\">Primary Name Server</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"soa_primaryns\" id=\"soa_primaryns\" value=\"" + parts[0] + "\"> \
|
||||||
|
<label for=\"soa_adminemail\">Primary Contact</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"soa_adminemail\" id=\"soa_adminemail\" value=\"" + parts[1] + "\"> \
|
||||||
|
<label for=\"soa_serial\">Serial</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"soa_serial\" id=\"soa_serial\" value=\"" + parts[2] + "\"> \
|
||||||
|
<label for=\"soa_zonerefresh\">Zone refresh timer</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"soa_zonerefresh\" id=\"soa_zonerefresh\" value=\"" + parts[3] + "\"> \
|
||||||
|
<label for=\"soa_failedzonerefresh\">Failed refresh retry timer</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"soa_failedzonerefresh\" id=\"soa_failedzonerefresh\" value=\"" + parts[4] + "\"> \
|
||||||
|
<label for=\"soa_zoneexpiry\">Zone expiry timer</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"soa_zoneexpiry\" id=\"soa_zoneexpiry\" value=\"" + parts[5] + "\"> \
|
||||||
|
<label for=\"soa_minimumttl\">Minimum TTL</label> \
|
||||||
|
<input type=\"text\" class=\"form-control\" name=\"soa_minimumttl\" id=\"soa_minimumttl\" value=\"" + parts[6] + "\"> \
|
||||||
|
";
|
||||||
|
}
|
||||||
|
modal.find('.modal-body p').html(form);
|
||||||
|
modal.find('#button_save').click(function() {
|
||||||
|
soa_primaryns = modal.find('#soa_primaryns').val();
|
||||||
|
soa_adminemail = modal.find('#soa_adminemail').val();
|
||||||
|
soa_serial = modal.find('#soa_serial').val();
|
||||||
|
soa_zonerefresh = modal.find('#soa_zonerefresh').val();
|
||||||
|
soa_failedzonerefresh = modal.find('#soa_failedzonerefresh').val();
|
||||||
|
soa_zoneexpiry = modal.find('#soa_zoneexpiry').val();
|
||||||
|
soa_minimumttl = modal.find('#soa_minimumttl').val();
|
||||||
|
|
||||||
|
data = soa_primaryns + " " + soa_adminemail + " " + soa_serial + " " + soa_zonerefresh + " " + soa_failedzonerefresh + " " + soa_zoneexpiry + " " + soa_minimumttl;
|
||||||
|
record_data.val(data);
|
||||||
|
modal.modal('hide');
|
||||||
|
})
|
||||||
|
modal.modal('show');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
{% endif %}
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
{% block modals %}
|
||||||
|
<div class="modal fade modal-warning" id="modal_delete">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal"
|
||||||
|
aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
<h4 class="modal-title">Confirmation</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p></p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-flat btn-default pull-left"
|
||||||
|
data-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-flat btn-danger" id="button_delete_confirm">Delete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- /.modal-content -->
|
||||||
|
</div>
|
||||||
|
<!-- /.modal-dialog -->
|
||||||
|
</div>
|
||||||
|
<div class="modal fade modal-primary" id="modal_apply_changes">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal"
|
||||||
|
aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
<h4 class="modal-title">Confirmation</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p></p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-flat btn-default pull-left"
|
||||||
|
data-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-flat btn-primary" id="button_apply_confirm">Apply</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- /.modal-content -->
|
||||||
|
</div>
|
||||||
|
<!-- /.modal-dialog -->
|
||||||
|
</div>
|
||||||
|
<div class="modal fade modal-primary" id="modal_custom_record">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal"
|
||||||
|
aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
<h4 class="modal-title">Custom Record</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p></p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-flat btn-primary" id="button_save">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- /.modal-content -->
|
||||||
|
</div>
|
||||||
|
<!-- /.modal-dialog -->
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
@ -165,7 +165,7 @@
|
|||||||
'enable_otp' : enable_otp
|
'enable_otp' : enable_otp
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
applyChanges(postdata, $SCRIPT_ROOT + '/user/profile');
|
applyChanges(postdata, $SCRIPT_ROOT + '/user/profile', false, true);
|
||||||
location.reload();
|
location.reload();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
497
app/views.py
497
app/views.py
@ -11,16 +11,21 @@ from io import BytesIO
|
|||||||
import jinja2
|
import jinja2
|
||||||
import qrcode as qrc
|
import qrcode as qrc
|
||||||
import qrcode.image.svg as qrc_svg
|
import qrcode.image.svg as qrc_svg
|
||||||
from flask import g, request, make_response, jsonify, render_template, session, redirect, url_for, send_from_directory, abort
|
from flask import g, request, make_response, jsonify, render_template, session, redirect, url_for, send_from_directory, abort, flash
|
||||||
from flask_login import login_user, logout_user, current_user, login_required
|
from flask_login import login_user, logout_user, current_user, login_required
|
||||||
from werkzeug import secure_filename
|
from werkzeug import secure_filename
|
||||||
from werkzeug.security import gen_salt
|
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, DomainTemplate, DomainTemplateRecord
|
||||||
from app import app, login_manager, github
|
from app import app, login_manager, github, google
|
||||||
from lib import utils
|
from app.lib import utils
|
||||||
|
from app.lib.log import logger
|
||||||
|
from app.decorators import admin_role_required, can_access_domain
|
||||||
|
|
||||||
|
# LOG CONFIG
|
||||||
|
logging = logger('MODEL', app.config['LOG_LEVEL'], app.config['LOG_FILE']).config()
|
||||||
|
|
||||||
|
# FILTERS
|
||||||
jinja2.filters.FILTERS['display_record_name'] = utils.display_record_name
|
jinja2.filters.FILTERS['display_record_name'] = utils.display_record_name
|
||||||
jinja2.filters.FILTERS['display_master_name'] = utils.display_master_name
|
jinja2.filters.FILTERS['display_master_name'] = utils.display_master_name
|
||||||
jinja2.filters.FILTERS['display_second_to_time'] = utils.display_time
|
jinja2.filters.FILTERS['display_second_to_time'] = utils.display_time
|
||||||
@ -34,35 +39,45 @@ 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))
|
if auto_ptr_setting is None:
|
||||||
|
return dict(auto_ptr_setting=False)
|
||||||
|
else:
|
||||||
|
return dict(auto_ptr_setting=strtobool(auto_ptr_setting.value))
|
||||||
|
|
||||||
|
|
||||||
# START USER AUTHENTICATION HANDLER
|
# START USER AUTHENTICATION HANDLER
|
||||||
@app.before_request
|
@app.before_request
|
||||||
@ -92,6 +107,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 +116,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,47 +126,42 @@ 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
|
|
||||||
def admin_role_required(f):
|
|
||||||
@wraps(f)
|
|
||||||
def decorated_function(*args, **kwargs):
|
|
||||||
if g.user.role.name != 'Administrator':
|
|
||||||
return redirect(url_for('error', code=401))
|
|
||||||
return f(*args, **kwargs)
|
|
||||||
return decorated_function
|
|
||||||
# END CUSTOMIZE DECORATOR
|
|
||||||
|
|
||||||
# START VIEWS
|
# START VIEWS
|
||||||
@app.errorhandler(400)
|
@app.errorhandler(400)
|
||||||
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/<path:code>')
|
||||||
def error(code, msg=None):
|
def error(code, msg=None):
|
||||||
supported_code = ('400', '401', '404', '500')
|
supported_code = ('400', '401', '404', '500')
|
||||||
if code in supported_code:
|
if code in supported_code:
|
||||||
return render_template('errors/%s.html' % code, msg=msg), int(code)
|
return render_template('errors/{0}.html'.format(code), msg=msg), int(code)
|
||||||
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,25 +170,57 @@ def register():
|
|||||||
else:
|
else:
|
||||||
return render_template('errors/404.html'), 404
|
return render_template('errors/404.html'), 404
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/google/login')
|
||||||
|
def google_login():
|
||||||
|
if not app.config.get('GOOGLE_OAUTH_ENABLE'):
|
||||||
|
return abort(400)
|
||||||
|
return google.authorize(callback=url_for('authorized', _external=True))
|
||||||
|
|
||||||
|
|
||||||
@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():
|
||||||
# these parameters will be needed in multiple paths
|
|
||||||
LDAP_ENABLED = True if 'LDAP_TYPE' in app.config.keys() else False
|
|
||||||
LOGIN_TITLE = app.config['LOGIN_TITLE'] if 'LOGIN_TITLE' in app.config.keys() else ''
|
LOGIN_TITLE = app.config['LOGIN_TITLE'] if 'LOGIN_TITLE' in app.config.keys() else ''
|
||||||
BASIC_ENABLED = app.config['BASIC_ENABLED']
|
BASIC_ENABLED = app.config['BASIC_ENABLED']
|
||||||
SIGNUP_ENABLED = app.config['SIGNUP_ENABLED']
|
SIGNUP_ENABLED = app.config['SIGNUP_ENABLED']
|
||||||
|
LDAP_ENABLE = app.config.get('LDAP_ENABLE')
|
||||||
GITHUB_ENABLE = app.config.get('GITHUB_OAUTH_ENABLE')
|
GITHUB_ENABLE = app.config.get('GITHUB_OAUTH_ENABLE')
|
||||||
|
GOOGLE_ENABLE = app.config.get('GOOGLE_OAUTH_ENABLE')
|
||||||
|
|
||||||
if g.user is not None and current_user.is_authenticated:
|
if g.user is not None and current_user.is_authenticated:
|
||||||
return redirect(url_for('dashboard'))
|
return redirect(url_for('dashboard'))
|
||||||
|
|
||||||
|
if 'google_token' in session:
|
||||||
|
user_data = google.get('userinfo').data
|
||||||
|
first_name = user_data['given_name']
|
||||||
|
surname = user_data['family_name']
|
||||||
|
email = user_data['email']
|
||||||
|
user = User.query.filter_by(username=email).first()
|
||||||
|
if not user:
|
||||||
|
# create user
|
||||||
|
user = User(username=email,
|
||||||
|
firstname=first_name,
|
||||||
|
lastname=surname,
|
||||||
|
plain_text_password=gen_salt(7),
|
||||||
|
email=email)
|
||||||
|
|
||||||
|
result = user.create_local_user()
|
||||||
|
if not result['status']:
|
||||||
|
session.pop('google_token', None)
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
session['user_id'] = user.id
|
||||||
|
login_user(user, remember = False)
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
if 'github_token' in session:
|
if 'github_token' in session:
|
||||||
me = github.get('user')
|
me = github.get('user')
|
||||||
user_info = me.data
|
user_info = me.data
|
||||||
@ -188,7 +230,11 @@ def login():
|
|||||||
user = User(username=user_info['name'],
|
user = User(username=user_info['name'],
|
||||||
plain_text_password=gen_salt(7),
|
plain_text_password=gen_salt(7),
|
||||||
email=user_info['email'])
|
email=user_info['email'])
|
||||||
user.create_local_user()
|
|
||||||
|
result = user.create_local_user()
|
||||||
|
if not result['status']:
|
||||||
|
session.pop('github_token', None)
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
session['user_id'] = user.id
|
session['user_id'] = user.id
|
||||||
login_user(user, remember = False)
|
login_user(user, remember = False)
|
||||||
@ -197,7 +243,8 @@ def login():
|
|||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
return render_template('login.html',
|
return render_template('login.html',
|
||||||
github_enabled=GITHUB_ENABLE,
|
github_enabled=GITHUB_ENABLE,
|
||||||
ldap_enabled=LDAP_ENABLED, login_title=LOGIN_TITLE,
|
google_enabled=GOOGLE_ENABLE,
|
||||||
|
ldap_enabled=LDAP_ENABLE, login_title=LOGIN_TITLE,
|
||||||
basic_enabled=BASIC_ENABLED, signup_enabled=SIGNUP_ENABLED)
|
basic_enabled=BASIC_ENABLED, signup_enabled=SIGNUP_ENABLED)
|
||||||
|
|
||||||
# process login
|
# process login
|
||||||
@ -223,19 +270,18 @@ def login():
|
|||||||
try:
|
try:
|
||||||
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_ENABLE, 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_ENABLE, 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:
|
||||||
if otp_token:
|
if otp_token:
|
||||||
good_token = user.verify_totp(otp_token)
|
good_token = user.verify_totp(otp_token)
|
||||||
if not good_token:
|
if not good_token:
|
||||||
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_ENABLE, login_title=LOGIN_TITLE, basic_enabled=BASIC_ENABLED, signup_enabled=SIGNUP_ENABLED)
|
||||||
else:
|
else:
|
||||||
return render_template('login.html', error='Token required', ldap_enabled=LDAP_ENABLED, login_title=LOGIN_TITLE, basic_enabled=BASIC_ENABLED, signup_enabled=SIGNUP_ENABLED)
|
return render_template('login.html', error='Token required', ldap_enabled=LDAP_ENABLE, login_title=LOGIN_TITLE, basic_enabled=BASIC_ENABLED, signup_enabled=SIGNUP_ENABLED)
|
||||||
|
|
||||||
login_user(user, remember = remember_me)
|
login_user(user, remember = remember_me)
|
||||||
return redirect(request.args.get('next') or url_for('index'))
|
return redirect(request.args.get('next') or url_for('index'))
|
||||||
@ -251,18 +297,19 @@ def login():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
result = user.create_local_user()
|
result = user.create_local_user()
|
||||||
if result == True:
|
if result['status'] == True:
|
||||||
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_ENABLE, 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['msg'])
|
||||||
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():
|
||||||
session.pop('user_id', None)
|
session.pop('user_id', None)
|
||||||
session.pop('github_token', None)
|
session.pop('github_token', None)
|
||||||
|
session.pop('google_token', None)
|
||||||
logout_user()
|
logout_user()
|
||||||
return redirect(url_for('login'))
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
@ -271,10 +318,6 @@ def logout():
|
|||||||
@login_required
|
@login_required
|
||||||
def dashboard():
|
def dashboard():
|
||||||
d = Domain().update()
|
d = Domain().update()
|
||||||
if current_user.role.name == 'Administrator':
|
|
||||||
domains = Domain.query.all()
|
|
||||||
else:
|
|
||||||
domains = User(id=current_user.id).get_domain()
|
|
||||||
|
|
||||||
# stats for dashboard
|
# stats for dashboard
|
||||||
domain_count = Domain.query.count()
|
domain_count = Domain.query.count()
|
||||||
@ -284,15 +327,83 @@ 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', domain_count=domain_count, users=users, history_number=history_number, uptime=uptime, histories=history)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/dashboard-domains', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
def dashboard_domains():
|
||||||
|
if current_user.role.name == 'Administrator':
|
||||||
|
domains = Domain.query
|
||||||
|
else:
|
||||||
|
domains = User(id=current_user.id).get_domain_query()
|
||||||
|
|
||||||
|
template = app.jinja_env.get_template("dashboard_domain.html")
|
||||||
|
render = template.make_module(vars={"current_user": current_user})
|
||||||
|
|
||||||
|
columns = [Domain.name, Domain.dnssec, Domain.type, Domain.serial, Domain.master]
|
||||||
|
# History.created_on.desc()
|
||||||
|
order_by = []
|
||||||
|
for i in range(len(columns)):
|
||||||
|
column_index = request.args.get("order[{0}][column]".format(i))
|
||||||
|
sort_direction = request.args.get("order[{0}][dir]".format(i))
|
||||||
|
if column_index is None:
|
||||||
|
break
|
||||||
|
if sort_direction != "asc" and sort_direction != "desc":
|
||||||
|
sort_direction = "asc"
|
||||||
|
|
||||||
|
column = columns[int(column_index)]
|
||||||
|
order_by.append(getattr(column, sort_direction)())
|
||||||
|
|
||||||
|
if order_by:
|
||||||
|
domains = domains.order_by(*order_by)
|
||||||
|
|
||||||
|
total_count = domains.count()
|
||||||
|
|
||||||
|
search = request.args.get("search[value]")
|
||||||
|
if search:
|
||||||
|
start = "" if search.startswith("^") else "%"
|
||||||
|
end = "" if search.endswith("$") else "%"
|
||||||
|
domains = domains.filter(Domain.name.ilike(start + search.strip("^$") + end))
|
||||||
|
|
||||||
|
filtered_count = domains.count()
|
||||||
|
|
||||||
|
start = int(request.args.get("start", 0))
|
||||||
|
length = min(int(request.args.get("length", 0)), 100)
|
||||||
|
|
||||||
|
if length != -1:
|
||||||
|
domains = domains[start:start + length]
|
||||||
|
|
||||||
|
if current_user.role.name != 'Administrator':
|
||||||
|
domains = [d[2] for d in domains]
|
||||||
|
|
||||||
|
data = []
|
||||||
|
for domain in domains:
|
||||||
|
data.append([
|
||||||
|
render.name(domain),
|
||||||
|
render.dnssec(domain),
|
||||||
|
render.type(domain),
|
||||||
|
render.serial(domain),
|
||||||
|
render.master(domain),
|
||||||
|
render.actions(domain),
|
||||||
|
])
|
||||||
|
|
||||||
|
response_data = {
|
||||||
|
"draw": int(request.args.get("draw", 0)),
|
||||||
|
"recordsTotal": total_count,
|
||||||
|
"recordsFiltered": filtered_count,
|
||||||
|
"data": data,
|
||||||
|
}
|
||||||
|
return jsonify(response_data)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/domain/<path:domain_name>', methods=['GET', 'POST'])
|
@app.route('/domain/<path:domain_name>', methods=['GET', 'POST'])
|
||||||
@app.route('/domain', methods=['GET', 'POST'])
|
@app.route('/domain', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
|
@can_access_domain
|
||||||
def domain(domain_name):
|
def domain(domain_name):
|
||||||
r = Record()
|
r = Record()
|
||||||
domain = Domain.query.filter(Domain.name == domain_name).first()
|
domain = Domain.query.filter(Domain.name == domain_name).first()
|
||||||
@ -321,7 +432,7 @@ def domain(domain_name):
|
|||||||
if not re.search('ip6\.arpa|in-addr\.arpa$', domain_name):
|
if not re.search('ip6\.arpa|in-addr\.arpa$', domain_name):
|
||||||
editable_records = app.config['RECORDS_ALLOW_EDIT']
|
editable_records = app.config['RECORDS_ALLOW_EDIT']
|
||||||
else:
|
else:
|
||||||
editable_records = ['PTR']
|
editable_records = app.config['REVERSE_ALLOW_EDIT']
|
||||||
return render_template('domain.html', domain=domain, records=records, editable_records=editable_records)
|
return render_template('domain.html', domain=domain, records=records, editable_records=editable_records)
|
||||||
else:
|
else:
|
||||||
return redirect(url_for('error', code=404))
|
return redirect(url_for('error', code=404))
|
||||||
@ -331,10 +442,13 @@ def domain(domain_name):
|
|||||||
@login_required
|
@login_required
|
||||||
@admin_role_required
|
@admin_role_required
|
||||||
def domain_add():
|
def domain_add():
|
||||||
|
templates = DomainTemplate.query.all()
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
try:
|
try:
|
||||||
domain_name = request.form.getlist('domain_name')[0]
|
domain_name = request.form.getlist('domain_name')[0]
|
||||||
domain_type = request.form.getlist('radio_type')[0]
|
domain_type = request.form.getlist('radio_type')[0]
|
||||||
|
domain_template = request.form.getlist('domain_template')[0]
|
||||||
|
logging.info("Selected template ==== {0}".format(domain_template))
|
||||||
soa_edit_api = request.form.getlist('radio_type_soa_edit_api')[0]
|
soa_edit_api = request.form.getlist('radio_type_soa_edit_api')[0]
|
||||||
|
|
||||||
if ' ' in domain_name or not domain_name or not domain_type:
|
if ' ' in domain_name or not domain_name or not domain_type:
|
||||||
@ -350,17 +464,33 @@ def domain_add():
|
|||||||
d = Domain()
|
d = Domain()
|
||||||
result = d.add(domain_name=domain_name, domain_type=domain_type, soa_edit_api=soa_edit_api, domain_master_ips=domain_master_ips)
|
result = d.add(domain_name=domain_name, domain_type=domain_type, soa_edit_api=soa_edit_api, domain_master_ips=domain_master_ips)
|
||||||
if result['status'] == 'ok':
|
if result['status'] == 'ok':
|
||||||
history = History(msg='Add domain %s' % domain_name, detail=str({'domain_type': domain_type, 'domain_master_ips': domain_master_ips}), created_by=current_user.username)
|
history = History(msg='Add domain {0}'.format(domain_name), detail=str({'domain_type': domain_type, 'domain_master_ips': domain_master_ips}), created_by=current_user.username)
|
||||||
history.add()
|
history.add()
|
||||||
|
if domain_template != '0':
|
||||||
|
template = DomainTemplate.query.filter(DomainTemplate.id == domain_template).first()
|
||||||
|
template_records = DomainTemplateRecord.query.filter(DomainTemplateRecord.template_id == domain_template).all()
|
||||||
|
record_data = []
|
||||||
|
for template_record in template_records:
|
||||||
|
record_row = {'record_data': template_record.data, 'record_name': template_record.name, 'record_status': template_record.status, 'record_ttl': template_record.ttl, 'record_type': template_record.type}
|
||||||
|
record_data.append(record_row)
|
||||||
|
r = Record()
|
||||||
|
result = r.apply(domain_name, record_data)
|
||||||
|
if result['status'] == 'ok':
|
||||||
|
history = History(msg='Applying template {0} to {1}, created records successfully.'.format(template.name, domain_name), detail=str(result), created_by=current_user.username)
|
||||||
|
history.add()
|
||||||
|
else:
|
||||||
|
history = History(msg='Applying template {0} to {1}, FAILED to created records.'.format(template.name, domain_name), detail=str(result), created_by=current_user.username)
|
||||||
|
history.add()
|
||||||
return redirect(url_for('dashboard'))
|
return redirect(url_for('dashboard'))
|
||||||
else:
|
else:
|
||||||
return render_template('errors/400.html', msg=result['msg']), 400
|
return render_template('errors/400.html', msg=result['msg']), 400
|
||||||
except:
|
except:
|
||||||
|
logging.error(traceback.print_exc())
|
||||||
return redirect(url_for('error', code=500))
|
return redirect(url_for('error', code=500))
|
||||||
return render_template('domain_add.html')
|
return render_template('domain_add.html', templates=templates)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/admin/domain/<string:domain_name>/delete', methods=['GET'])
|
@app.route('/admin/domain/<path:domain_name>/delete', methods=['GET'])
|
||||||
@login_required
|
@login_required
|
||||||
@admin_role_required
|
@admin_role_required
|
||||||
def domain_delete(domain_name):
|
def domain_delete(domain_name):
|
||||||
@ -370,13 +500,13 @@ def domain_delete(domain_name):
|
|||||||
if result['status'] == 'error':
|
if result['status'] == 'error':
|
||||||
return redirect(url_for('error', code=500))
|
return redirect(url_for('error', code=500))
|
||||||
|
|
||||||
history = History(msg='Delete domain %s' % domain_name, created_by=current_user.username)
|
history = History(msg='Delete domain {0}'.format(domain_name), created_by=current_user.username)
|
||||||
history.add()
|
history.add()
|
||||||
|
|
||||||
return redirect(url_for('dashboard'))
|
return redirect(url_for('dashboard'))
|
||||||
|
|
||||||
|
|
||||||
@app.route('/admin/domain/<string:domain_name>/manage', methods=['GET', 'POST'])
|
@app.route('/admin/domain/<path:domain_name>/manage', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@admin_role_required
|
@admin_role_required
|
||||||
def domain_management(domain_name):
|
def domain_management(domain_name):
|
||||||
@ -403,46 +533,46 @@ def domain_management(domain_name):
|
|||||||
# grant/revoke user privielges
|
# grant/revoke user privielges
|
||||||
d.grant_privielges(new_user_list)
|
d.grant_privielges(new_user_list)
|
||||||
|
|
||||||
history = History(msg='Change domain %s access control' % domain_name, detail=str({'user_has_access': new_user_list}), created_by=current_user.username)
|
history = History(msg='Change domain {0} access control'.format(domain_name), detail=str({'user_has_access': new_user_list}), created_by=current_user.username)
|
||||||
history.add()
|
history.add()
|
||||||
|
|
||||||
return redirect(url_for('domain_management', domain_name=domain_name))
|
return redirect(url_for('domain_management', domain_name=domain_name))
|
||||||
|
|
||||||
|
|
||||||
@app.route('/domain/<string:domain_name>/apply', methods=['POST'], strict_slashes=False)
|
@app.route('/domain/<path:domain_name>/apply', methods=['POST'], strict_slashes=False)
|
||||||
@login_required
|
@login_required
|
||||||
|
@can_access_domain
|
||||||
def record_apply(domain_name):
|
def record_apply(domain_name):
|
||||||
"""
|
"""
|
||||||
example jdata: {u'record_ttl': u'1800', u'record_type': u'CNAME', u'record_name': u'test4', u'record_status': u'Active', u'record_data': u'duykhanh.me'}
|
example jdata: {u'record_ttl': u'1800', u'record_type': u'CNAME', u'record_name': u'test4', u'record_status': u'Active', u'record_data': u'duykhanh.me'}
|
||||||
"""
|
"""
|
||||||
#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)
|
||||||
if result['status'] == 'ok':
|
if result['status'] == 'ok':
|
||||||
history = History(msg='Apply record changes to domain %s' % domain_name, detail=str(jdata), created_by=current_user.username)
|
history = History(msg='Apply record changes to domain {0}'.format(domain_name), detail=str(jdata), created_by=current_user.username)
|
||||||
history.add()
|
history.add()
|
||||||
return make_response(jsonify( result ), 200)
|
return make_response(jsonify( result ), 200)
|
||||||
else:
|
else:
|
||||||
return make_response(jsonify( result ), 400)
|
return make_response(jsonify( result ), 400)
|
||||||
except:
|
except:
|
||||||
print traceback.format_exc()
|
logging.error(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)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/domain/<string:domain_name>/update', methods=['POST'], strict_slashes=False)
|
@app.route('/domain/<path:domain_name>/update', methods=['POST'], strict_slashes=False)
|
||||||
@login_required
|
@login_required
|
||||||
|
@can_access_domain
|
||||||
def record_update(domain_name):
|
def record_update(domain_name):
|
||||||
"""
|
"""
|
||||||
This route is used for domain work as Slave Zone only
|
This route is used for domain work as Slave Zone only
|
||||||
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,11 +582,11 @@ 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()
|
logging.error(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)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/domain/<string:domain_name>/record/<string:record_name>/type/<string:record_type>/delete', methods=['GET'])
|
@app.route('/domain/<path:domain_name>/record/<path:record_name>/type/<path:record_type>/delete', methods=['GET'])
|
||||||
@login_required
|
@login_required
|
||||||
@admin_role_required
|
@admin_role_required
|
||||||
def record_delete(domain_name, record_name, record_type):
|
def record_delete(domain_name, record_name, record_type):
|
||||||
@ -464,21 +594,23 @@ 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()
|
logging.error(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))
|
||||||
|
|
||||||
|
|
||||||
@app.route('/domain/<string:domain_name>/dnssec', methods=['GET'])
|
@app.route('/domain/<path:domain_name>/dnssec', methods=['GET'])
|
||||||
|
@can_access_domain
|
||||||
@login_required
|
@login_required
|
||||||
def domain_dnssec(domain_name):
|
def domain_dnssec(domain_name):
|
||||||
domain = Domain()
|
domain = Domain()
|
||||||
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/<path:domain_name>/managesetting', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@admin_role_required
|
@admin_role_required
|
||||||
def admin_setdomainsetting(domain_name):
|
def admin_setdomainsetting(domain_name):
|
||||||
@ -488,9 +620,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'])
|
||||||
@ -499,14 +631,14 @@ def admin_setdomainsetting(domain_name):
|
|||||||
|
|
||||||
if setting:
|
if setting:
|
||||||
if setting.set(new_value):
|
if setting.set(new_value):
|
||||||
history = History(msg='Setting %s changed value to %s for %s' % (new_setting, new_value, domain.name), created_by=current_user.username)
|
history = History(msg='Setting {0} changed value to {1} for {2}'.format(new_setting, new_value, domain.name), created_by=current_user.username)
|
||||||
history.add()
|
history.add()
|
||||||
return make_response(jsonify( { 'status': 'ok', 'msg': 'Setting updated.' } ))
|
return make_response(jsonify( { 'status': 'ok', 'msg': 'Setting updated.' } ))
|
||||||
else:
|
else:
|
||||||
return make_response(jsonify( { 'status': 'error', 'msg': 'Unable to set value of setting.' } ))
|
return make_response(jsonify( { 'status': 'error', 'msg': 'Unable to set value of setting.' } ))
|
||||||
else:
|
else:
|
||||||
if domain.add_setting(new_setting, new_value):
|
if domain.add_setting(new_setting, new_value):
|
||||||
history = History(msg='New setting %s with value %s for %s has been created' % (new_setting, new_value, domain.name), created_by=current_user.username)
|
history = History(msg='New setting {0} with value {1} for {2} has been created'.format(new_setting, new_value, domain.name), created_by=current_user.username)
|
||||||
history.add()
|
history.add()
|
||||||
return make_response(jsonify( { 'status': 'ok', 'msg': 'New setting created and updated.' } ))
|
return make_response(jsonify( { 'status': 'ok', 'msg': 'New setting created and updated.' } ))
|
||||||
else:
|
else:
|
||||||
@ -514,10 +646,182 @@ 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()
|
logging.error(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)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/templates', methods=['GET', 'POST'])
|
||||||
|
@app.route('/templates/list', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
@admin_role_required
|
||||||
|
def templates():
|
||||||
|
templates = DomainTemplate.query.all()
|
||||||
|
return render_template('template.html', templates=templates)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/template/create', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
@admin_role_required
|
||||||
|
def create_template():
|
||||||
|
if request.method == 'GET':
|
||||||
|
return render_template('template_add.html')
|
||||||
|
if request.method == 'POST':
|
||||||
|
try:
|
||||||
|
name = request.form.getlist('name')[0]
|
||||||
|
description = request.form.getlist('description')[0]
|
||||||
|
|
||||||
|
if ' ' in name or not name or not type:
|
||||||
|
flash("Please correct your input", 'error')
|
||||||
|
return redirect(url_for('create_template'))
|
||||||
|
|
||||||
|
if DomainTemplate.query.filter(DomainTemplate.name == name).first():
|
||||||
|
flash("A template with the name {0} already exists!".format(name), 'error')
|
||||||
|
return redirect(url_for('create_template'))
|
||||||
|
t = DomainTemplate(name=name, description=description)
|
||||||
|
result = t.create()
|
||||||
|
if result['status'] == 'ok':
|
||||||
|
history = History(msg='Add domain template {0}'.format(name), detail=str({'name': name, 'description': description}), created_by=current_user.username)
|
||||||
|
history.add()
|
||||||
|
return redirect(url_for('templates'))
|
||||||
|
else:
|
||||||
|
flash(result['msg'], 'error')
|
||||||
|
return redirect(url_for('create_template'))
|
||||||
|
except:
|
||||||
|
logging.error(traceback.print_exc())
|
||||||
|
return redirect(url_for('error', code=500))
|
||||||
|
return redirect(url_for('templates'))
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/template/createfromzone', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
@admin_role_required
|
||||||
|
def create_template_from_zone():
|
||||||
|
try:
|
||||||
|
jdata = request.json
|
||||||
|
name = jdata['name']
|
||||||
|
description = jdata['description']
|
||||||
|
domain_name = jdata['domain']
|
||||||
|
|
||||||
|
if ' ' in name or not name or not type:
|
||||||
|
return make_response(jsonify({'status': 'error', 'msg': 'Please correct template name'}), 500)
|
||||||
|
|
||||||
|
if DomainTemplate.query.filter(DomainTemplate.name == name).first():
|
||||||
|
return make_response(jsonify({'status': 'error', 'msg': 'A template with the name {0} already exists!'.format(name)}), 500)
|
||||||
|
|
||||||
|
t = DomainTemplate(name=name, description=description)
|
||||||
|
result = t.create()
|
||||||
|
if result['status'] == 'ok':
|
||||||
|
history = History(msg='Add domain template {0}'.format(name), detail=str({'name': name, 'description': description}), created_by=current_user.username)
|
||||||
|
history.add()
|
||||||
|
|
||||||
|
records = []
|
||||||
|
r = Record()
|
||||||
|
domain = Domain.query.filter(Domain.name == domain_name).first()
|
||||||
|
if domain:
|
||||||
|
# query domain info from PowerDNS API
|
||||||
|
zone_info = r.get_record_data(domain.name)
|
||||||
|
if zone_info:
|
||||||
|
jrecords = zone_info['records']
|
||||||
|
|
||||||
|
if NEW_SCHEMA:
|
||||||
|
for jr in jrecords:
|
||||||
|
name = '@' if jr['name'] == domain_name else jr['name']
|
||||||
|
if jr['type'] in app.config['RECORDS_ALLOW_EDIT']:
|
||||||
|
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']:
|
||||||
|
record = DomainTemplateRecord(name=name, type=jr['type'], status=True if jr['disabled'] else False, ttl=jr['ttl'], data=jr['content'])
|
||||||
|
records.append(record)
|
||||||
|
result_records = t.replace_records(records)
|
||||||
|
|
||||||
|
if result_records['status'] == 'ok':
|
||||||
|
return make_response(jsonify({'status': 'ok', 'msg': result['msg']}), 200)
|
||||||
|
else:
|
||||||
|
result = t.delete_template()
|
||||||
|
return make_response(jsonify({'status': 'error', 'msg': result_records['msg']}), 500)
|
||||||
|
|
||||||
|
else:
|
||||||
|
return make_response(jsonify({'status': 'error', 'msg': result['msg']}), 500)
|
||||||
|
except:
|
||||||
|
logging.error(traceback.print_exc())
|
||||||
|
return make_response(jsonify({'status': 'error', 'msg': 'Error when applying new changes'}), 500)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/template/<string:template>/edit', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@admin_role_required
|
||||||
|
def edit_template(template):
|
||||||
|
try:
|
||||||
|
t = DomainTemplate.query.filter(DomainTemplate.name == template).first()
|
||||||
|
if t is not None:
|
||||||
|
records = []
|
||||||
|
for jr in t.records:
|
||||||
|
if jr.type in app.config['RECORDS_ALLOW_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'])
|
||||||
|
except:
|
||||||
|
logging.error(traceback.print_exc())
|
||||||
|
return redirect(url_for('error', code=500))
|
||||||
|
return redirect(url_for('templates'))
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/template/<string:template>/apply', methods=['POST'], strict_slashes=False)
|
||||||
|
@login_required
|
||||||
|
def apply_records(template):
|
||||||
|
try:
|
||||||
|
jdata = request.json
|
||||||
|
records = []
|
||||||
|
|
||||||
|
for j in jdata:
|
||||||
|
name = '@' if j['record_name'] in ['@', ''] else j['record_name']
|
||||||
|
type = j['record_type']
|
||||||
|
data = j['record_data']
|
||||||
|
disabled = True if j['record_status'] == 'Disabled' else False
|
||||||
|
ttl = int(j['record_ttl']) if j['record_ttl'] else 3600
|
||||||
|
|
||||||
|
dtr = DomainTemplateRecord(name=name, type=type, data=data, status=disabled, ttl=ttl)
|
||||||
|
records.append(dtr)
|
||||||
|
|
||||||
|
t = DomainTemplate.query.filter(DomainTemplate.name == template).first()
|
||||||
|
result = t.replace_records(records)
|
||||||
|
if result['status'] == 'ok':
|
||||||
|
history = History(msg='Apply domain template record changes to domain template {0}'.format(template), detail=str(jdata), created_by=current_user.username)
|
||||||
|
history.add()
|
||||||
|
return make_response(jsonify(result), 200)
|
||||||
|
else:
|
||||||
|
return make_response(jsonify(result), 400)
|
||||||
|
except:
|
||||||
|
logging.error(traceback.print_exc())
|
||||||
|
return make_response(jsonify({'status': 'error', 'msg': 'Error when applying new changes'}), 500)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/template/<string:template>/delete', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@admin_role_required
|
||||||
|
def delete_template(template):
|
||||||
|
try:
|
||||||
|
t = DomainTemplate.query.filter(DomainTemplate.name == template).first()
|
||||||
|
if t is not None:
|
||||||
|
result = t.delete_template()
|
||||||
|
if result['status'] == 'ok':
|
||||||
|
history = History(msg='Deleted domain template {0}'.format(template), detail=str({'name': template}), created_by=current_user.username)
|
||||||
|
history.add()
|
||||||
|
return redirect(url_for('templates'))
|
||||||
|
else:
|
||||||
|
flash(result['msg'], 'error')
|
||||||
|
return redirect(url_for('templates'))
|
||||||
|
except:
|
||||||
|
logging.error(traceback.print_exc())
|
||||||
|
return redirect(url_for('error', code=500))
|
||||||
|
return redirect(url_for('templates'))
|
||||||
|
|
||||||
|
|
||||||
@app.route('/admin', methods=['GET', 'POST'])
|
@app.route('/admin', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@admin_role_required
|
@admin_role_required
|
||||||
@ -531,12 +835,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
|
||||||
@ -553,14 +858,11 @@ def admin_createuser():
|
|||||||
return render_template('admin_createuser.html', user=user, blank_password=True)
|
return render_template('admin_createuser.html', user=user, blank_password=True)
|
||||||
|
|
||||||
result = user.create_local_user();
|
result = user.create_local_user();
|
||||||
|
if result['status']:
|
||||||
|
return redirect(url_for('admin_manageuser'))
|
||||||
|
|
||||||
if result == 'Email already existed':
|
return render_template('admin_createuser.html', user=user, error=result['msg'])
|
||||||
return render_template('admin_createuser.html', user=user, duplicate_email=True)
|
|
||||||
|
|
||||||
if result == 'Username already existed':
|
|
||||||
return render_template('admin_createuser.html', user=user, duplicate_username=True)
|
|
||||||
|
|
||||||
return redirect(url_for('admin_manageuser'))
|
|
||||||
|
|
||||||
@app.route('/admin/manageuser', methods=['GET', 'POST'])
|
@app.route('/admin/manageuser', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@ -576,15 +878,14 @@ 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':
|
||||||
user = User(username=data)
|
user = User(username=data)
|
||||||
result = user.delete()
|
result = user.delete()
|
||||||
if result:
|
if result:
|
||||||
history = History(msg='Delete username %s' % data, created_by=current_user.username)
|
history = History(msg='Delete username {0}'.format(data), created_by=current_user.username)
|
||||||
history.add()
|
history.add()
|
||||||
return make_response(jsonify( { 'status': 'ok', 'msg': 'User has been removed.' } ), 200)
|
return make_response(jsonify( { 'status': 'ok', 'msg': 'User has been removed.' } ), 200)
|
||||||
else:
|
else:
|
||||||
@ -594,7 +895,7 @@ def admin_manageuser():
|
|||||||
user = User(username=data)
|
user = User(username=data)
|
||||||
result = user.revoke_privilege()
|
result = user.revoke_privilege()
|
||||||
if result:
|
if result:
|
||||||
history = History(msg='Revoke %s user privielges' % data, created_by=current_user.username)
|
history = History(msg='Revoke {0} user privielges'.format(data), created_by=current_user.username)
|
||||||
history.add()
|
history.add()
|
||||||
return make_response(jsonify( { 'status': 'ok', 'msg': 'Revoked user privielges.' } ), 200)
|
return make_response(jsonify( { 'status': 'ok', 'msg': 'Revoked user privielges.' } ), 200)
|
||||||
else:
|
else:
|
||||||
@ -606,7 +907,7 @@ def admin_manageuser():
|
|||||||
user = User(username=username)
|
user = User(username=username)
|
||||||
result = user.set_admin(is_admin)
|
result = user.set_admin(is_admin)
|
||||||
if result:
|
if result:
|
||||||
history = History(msg='Change user role of %s' % username, created_by=current_user.username)
|
history = History(msg='Change user role of {0}'.format(username), created_by=current_user.username)
|
||||||
history.add()
|
history.add()
|
||||||
return make_response(jsonify( { 'status': 'ok', 'msg': 'Changed user role successfully.' } ), 200)
|
return make_response(jsonify( { 'status': 'ok', 'msg': 'Changed user role successfully.' } ), 200)
|
||||||
else:
|
else:
|
||||||
@ -614,7 +915,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()
|
logging.error(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 +938,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,7 +947,8 @@ 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/<path:setting>/toggle', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@admin_role_required
|
@admin_role_required
|
||||||
def admin_settings_toggle(setting):
|
def admin_settings_toggle(setting):
|
||||||
@ -655,19 +958,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/<path: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,13 +987,13 @@ 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']
|
||||||
user = User(username=current_user.username)
|
user = User(username=current_user.username)
|
||||||
user.update_profile(enable_otp=enable_otp)
|
user.update_profile(enable_otp=enable_otp)
|
||||||
return make_response(jsonify( { 'status': 'ok', 'msg': 'Change OTP Authentication successfully. Status: %s' % enable_otp } ), 200)
|
return make_response(jsonify( { 'status': 'ok', 'msg': 'Change OTP Authentication successfully. Status: {0}'.format(enable_otp) } ), 200)
|
||||||
|
|
||||||
# get new avatar
|
# get new avatar
|
||||||
save_file_name = None
|
save_file_name = None
|
||||||
@ -702,7 +1007,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()
|
||||||
@ -710,7 +1014,7 @@ def user_profile():
|
|||||||
return render_template('user_profile.html')
|
return render_template('user_profile.html')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/user/avatar/<string:filename>')
|
@app.route('/user/avatar/<path:filename>')
|
||||||
def user_avatar(filename):
|
def user_avatar(filename):
|
||||||
return send_from_directory(os.path.join(app.config['UPLOAD_DIR'], 'avatar'), filename)
|
return send_from_directory(os.path.join(app.config['UPLOAD_DIR'], 'avatar'), filename)
|
||||||
|
|
||||||
@ -737,6 +1041,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():
|
||||||
@ -768,39 +1073,39 @@ def dyndns_update():
|
|||||||
break
|
break
|
||||||
|
|
||||||
if not domain:
|
if not domain:
|
||||||
history = History(msg="DynDNS update: attempted update of %s but it does not exist for this user" % hostname, created_by=current_user.username)
|
history = History(msg="DynDNS update: attempted update of {0} but it does not exist for this user".format(hostname), created_by=current_user.username)
|
||||||
history.add()
|
history.add()
|
||||||
return render_template('dyndns.html', response='nohost'), 200
|
return render_template('dyndns.html', response='nohost'), 200
|
||||||
|
|
||||||
r = Record()
|
r = Record()
|
||||||
r.name = hostname
|
r.name = hostname
|
||||||
# check if the user requested record exists within this domain
|
# check if the user requested record exists within this domain
|
||||||
if r.exists(domain.name) and r.is_allowed:
|
if r.exists(domain.name) and r.is_allowed_edit():
|
||||||
if r.data == myip:
|
if r.data == myip:
|
||||||
# record content did not change, return 'nochg'
|
# record content did not change, return 'nochg'
|
||||||
history = History(msg="DynDNS update: attempted update of %s but record did not change" % hostname, created_by=current_user.username)
|
history = History(msg="DynDNS update: attempted update of {0} but record did not change".format(hostname), created_by=current_user.username)
|
||||||
history.add()
|
history.add()
|
||||||
return render_template('dyndns.html', response='nochg'), 200
|
return render_template('dyndns.html', response='nochg'), 200
|
||||||
else:
|
else:
|
||||||
oldip = r.data
|
oldip = r.data
|
||||||
result = r.update(domain.name, myip)
|
result = r.update(domain.name, myip)
|
||||||
if result['status'] == 'ok':
|
if result['status'] == 'ok':
|
||||||
history = History(msg='DynDNS update: updated record %s in zone %s, it changed from %s to %s' % (hostname,domain.name,oldip,myip), detail=str(result), created_by=current_user.username)
|
history = History(msg='DynDNS update: updated record {0} in zone {1}, it changed from {2} to {3}'.format(hostname,domain.name,oldip,myip), detail=str(result), created_by=current_user.username)
|
||||||
history.add()
|
history.add()
|
||||||
return render_template('dyndns.html', response='good'), 200
|
return render_template('dyndns.html', response='good'), 200
|
||||||
else:
|
else:
|
||||||
return render_template('dyndns.html', response='911'), 200
|
return render_template('dyndns.html', response='911'), 200
|
||||||
elif r.is_allowed:
|
elif r.is_allowed_edit():
|
||||||
ondemand_creation = DomainSetting.query.filter(DomainSetting.domain == domain).filter(DomainSetting.setting == 'create_via_dyndns').first()
|
ondemand_creation = DomainSetting.query.filter(DomainSetting.domain == domain).filter(DomainSetting.setting == 'create_via_dyndns').first()
|
||||||
if (ondemand_creation != None) and (strtobool(ondemand_creation.value) == True):
|
if (ondemand_creation != None) and (strtobool(ondemand_creation.value) == True):
|
||||||
record = Record(name=hostname,type='A',data=myip,status=False,ttl=3600)
|
record = Record(name=hostname,type='A',data=myip,status=False,ttl=3600)
|
||||||
result = record.add(domain.name)
|
result = record.add(domain.name)
|
||||||
if result['status'] == 'ok':
|
if result['status'] == 'ok':
|
||||||
history = History(msg='DynDNS update: created record %s in zone %s, it now represents %s' % (hostname,domain.name,myip), detail=str(result), created_by=current_user.username)
|
history = History(msg='DynDNS update: created record {0} in zone {1}, it now represents {2}'.format(hostname,domain.name,myip), detail=str(result), created_by=current_user.username)
|
||||||
history.add()
|
history.add()
|
||||||
return render_template('dyndns.html', response='good'), 200
|
return render_template('dyndns.html', response='good'), 200
|
||||||
|
|
||||||
history = History(msg="DynDNS update: attempted update of %s but it does not exist for this user" % hostname, created_by=current_user.username)
|
history = History(msg='DynDNS update: attempted update of {0} but it does not exist for this user'.format(hostname), created_by=current_user.username)
|
||||||
history.add()
|
history.add()
|
||||||
return render_template('dyndns.html', response='nohost'), 200
|
return render_template('dyndns.html', response='nohost'), 200
|
||||||
|
|
||||||
|
@ -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
|
||||||
@ -36,14 +36,22 @@ SQLALCHEMY_MIGRATE_REPO = os.path.join(basedir, 'db_repository')
|
|||||||
SQLALCHEMY_TRACK_MODIFICATIONS = True
|
SQLALCHEMY_TRACK_MODIFICATIONS = True
|
||||||
|
|
||||||
# LDAP CONFIG
|
# LDAP CONFIG
|
||||||
|
LDAP_ENABLE = False
|
||||||
LDAP_TYPE = 'ldap'
|
LDAP_TYPE = 'ldap'
|
||||||
LDAP_URI = 'ldaps://your-ldap-server:636'
|
LDAP_URI = 'ldaps://your-ldap-server:636'
|
||||||
|
# with LDAP_BIND_TYPE you can specify 'direct' or 'search' to use user credentials
|
||||||
|
# for binding or a predefined LDAP_USERNAME and LDAP_PASSWORD, binding with non-DN only works with AD
|
||||||
|
LDAP_BIND_TYPE= 'direct' # direct or search
|
||||||
LDAP_USERNAME = 'cn=dnsuser,ou=users,ou=services,dc=duykhanh,dc=me'
|
LDAP_USERNAME = 'cn=dnsuser,ou=users,ou=services,dc=duykhanh,dc=me'
|
||||||
LDAP_PASSWORD = 'dnsuser'
|
LDAP_PASSWORD = 'dnsuser'
|
||||||
LDAP_SEARCH_BASE = 'ou=System Admins,ou=People,dc=duykhanh,dc=me'
|
LDAP_SEARCH_BASE = 'ou=System Admins,ou=People,dc=duykhanh,dc=me'
|
||||||
# Additional options only if LDAP_TYPE=ldap
|
# Additional options only if LDAP_TYPE=ldap
|
||||||
LDAP_USERNAMEFIELD = 'uid'
|
LDAP_USERNAMEFIELD = 'uid'
|
||||||
LDAP_FILTER = '(objectClass=inetorgperson)'
|
LDAP_FILTER = '(objectClass=inetorgperson)'
|
||||||
|
# enable LDAP_GROUP_SECURITY to allow Admin and User roles based on LDAP groups
|
||||||
|
#LDAP_GROUP_SECURITY = True # True or False
|
||||||
|
#LDAP_ADMIN_GROUP = 'CN=DnsAdmins,CN=Users,DC=example,DC=me'
|
||||||
|
#LDAP_USER_GROUP = 'CN=Domain Admins,CN=Users,DC=example,DC=me'
|
||||||
|
|
||||||
## AD CONFIG
|
## AD CONFIG
|
||||||
#LDAP_TYPE = 'ad'
|
#LDAP_TYPE = 'ad'
|
||||||
@ -58,12 +66,24 @@ LDAP_FILTER = '(objectClass=inetorgperson)'
|
|||||||
|
|
||||||
# Github Oauth
|
# Github Oauth
|
||||||
GITHUB_OAUTH_ENABLE = False
|
GITHUB_OAUTH_ENABLE = False
|
||||||
GITHUB_OAUTH_KEY = 'G0j1Q15aRsn36B3aD6nwKLiYbeirrUPU8nDd1wOC'
|
GITHUB_OAUTH_KEY = ''
|
||||||
GITHUB_OAUTH_SECRET = '0WYrKWePeBDkxlezzhFbDn1PBnCwEa0vCwVFvy6iLtgePlpT7WfUlAa9sZgm'
|
GITHUB_OAUTH_SECRET = ''
|
||||||
GITHUB_OAUTH_SCOPE = 'email'
|
GITHUB_OAUTH_SCOPE = 'email'
|
||||||
GITHUB_OAUTH_URL = 'http://127.0.0.1:5000/api/v3/'
|
GITHUB_OAUTH_URL = 'http://127.0.0.1:9191/api/v3/'
|
||||||
GITHUB_OAUTH_TOKEN = 'http://127.0.0.1:5000/oauth/token'
|
GITHUB_OAUTH_TOKEN = 'http://127.0.0.1:9191/oauth/token'
|
||||||
GITHUB_OAUTH_AUTHORIZE = 'http://127.0.0.1:5000/oauth/authorize'
|
GITHUB_OAUTH_AUTHORIZE = 'http://127.0.0.1:9191/oauth/authorize'
|
||||||
|
|
||||||
|
# Google OAuth
|
||||||
|
GOOGLE_OAUTH_ENABLE = False
|
||||||
|
GOOGLE_OAUTH_CLIENT_ID = ' '
|
||||||
|
GOOGLE_OAUTH_CLIENT_SECRET = ' '
|
||||||
|
GOOGLE_REDIRECT_URI = '/user/authorized'
|
||||||
|
GOOGLE_TOKEN_URL = 'https://accounts.google.com/o/oauth2/token'
|
||||||
|
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/'
|
||||||
|
|
||||||
#Default Auth
|
#Default Auth
|
||||||
BASIC_ENABLED = True
|
BASIC_ENABLED = True
|
||||||
@ -72,10 +92,13 @@ SIGNUP_ENABLED = True
|
|||||||
# POWERDNS CONFIG
|
# POWERDNS CONFIG
|
||||||
PDNS_STATS_URL = 'http://172.16.214.131:8081/'
|
PDNS_STATS_URL = 'http://172.16.214.131:8081/'
|
||||||
PDNS_API_KEY = 'you never know'
|
PDNS_API_KEY = 'you never know'
|
||||||
PDNS_VERSION = '3.4.7'
|
PDNS_VERSION = '4.1.1'
|
||||||
|
|
||||||
# RECORDS ALLOWED TO EDIT
|
# RECORDS ALLOWED TO EDIT
|
||||||
RECORDS_ALLOW_EDIT = ['A', 'AAAA', 'CNAME', 'SPF', 'PTR', 'MX', 'TXT']
|
RECORDS_ALLOW_EDIT = ['A', 'AAAA', 'CAA', 'CNAME', 'MX', 'PTR', 'SPF', 'SRV', 'TXT', 'NS']
|
||||||
|
|
||||||
|
# RECORDS ALLOWED TO EDIT FOR REVERSE DOMAINS
|
||||||
|
REVERSE_ALLOW_EDIT = ['PTR', 'NS']
|
||||||
|
|
||||||
# EXPERIMENTAL FEATURES
|
# EXPERIMENTAL FEATURES
|
||||||
PRETTY_IPV6_PTR = False
|
PRETTY_IPV6_PTR = False
|
||||||
|
97
configs/development.py
Normal file
97
configs/development.py
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
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_ENABLE = False
|
||||||
|
LDAP_TYPE = 'ldap'
|
||||||
|
LDAP_URI = 'ldaps://your-ldap-server:636'
|
||||||
|
# with LDAP_BIND_TYPE you can specify 'direct' or 'search' to use user credentials
|
||||||
|
# for binding or a predefined LDAP_USERNAME and LDAP_PASSWORD, binding with non-DN only works with AD
|
||||||
|
LDAP_BIND_TYPE= 'direct' # direct or search
|
||||||
|
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)'
|
||||||
|
# enable LDAP_GROUP_SECURITY to allow Admin and User roles based on LDAP groups
|
||||||
|
#LDAP_GROUP_SECURITY = True # True or False
|
||||||
|
#LDAP_ADMIN_GROUP = 'CN=DnsAdmins,CN=Users,DC=example,DC=me'
|
||||||
|
#LDAP_USER_GROUP = 'CN=Domain Admins,CN=Users,DC=example,DC=me'
|
||||||
|
|
||||||
|
## AD CONFIG
|
||||||
|
#LDAP_TYPE = 'ad'
|
||||||
|
#LDAP_URI = 'ldaps://your-ad-server:636'
|
||||||
|
#LDAP_USERNAME = 'cn=dnsuser,ou=Users,dc=domain,dc=local'
|
||||||
|
#LDAP_PASSWORD = 'dnsuser'
|
||||||
|
#LDAP_SEARCH_BASE = 'dc=domain,dc=local'
|
||||||
|
## You may prefer 'userPrincipalName' instead
|
||||||
|
#LDAP_USERNAMEFIELD = 'sAMAccountName'
|
||||||
|
## AD Group that you would like to have accesss to web app
|
||||||
|
#LDAP_FILTER = 'memberof=cn=DNS_users,ou=Groups,dc=domain,dc=local'
|
||||||
|
|
||||||
|
|
||||||
|
## GITHUB AUTHENTICATION
|
||||||
|
GITHUB_OAUTH_ENABLE = False
|
||||||
|
GITHUB_OAUTH_KEY = ''
|
||||||
|
GITHUB_OAUTH_SECRET = ''
|
||||||
|
GITHUB_OAUTH_SCOPE = 'email'
|
||||||
|
GITHUB_OAUTH_URL = 'http://127.0.0.1:9191/api/v3/'
|
||||||
|
GITHUB_OAUTH_TOKEN = 'http://127.0.0.1:9191/oauth/token'
|
||||||
|
GITHUB_OAUTH_AUTHORIZE = 'http://127.0.0.1:9191/oauth/authorize'
|
||||||
|
|
||||||
|
|
||||||
|
# GOOGLE AUTHENTICATION
|
||||||
|
GOOGLE_OAUTH_ENABLE = False
|
||||||
|
GOOGLE_OAUTH_CLIENT_ID = ''
|
||||||
|
GOOGLE_OAUTH_CLIENT_SECRET = ''
|
||||||
|
GOOGLE_REDIRECT_URI = '/user/authorized'
|
||||||
|
GOOGLE_TOKEN_URL = 'https://accounts.google.com/o/oauth2/token'
|
||||||
|
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/'
|
||||||
|
|
||||||
|
|
||||||
|
# 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', 'CAA', 'CNAME', 'MX', 'PTR', 'SPF', 'SRV', 'TXT', 'NS']
|
||||||
|
|
||||||
|
# RECORDS ALLOWED TO EDIT FOR REVERSE DOMAINS
|
||||||
|
REVERSE_ALLOW_EDIT = ['PTR', 'NS']
|
||||||
|
|
||||||
|
# EXPERIMENTAL FEATURES
|
||||||
|
PRETTY_IPV6_PTR = False
|
52
create_db.py
52
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, DomainTemplate
|
||||||
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,19 +56,36 @@ 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:
|
||||||
db.session.add(setting)
|
db.session.add(setting)
|
||||||
|
|
||||||
|
|
||||||
|
def init_domain_templates(db, domain_template_names):
|
||||||
|
|
||||||
|
# Get key name of data
|
||||||
|
name_of_domain_templates = map(lambda r: r.name, domain_template_names)
|
||||||
|
|
||||||
|
# Query to get current data
|
||||||
|
rows = db.session.query(DomainTemplate).filter(DomainTemplate.name.in_(name_of_domain_templates)).all()
|
||||||
|
|
||||||
|
# Check which data that need to insert
|
||||||
|
name_of_rows = map(lambda r: r.name, rows)
|
||||||
|
domain_templates = filter(lambda r: r.name not in name_of_rows, domain_template_names)
|
||||||
|
|
||||||
|
# Insert data
|
||||||
|
for domain_template in domain_templates:
|
||||||
|
db.session.add(domain_template)
|
||||||
|
|
||||||
def init_records():
|
def init_records():
|
||||||
# Create initial user roles and turn off maintenance mode
|
# Create initial user roles and turn off maintenance mode
|
||||||
init_roles(db, [
|
init_roles(db, [
|
||||||
@ -80,7 +101,12 @@ def init_records():
|
|||||||
Setting('default_domain_table_size', '10'),
|
Setting('default_domain_table_size', '10'),
|
||||||
Setting('auto_ptr','False')
|
Setting('auto_ptr','False')
|
||||||
])
|
])
|
||||||
|
# TODO: add sample records to sample templates
|
||||||
|
init_domain_templates(db, [
|
||||||
|
DomainTemplate('basic_template_1', 'Basic Template #1'),
|
||||||
|
DomainTemplate('basic_template_2', 'Basic Template #2'),
|
||||||
|
DomainTemplate('basic_template_3', 'Basic Template #3')
|
||||||
|
])
|
||||||
db_commit = db.session.commit()
|
db_commit = db.session.commit()
|
||||||
commit_version_control(db_commit)
|
commit_version_control(db_commit)
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
2
run.py
2
run.py
@ -3,7 +3,7 @@ from app import app
|
|||||||
from config import PORT
|
from config import PORT
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from config import BIND_ADDRESS
|
from config import BIND_ADDRESS
|
||||||
except:
|
except:
|
||||||
BIND_ADDRESS = '127.0.0.1'
|
BIND_ADDRESS = '127.0.0.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…
Reference in New Issue
Block a user