mirror of
https://github.com/cwinfo/powerdns-admin.git
synced 2024-11-08 14:40:27 +00:00
Merge branch 'master' into historyfix
This commit is contained in:
commit
7635686c43
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1,5 @@
|
||||
flask_session
|
||||
|
||||
# gedit
|
||||
*~
|
||||
|
||||
|
@ -15,6 +15,17 @@ SQLA_DB_HOST = '127.0.0.1'
|
||||
SQLA_DB_NAME = 'pda'
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = True
|
||||
|
||||
#CAPTCHA Config
|
||||
CAPTCHA_ENABLE = True
|
||||
CAPTCHA_LENGTH = 6
|
||||
CAPTCHA_WIDTH = 160
|
||||
CAPTCHA_HEIGHT = 60
|
||||
CAPTCHA_SESSION_KEY = 'captcha_image'
|
||||
|
||||
#Server side sessions tracking
|
||||
#Set to TRUE for CAPTCHA, or enable another stateful session tracking system
|
||||
FILESYSTEM_SESSIONS_ENABLED = True
|
||||
|
||||
### DATABASE - MySQL
|
||||
#SQLALCHEMY_DATABASE_URI = 'mysql://{}:{}@{}/{}'.format(
|
||||
# urllib.parse.quote_plus(SQLA_DB_USER),
|
||||
|
@ -4,6 +4,7 @@ PORT = 80
|
||||
SQLALCHEMY_DATABASE_URI = 'sqlite:////data/powerdns-admin.db'
|
||||
SESSION_COOKIE_SAMESITE = 'Lax'
|
||||
CSRF_COOKIE_HTTPONLY = True
|
||||
FILESYSTEM_SESSIONS_ENABLED = True
|
||||
|
||||
legal_envvars = (
|
||||
'SECRET_KEY',
|
||||
|
@ -1,11 +1,11 @@
|
||||
version: "2.1"
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
powerdns-admin:
|
||||
image: powerdns-admin-test
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker-test/Dockerfile
|
||||
image: powerdns-admin-test
|
||||
container_name: powerdns-admin-test
|
||||
ports:
|
||||
- "9191:80"
|
||||
@ -17,10 +17,10 @@ services:
|
||||
- pdns-server
|
||||
|
||||
pdns-server:
|
||||
image: pdns-server-test
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker-test/Dockerfile.pdns
|
||||
image: pdns-server-test
|
||||
ports:
|
||||
- "5053:53"
|
||||
- "5053:53/udp"
|
||||
|
@ -1,15 +1,35 @@
|
||||
FROM debian:stretch-slim
|
||||
FROM debian:bullseye-slim
|
||||
LABEL maintainer="k@ndk.name"
|
||||
|
||||
ENV LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 LANGUAGE=en_US.UTF-8
|
||||
|
||||
RUN apt-get update -y \
|
||||
&& apt-get install -y --no-install-recommends apt-transport-https locales locales-all python3-pip python3-setuptools python3-dev curl libsasl2-dev libldap2-dev libssl-dev libxml2-dev libxslt1-dev libxmlsec1-dev libffi-dev build-essential libmariadb-dev-compat \
|
||||
&& curl -sL https://deb.nodesource.com/setup_10.x | bash - \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
apt-transport-https \
|
||||
curl \
|
||||
build-essential \
|
||||
libffi-dev \
|
||||
libldap2-dev \
|
||||
libmariadb-dev-compat \
|
||||
libsasl2-dev \
|
||||
libssl-dev \
|
||||
libxml2-dev \
|
||||
libxmlsec1-dev \
|
||||
libxmlsec1-openssl \
|
||||
libxslt1-dev \
|
||||
locales \
|
||||
locales-all \
|
||||
pkg-config \
|
||||
python3-dev \
|
||||
python3-pip \
|
||||
python3-setuptools \
|
||||
&& curl -sL https://deb.nodesource.com/setup_lts.x | bash - \
|
||||
&& curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
|
||||
&& echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \
|
||||
&& apt-get update -y \
|
||||
&& apt-get install -y nodejs yarn \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
nodejs \
|
||||
yarn \
|
||||
&& apt-get clean -y \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
@ -21,8 +41,6 @@ RUN pip3 install --upgrade pip
|
||||
RUN pip3 install -r requirements.txt
|
||||
|
||||
COPY . /app
|
||||
COPY ./docker/entrypoint.sh /usr/local/bin/
|
||||
RUN chmod +x /usr/local/bin/entrypoint.sh
|
||||
|
||||
ENV FLASK_APP=powerdnsadmin/__init__.py
|
||||
RUN yarn install --pure-lockfile --production \
|
||||
@ -31,4 +49,4 @@ RUN yarn install --pure-lockfile --production \
|
||||
|
||||
COPY ./docker-test/wait-for-pdns.sh /opt
|
||||
RUN chmod u+x /opt/wait-for-pdns.sh
|
||||
CMD ["/opt/wait-for-pdns.sh", "/usr/local/bin/pytest","--capture=no","-vv"]
|
||||
CMD ["/opt/wait-for-pdns.sh", "/usr/local/bin/pytest", "-W", "ignore::DeprecationWarning", "--capture=no", "-vv"]
|
||||
|
@ -10,9 +10,9 @@ fi
|
||||
|
||||
# Import schema structure
|
||||
if [ -e "/data/pdns.sql" ]; then
|
||||
rm /data/pdns.db
|
||||
rm -f /data/pdns.db
|
||||
cat /data/pdns.sql | sqlite3 /data/pdns.db
|
||||
rm /data/pdns.sql
|
||||
rm -f /data/pdns.sql
|
||||
echo "Imported schema structure"
|
||||
fi
|
||||
|
||||
|
@ -7,6 +7,7 @@ ARG BUILD_DEPENDENCIES="build-base \
|
||||
openldap-dev \
|
||||
python3-dev \
|
||||
xmlsec-dev \
|
||||
npm \
|
||||
yarn \
|
||||
cargo"
|
||||
|
||||
|
@ -56,9 +56,9 @@ def seed_data():
|
||||
|
||||
op.bulk_insert(template_table,
|
||||
[
|
||||
{id: 1, 'name': 'basic_template_1', 'description': 'Basic Template #1'},
|
||||
{id: 2, 'name': 'basic_template_2', 'description': 'Basic Template #2'},
|
||||
{id: 3, 'name': 'basic_template_3', 'description': 'Basic Template #3'}
|
||||
{'id': 1, 'name': 'basic_template_1', 'description': 'Basic Template #1'},
|
||||
{'id': 2, 'name': 'basic_template_2', 'description': 'Basic Template #2'},
|
||||
{'id': 3, 'name': 'basic_template_3', 'description': 'Basic Template #3'}
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -0,0 +1,24 @@
|
||||
"""Add unique index to settings table keys
|
||||
|
||||
Revision ID: b24bf17725d2
|
||||
Revises: f41520e41cee
|
||||
Create Date: 2023-02-18 00:00:00.000000
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'b24bf17725d2'
|
||||
down_revision = 'f41520e41cee'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_index(op.f('ix_setting_name'), 'setting', ['name'], unique=True)
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_index(op.f('ix_setting_name'), table_name='setting')
|
14
package.json
14
package.json
@ -1,15 +1,17 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"admin-lte": "2.4.9",
|
||||
"bootstrap": "^3.4.1",
|
||||
"bootstrap-datepicker": "^1.8.0",
|
||||
"@fortawesome/fontawesome-free": "6.3.0",
|
||||
"admin-lte": "3.2.0",
|
||||
"bootstrap": "4.6.2",
|
||||
"bootstrap-datepicker": "^1.9.0",
|
||||
"bootstrap-validator": "^0.11.9",
|
||||
"datatables.net-plugins": "^1.10.19",
|
||||
"datatables.net-plugins": "^1.13.1",
|
||||
"icheck": "^1.0.2",
|
||||
"jquery-slimscroll": "^1.3.8",
|
||||
"jquery-ui-dist": "^1.12.1",
|
||||
"jquery-sparkline": "^2.4.0",
|
||||
"jquery-ui-dist": "^1.13.2",
|
||||
"jquery.quicksearch": "^2.4.0",
|
||||
"jtimeout": "^3.1.0",
|
||||
"jtimeout": "^3.2.0",
|
||||
"multiselect": "^0.9.12"
|
||||
}
|
||||
}
|
||||
|
@ -2,67 +2,65 @@ from flask_assets import Bundle, Environment, Filter
|
||||
|
||||
|
||||
class ConcatFilter(Filter):
|
||||
"""
|
||||
Filter that merges files, placing a semicolon between them.
|
||||
"""
|
||||
Filter that merges files, placing a semicolon between them.
|
||||
|
||||
Fixes issues caused by missing semicolons at end of JS assets, for example
|
||||
with last statement of jquery.pjax.js.
|
||||
"""
|
||||
def concat(self, out, hunks, **kw):
|
||||
out.write(';'.join([h.data() for h, info in hunks]))
|
||||
Fixes issues caused by missing semicolons at end of JS assets, for example
|
||||
with last statement of jquery.pjax.js.
|
||||
"""
|
||||
def concat(self, out, hunks, **kw):
|
||||
out.write(';'.join([h.data() for h, info in hunks]))
|
||||
|
||||
css_login = Bundle(
|
||||
'node_modules/@fortawesome/fontawesome-free/css/all.css',
|
||||
'node_modules/icheck/skins/square/blue.css',
|
||||
'node_modules/admin-lte/dist/css/adminlte.css',
|
||||
filters=('cssmin', 'cssrewrite'),
|
||||
output='generated/login.css')
|
||||
|
||||
css_login = Bundle('node_modules/bootstrap/dist/css/bootstrap.css',
|
||||
'node_modules/font-awesome/css/font-awesome.css',
|
||||
'node_modules/ionicons/dist/css/ionicons.css',
|
||||
'node_modules/icheck/skins/square/blue.css',
|
||||
'node_modules/admin-lte/dist/css/AdminLTE.css',
|
||||
filters=('cssmin', 'cssrewrite'),
|
||||
output='generated/login.css')
|
||||
js_login = Bundle(
|
||||
'node_modules/jquery/dist/jquery.js',
|
||||
'node_modules/bootstrap/dist/js/bootstrap.js',
|
||||
'node_modules/icheck/icheck.js',
|
||||
'custom/js/custom.js',
|
||||
filters=(ConcatFilter, 'rjsmin'),
|
||||
output='generated/login.js')
|
||||
|
||||
js_login = Bundle('node_modules/jquery/dist/jquery.js',
|
||||
'node_modules/bootstrap/dist/js/bootstrap.js',
|
||||
'node_modules/icheck/icheck.js',
|
||||
'custom/js/custom.js',
|
||||
filters=(ConcatFilter, 'rjsmin'),
|
||||
output='generated/login.js')
|
||||
|
||||
js_validation = Bundle('node_modules/bootstrap-validator/dist/validator.js',
|
||||
output='generated/validation.js')
|
||||
js_validation = Bundle(
|
||||
'node_modules/bootstrap-validator/dist/validator.js',
|
||||
output='generated/validation.js')
|
||||
|
||||
css_main = Bundle(
|
||||
'node_modules/bootstrap/dist/css/bootstrap.css',
|
||||
'node_modules/font-awesome/css/font-awesome.css',
|
||||
'node_modules/ionicons/dist/css/ionicons.css',
|
||||
'node_modules/datatables.net-bs/css/dataTables.bootstrap.css',
|
||||
'node_modules/icheck/skins/square/blue.css',
|
||||
'node_modules/multiselect/css/multi-select.css',
|
||||
'node_modules/admin-lte/dist/css/AdminLTE.css',
|
||||
'node_modules/admin-lte/dist/css/skins/_all-skins.css',
|
||||
'custom/css/custom.css',
|
||||
'node_modules/bootstrap-datepicker/dist/css/bootstrap-datepicker.css',
|
||||
filters=('cssmin', 'cssrewrite'),
|
||||
output='generated/main.css')
|
||||
'node_modules/@fortawesome/fontawesome-free/css/all.css',
|
||||
'node_modules/datatables.net-bs4/css/dataTables.bootstrap4.css',
|
||||
'node_modules/icheck/skins/square/blue.css',
|
||||
'node_modules/multiselect/css/multi-select.css',
|
||||
'node_modules/admin-lte/dist/css/adminlte.css',
|
||||
'custom/css/custom.css',
|
||||
'node_modules/bootstrap-datepicker/dist/css/bootstrap-datepicker.css',
|
||||
filters=('cssmin', 'cssrewrite'),
|
||||
output='generated/main.css')
|
||||
|
||||
js_main = Bundle('node_modules/jquery/dist/jquery.js',
|
||||
'node_modules/jquery-ui-dist/jquery-ui.js',
|
||||
'node_modules/bootstrap/dist/js/bootstrap.js',
|
||||
'node_modules/datatables.net/js/jquery.dataTables.js',
|
||||
'node_modules/datatables.net-bs/js/dataTables.bootstrap.js',
|
||||
'node_modules/jquery-sparkline/jquery.sparkline.js',
|
||||
'node_modules/jquery-slimscroll/jquery.slimscroll.js',
|
||||
'node_modules/icheck/icheck.js',
|
||||
'node_modules/fastclick/lib/fastclick.js',
|
||||
'node_modules/moment/moment.js',
|
||||
'node_modules/admin-lte/dist/js/adminlte.js',
|
||||
'node_modules/multiselect/js/jquery.multi-select.js',
|
||||
'node_modules/datatables.net-plugins/sorting/natural.js',
|
||||
'node_modules/jtimeout/src/jTimeout.js',
|
||||
'node_modules/jquery.quicksearch/src/jquery.quicksearch.js',
|
||||
'custom/js/custom.js',
|
||||
'node_modules/bootstrap-datepicker/dist/js/bootstrap-datepicker.js',
|
||||
filters=(ConcatFilter, 'rjsmin'),
|
||||
output='generated/main.js')
|
||||
js_main = Bundle(
|
||||
'node_modules/jquery/dist/jquery.js',
|
||||
'node_modules/jquery-ui-dist/jquery-ui.js',
|
||||
'node_modules/bootstrap/dist/js/bootstrap.bundle.js',
|
||||
'node_modules/datatables.net/js/jquery.dataTables.js',
|
||||
'node_modules/datatables.net-bs4/js/dataTables.bootstrap4.js',
|
||||
'node_modules/jquery-sparkline/jquery.sparkline.js',
|
||||
'node_modules/jquery-slimscroll/jquery.slimscroll.js',
|
||||
'node_modules/icheck/icheck.js',
|
||||
'node_modules/fastclick/lib/fastclick.js',
|
||||
'node_modules/moment/moment.js',
|
||||
'node_modules/admin-lte/dist/js/adminlte.js',
|
||||
'node_modules/multiselect/js/jquery.multi-select.js',
|
||||
'node_modules/datatables.net-plugins/sorting/natural.js',
|
||||
'node_modules/jtimeout/src/jTimeout.js',
|
||||
'node_modules/jquery.quicksearch/src/jquery.quicksearch.js',
|
||||
'custom/js/custom.js',
|
||||
'node_modules/bootstrap-datepicker/dist/js/bootstrap-datepicker.js',
|
||||
filters=(ConcatFilter, 'rjsmin'),
|
||||
output='generated/main.js')
|
||||
|
||||
assets = Environment()
|
||||
assets.register('js_login', js_login)
|
||||
|
@ -388,7 +388,7 @@ def apikey_can_configure_dnssec(http_methods=[]):
|
||||
def allowed_record_types(f):
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
if request.method == 'GET':
|
||||
if request.method in ['GET', 'DELETE', 'PUT']:
|
||||
return f(*args, **kwargs)
|
||||
|
||||
if g.apikey.role.name in ['Administrator', 'Operator']:
|
||||
|
@ -8,10 +8,17 @@ SECRET_KEY = 'e951e5a1f4b94151b360f47edf596dd2'
|
||||
BIND_ADDRESS = '0.0.0.0'
|
||||
PORT = 9191
|
||||
HSTS_ENABLED = False
|
||||
FILESYSTEM_SESSIONS_ENABLED = False
|
||||
FILESYSTEM_SESSIONS_ENABLED = True
|
||||
SESSION_COOKIE_SAMESITE = 'Lax'
|
||||
CSRF_COOKIE_HTTPONLY = True
|
||||
|
||||
#CAPTCHA Config
|
||||
CAPTCHA_ENABLE = True
|
||||
CAPTCHA_LENGTH = 6
|
||||
CAPTCHA_WIDTH = 160
|
||||
CAPTCHA_HEIGHT = 60
|
||||
CAPTCHA_SESSION_KEY = 'captcha_image'
|
||||
|
||||
### DATABASE CONFIG
|
||||
SQLA_DB_USER = 'pda'
|
||||
SQLA_DB_PASSWORD = 'changeme'
|
||||
@ -20,15 +27,15 @@ SQLA_DB_NAME = 'pda'
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = True
|
||||
|
||||
### DATABASE - MySQL
|
||||
SQLALCHEMY_DATABASE_URI = 'mysql://{}:{}@{}/{}'.format(
|
||||
urllib.parse.quote_plus(SQLA_DB_USER),
|
||||
urllib.parse.quote_plus(SQLA_DB_PASSWORD),
|
||||
SQLA_DB_HOST,
|
||||
SQLA_DB_NAME
|
||||
)
|
||||
# SQLALCHEMY_DATABASE_URI = 'mysql://{}:{}@{}/{}'.format(
|
||||
# urllib.parse.quote_plus(SQLA_DB_USER),
|
||||
# urllib.parse.quote_plus(SQLA_DB_PASSWORD),
|
||||
# SQLA_DB_HOST,
|
||||
# SQLA_DB_NAME
|
||||
# )
|
||||
|
||||
### DATABASE - SQLite
|
||||
# SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'pdns.db')
|
||||
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'pdns.db')
|
||||
|
||||
# SAML Authnetication
|
||||
SAML_ENABLED = False
|
||||
|
@ -2,6 +2,7 @@ import json
|
||||
import re
|
||||
import traceback
|
||||
from flask import current_app
|
||||
from flask_login import current_user
|
||||
from urllib.parse import urljoin
|
||||
from distutils.util import strtobool
|
||||
|
||||
@ -548,11 +549,12 @@ class Domain(db.Model):
|
||||
domain.apikeys[:] = []
|
||||
|
||||
# Remove history for domain
|
||||
domain_history = History.query.filter(
|
||||
History.domain_id == domain.id
|
||||
)
|
||||
if domain_history:
|
||||
domain_history.delete()
|
||||
if not Setting().get('preserve_history'):
|
||||
domain_history = History.query.filter(
|
||||
History.domain_id == domain.id
|
||||
)
|
||||
if domain_history:
|
||||
domain_history.delete()
|
||||
|
||||
# then remove domain
|
||||
Domain.query.filter(Domain.name == domain_name).delete()
|
||||
@ -851,6 +853,7 @@ class Domain(db.Model):
|
||||
|
||||
headers = {'X-API-Key': self.PDNS_API_KEY, 'Content-Type': 'application/json'}
|
||||
|
||||
account_name_old = Account().get_name_by_id(domain.account_id)
|
||||
account_name = Account().get_name_by_id(account_id)
|
||||
|
||||
post_data = {"account": account_name}
|
||||
@ -874,6 +877,13 @@ class Domain(db.Model):
|
||||
self.update()
|
||||
msg_str = 'Account changed for domain {0} successfully'
|
||||
current_app.logger.info(msg_str.format(domain_name))
|
||||
history = History(msg='Update domain {0} associate account {1}'.format(domain.name, 'none' if account_name == '' else account_name),
|
||||
detail = json.dumps({
|
||||
'assoc_account': 'None' if account_name == '' else account_name,
|
||||
'dissoc_account': 'None' if account_name_old == '' else account_name_old
|
||||
}),
|
||||
created_by=current_user.username)
|
||||
history.add()
|
||||
return {'status': 'ok', 'msg': 'account changed successfully'}
|
||||
|
||||
except Exception as e:
|
||||
|
@ -422,6 +422,25 @@ class Record(object):
|
||||
]
|
||||
|
||||
d = Domain()
|
||||
for r in del_rrsets:
|
||||
for record in r['records']:
|
||||
# Format the reverse record name
|
||||
# It is the reverse of forward record's content.
|
||||
reverse_host_address = dns.reversename.from_address(
|
||||
record['content']).to_text()
|
||||
|
||||
# Create the reverse domain name in PDNS
|
||||
domain_reverse_name = d.get_reverse_domain_name(
|
||||
reverse_host_address)
|
||||
d.create_reverse_domain(domain_name,
|
||||
domain_reverse_name)
|
||||
|
||||
# Delete the reverse zone
|
||||
self.name = reverse_host_address
|
||||
self.type = 'PTR'
|
||||
self.data = record['content']
|
||||
self.delete(domain_reverse_name)
|
||||
|
||||
for r in new_rrsets:
|
||||
for record in r['records']:
|
||||
# Format the reverse record name
|
||||
@ -455,25 +474,6 @@ class Record(object):
|
||||
# Format the rrset
|
||||
rrset = {"rrsets": rrset_data}
|
||||
self.add(domain_reverse_name, rrset)
|
||||
|
||||
for r in del_rrsets:
|
||||
for record in r['records']:
|
||||
# Format the reverse record name
|
||||
# It is the reverse of forward record's content.
|
||||
reverse_host_address = dns.reversename.from_address(
|
||||
record['content']).to_text()
|
||||
|
||||
# Create the reverse domain name in PDNS
|
||||
domain_reverse_name = d.get_reverse_domain_name(
|
||||
reverse_host_address)
|
||||
d.create_reverse_domain(domain_name,
|
||||
domain_reverse_name)
|
||||
|
||||
# Delete the reverse zone
|
||||
self.name = reverse_host_address
|
||||
self.type = 'PTR'
|
||||
self.data = record['content']
|
||||
self.delete(domain_reverse_name)
|
||||
return {
|
||||
'status': 'ok',
|
||||
'msg': 'Auto-PTR record was updated successfully'
|
||||
|
@ -5,7 +5,7 @@ class Role(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String(64), index=True, unique=True)
|
||||
description = db.Column(db.String(128))
|
||||
users = db.relationship('User', backref='role', lazy=True)
|
||||
users = db.relationship('User', back_populates='role', lazy=True)
|
||||
apikeys = db.relationship('ApiKey', back_populates='role', lazy=True)
|
||||
|
||||
def __init__(self, id=None, name=None, description=None):
|
||||
@ -20,4 +20,4 @@ class Role(db.Model):
|
||||
self.description = description
|
||||
|
||||
def __repr__(self):
|
||||
return '<Role {0}r>'.format(self.name)
|
||||
return '<Role {0}>'.format(self.name)
|
||||
|
@ -11,7 +11,7 @@ from .base import db
|
||||
|
||||
class Setting(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String(64))
|
||||
name = db.Column(db.String(64), unique=True, index=True)
|
||||
value = db.Column(db.Text())
|
||||
|
||||
defaults = {
|
||||
@ -31,6 +31,7 @@ class Setting(db.Model):
|
||||
'delete_sso_accounts': False,
|
||||
'bg_domain_updates': False,
|
||||
'enable_api_rr_history': True,
|
||||
'preserve_history': False,
|
||||
'site_name': 'PowerDNS-Admin',
|
||||
'site_url': 'http://localhost:9191',
|
||||
'session_timeout': 10,
|
||||
|
@ -34,6 +34,7 @@ class User(db.Model):
|
||||
otp_secret = db.Column(db.String(16))
|
||||
confirmed = db.Column(db.SmallInteger, nullable=False, default=0)
|
||||
role_id = db.Column(db.Integer, db.ForeignKey('role.id'))
|
||||
role = db.relationship('Role', back_populates="users", lazy=True)
|
||||
accounts = None
|
||||
|
||||
def __init__(self,
|
||||
|
@ -1,5 +1,5 @@
|
||||
from .base import (
|
||||
csrf, login_manager, handle_bad_request, handle_unauthorized_access,
|
||||
captcha, csrf, login_manager, handle_bad_request, handle_unauthorized_access,
|
||||
handle_access_forbidden, handle_page_not_found, handle_internal_server_error
|
||||
)
|
||||
|
||||
@ -14,6 +14,7 @@ from .api import api_bp, apilist_bp
|
||||
def init_app(app):
|
||||
login_manager.init_app(app)
|
||||
csrf.init_app(app)
|
||||
captcha.init_app(app)
|
||||
|
||||
app.register_blueprint(index_bp)
|
||||
app.register_blueprint(user_bp)
|
||||
|
@ -610,14 +610,21 @@ def manage_user():
|
||||
@operator_role_required
|
||||
def edit_account(account_name=None):
|
||||
users = User.query.all()
|
||||
account = Account.query.filter(
|
||||
Account.name == account_name).first()
|
||||
all_accounts = Account.query.all()
|
||||
accounts = {acc.id: acc for acc in all_accounts}
|
||||
domains = Domain.query.all()
|
||||
|
||||
if request.method == 'GET':
|
||||
if account_name is None:
|
||||
if account_name is None or not account:
|
||||
return render_template('admin_edit_account.html',
|
||||
account=None,
|
||||
account_user_ids=[],
|
||||
users=users,
|
||||
domains=domains,
|
||||
accounts=accounts,
|
||||
create=1)
|
||||
|
||||
else:
|
||||
account = Account.query.filter(
|
||||
Account.name == account_name).first()
|
||||
@ -626,11 +633,14 @@ def edit_account(account_name=None):
|
||||
account=account,
|
||||
account_user_ids=account_user_ids,
|
||||
users=users,
|
||||
domains=domains,
|
||||
accounts=accounts,
|
||||
create=0)
|
||||
|
||||
if request.method == 'POST':
|
||||
fdata = request.form
|
||||
new_user_list = request.form.getlist('account_multi_user')
|
||||
new_domain_list = request.form.getlist('account_domains')
|
||||
|
||||
# on POST, synthesize account and account_user_ids from form data
|
||||
if not account_name:
|
||||
@ -654,6 +664,8 @@ def edit_account(account_name=None):
|
||||
account=account,
|
||||
account_user_ids=account_user_ids,
|
||||
users=users,
|
||||
domains=domains,
|
||||
accounts=accounts,
|
||||
create=create,
|
||||
invalid_accountname=True)
|
||||
|
||||
@ -662,19 +674,33 @@ def edit_account(account_name=None):
|
||||
account=account,
|
||||
account_user_ids=account_user_ids,
|
||||
users=users,
|
||||
domains=domains,
|
||||
accounts=accounts,
|
||||
create=create,
|
||||
duplicate_accountname=True)
|
||||
|
||||
result = account.create_account()
|
||||
history = History(msg='Create account {0}'.format(account.name),
|
||||
created_by=current_user.username)
|
||||
|
||||
else:
|
||||
result = account.update_account()
|
||||
history = History(msg='Update account {0}'.format(account.name),
|
||||
created_by=current_user.username)
|
||||
|
||||
if result['status']:
|
||||
account = Account.query.filter(
|
||||
Account.name == account_name).first()
|
||||
old_domains = Domain.query.filter(Domain.account_id == account.id).all()
|
||||
|
||||
for domain_name in new_domain_list:
|
||||
domain = Domain.query.filter(
|
||||
Domain.name == domain_name).first()
|
||||
if account.id != domain.account_id:
|
||||
Domain(name=domain_name).assoc_account(account.id)
|
||||
|
||||
for domain in old_domains:
|
||||
if domain.name not in new_domain_list:
|
||||
Domain(name=domain.name).assoc_account(None)
|
||||
|
||||
history = History(msg='{0} account {1}'.format('Create' if create else 'Update', account.name),
|
||||
created_by=current_user.username)
|
||||
|
||||
account.grant_privileges(new_user_list)
|
||||
history.add()
|
||||
return redirect(url_for('admin.manage_account'))
|
||||
@ -769,7 +795,7 @@ class DetailedHistory():
|
||||
if 'domain_type' in detail_dict and 'account_id' in detail_dict: # this is a domain creation
|
||||
self.detailed_msg = render_template_string("""
|
||||
<table class="table table-bordered table-striped">
|
||||
<tr><td>Domain type:</td><td>{{ domaintype }}</td></tr>
|
||||
<tr><td>Domain Type:</td><td>{{ domaintype }}</td></tr>
|
||||
<tr><td>Account:</td><td>{{ account }}</td></tr>
|
||||
</table>
|
||||
""",
|
||||
@ -778,22 +804,23 @@ class DetailedHistory():
|
||||
|
||||
elif 'authenticator' in detail_dict: # this is a user authentication
|
||||
self.detailed_msg = render_template_string("""
|
||||
<table class="table table-bordered table-striped" style="width:565px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="3" style="background: rgba({{ background_rgba }});">
|
||||
<p style="color:white;">User {{ username }} authentication {{ auth_result }}</p>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<table class="table table-bordered table-striped"">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Authenticator Type:</td>
|
||||
<td colspan="2">{{ authenticator }}</td>
|
||||
<td>Username:</td>
|
||||
<td>{{ username }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>IP Address</td>
|
||||
<td colspan="2">{{ ip_address }}</td>
|
||||
<td>Authentication Result:</td>
|
||||
<td>{{ auth_result }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Authenticator Type:</td>
|
||||
<td>{{ authenticator }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>IP Address:</td>
|
||||
<td>{{ ip_address }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@ -890,6 +917,16 @@ class DetailedHistory():
|
||||
''',
|
||||
history_status=DetailedHistory.get_key_val(detail_dict, 'status'),
|
||||
history_msg=DetailedHistory.get_key_val(detail_dict, 'msg'))
|
||||
|
||||
elif 'Update domain' in history.msg and 'associate account' in history.msg: # When an account gets associated or dissociate with domains
|
||||
self.detailed_msg = render_template_string('''
|
||||
<table class="table table-bordered table-striped">
|
||||
<tr><td>Associate: </td><td>{{ history_assoc_account }}</td></tr>
|
||||
<tr><td>Dissociate:</td><td>{{ history_dissoc_account }}</td></tr>
|
||||
</table>
|
||||
''',
|
||||
history_assoc_account=DetailedHistory.get_key_val(detail_dict, 'assoc_account'),
|
||||
history_dissoc_account=DetailedHistory.get_key_val(detail_dict, 'dissoc_account'))
|
||||
|
||||
# check for lower key as well for old databases
|
||||
@staticmethod
|
||||
@ -927,6 +964,13 @@ def history():
|
||||
'msg': 'You do not have permission to remove history.'
|
||||
}), 401)
|
||||
|
||||
if Setting().get('preserve_history'):
|
||||
return make_response(
|
||||
jsonify({
|
||||
'status': 'error',
|
||||
'msg': 'History removal is not allowed (toggle preserve_history in settings).'
|
||||
}), 401)
|
||||
|
||||
h = History()
|
||||
result = h.remove_all()
|
||||
if result:
|
||||
@ -1282,6 +1326,7 @@ def setting_basic():
|
||||
'otp_field_enabled',
|
||||
'otp_force',
|
||||
'pdns_api_timeout',
|
||||
'preserve_history',
|
||||
'pretty_ipv6_ptr',
|
||||
'record_helper',
|
||||
'record_quick_edit',
|
||||
|
@ -1,22 +1,21 @@
|
||||
import json
|
||||
from urllib.parse import urljoin
|
||||
import secrets
|
||||
import string
|
||||
from base64 import b64encode
|
||||
from flask import (
|
||||
Blueprint, g, request, abort, current_app, make_response, jsonify,
|
||||
)
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from flask import (Blueprint, g, request, abort, current_app, make_response, jsonify)
|
||||
from flask_login import current_user
|
||||
|
||||
from .base import csrf
|
||||
from ..models.base import db
|
||||
from ..models import (
|
||||
User, Domain, DomainUser, Account, AccountUser, History, Setting, ApiKey,
|
||||
Role,
|
||||
from ..decorators import (
|
||||
api_basic_auth, api_can_create_domain, is_json, apikey_auth,
|
||||
apikey_can_create_domain, apikey_can_remove_domain,
|
||||
apikey_is_admin, apikey_can_access_domain, apikey_can_configure_dnssec,
|
||||
api_role_can, apikey_or_basic_auth,
|
||||
callback_if_request_body_contains_key, allowed_record_types, allowed_record_ttl
|
||||
)
|
||||
from ..lib import utils, helper
|
||||
from ..lib.schema import (
|
||||
ApiKeySchema, DomainSchema, ApiPlainKeySchema, UserSchema, AccountSchema,
|
||||
UserDetailedSchema,
|
||||
)
|
||||
from ..lib.errors import (
|
||||
StructuredException,
|
||||
DomainNotExists, DomainAlreadyExists, DomainAccessForbidden,
|
||||
@ -26,15 +25,15 @@ from ..lib.errors import (
|
||||
UserCreateFail, UserCreateDuplicate, UserUpdateFail, UserDeleteFail,
|
||||
UserUpdateFailEmail, InvalidAccountNameException
|
||||
)
|
||||
from ..decorators import (
|
||||
api_basic_auth, api_can_create_domain, is_json, apikey_auth,
|
||||
apikey_can_create_domain, apikey_can_remove_domain,
|
||||
apikey_is_admin, apikey_can_access_domain, apikey_can_configure_dnssec,
|
||||
api_role_can, apikey_or_basic_auth,
|
||||
callback_if_request_body_contains_key, allowed_record_types, allowed_record_ttl
|
||||
from ..lib.schema import (
|
||||
ApiKeySchema, DomainSchema, ApiPlainKeySchema, UserSchema, AccountSchema,
|
||||
UserDetailedSchema,
|
||||
)
|
||||
import secrets
|
||||
import string
|
||||
from ..models import (
|
||||
User, Domain, DomainUser, Account, AccountUser, History, Setting, ApiKey,
|
||||
Role,
|
||||
)
|
||||
from ..models.base import db
|
||||
|
||||
api_bp = Blueprint('api', __name__, url_prefix='/api/v1')
|
||||
apilist_bp = Blueprint('apilist', __name__, url_prefix='/')
|
||||
@ -56,10 +55,10 @@ def get_user_domains():
|
||||
.outerjoin(Account, Domain.account_id == Account.id) \
|
||||
.outerjoin(AccountUser, Account.id == AccountUser.account_id) \
|
||||
.filter(
|
||||
db.or_(
|
||||
DomainUser.user_id == current_user.id,
|
||||
AccountUser.user_id == current_user.id
|
||||
)).all()
|
||||
db.or_(
|
||||
DomainUser.user_id == current_user.id,
|
||||
AccountUser.user_id == current_user.id
|
||||
)).all()
|
||||
return domains
|
||||
|
||||
|
||||
@ -71,10 +70,10 @@ def get_user_apikeys(domain_name=None):
|
||||
.outerjoin(Account, Domain.account_id == Account.id) \
|
||||
.outerjoin(AccountUser, Account.id == AccountUser.account_id) \
|
||||
.filter(
|
||||
db.or_(
|
||||
DomainUser.user_id == User.id,
|
||||
AccountUser.user_id == User.id
|
||||
)
|
||||
db.or_(
|
||||
DomainUser.user_id == User.id,
|
||||
AccountUser.user_id == User.id
|
||||
)
|
||||
) \
|
||||
.filter(User.id == current_user.id)
|
||||
|
||||
@ -167,12 +166,7 @@ def handle_request_is_not_json(err):
|
||||
def before_request():
|
||||
# Check site is in maintenance mode
|
||||
maintenance = Setting().get('maintenance')
|
||||
if (
|
||||
maintenance and current_user.is_authenticated and
|
||||
current_user.role.name not in [
|
||||
'Administrator', 'Operator'
|
||||
]
|
||||
):
|
||||
if (maintenance and current_user.is_authenticated and current_user.role.name not in ['Administrator', 'Operator']):
|
||||
return make_response(
|
||||
jsonify({
|
||||
"status": False,
|
||||
@ -224,14 +218,13 @@ def api_login_create_zone():
|
||||
|
||||
history = History(msg='Add domain {0}'.format(
|
||||
data['name'].rstrip('.')),
|
||||
detail=json.dumps(data),
|
||||
created_by=current_user.username,
|
||||
domain_id=domain_id)
|
||||
detail=json.dumps(data),
|
||||
created_by=current_user.username,
|
||||
domain_id=domain_id)
|
||||
history.add()
|
||||
|
||||
if current_user.role.name not in ['Administrator', 'Operator']:
|
||||
current_app.logger.debug(
|
||||
"User is ordinary user, assigning created domain")
|
||||
current_app.logger.debug("User is ordinary user, assigning created domain")
|
||||
domain = Domain(name=data['name'].rstrip('.'))
|
||||
domain.update()
|
||||
domain.grant_privileges([current_user.id])
|
||||
@ -299,9 +292,9 @@ def api_login_delete_zone(domain_name):
|
||||
|
||||
history = History(msg='Delete domain {0}'.format(
|
||||
utils.pretty_domain_name(domain_name)),
|
||||
detail='',
|
||||
created_by=current_user.username,
|
||||
domain_id=domain_id)
|
||||
detail='',
|
||||
created_by=current_user.username,
|
||||
domain_id=domain_id)
|
||||
history.add()
|
||||
|
||||
except Exception as e:
|
||||
@ -326,14 +319,14 @@ def api_generate_apikey():
|
||||
|
||||
if 'domains' not in data:
|
||||
domains = []
|
||||
elif not isinstance(data['domains'], (list, )):
|
||||
elif not isinstance(data['domains'], (list,)):
|
||||
abort(400)
|
||||
else:
|
||||
domains = [d['name'] if isinstance(d, dict) else d for d in data['domains']]
|
||||
|
||||
if 'accounts' not in data:
|
||||
accounts = []
|
||||
elif not isinstance(data['accounts'], (list, )):
|
||||
elif not isinstance(data['accounts'], (list,)):
|
||||
abort(400)
|
||||
else:
|
||||
accounts = [a['name'] if isinstance(a, dict) else a for a in data['accounts']]
|
||||
@ -385,8 +378,7 @@ def api_generate_apikey():
|
||||
user_domain_list = [item.name for item in user_domain_obj_list]
|
||||
|
||||
current_app.logger.debug("Input domain list: {0}".format(domain_list))
|
||||
current_app.logger.debug(
|
||||
"User domain list: {0}".format(user_domain_list))
|
||||
current_app.logger.debug("User domain list: {0}".format(user_domain_list))
|
||||
|
||||
inter = set(domain_list).intersection(set(user_domain_list))
|
||||
|
||||
@ -539,14 +531,14 @@ def api_update_apikey(apikey_id):
|
||||
|
||||
if 'domains' not in data:
|
||||
domains = None
|
||||
elif not isinstance(data['domains'], (list, )):
|
||||
elif not isinstance(data['domains'], (list,)):
|
||||
abort(400)
|
||||
else:
|
||||
domains = [d['name'] if isinstance(d, dict) else d for d in data['domains']]
|
||||
|
||||
if 'accounts' not in data:
|
||||
accounts = None
|
||||
elif not isinstance(data['accounts'], (list, )):
|
||||
elif not isinstance(data['accounts'], (list,)):
|
||||
abort(400)
|
||||
else:
|
||||
accounts = [a['name'] if isinstance(a, dict) else a for a in data['accounts']]
|
||||
@ -963,9 +955,7 @@ def api_delete_account(account_id):
|
||||
account = account_list[0]
|
||||
else:
|
||||
abort(404)
|
||||
current_app.logger.debug(
|
||||
f'Deleting Account {account.name}'
|
||||
)
|
||||
current_app.logger.debug(f'Deleting Account {account.name}')
|
||||
|
||||
# Remove account association from domains first
|
||||
if len(account.domains) > 0:
|
||||
@ -1047,7 +1037,7 @@ def api_remove_account_user(account_id, user_id):
|
||||
user_list = User.query.join(AccountUser).filter(
|
||||
AccountUser.account_id == account_id,
|
||||
AccountUser.user_id == user_id,
|
||||
).all()
|
||||
).all()
|
||||
if not user_list:
|
||||
abort(404)
|
||||
if not account.remove_user(user):
|
||||
@ -1194,17 +1184,13 @@ def api_get_zones(server_id):
|
||||
return jsonify(domain_schema.dump(domain_obj_list)), 200
|
||||
else:
|
||||
resp = helper.forward_request()
|
||||
if (
|
||||
g.apikey.role.name not in ['Administrator', 'Operator']
|
||||
and resp.status_code == 200
|
||||
):
|
||||
if (g.apikey.role.name not in ['Administrator', 'Operator'] and resp.status_code == 200):
|
||||
domain_list = [d['name']
|
||||
for d in domain_schema.dump(g.apikey.domains)]
|
||||
|
||||
accounts_domains = [d.name for a in g.apikey.accounts for d in a.domains]
|
||||
allowed_domains = set(domain_list + accounts_domains)
|
||||
current_app.logger.debug("Account domains: {}".format(
|
||||
'/'.join(accounts_domains)))
|
||||
current_app.logger.debug("Account domains: {}".format('/'.join(accounts_domains)))
|
||||
content = json.dumps([i for i in json.loads(resp.content)
|
||||
if i['name'].rstrip('.') in allowed_domains])
|
||||
return content, resp.status_code, resp.headers.items()
|
||||
@ -1225,6 +1211,7 @@ def api_server_config_forward(server_id):
|
||||
resp = helper.forward_request()
|
||||
return resp.content, resp.status_code, resp.headers.items()
|
||||
|
||||
|
||||
# The endpoint to synchronize Domains in background
|
||||
@api_bp.route('/sync_domains', methods=['GET'])
|
||||
@apikey_or_basic_auth
|
||||
@ -1233,6 +1220,7 @@ def sync_domains():
|
||||
domain.update()
|
||||
return 'Finished synchronization in background', 200
|
||||
|
||||
|
||||
@api_bp.route('/health', methods=['GET'])
|
||||
@apikey_auth
|
||||
def health():
|
||||
@ -1246,7 +1234,8 @@ def health():
|
||||
try:
|
||||
domain.get_domain_info(domain_to_query.name)
|
||||
except Exception as e:
|
||||
current_app.logger.error("Health Check - Failed to query authoritative server for domain {}".format(domain_to_query.name))
|
||||
current_app.logger.error(
|
||||
"Health Check - Failed to query authoritative server for domain {}".format(domain_to_query.name))
|
||||
return make_response("Down", 503)
|
||||
|
||||
return make_response("Up", 200)
|
||||
|
@ -3,10 +3,12 @@ import base64
|
||||
from flask import render_template, url_for, redirect, session, request, current_app
|
||||
from flask_login import LoginManager
|
||||
from flask_seasurf import SeaSurf
|
||||
from flask_session_captcha import FlaskSessionCaptcha
|
||||
|
||||
from ..models.user import User
|
||||
|
||||
|
||||
captcha = FlaskSessionCaptcha()
|
||||
csrf = SeaSurf()
|
||||
login_manager = LoginManager()
|
||||
|
||||
|
@ -89,14 +89,14 @@ def domain(domain_name):
|
||||
# - Find a way to make it consistent, or
|
||||
# - Only allow one comment for that case
|
||||
if StrictVersion(Setting().get('pdns_version')) >= StrictVersion('4.0.0'):
|
||||
pretty_v6 = Setting().get('pretty_ipv6_ptr')
|
||||
for r in rrsets:
|
||||
if r['type'] in records_allow_to_edit:
|
||||
r_name = r['name'].rstrip('.')
|
||||
|
||||
# If it is reverse zone and pretty_ipv6_ptr setting
|
||||
# is enabled, we reformat the name for ipv6 records.
|
||||
if Setting().get('pretty_ipv6_ptr') and r[
|
||||
'type'] == 'PTR' and 'ip6.arpa' in r_name and '*' not in r_name:
|
||||
if pretty_v6 and r['type'] == 'PTR' and 'ip6.arpa' in r_name and '*' not in r_name:
|
||||
r_name = dns.reversename.to_address(
|
||||
dns.name.from_text(r_name))
|
||||
|
||||
|
@ -10,7 +10,7 @@ from yaml import Loader, load
|
||||
from flask import Blueprint, render_template, make_response, url_for, current_app, g, session, request, redirect, abort
|
||||
from flask_login import login_user, logout_user, login_required, current_user
|
||||
|
||||
from .base import csrf, login_manager
|
||||
from .base import captcha, csrf, login_manager
|
||||
from ..lib import utils
|
||||
from ..decorators import dyndns_login_required
|
||||
from ..models.base import db
|
||||
@ -400,7 +400,7 @@ def login():
|
||||
desc_prop = Setting().get('oidc_oauth_account_description_property')
|
||||
|
||||
account_to_add = []
|
||||
#If the name_property and desc_property exist in me (A variable that contains all the userinfo from the IdP).
|
||||
#If the name_property and desc_property exist in me (A variable that contains all the userinfo from the IdP).
|
||||
if name_prop in me and desc_prop in me:
|
||||
accounts_name_prop = [me[name_prop]] if type(me[name_prop]) is not list else me[name_prop]
|
||||
accounts_desc_prop = [me[desc_prop]] if type(me[desc_prop]) is not list else me[desc_prop]
|
||||
@ -415,7 +415,7 @@ def login():
|
||||
account_to_add.append(account)
|
||||
user_accounts = user.get_accounts()
|
||||
|
||||
# Add accounts
|
||||
# Add accounts
|
||||
for account in account_to_add:
|
||||
if account not in user_accounts:
|
||||
account.add_user(user)
|
||||
@ -581,7 +581,7 @@ def get_azure_groups(uri):
|
||||
def authenticate_user(user, authenticator, remember=False):
|
||||
login_user(user, remember=remember)
|
||||
signin_history(user.username, authenticator, True)
|
||||
if Setting().get('otp_force') and Setting().get('otp_field_enabled') and not user.otp_secret:
|
||||
if Setting().get('otp_force') and Setting().get('otp_field_enabled') and not user.otp_secret and session['authentication_type'] not in ['OAuth']:
|
||||
user.update_profile(enable_otp=True)
|
||||
user_id = current_user.id
|
||||
prepare_welcome_user(user_id)
|
||||
@ -651,50 +651,73 @@ def logout():
|
||||
|
||||
@index_bp.route('/register', methods=['GET', 'POST'])
|
||||
def register():
|
||||
if Setting().get('signup_enabled'):
|
||||
if request.method == 'GET':
|
||||
return render_template('register.html')
|
||||
elif request.method == 'POST':
|
||||
username = request.form.get('username', '').strip()
|
||||
password = request.form.get('password', '')
|
||||
firstname = request.form.get('firstname', '').strip()
|
||||
lastname = request.form.get('lastname', '').strip()
|
||||
email = request.form.get('email', '').strip()
|
||||
rpassword = request.form.get('rpassword', '')
|
||||
CAPTCHA_ENABLE = current_app.config.get('CAPTCHA_ENABLE')
|
||||
if Setting().get('signup_enabled'):
|
||||
if current_user.is_authenticated:
|
||||
return redirect(url_for('index.index'))
|
||||
if request.method == 'GET':
|
||||
return render_template('register.html', captcha_enable=CAPTCHA_ENABLE)
|
||||
elif request.method == 'POST':
|
||||
username = request.form.get('username', '').strip()
|
||||
password = request.form.get('password', '')
|
||||
firstname = request.form.get('firstname', '').strip()
|
||||
lastname = request.form.get('lastname', '').strip()
|
||||
email = request.form.get('email', '').strip()
|
||||
rpassword = request.form.get('rpassword', '')
|
||||
|
||||
if not username or not password or not email:
|
||||
return render_template(
|
||||
'register.html', error='Please input required information')
|
||||
is_valid_email = re.compile(r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$')
|
||||
|
||||
if password != rpassword:
|
||||
return render_template(
|
||||
'register.html',
|
||||
error="Password confirmation does not match")
|
||||
error_messages = {}
|
||||
if not firstname:
|
||||
error_messages['firstname'] = 'First Name is required'
|
||||
if not lastname:
|
||||
error_messages['lastname'] = 'Last Name is required'
|
||||
if not username:
|
||||
error_messages['username'] = 'Username is required'
|
||||
if not password:
|
||||
error_messages['password'] = 'Password is required'
|
||||
if not rpassword:
|
||||
error_messages['rpassword'] = 'Password confirmation is required'
|
||||
if not email:
|
||||
error_messages['email'] = 'Email is required'
|
||||
if not is_valid_email.match(email):
|
||||
error_messages['email'] = 'Invalid email address'
|
||||
if password != rpassword:
|
||||
error_messages['password'] = 'Password confirmation does not match'
|
||||
error_messages['rpassword'] = 'Password confirmation does not match'
|
||||
|
||||
user = User(username=username,
|
||||
plain_text_password=password,
|
||||
firstname=firstname,
|
||||
lastname=lastname,
|
||||
email=email)
|
||||
if not captcha.validate():
|
||||
return render_template(
|
||||
'register.html', error='Invalid CAPTCHA answer', error_messages=error_messages, captcha_enable=CAPTCHA_ENABLE)
|
||||
|
||||
try:
|
||||
result = user.create_local_user()
|
||||
if result and result['status']:
|
||||
if Setting().get('verify_user_email'):
|
||||
send_account_verification(email)
|
||||
if Setting().get('otp_force') and Setting().get('otp_field_enabled'):
|
||||
user.update_profile(enable_otp=True)
|
||||
prepare_welcome_user(user.id)
|
||||
return redirect(url_for('index.welcome'))
|
||||
else:
|
||||
return redirect(url_for('index.login'))
|
||||
else:
|
||||
return render_template('register.html',
|
||||
error=result['msg'])
|
||||
except Exception as e:
|
||||
return render_template('register.html', error=e)
|
||||
if error_messages:
|
||||
return render_template('register.html', error_messages=error_messages, captcha_enable=CAPTCHA_ENABLE)
|
||||
|
||||
user = User(username=username,
|
||||
plain_text_password=password,
|
||||
firstname=firstname,
|
||||
lastname=lastname,
|
||||
email=email
|
||||
)
|
||||
|
||||
try:
|
||||
result = user.create_local_user()
|
||||
if result and result['status']:
|
||||
if Setting().get('verify_user_email'):
|
||||
send_account_verification(email)
|
||||
if Setting().get('otp_force') and Setting().get('otp_field_enabled'):
|
||||
user.update_profile(enable_otp=True)
|
||||
prepare_welcome_user(user.id)
|
||||
return redirect(url_for('index.welcome'))
|
||||
else:
|
||||
return redirect(url_for('index.login'))
|
||||
else:
|
||||
return render_template('register.html',
|
||||
error=result['msg'], captcha_enable=CAPTCHA_ENABLE)
|
||||
except Exception as e:
|
||||
return render_template('register.html', error=e, captcha_enable=CAPTCHA_ENABLE)
|
||||
else:
|
||||
return render_template('errors/404.html'), 404
|
||||
return render_template('errors/404.html'), 404
|
||||
|
||||
|
||||
# Show welcome page on first login if otp_force is enabled
|
||||
|
@ -287,4 +287,18 @@ function copy_otp_secret_to_clipboard() {
|
||||
navigator.clipboard.writeText(copyBox.value);
|
||||
$("#copy_tooltip").css("visibility", "visible");
|
||||
setTimeout(function(){ $("#copy_tooltip").css("visibility", "collapse"); }, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
// Side menu nav bar active selection
|
||||
/** add active class and stay opened when selected */
|
||||
var url = window.location;
|
||||
|
||||
// for sidebar menu entirely but not cover treeview
|
||||
$('ul.nav-sidebar a').filter(function() {
|
||||
return this.href == url;
|
||||
}).addClass('active');
|
||||
|
||||
// for treeview
|
||||
$('ul.nav-treeview a').filter(function() {
|
||||
return this.href == url;
|
||||
}).parentsUntil(".nav-sidebar > .nav-treeview").addClass('menu-open').prev('a').addClass('active');
|
||||
|
@ -1,166 +1,211 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% set active_page = "admin_accounts" %}
|
||||
{% block title %}<title>Edit Account - {{ SITE_NAME }}</title>{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
<title>
|
||||
Edit Account - {{ SITE_NAME }}
|
||||
</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block dashboard_stat %}
|
||||
<!-- Content Header (Page header) -->
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
Account
|
||||
<small>{% if create %}New account{% else %}{{ account.name }}{% endif %}</small>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}"><i class="fa fa-dashboard"></i>Home</a></li>
|
||||
<li><a href="{{ url_for('admin.manage_account') }}">Accounts</a></li>
|
||||
<li class="active">{% if create %}Add{% else %}Edit{% endif %} account</li>
|
||||
</ol>
|
||||
</section>
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
{% if create %}Add Account{% else %}Edit Account{% endif %}
|
||||
<small>{% if create %}Account{% else %}{{ account.name }}{% endif %}</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Home</a></li>
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('admin.manage_account') }}">Accounts</a></li>
|
||||
<li class="breadcrumb-item active">{% if create %}Add Account{% else %}Edit Account: {{ account.name }}{% endif %}</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">{% if create %}Add{% else %}Edit{% endif %} account</h3>
|
||||
<div class="col-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{% if create %}Add{% else %}Edit{% endif %} Account</h3>
|
||||
</div>
|
||||
<form role="form" method="post" action="{% if create %}{{ url_for('admin.edit_account') }}{% else %}{{ url_for('admin.edit_account', account_name=account.name) }}{% endif %}">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" name="create" value="{{ create }}">
|
||||
<div class="card-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="fa-solid fa-ban"></i> Error!</h4>
|
||||
{{ error }}
|
||||
</div>
|
||||
<!-- /.box-header -->
|
||||
<!-- form start -->
|
||||
<form role="form" method="post"
|
||||
action="{% if create %}{{ url_for('admin.edit_account') }}{% else %}{{ url_for('admin.edit_account', account_name=account.name) }}{% endif %}">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" name="create" value="{{ create }}">
|
||||
<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 {% if invalid_accountname or duplicate_accountname %}has-error{% endif %}">
|
||||
<label class="control-label" for="accountname">Name</label>
|
||||
<input type="text" class="form-control" placeholder="Account Name (required)"
|
||||
name="accountname" {% if account %}value="{{ account.name }}" {% endif %}
|
||||
{% if not create %}disabled{% endif %}>
|
||||
<span class="fa fa-cog form-control-feedback"></span>
|
||||
{% if invalid_accountname %}
|
||||
<span class="help-block">Cannot be blank and must only contain alphanumeric
|
||||
characters{% if SETTING.get('account_name_extra_chars') %}, dots, hyphens or underscores{% endif %}.</span>
|
||||
{% elif duplicate_accountname %}
|
||||
<span class="help-block">Account name already in use.</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="form-group has-feedback">
|
||||
<label class="control-label" for="accountdescription">Description</label>
|
||||
<input type="text" class="form-control" placeholder="Account Description (optional)"
|
||||
name="accountdescription" {% if account %}value="{{ account.description }}" {% endif %}>
|
||||
<span class="fa fa-industry form-control-feedback"></span>
|
||||
</div>
|
||||
<div class="form-group has-feedback">
|
||||
<label class="control-label" for="accountcontact">Contact Person</label>
|
||||
<input type="text" class="form-control" placeholder="Contact Person (optional)"
|
||||
name="accountcontact" {% if account %}value="{{ account.contact }}" {% endif %}>
|
||||
<span class="fa fa-user form-control-feedback"></span>
|
||||
</div>
|
||||
<div class="form-group has-feedback">
|
||||
<label class="control-label" for="accountmail">Mail Address</label>
|
||||
<input type="email" class="form-control" placeholder="Mail Address (optional)"
|
||||
name="accountmail" {% if account %}value="{{ account.mail }}" {% endif %}>
|
||||
<span class="fa fa-envelope form-control-feedback"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Access Control</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<p>Users on the right have access to manage records in all domains
|
||||
associated with the account.</p>
|
||||
<p>Click on users to move between columns.</p>
|
||||
<div class="form-group col-xs-2">
|
||||
<select multiple="multiple" class="form-control" id="account_multi_user"
|
||||
name="account_multi_user">
|
||||
{% for user in users %}
|
||||
<option {% if user.id in account_user_ids|default([]) %}selected{% endif %}
|
||||
value="{{ user.username }}">{{ user.username }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="submit"
|
||||
class="btn btn-flat btn-primary">{% if create %}Create{% else %}Update{% endif %}
|
||||
Account</button>
|
||||
</div>
|
||||
</form>
|
||||
<span class="help-block">{{ error }}</span>
|
||||
{% endif %}
|
||||
<div class="form-group has-feedback {% if invalid_accountname or duplicate_accountname %}has-error{% endif %}">
|
||||
<label class="control-label" for="accountname">Name</label>
|
||||
<input type="text" class="form-control" placeholder="Account Name (required)"
|
||||
name="accountname" {% if account %}value="{{ account.name }}" {% endif %}
|
||||
{% if not create %}disabled{% endif %} required>
|
||||
<span class="form-control-feedback"></span>
|
||||
{% if invalid_accountname %}
|
||||
<span class="help-block text-danger">Cannot be blank and must only contain alphanumeric
|
||||
characters{% if SETTING.get('account_name_extra_chars') %}, dots, hyphens or underscores{% endif %}.
|
||||
</span>
|
||||
{% elif duplicate_accountname %}
|
||||
<span class="help-block text-danger">Account name already in use.</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="form-group has-feedback">
|
||||
<label class="control-label" for="accountdescription">Description</label>
|
||||
<input type="text" class="form-control" placeholder="Account Description (optional)"
|
||||
name="accountdescription" {% if account %}value="{{ account.description }}" {% endif %}>
|
||||
<span class="orm-control-feedback"></span>
|
||||
</div>
|
||||
<div class="form-group has-feedback">
|
||||
<label class="control-label" for="accountcontact">Contact Person</label>
|
||||
<input type="text" class="form-control" placeholder="Contact Person (optional)"
|
||||
name="accountcontact" {% if account %}value="{{ account.contact }}" {% endif %}>
|
||||
<span class="form-control-feedback"></span>
|
||||
</div>
|
||||
<div class="form-group has-feedback">
|
||||
<label class="control-label" for="accountmail">Mail Address</label>
|
||||
<input type="email" class="form-control" placeholder="Mail Address (optional)"
|
||||
name="accountmail" {% if account %}value="{{ account.mail }}" {% endif %}>
|
||||
<span class="form-control-feedback"></span>
|
||||
</div>
|
||||
</div>
|
||||
</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 account</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<p>
|
||||
An account allows grouping of domains belonging to a particular entity, such as a customer or
|
||||
department.<br />
|
||||
A domain can be assigned to an account upon domain creation or through the domain administration
|
||||
page.
|
||||
</p>
|
||||
<p>Fill in all the fields to the in the form to the left.</p>
|
||||
<p>
|
||||
<strong>Name</strong> is an account identifier. It will be lowercased and can contain alphanumeric
|
||||
characters{% if SETTING.get('account_name_extra_chars') %}, dots, hyphens and underscores (no space or other special character is allowed)
|
||||
{% else %} (no extra character is allowed){% endif %}.<br />
|
||||
<strong>Description</strong> is a user friendly name for this account.<br />
|
||||
<strong>Contact person</strong> is the name of a contact person at the account.<br />
|
||||
<strong>Mail Address</strong> is an e-mail address for the contact person.
|
||||
</p>
|
||||
</div>
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Access Control</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Users on the right have access to manage records in all domains
|
||||
associated with the account.
|
||||
</p>
|
||||
<p>Click on users to move between columns.</p>
|
||||
<div class="form-group col-2">
|
||||
<select multiple="multiple" class="form-control" id="account_multi_user"
|
||||
name="account_multi_user">
|
||||
{% for user in users %}
|
||||
<option {% if user.id in account_user_ids|default([]) %}selected{% endif %}
|
||||
value="{{ user.username }}">{{ user.username }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Domains on the right are associated with the account. Red marked domain names are already associated with other accounts.
|
||||
Moving already associated domains to this account will overwrite the previous associated account.
|
||||
</p>
|
||||
<p>Hover over the red domain names to show the associated account. Click on domains to move between columns.</p>
|
||||
<div class="form-group col-2">
|
||||
<select multiple="multiple" class="form-control" id="account_domains" name="account_domains">
|
||||
{% for domain in domains %}
|
||||
{% if account != None and domain.account_id != None and account.id != domain.account_id %}
|
||||
{% with account_id=domain.account_id %}
|
||||
<option style="color: red" data-toggle="tooltip" title="Associated with: {{ accounts[account_id].name }}" value="{{ domain.name }}">
|
||||
{{ domain.name }}
|
||||
</option>
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
<option {% if account.id == domain.account_id %}selected{% endif %} value="{{ domain.name }}">
|
||||
{{ domain.name }}
|
||||
</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<button type="button" class="btn btn-secondary float-left" onclick="window.location.href='{{ url_for('admin.manage_account') }}'">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary float-right">
|
||||
{% if create %}Create{% else %}Update{% endif %} Account
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Help with creating a new account</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>
|
||||
An account allows grouping of domains belonging to a particular entity, such as a customer or
|
||||
department.
|
||||
<br />
|
||||
A domain can be assigned to an account upon domain creation or through the domain administration
|
||||
page.
|
||||
</p>
|
||||
<p>Fill in all the fields to the in the form to the left.</p>
|
||||
<p>
|
||||
<strong>Name</strong> is an account identifier. It will be lowercased and can contain alphanumeric
|
||||
characters{% if SETTING.get('account_name_extra_chars') %}, dots, hyphens and underscores (no space or other special character is allowed)
|
||||
{% else %} (no extra character is allowed){% endif %}.<br />
|
||||
<strong>Description</strong> is a user friendly name for this account.<br />
|
||||
<strong>Contact person</strong> is the name of a contact person at the account.<br />
|
||||
<strong>Mail Address</strong> is an e-mail address for the contact person.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
{% block extrascripts %}
|
||||
<script>
|
||||
$("#account_multi_user").multiSelect({
|
||||
selectableHeader: "<input type='text' class='search-input' autocomplete='off' placeholder='Username'>",
|
||||
selectionHeader: "<input type='text' class='search-input' autocomplete='off' placeholder='Username'>",
|
||||
afterInit: function (ms) {
|
||||
var that = this,
|
||||
$selectableSearch = that.$selectableUl.prev(),
|
||||
$selectionSearch = that.$selectionUl.prev(),
|
||||
selectableSearchString = '#' + that.$container.attr('id') + ' .ms-elem-selectable:not(.ms-selected)',
|
||||
selectionSearchString = '#' + that.$container.attr('id') + ' .ms-elem-selection.ms-selected';
|
||||
function addMultiSelect(id, placeholder) {
|
||||
$(id).multiSelect({
|
||||
selectableHeader: `<input type='text' class='search-input' autocomplete='off' placeholder='${placeholder}'>`,
|
||||
selectionHeader: `<input type='text' class='search-input' autocomplete='off' placeholder='${placeholder}'>`,
|
||||
afterInit: function (ms) {
|
||||
var that = this,
|
||||
$selectableSearch = that.$selectableUl.prev(),
|
||||
$selectionSearch = that.$selectionUl.prev(),
|
||||
selectableSearchString = '#' + that.$container.attr('id') + ' .ms-elem-selectable:not(.ms-selected)',
|
||||
selectionSearchString = '#' + that.$container.attr('id') + ' .ms-elem-selection.ms-selected';
|
||||
|
||||
that.qs1 = $selectableSearch.quicksearch(selectableSearchString)
|
||||
.on('keydown', function (e) {
|
||||
if (e.which === 40) {
|
||||
that.$selectableUl.focus();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
that.qs1 = $selectableSearch.quicksearch(selectableSearchString)
|
||||
.on('keydown', function (e) {
|
||||
if (e.which === 40) {
|
||||
that.$selectableUl.focus();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
that.qs2 = $selectionSearch.quicksearch(selectionSearchString)
|
||||
.on('keydown', function (e) {
|
||||
if (e.which == 40) {
|
||||
that.$selectionUl.focus();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
},
|
||||
afterSelect: function () {
|
||||
this.qs1.cache();
|
||||
this.qs2.cache();
|
||||
},
|
||||
afterDeselect: function () {
|
||||
this.qs1.cache();
|
||||
this.qs2.cache();
|
||||
}
|
||||
that.qs2 = $selectionSearch.quicksearch(selectionSearchString)
|
||||
.on('keydown', function (e) {
|
||||
if (e.which == 40) {
|
||||
that.$selectionUl.focus();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
},
|
||||
afterSelect: function () {
|
||||
this.qs1.cache();
|
||||
this.qs2.cache();
|
||||
},
|
||||
afterDeselect: function () {
|
||||
this.qs1.cache();
|
||||
this.qs2.cache();
|
||||
}
|
||||
});
|
||||
}
|
||||
addMultiSelect("#account_multi_user", "Username")
|
||||
addMultiSelect("#account_domains", "Domain")
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@ -1,80 +1,92 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% set active_page = "admin_keys" %}
|
||||
|
||||
{% if (key is not none and key.role.name != "User") %}{% set hide_opts = True %}{%else %}{% set hide_opts = False %}{% endif %}
|
||||
|
||||
{% block title %}
|
||||
<title>Edit Key - {{ SITE_NAME }}</title>
|
||||
<title>
|
||||
Edit Key - {{ SITE_NAME }}
|
||||
</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block dashboard_stat %}
|
||||
<!-- Content Header (Page header) -->
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
Key
|
||||
<small>{% if create %}New key{% else %}{{ key.id }}{% endif %}</small>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}"><i class="fa fa-dashboard"></i>Home</a></li>
|
||||
<li><a href="{{ url_for('admin.manage_keys') }}">Key</a></li>
|
||||
<li class="active">{% if create %}Add{% else %}Edit{% endif %} key</li>
|
||||
</ol>
|
||||
</section>
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
API Keys
|
||||
<small>{% if create %}Add API Key{% else %}Edit API Key - {{ key.id }}{% endif %}</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('admin.manage_keys') }}">API Keys</a></li>
|
||||
<li class="breadcrumb-item active">{% if create %}Add API Key{% else %}Edit API Key - {% endif %}{{ key.id }}</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="content">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">{% if create %}Add{% else %}Edit{% endif %} key</h3>
|
||||
</div>
|
||||
<!-- /.box-header -->
|
||||
<!-- form start -->
|
||||
<form role="form" method="post"
|
||||
action="{% if create %}{{ url_for('admin.edit_key') }}{% else %}{{ url_for('admin.edit_key', key_id=key.id) }}{% endif %}">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" name="create" value="{{ create }}">
|
||||
<div class="box-body">
|
||||
<div class="form-group has-feedback">
|
||||
<label class="control-label" for="role">Role</label>
|
||||
<select class="key_role form-control" id="key_role" name="key_role">
|
||||
{% for role in roles %}
|
||||
<option value="{{ role.name }}"
|
||||
{% if (key is not none) and (role.id==key.role.id) %}selected{% endif %}
|
||||
{% if (key is none) and (role.name=="User") %}selected{% endif %}
|
||||
>{{ role.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group has-feedback">
|
||||
<label class="control-label" for="description">Description</label>
|
||||
<input type="text" class="form-control" placeholder="Description" name="description"
|
||||
{% if key is not none %} value="{{ key.description }}" {% endif %}> <span
|
||||
class="glyphicon glyphicon-pencil form-control-feedback"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-header with-border key-opts"{% if hide_opts %} style="display: none;"{% endif %}>
|
||||
<h3 class="box-title">Accounts Access Control</h3>
|
||||
</div>
|
||||
<div class="box-body key-opts"{% if hide_opts %} style="display: none;"{% endif %}>
|
||||
<p>This key will be linked to the accounts on the right,</p>
|
||||
<p>thus granting access to domains owned by the selected accounts.</p>
|
||||
<p>Click on accounts to move between the columns.</p>
|
||||
<div class="form-group col-xs-2">
|
||||
<select multiple="multiple" class="form-control" id="key_multi_account"
|
||||
name="key_multi_account">
|
||||
{% for account in accounts %}
|
||||
<option {% if key and account in key.accounts %}selected{% endif %} value="{{ account.name }}">{{ account.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-header with-border key-opts"{% if hide_opts %} style="display: none;"{% endif %}>
|
||||
<h3 class="box-title">Domain Access Control</h3>
|
||||
</div>
|
||||
<div class="box-body key-opts"{% if hide_opts %} style="display: none;"{% endif %}>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<div class="card ">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{% if create %}Add{% else %}Edit{% endif %} Key</h3>
|
||||
</div>
|
||||
<form role="form" method="post"
|
||||
action="{% if create %}{{ url_for('admin.edit_key') }}{% else %}{{ url_for('admin.edit_key', key_id=key.id) }}{% endif %}">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" name="create" value="{{ create }}">
|
||||
<div class="card-body">
|
||||
<div class="form-group has-feedback">
|
||||
<label class="control-label" for="role">Role</label>
|
||||
<select class="key_role form-control" id="key_role" name="key_role">
|
||||
{% for role in roles %}
|
||||
<option value="{{ role.name }}"
|
||||
{% if (key is not none) and (role.id==key.role.id) %}selected{% endif %}
|
||||
{% if (key is none) and (role.name=="User") %}selected{% endif %}>
|
||||
{{ role.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group has-feedback">
|
||||
<label class="control-label" for="description">Description</label>
|
||||
<input type="text" class="form-control" placeholder="Description" name="description"
|
||||
{% if key is not none %} value="{{ key.description }}" {% endif %}>
|
||||
<span class="glyphicon glyphicon-pencil form-control-feedback"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-header key-opts"{% if hide_opts %} style="display: none;"{% endif %}>
|
||||
<h3 class="card-title">Accounts Access Control</h3>
|
||||
</div>
|
||||
<div class="card-body key-opts"{% if hide_opts %} style="display: none;"{% endif %}>
|
||||
<p>This key will be linked to the accounts on the right,</p>
|
||||
<p>thus granting access to domains owned by the selected accounts.</p>
|
||||
<p>Click on accounts to move between the columns.</p>
|
||||
<div class="form-group col-2">
|
||||
<select multiple="multiple" class="form-control" id="key_multi_account" name="key_multi_account">
|
||||
{% for account in accounts %}
|
||||
<option {% if key and account in key.accounts %}selected{% endif %} value="{{ account.name }}">{{ account.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-header key-opts"{% if hide_opts %} style="display: none;"{% endif %}>
|
||||
<h3 class="card-title">Domain Access Control</h3>
|
||||
</div>
|
||||
<div class="card-body key-opts"{% if hide_opts %} style="display: none;"{% endif %}>
|
||||
<p>This key will have acess to the domains on the right.</p>
|
||||
<p>Click on domains to move between the columns.</p>
|
||||
<div class="form-group col-xs-2">
|
||||
<div class="form-group col-2">
|
||||
<select multiple="multiple" class="form-control" id="key_multi_domain"
|
||||
name="key_multi_domain">
|
||||
{% for domain in domains %}
|
||||
@ -83,21 +95,24 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="submit"
|
||||
class="btn btn-flat btn-primary" id="key_submit">{% if create %}Create{% else %}Update{% endif %}
|
||||
Key</button>
|
||||
<div class="card-footer">
|
||||
<button type="button" class="btn btn-secondary float-left" onclick="window.location.href='{{ url_for('admin.manage_keys') }}'">
|
||||
<i class="fa-solid fa-window-close"></i> Cancel
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary float-right" id="key_submit">
|
||||
<i class="fa-solid fa-save"></i> {% if create %}Create{% else %}Update{% endif %} Key
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Help with {% if create %}creating a new{% else%}updating a{% endif %} key
|
||||
<div class="col-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Help with {% if create %}creating a new{% else%}updating a{% endif %} key
|
||||
</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="card-body">
|
||||
<p>Fill in all the fields in the form to the left.</p>
|
||||
<p><strong>Role</strong> The role of the key.</p>
|
||||
<p><strong>Description</strong> The key description.</p>
|
||||
@ -106,8 +121,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
{% block extrascripts %}
|
||||
<script>
|
||||
$('form').submit(function (e) {
|
||||
@ -225,7 +242,7 @@
|
||||
{% if plain_key %}
|
||||
$(document.body).ready(function () {
|
||||
var modal = $("#modal_show_key");
|
||||
var info = "{{ plain_key }}";
|
||||
var info = "Please copy this key to a secure location. You will be unable to view it again once you close this window: {{ plain_key }}";
|
||||
modal.find('.modal-body p').text(info);
|
||||
modal.find('#button_key_confirm').click(redirect_modal);
|
||||
modal.find('#button_close_modal').click(redirect_modal);
|
||||
@ -241,45 +258,46 @@
|
||||
{% endblock %}
|
||||
{% block modals %}
|
||||
<div class="modal fade" id="modal_show_key">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content modal-sm">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close" id="button_close_modal">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title">Your API key</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-flat btn-primary center-block" id="button_key_confirm">
|
||||
Confirm</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">New API Key</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close" id="button_close_modal">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" id="button_key_confirm">
|
||||
Confirm
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" id="modal_warning">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content modal-sm">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close" id="button_close_warn_modal">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title">WARNING</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-flat btn-primary center-block" id="button_key_confirm_warn">
|
||||
OK</button>
|
||||
</div>
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">WARNING</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close" id="button_close_warn_modal">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
<div class="modal-body">
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary close" data-dismiss="modal" id="button_close_modal">
|
||||
Close
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary" id="button_key_confirm_warn">
|
||||
OK
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -1,127 +1,148 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% set active_page = "admin_users" %}
|
||||
{% block title %}<title>Edit User - {{ SITE_NAME }}</title>{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
<title>
|
||||
{% if create %}Add user{% else %}Edit user "{{ user.username }}"{% endif %} - {{ SITE_NAME }}
|
||||
</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block dashboard_stat %}
|
||||
<!-- Content Header (Page header) -->
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
User
|
||||
<small>{% if create %}New user{% else %}{{ user.username }}{% endif %}</small>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}"><i class="fa fa-dashboard"></i>Home</a></li>
|
||||
<li><a href="{{ url_for('admin.manage_user') }}">User</a></li>
|
||||
<li class="active">{% if create %}Add{% else %}Edit{% endif %} user</li>
|
||||
</ol>
|
||||
</section>
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
Users
|
||||
<small>{% if create %}New user{% else %}Edit user {{ user.username }}{% endif %}</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('admin.manage_user') }}">Users</a></li>
|
||||
<li class="breadcrumb-item active">{% if create %}Add user{% else %}Edit user "{{ user.username }}"{% endif %}</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">{% if create %}Add{% else %}Edit{% endif %} user</h3>
|
||||
<div class="col-4">
|
||||
<div class="card card-secondary">
|
||||
<div class="card-header with-border">
|
||||
<h3 class="card-title">{% if create %}Add{% else %}Edit{% endif %} user</h3>
|
||||
</div>
|
||||
<form role="form" method="post"
|
||||
action="{% if create %}{{ url_for('admin.edit_user') }}{% else %}{{ url_for('admin.edit_user', user_username=user.username) }}{% endif %}">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" name="create" value="{{ create }}">
|
||||
<div class="card-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="fa-solid fa-ban"></i> Error!</h4>
|
||||
{{ error }}
|
||||
</div>
|
||||
<!-- /.box-header -->
|
||||
<!-- form start -->
|
||||
<form role="form" method="post"
|
||||
action="{% if create %}{{ url_for('admin.edit_user') }}{% else %}{{ url_for('admin.edit_user', user_username=user.username) }}{% endif %}">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" name="create" value="{{ create }}">
|
||||
<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">
|
||||
<label class="control-label" for="firstname">First Name</label>
|
||||
<input type="text" class="form-control" placeholder="First Name" name="firstname"
|
||||
{% if user %}value="{{ user.firstname }}" {% endif %}> <span
|
||||
class="glyphicon glyphicon-user form-control-feedback"></span>
|
||||
</div>
|
||||
<div class="form-group has-feedback">
|
||||
<label class="control-label" for="lastname">Last Name</label>
|
||||
<input type="text" class="form-control" placeholder="Last name" name="lastname"
|
||||
{% if user %}value="{{ user.lastname }}" {% endif %}> <span
|
||||
class="glyphicon glyphicon-user form-control-feedback"></span>
|
||||
</div>
|
||||
<div class="form-group has-feedback">
|
||||
<label class="control-label" for="email">E-mail address</label>
|
||||
<input type="email" class="form-control" placeholder="Email" name="email" id="email"
|
||||
{% if user %}value="{{ user.email }}" {% endif %}> <span
|
||||
class="glyphicon glyphicon-envelope form-control-feedback"></span>
|
||||
</div>
|
||||
<p class="login-box-msg">Enter the account details below</p>
|
||||
<div class="form-group has-feedback">
|
||||
<label class="control-label" for="username">Username</label>
|
||||
<input type="text" class="form-control" placeholder="Username" name="username"
|
||||
{% if user %}value="{{ user.username }}" {% endif %}
|
||||
{% if not create %}disabled{% endif %}> <span
|
||||
class="glyphicon glyphicon-user form-control-feedback"></span>
|
||||
</div>
|
||||
<div class="form-group has-feedback {% if blank_password %}has-error{% endif %}">
|
||||
<label class="control-label" for="username">Password</label>
|
||||
<input type="password" class="form-control"
|
||||
placeholder="Password {% if create %}(Required){% else %}(Leave blank to keep unchanged){% endif %}"
|
||||
name="password"> <span class="glyphicon glyphicon-lock form-control-feedback"></span>
|
||||
{% if blank_password %}
|
||||
<span class="help-block">The password cannot be blank.</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="submit"
|
||||
class="btn btn-flat btn-primary">{% if create %}Create{% else %}Update{% endif %}
|
||||
User</button>
|
||||
</div>
|
||||
</form>
|
||||
<span class="help-block">{{ error }}</span>
|
||||
{% endif %}
|
||||
<div class="form-group has-feedback">
|
||||
<label class="control-label" for="firstname">First Name</label>
|
||||
<input type="text" class="form-control" placeholder="First Name" name="firstname"
|
||||
{% if user %}value="{{ user.firstname }}" {% endif %}>
|
||||
<span class="form-control-feedback"></span>
|
||||
</div>
|
||||
<div class="form-group has-feedback">
|
||||
<label class="control-label" for="lastname">Last Name</label>
|
||||
<input type="text" class="form-control" placeholder="Last name" name="lastname"
|
||||
{% if user %}value="{{ user.lastname }}" {% endif %}>
|
||||
<span class="form-control-feedback"></span>
|
||||
</div>
|
||||
<div class="form-group has-feedback">
|
||||
<label class="control-label" for="email">E-mail address</label>
|
||||
<input type="email" class="form-control" placeholder="Email" name="email" id="email"
|
||||
{% if user %}value="{{ user.email }}" {% endif %}>
|
||||
<span class="form-control-feedback"></span>
|
||||
</div>
|
||||
<p class="login-box-msg">Enter the account details below</p>
|
||||
<div class="form-group has-feedback">
|
||||
<label class="control-label" for="username">Username</label>
|
||||
<input type="text" class="form-control" placeholder="Username" name="username"
|
||||
{% if user %}value="{{ user.username }}" {% endif %}
|
||||
{% if not create %}disabled{% endif %}>
|
||||
<span class="form-control-feedback"></span>
|
||||
</div>
|
||||
<div class="form-group has-feedback {% if blank_password %}has-error{% endif %}">
|
||||
<label class="control-label" for="username">Password</label>
|
||||
<input type="password" class="form-control"
|
||||
placeholder="Password {% if create %}(Required){% else %}(Leave blank to keep unchanged){% endif %}"
|
||||
name="password">
|
||||
<span class="form-control-feedback"></span>
|
||||
{% if blank_password %}
|
||||
<span class="help-block">The password cannot be blank.</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% if not create %}
|
||||
<div class="box box-secondary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Two Factor Authentication</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<p>If two factor authentication was configured and is causing problems due to a lost device or
|
||||
technical issue, it can be disabled here.</p>
|
||||
<p>The user will need to reconfigure two factor authentication, to re-enable it.</p>
|
||||
<p><strong>Beware: This could compromise security!</strong></p>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="button" class="btn btn-flat btn-warning button_otp_disable" id="{{ user.username }}"
|
||||
{% if not user.otp_secret %}disabled{% endif %}>Disable Two Factor Authentication</button>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
{% if create %}Create{% else %}Update{% endif %} User
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% if not create %}
|
||||
{% if user.otp_secret %}
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Two Factor Authentication</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>If two factor authentication is configured for this user and is causing problems due to a lost device or
|
||||
technical issue, it can be disabled here.
|
||||
</p>
|
||||
<p>The user will need to reconfigure two factor authentication, to re-enable it.</p>
|
||||
<p><strong>Beware: This could compromise security!</strong></p>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<button type="button" class="btn btn-warning button_otp_disable" id="{{ user.username }}">
|
||||
Disable Two Factor Authentication
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">
|
||||
Help with {% if create %}creating a new{% else%}editing a{% endif %} user
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Fill in all the fields to the in the form to the left.</p>
|
||||
{% if create %}
|
||||
<p><strong>Newly created users do not have access to any domains.</strong> You will need to grant
|
||||
access to the user once it is created via the domain management buttons on the dashboard.
|
||||
</p>
|
||||
{% else %}
|
||||
<p><strong>Username</strong> cannot be changed.</p>
|
||||
<p><strong>Password</strong> can be left empty to keep the current password.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Help with {% if create %}creating a new{% else%}updating a{% endif %} user
|
||||
</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<p>Fill in all the fields to the in the form to the left.</p>
|
||||
{% if create %}
|
||||
<p><strong>Newly created users do not have access to any domains.</strong> You will need to grant
|
||||
access to the user once it is created via the domain management buttons on the dashboard.</p>
|
||||
{% else %}
|
||||
<p><strong>Password</strong> can be left empty to keep the current password.</p>
|
||||
<p><strong>Username</strong> cannot be changed.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
{% block extrascripts %}
|
||||
<script>
|
||||
// handle disabling two factor authentication
|
||||
@ -147,18 +168,21 @@
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<h4 class="modal-title">Confirmation</h4>
|
||||
<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_otp_disable_confirm">Disable Two Factor
|
||||
Authentication</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">
|
||||
Close
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger float-right" id="button_otp_disable_confirm">
|
||||
Disable Two Factor Authentication
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,190 +1,201 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% set active_page = "admin_global_search" %}
|
||||
|
||||
{% block title %}
|
||||
<title>Global Search - {{ SITE_NAME }}</title>
|
||||
{% endblock %} {% block dashboard_stat %}
|
||||
<!-- Content Header (Page header) -->
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
Global Search <small>Search for domains, records and comments directly from PDNS API</small>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}"><i class="fa fa-dashboard"></i> Home</a></li>
|
||||
<li class="active">Global Search</li>
|
||||
</ol>
|
||||
</section>
|
||||
{% endblock %} {% block content %}
|
||||
<section class="content">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="box-body">
|
||||
<!-- search form -->
|
||||
<form action="" method="get">
|
||||
<div class="input-group">
|
||||
<input type="text" name="q" class="form-control" placeholder="Your keyword...">
|
||||
<div class="input-group-btn">
|
||||
<button type="submit" class="btn btn-success"><i class="fa fa-search"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div>
|
||||
<p><b>Hints:</b> The * character can be used in your keyword as a wildcard character and the ? character can be used as
|
||||
a wildcard for a
|
||||
single character.</p>
|
||||
</div>
|
||||
<!-- /.search form -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<h3 class="box-title">Domains ({{ domains|length }})</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<table id="tbl_domain" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Domain</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for domain in domains %}
|
||||
<tr class="odd gradeX">
|
||||
<td>
|
||||
<a href="{{ url_for('domain.domain', domain_name=domain['name']) }}">{{ domain['name'] }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
</div>
|
||||
<!-- /.box -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<h3 class="box-title">Records ({{ records|length }})</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<table id="tbl_record" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Status</th>
|
||||
<th>TTL</th>
|
||||
<th>Data</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for record in records %}
|
||||
<tr class="odd gradeX">
|
||||
<td>
|
||||
<a href="{{ url_for('domain.domain', domain_name=record['zone_id']) }}">{{ record['name'] }}</a>
|
||||
</td>
|
||||
<td>{{ record['type'] }}</td>
|
||||
<td>{{ 'Disabled' if record['disabled'] else 'Active' }}</td>
|
||||
<td>{{ record['ttl'] }}</td>
|
||||
<td>{{ record['content'] }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
</div>
|
||||
<!-- /.box -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<h3 class="box-title">Comments ({{ comments|length }})</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<table id="tbl_comment" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Comment</th>
|
||||
<th>Record</th>
|
||||
<th>Domain</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for comment in comments %}
|
||||
<tr class="odd gradeX">
|
||||
<td>{{ comment['content'] }}</td>
|
||||
<td>{{ comment['name'] }}</td>
|
||||
<td>
|
||||
<a href="{{ url_for('domain.domain', domain_name=comment['zone_id']) }}">{{ comment['zone_id'] }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
</div>
|
||||
<!-- /.box -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
</section>
|
||||
<title>
|
||||
Global Search - {{ SITE_NAME }}
|
||||
</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block dashboard_stat %}
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
Global Search
|
||||
<small>Search for domains, records and comments directly from PDNS API</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item active">Global Search</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Global Search</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="" method="get">
|
||||
<div class="input-group">
|
||||
<input type="text" name="q" class="form-control" placeholder="Your keyword...">
|
||||
<div class="input-group-btn">
|
||||
<button type="submit" class="btn btn-success"><i class="fa fa-search"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div>
|
||||
<p><b>Hints:</b> The * character can be used in your keyword as a wildcard character and the ? character can be used as
|
||||
a wildcard for a
|
||||
single character.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Domains ({{ domains|length }})</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table id="tbl_domain" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Domain</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for domain in domains %}
|
||||
<tr class="odd gradeX">
|
||||
<td>
|
||||
<a href="{{ url_for('domain.domain', domain_name=domain['name']) }}">{{ domain['name'] }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Records ({{ records|length }})</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table id="tbl_record" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Status</th>
|
||||
<th>TTL</th>
|
||||
<th>Data</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for record in records %}
|
||||
<tr class="odd gradeX">
|
||||
<td>
|
||||
<a href="{{ url_for('domain.domain', domain_name=record['zone_id']) }}">{{ record['name'] }}</a>
|
||||
</td>
|
||||
<td>{{ record['type'] }}</td>
|
||||
<td>{{ 'Disabled' if record['disabled'] else 'Active' }}</td>
|
||||
<td>{{ record['ttl'] }}</td>
|
||||
<td>{{ record['content'] }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Comments ({{ comments|length }})</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table id="tbl_comment" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Comment</th>
|
||||
<th>Record</th>
|
||||
<th>Domain</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for comment in comments %}
|
||||
<tr class="odd gradeX">
|
||||
<td>{{ comment['content'] }}</td>
|
||||
<td>{{ comment['name'] }}</td>
|
||||
<td>
|
||||
<a href="{{ url_for('domain.domain', domain_name=comment['zone_id']) }}">{{ comment['zone_id'] }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
{% block extrascripts %}
|
||||
<script>
|
||||
<script>
|
||||
// set up domain result data table
|
||||
$("#tbl_domain").DataTable({
|
||||
"paging": false,
|
||||
"lengthChange": false,
|
||||
"searching": false,
|
||||
"ordering": true,
|
||||
"info": false,
|
||||
"autoWidth": false,
|
||||
"order": [
|
||||
[0, "asc"]
|
||||
]
|
||||
"paging": false,
|
||||
"lengthChange": false,
|
||||
"searching": false,
|
||||
"ordering": true,
|
||||
"info": false,
|
||||
"autoWidth": false,
|
||||
"order": [
|
||||
[0, "asc"]
|
||||
]
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// set up domain result data table
|
||||
$("#tbl_record").DataTable({
|
||||
"paging": false,
|
||||
"lengthChange": false,
|
||||
"searching": false,
|
||||
"ordering": true,
|
||||
"info": false,
|
||||
"autoWidth": false,
|
||||
"order": [
|
||||
[0, "asc"]
|
||||
]
|
||||
"paging": false,
|
||||
"lengthChange": false,
|
||||
"searching": false,
|
||||
"ordering": true,
|
||||
"info": false,
|
||||
"autoWidth": false,
|
||||
"order": [
|
||||
[0, "asc"]
|
||||
]
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// set up domain result data table
|
||||
$("#tbl_comment").DataTable({
|
||||
"paging": false,
|
||||
"lengthChange": false,
|
||||
"searching": false,
|
||||
"ordering": true,
|
||||
"info": false,
|
||||
"autoWidth": false,
|
||||
"order": [
|
||||
[0, "asc"]
|
||||
]
|
||||
"paging": false,
|
||||
"lengthChange": false,
|
||||
"searching": false,
|
||||
"ordering": true,
|
||||
"info": false,
|
||||
"autoWidth": false,
|
||||
"order": [
|
||||
[0, "asc"]
|
||||
]
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@ -1,81 +1,108 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% set active_page = "admin_history" %}
|
||||
|
||||
{% block title %}
|
||||
<title>History - {{ SITE_NAME }}</title>
|
||||
{% endblock %} {% block dashboard_stat %}
|
||||
<!-- Content Header (Page header) -->
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
History <small>Recent events</small>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}"><i class="fa fa-dashboard"></i> Home</a></li>
|
||||
<li class="active">History</li>
|
||||
</ol>
|
||||
</section>
|
||||
<title>
|
||||
History - {{ SITE_NAME }}
|
||||
</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block dashboard_stat %}
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
History
|
||||
<small>Recent Events</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Home</a></li>
|
||||
<li class="breadcrumb-item active">History</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% import 'applied_change_macro.html' as applied_change_macro %}
|
||||
|
||||
|
||||
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<h3 class="box-title">History Management</h3>
|
||||
<div class="col-12">
|
||||
<div class="card card-outline card-secondary">
|
||||
<div class="card-header with-border">
|
||||
<h3 class="card-title">History Management</h3>
|
||||
{% if current_user.role.name != 'User' %}
|
||||
<button type="button" class="btn btn-danger float-right" data-toggle="modal" data-target="#modal_clear_history" {% if current_user.role.name != 'Administrator' %}disabled{% endif %}>
|
||||
<i class="fa-solid fa-trash"></i>
|
||||
Clear History
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card-body clearfix">
|
||||
<form id="history-search-form" autocomplete="off">
|
||||
<ul class="nav nav-tabs" id="custom-content-below-tab" role="tablist">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="#tabs-act" data-toggle="pill" role="tab">
|
||||
Search for All Activity
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#tabs-domain" data-toggle="pill" role="tab">
|
||||
Search By Domain
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link with-border" href="#tabs-account" data-toggle="pill" role="tab">
|
||||
Search By Account
|
||||
</a>
|
||||
</li>
|
||||
{% if current_user.role.name != 'User' %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#tabs-auth" data-toggle="pill" role="tab">
|
||||
Search for User Authentication
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane" id="tabs-act">
|
||||
</div>
|
||||
<div class="tab-pane" id="tabs-domain">
|
||||
<td>
|
||||
<label>Domain Name</label>
|
||||
</td>
|
||||
<td>
|
||||
<div class="autocomplete" style="width:250px;">
|
||||
<input type="text" class="form-control" id="domain_name_filter" name="domain_name_filter" placeholder="Enter * to search for any domain" value="">
|
||||
</div>
|
||||
<div class="box-body clearfix">
|
||||
<button type="button" class="btn btn-flat btn-danger pull-right" data-toggle="modal"
|
||||
data-target="#modal_clear_history"
|
||||
{% if current_user.role.name != 'Administrator' %}disabled{% endif %}>
|
||||
Clear History <i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="box-body clearfix">
|
||||
<form id="history-search-form" autocomplete="off">
|
||||
<!-- Custom Tabs -->
|
||||
<div class="nav-tabs-custom" id="tabs">
|
||||
<ul class="nav nav-tabs" id="nav_nav_tabs" name="nav_nav_tabs">
|
||||
<li id="activity_tab" class="active"><a href="#tabs-act" data-toggle="tab">Search for All Activity</a></li>
|
||||
<li id="domain_tab"><a href="#tabs-domain" data-toggle="tab">Search By Domain</a></li>
|
||||
<li id="account_tab"><a href="#tabs-account" data-toggle="tab">Search By Account</a></li>
|
||||
{% if current_user.role.name != 'User' %}
|
||||
<li id="user_auth_tab"><a href="#tabs-auth" data-toggle="tab">Search for User Authentication</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane" id="tabs-act">
|
||||
</div>
|
||||
<div class="tab-pane" id="tabs-domain">
|
||||
<td><label>Domain Name</label></td>
|
||||
<td>
|
||||
<div class="autocomplete" style="width:250px;">
|
||||
<input type="text" class="form-control" id="domain_name_filter" name="domain_name_filter" placeholder="Enter * to search for any domain" value="">
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div style="position: relative; top:10px;">
|
||||
<td>Record Changelog only  </td>
|
||||
<td>
|
||||
<input type="checkbox" id="domain_changelog_only_checkbox" name="domain_changelog_only_checkbox"
|
||||
class="checkbox" style="border:2px dotted #00f;display:block;background:#ff0000;">
|
||||
</td>
|
||||
</div>
|
||||
</td>
|
||||
</div>
|
||||
<div class="tab-pane" id="tabs-account">
|
||||
<td><label>Account Name</label></td>
|
||||
<td>
|
||||
<div class="autocomplete" style="width:250px;">
|
||||
<input type="text" class="form-control" id="account_name_filter" name="account_name_filter" placeholder="Enter * to search for any account" value="">
|
||||
</div>
|
||||
</td>
|
||||
</div>
|
||||
<div class="tab-pane" id="tabs-auth">
|
||||
<td><label>Username</label></td>
|
||||
</td>
|
||||
<td>
|
||||
<div style="position: relative; top:10px;">
|
||||
<td>Record Changelog only  </td>
|
||||
<td>
|
||||
<input type="checkbox" id="domain_changelog_only_checkbox" name="domain_changelog_only_checkbox"
|
||||
class="checkbox" style="border:2px dotted #00f;display:block;background:#ff0000;">
|
||||
</td>
|
||||
</div>
|
||||
</td>
|
||||
</div>
|
||||
<div class="tab-pane" id="tabs-account">
|
||||
<td><label>Account Name</label></td>
|
||||
<td>
|
||||
<div class="autocomplete" style="width:250px;">
|
||||
<input type="text" class="form-control" id="account_name_filter" name="account_name_filter" placeholder="Enter * to search for any account" value="">
|
||||
</div>
|
||||
</td>
|
||||
</div>
|
||||
<div class="tab-pane" id="tabs-auth">
|
||||
<td><label>Username</label></td>
|
||||
<td>
|
||||
<div class="autocomplete" style="width:250px;">
|
||||
<input type="text" class="form-control" id="auth_name_filter" name="auth_name_filter" placeholder="Enter * to search for any username" value="">
|
||||
@ -109,9 +136,7 @@
|
||||
</td>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End Custom Tabs -->
|
||||
|
||||
<div class="box-body">
|
||||
<div class="card-body">
|
||||
<table id="Filters-Table">
|
||||
<thead>
|
||||
<th>Filters</th>
|
||||
@ -145,29 +170,18 @@
|
||||
<tr><td> </td></tr>
|
||||
<tr>
|
||||
<td>
|
||||
<button type="submit" id="search-submit" name="search-submit" class="btn btn-flat btn-primary button-filter">Search <i class="fa fa-search"></i></button>
|
||||
<button type="submit" id="search-submit" name="search-submit" class="btn btn-primary button-filter"><i class="fa fa-search"></i> Search</button>
|
||||
</td>
|
||||
<td>
|
||||
<!-- -->
|
||||
<button id="clear-filters" name="clear-filters" class="btn btn-flat btn-warning button-clearf">Clear Filters <i class="fa fa-trash"></i></button>
|
||||
<button id="clear-filters" name="clear-filters" class="btn btn-warning button-clearf"><i class="fa fa-trash"></i> Clear Filters</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="table_from_ajax"></div>
|
||||
|
||||
|
||||
<!-- /.box-body -->
|
||||
</div>
|
||||
<!-- /.box -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
</section>
|
||||
{% endblock %}
|
||||
{% block extrascripts %}
|
||||
@ -471,49 +485,45 @@
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% block modals %}
|
||||
<!-- Clear History Confirmation Box -->
|
||||
<div class="modal fade modal-warning" id="modal_clear_history">
|
||||
<!-- Clear History Confirmation Box -->
|
||||
<div class="modal fade modal-warning" id="modal_clear_history">
|
||||
<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>Are you sure you want to remove all history?</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"
|
||||
onclick="applyChanges({'_csrf_token': '{{ csrf_token() }}'}, $SCRIPT_ROOT + '/admin/history', false, true);">Clear
|
||||
History</button>
|
||||
</div>
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Confirmation</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
<div class="modal-body">
|
||||
<p>Are you sure you want to remove all history?</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-danger float-right" onclick="applyChanges({'_csrf_token': '{{ csrf_token() }}'}, $SCRIPT_ROOT + '/admin/history', false, true);">
|
||||
Clear History
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
<div class="modal fade" id="modal_history_info">
|
||||
</div>
|
||||
<!-- History Details Box -->
|
||||
<div class="modal fade" id="modal_history_info">
|
||||
<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">History Details</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="modal-info-content"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-flat btn-default pull-right" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">History Details</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
<div class="modal-body">
|
||||
<div id="modal-info-content"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default float-right" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
<!-- /.modal -->
|
||||
</div>
|
||||
{% endblock %}
|
@ -1,90 +1,86 @@
|
||||
|
||||
{% import 'applied_change_macro.html' as applied_change_macro %}
|
||||
|
||||
|
||||
{% if len_histories >= lim %}
|
||||
<p style="color: rgb(224, 3, 3);"><b>Limit of loaded history records has been reached! Only {{lim}} history records are shown. </b></p>
|
||||
<p style="color: rgb(224, 3, 3);">
|
||||
<b>Limit of loaded history records has been reached! Only {{lim}} history records are shown.</b>
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<div class="box-body"></div>
|
||||
<table id="tbl_history" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Changed by</th>
|
||||
<th>Content</th>
|
||||
<th>Time</th>
|
||||
<th>Detail</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for history in histories %}
|
||||
<tr class="odd gradeX">
|
||||
<td>{{ history.history.created_by }}</td>
|
||||
<td>{{ history.history.msg }}</td>
|
||||
<td>{{ history.history.created_on }}</td>
|
||||
|
||||
<td width="6%">
|
||||
<div id="history-info-div-{{ loop.index0 }}" style="display: none;">
|
||||
{{ history.detailed_msg | safe }}
|
||||
{% if history.change_set %}
|
||||
<div class="content">
|
||||
<div id="change_index_definition"></div>
|
||||
{% call applied_change_macro.applied_change_template(history.change_set) %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<button type="button" class="btn btn-flat btn-primary history-info-button"
|
||||
{% if history.detailed_msg == "" and history.change_set is none %}
|
||||
style="visibility: hidden;"
|
||||
{% endif %} value="{{ loop.index0 }}">Info <i class="fa fa-info"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="card-body"></div>
|
||||
<table id="tbl_history" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Changed by</th>
|
||||
<th>Content</th>
|
||||
<th>Time</th>
|
||||
<th>Detail</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for history in histories %}
|
||||
<tr class="odd gradeX">
|
||||
<td>{{ history.history.created_by }}</td>
|
||||
<td>{{ history.history.msg }}</td>
|
||||
<td>{{ history.history.created_on }}</td>
|
||||
<td width="6%">
|
||||
<div id="history-info-div-{{ loop.index0 }}" style="display: none;">
|
||||
{{ history.detailed_msg | safe }}
|
||||
{% if history.change_set %}
|
||||
<div class="content">
|
||||
<div id="change_index_definition"></div>
|
||||
{% call applied_change_macro.applied_change_template(history.change_set) %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary history-info-button"
|
||||
{% if history.detailed_msg == "" and history.change_set is none %}
|
||||
style="visibility: hidden;"
|
||||
{% endif %} value="{{ loop.index0 }}">
|
||||
<i class="fa-solid fa-info"></i>
|
||||
Info
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var table;
|
||||
$(document).ready(function () {
|
||||
|
||||
table = $('#tbl_history').DataTable({
|
||||
"order": [
|
||||
[2, "desc"]
|
||||
],
|
||||
"searching": true,
|
||||
"columnDefs": [{
|
||||
"type": "time",
|
||||
"render": function (data, type, row) {
|
||||
return moment.utc(data).local().format('YYYY-MM-DD HH:mm:ss');
|
||||
},
|
||||
"targets": 2
|
||||
}],
|
||||
"info": true,
|
||||
"autoWidth": false,
|
||||
orderCellsTop: true,
|
||||
fixedHeader: true
|
||||
});
|
||||
|
||||
var table;
|
||||
$(document).ready(function () {
|
||||
table = $('#tbl_history').DataTable({
|
||||
"order": [
|
||||
[2, "desc"]
|
||||
],
|
||||
"searching": true,
|
||||
"columnDefs": [{
|
||||
"type": "time",
|
||||
"render": function (data, type, row) {
|
||||
return moment.utc(data).local().format('YYYY-MM-DD HH:mm:ss');
|
||||
},
|
||||
"targets": 2
|
||||
}],
|
||||
"info": true,
|
||||
"autoWidth": false,
|
||||
orderCellsTop: true,
|
||||
fixedHeader: true
|
||||
});
|
||||
$(document.body).on('click', '.history-info-button', function () {
|
||||
var modal = $("#modal_history_info");
|
||||
var history_id = $(this).val();
|
||||
var info = $("#history-info-div-" + history_id).html();
|
||||
$('#modal-info-content').html(info);
|
||||
modal.modal('show');
|
||||
var modal = $("#modal_history_info");
|
||||
var history_id = $(this).val();
|
||||
var info = $("#history-info-div-" + history_id).html();
|
||||
$('#modal-info-content').html(info);
|
||||
modal.modal('show');
|
||||
});
|
||||
|
||||
$(document.body).on("click", ".button-filter", function (e) {
|
||||
e.stopPropagation();
|
||||
var nextRow = $("#filter-table")
|
||||
if (nextRow.css("visibility") == "visible")
|
||||
nextRow.css("visibility", "collapse")
|
||||
else
|
||||
nextRow.css("visibility", "visible")
|
||||
e.stopPropagation();
|
||||
var nextRow = $("#filter-table")
|
||||
if (nextRow.css("visibility") == "visible")
|
||||
nextRow.css("visibility", "collapse")
|
||||
else
|
||||
nextRow.css("visibility", "visible")
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
});
|
||||
</script>
|
@ -1,76 +1,97 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% set active_page = "admin_accounts" %}
|
||||
|
||||
{% block title %}
|
||||
<title>Account Management - {{ SITE_NAME }}</title>
|
||||
{% endblock %} {% block dashboard_stat %}
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
Accounts <small>Manage accounts</small>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}"><i class="fa fa-dashboard"></i> Home</a></li>
|
||||
<li class="active">Accounts</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">Account Management</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<a href="{{ url_for('admin.edit_account') }}">
|
||||
<button type="button" class="btn btn-flat btn-primary pull-left button_add_account">
|
||||
Add Account <i class="fa fa-plus"></i>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<table id="tbl_accounts" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Contact</th>
|
||||
<th>Mail</th>
|
||||
<th>Member</th>
|
||||
<th>Domain</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for account in accounts %}
|
||||
<tr class="odd gradeX">
|
||||
<td>{{ account.name }}</td>
|
||||
<td>{{ account.description }}</td>
|
||||
<td>{{ account.contact }}</td>
|
||||
<td>{{ account.mail }}</td>
|
||||
<td>{{ account.user_num }}</td>
|
||||
<td>{{ account.domains|length }}</td>
|
||||
<td width="15%">
|
||||
<button type="button" class="btn btn-flat btn-success"
|
||||
onclick="window.location.href='{{ url_for('admin.edit_account', account_name=account.name) }}'">
|
||||
Edit <i class="fa fa-cog"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-flat btn-danger button_delete"
|
||||
id="{{ account.name }}">
|
||||
Delete <i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
</div>
|
||||
<!-- /.box -->
|
||||
<title>
|
||||
Account Management - {{ SITE_NAME }}
|
||||
</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block dashboard_stat %}
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
Accounts
|
||||
<small>Manage</small>
|
||||
</h1>
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Home</a></li>
|
||||
<li class="breadcrumb-item active">Accounts</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Account Management</h3>
|
||||
<a href="{{ url_for('admin.edit_account') }}">
|
||||
<button type="button" class="btn btn-primary float-right button_add_account">
|
||||
<i class="fa-solid fa-plus"></i> Add Account
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table id="tbl_accounts" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Contact</th>
|
||||
<th>Mail</th>
|
||||
<th>Member</th>
|
||||
<th>Domain</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for account in accounts %}
|
||||
<tr class="odd gradeX">
|
||||
<td>{{ account.name }}</td>
|
||||
<td>{{ account.description }}</td>
|
||||
<td>{{ account.contact }}</td>
|
||||
<td>{{ account.mail }}</td>
|
||||
<td>{{ account.user_num }}</td>
|
||||
<td>{{ account.domains|length }}</td>
|
||||
<td width="15%">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-primary dropdown-toggle" type="button" id="dropdownMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="fa-solid fa-bars"></i> Actions
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownMenu">
|
||||
<button type="button" class="dropdown-item btn-warning" onclick="window.location.href='{{ url_for('admin.edit_account', account_name=account.name) }}'">
|
||||
<i class="fa-solid fa-edit"></i> Edit Account
|
||||
</button>
|
||||
<div class="dropdown-divider"></div>
|
||||
<button type="button"class="dropdown-item btn-secondary button_delete" id="{{ account.name }}">
|
||||
<font color="red">
|
||||
<i class="fa-solid fa-trash"></i> Delete Account
|
||||
</font>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
{% block extrascripts %}
|
||||
@ -114,21 +135,21 @@
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% block modals %}
|
||||
<div class="modal fade modal-warning" id="modal_delete">
|
||||
<div class="modal fade" 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">
|
||||
<h4 class="modal-title">Confirmation</h4>
|
||||
<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>
|
||||
<button type="button" class="btn btn-default float-left" data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-danger" id="button_delete_confirm">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
|
@ -1,133 +1,153 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% set active_page = "admin_keys" %}
|
||||
|
||||
{% block title %}
|
||||
<title>Key Management - {{ SITE_NAME }}</title>
|
||||
{% endblock %} {% block dashboard_stat %}
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
Key <small>Manage API keys</small>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}"><i class="fa fa-dashboard"></i> Home</a></li>
|
||||
<li class="active">Key</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">Key Management</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<a href="{{ url_for('admin.edit_key') }}">
|
||||
<button type="button" class="btn btn-flat btn-primary pull-left button_add_key">
|
||||
Add Key <i class="fa fa-plus"></i>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<table id="tbl_keys" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<th>Role</th>
|
||||
<th>Description</th>
|
||||
<th>Domains</th>
|
||||
<th>Accounts</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for key in keys %}
|
||||
<tr class="odd gradeX">
|
||||
<td>{{ key.id }}</td>
|
||||
<td>{{ key.role.name }}</td>
|
||||
<td>{{ key.description }}</td>
|
||||
<td>{% for domain in key.domains %}{{ domain.name }}{% if not loop.last %}, {% endif %}{% endfor %}</td>
|
||||
<td>{% for account in key.accounts %}{{ account.name }}{% if not loop.last %}, {% endif %}{% endfor %}</td>
|
||||
<td width="15%">
|
||||
<button type="button" class="btn btn-flat btn-success button_edit"
|
||||
onclick="window.location.href='{{ url_for('admin.edit_key', key_id=key.id) }}'">
|
||||
Edit <i class="fa fa-lock"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-flat btn-danger button_delete"
|
||||
id="{{ key.id }}">
|
||||
Delete <i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
</div>
|
||||
<!-- /.box -->
|
||||
<title>
|
||||
Key Management - {{ SITE_NAME }}
|
||||
</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block dashboard_stat %}
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
API Keys
|
||||
<small>Management</small>
|
||||
</h1>
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item active">API Keys</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="card">
|
||||
<div class="card-header with-border">
|
||||
<h3 class="card-title">Key Management</h3>
|
||||
<a href="{{ url_for('admin.edit_key') }}">
|
||||
<button type="button" class="btn btn-primary float-right button_add_key">
|
||||
<i class="fa-solid fa-plus"></i> Add Key
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table id="tbl_keys" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<th>Role</th>
|
||||
<th>Description</th>
|
||||
<th>Domains</th>
|
||||
<th>Accounts</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for key in keys %}
|
||||
<tr class="odd gradeX">
|
||||
<td>{{ key.id }}</td>
|
||||
<td>{{ key.role.name }}</td>
|
||||
<td>{{ key.description }}</td>
|
||||
<td>{% for domain in key.domains %}{{ domain.name }}{% if not loop.last %}, {% endif %}{% endfor %}</td>
|
||||
<td>{% for account in key.accounts %}{{ account.name }}{% if not loop.last %}, {% endif %}{% endfor %}</td>
|
||||
<td width="15%">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-primary dropdown-toggle" type="button" id="dropdownMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="fa-solid fa-bars"></i> Actions
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownMenu">
|
||||
<button type="button" class="dropdown-item btn-warning" onclick="window.location.href='{{ url_for('admin.edit_key', key_id=key.id) }}'">
|
||||
<i class="fa-solid fa-edit"></i> Edit API Key
|
||||
</button>
|
||||
<div class="dropdown-divider"></div>
|
||||
<button type="button"class="dropdown-item btn-secondary button_delete" id="{{ key.id }}">
|
||||
<font color="red">
|
||||
<i class="fa-solid fa-trash"></i> Delete API Key
|
||||
</font>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
{% block extrascripts %}
|
||||
<script>
|
||||
<script>
|
||||
// set up key data table
|
||||
$("#tbl_keys").DataTable({
|
||||
"paging": true,
|
||||
"lengthChange": true,
|
||||
"searching": true,
|
||||
"ordering": true,
|
||||
"info": false,
|
||||
"autoWidth": false,
|
||||
"lengthMenu": [
|
||||
[10, 25, 50, 100, -1],
|
||||
[10, 25, 50, 100, "All"]
|
||||
],
|
||||
"pageLength": 10
|
||||
"paging": true,
|
||||
"lengthChange": true,
|
||||
"searching": true,
|
||||
"ordering": true,
|
||||
"info": false,
|
||||
"autoWidth": false,
|
||||
"lengthMenu": [
|
||||
[10, 25, 50, 100, -1],
|
||||
[10, 25, 50, 100, "All"]
|
||||
],
|
||||
"pageLength": 10
|
||||
});
|
||||
|
||||
// handle deletion of keys
|
||||
$(document.body).on('click', '.button_delete', function () {
|
||||
var modal = $("#modal_delete");
|
||||
var key_id = $(this).prop('id');
|
||||
var info = "Are you sure you want to delete key #" + key_id + "?";
|
||||
modal.find('.modal-body p').text(info);
|
||||
modal.find('#button_delete_confirm').click(function () {
|
||||
var postdata = {
|
||||
'action': 'delete_key',
|
||||
'data': key_id,
|
||||
'_csrf_token': '{{ csrf_token() }}'
|
||||
}
|
||||
applyChanges(postdata, $SCRIPT_ROOT + '/admin/manage-keys', false, true);
|
||||
modal.modal('hide');
|
||||
})
|
||||
modal.modal('show');
|
||||
|
||||
var modal = $("#modal_delete");
|
||||
var key_id = $(this).prop('id');
|
||||
var info = "Are you sure you want to delete key #" + key_id + "?";
|
||||
modal.find('.modal-body p').text(info);
|
||||
modal.find('#button_delete_confirm').click(function () {
|
||||
var postdata = {
|
||||
'action': 'delete_key',
|
||||
'data': key_id,
|
||||
'_csrf_token': '{{ csrf_token() }}'
|
||||
}
|
||||
applyChanges(postdata, $SCRIPT_ROOT + '/admin/manage-keys', false, true);
|
||||
modal.modal('hide');
|
||||
})
|
||||
modal.modal('show');
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block modals %}
|
||||
<div class="modal fade modal-warning" id="modal_delete">
|
||||
<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 class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Confirmation</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
<div class="modal-body">
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">
|
||||
Close
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger" id="button_delete_confirm">
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -1,205 +1,237 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% set active_page = "admin_users" %}
|
||||
|
||||
{% block title %}
|
||||
<title>User Management - {{ SITE_NAME }}</title>
|
||||
{% endblock %} {% block dashboard_stat %}
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
User <small>Manage user privileges</small>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}"><i class="fa fa-dashboard"></i> Home</a></li>
|
||||
<li class="active">User</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">User Management</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<a href="{{ url_for('admin.edit_user') }}">
|
||||
<button type="button" class="btn btn-flat btn-primary pull-left button_add_user">
|
||||
Add User <i class="fa fa-plus"></i>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<table id="tbl_users" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Username</th>
|
||||
<th>First Name</th>
|
||||
<th>Last Name</th>
|
||||
<th>Email</th>
|
||||
<th>Role</th>
|
||||
<th>Privileges</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for user in users %}
|
||||
<tr class="odd gradeX">
|
||||
<td>{{ user.username }}</td>
|
||||
<td>{{ user.firstname }}</td>
|
||||
<td>{{ user.lastname }}</td>
|
||||
<td>{{ user.email }}</td>
|
||||
<td>
|
||||
<select id="{{ user.username }}" class="user_role"
|
||||
{% if user.username==current_user.username or (current_user.role.name=='Operator' and user.role.name=='Administrator') %}disabled{% endif %}>
|
||||
{% for role in roles %}
|
||||
<option value="{{ role.name }}"
|
||||
{% if role.id==user.role.id %}selected{% endif %}>{{ role.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
<td width="6%">
|
||||
<button type="button" class="btn btn-flat btn-warning button_revoke"
|
||||
id="{{ user.username }}"
|
||||
{% if current_user.role.name=='Operator' and user.role.name=='Administrator' %}disabled{% endif %}>
|
||||
Revoke <i class="fa fa-lock"></i>
|
||||
</button>
|
||||
</td>
|
||||
<td width="15%">
|
||||
<button type="button" class="btn btn-flat btn-success button_edit"
|
||||
onclick="window.location.href='{{ url_for('admin.edit_user', user_username=user.username) }}'"
|
||||
{% if current_user.role.name=='Operator' and user.role.name=='Administrator' %}disabled{% endif %}>
|
||||
Edit <i class="fa fa-lock"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-flat btn-danger button_delete"
|
||||
id="{{ user.username }}"
|
||||
{% if user.username==current_user.username or (current_user.role.name=='Operator' and user.role.name=='Administrator') %}disabled{% endif %}>
|
||||
Delete <i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
</div>
|
||||
<!-- /.box -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
</section>
|
||||
<title>
|
||||
User Management - {{ SITE_NAME }}
|
||||
</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block dashboard_stat %}
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
User
|
||||
<small>Manage user privileges</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Home</a></li>
|
||||
<li class="breadcrumb-item active">User</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">User Management</h3>
|
||||
<a href="{{ url_for('admin.edit_user') }}">
|
||||
<button type="button" class="btn btn-primary float-right button_add_user">
|
||||
<i class="fa-solid fa-plus"></i> Add User
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table id="tbl_users" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Username</th>
|
||||
<th>First Name</th>
|
||||
<th>Last Name</th>
|
||||
<th>Email</th>
|
||||
<th>Role</th>
|
||||
<th>Privileges</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for user in users %}
|
||||
<tr class="odd gradeX">
|
||||
<td>{{ user.username }}</td>
|
||||
<td>{{ user.firstname }}</td>
|
||||
<td>{{ user.lastname }}</td>
|
||||
<td>{{ user.email }}</td>
|
||||
<td>
|
||||
<select id="{{ user.username }}" class="user_role"
|
||||
{% if user.username==current_user.username or (current_user.role.name=='Operator' and user.role.name=='Administrator') %}disabled{% endif %}>
|
||||
{% for role in roles %}
|
||||
<option value="{{ role.name }}"
|
||||
{% if role.id==user.role.id %}selected{% endif %}>
|
||||
{{ role.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
<td width="6%">
|
||||
<button type="button" class="btn btn-warning button_revoke"
|
||||
id="{{ user.username }}"
|
||||
{% if current_user.role.name=='Operator' and user.role.name=='Administrator' %}disabled{% endif %}>
|
||||
<i class="fa-solid fa-link-slash"></i> Revoke
|
||||
</button>
|
||||
</td>
|
||||
<td width="15%">
|
||||
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-primary dropdown-toggle" type="button" id="dropdownMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
|
||||
{% if current_user.role.name=='Operator' and user.role.name=='Administrator' %}disabled{% endif %}>
|
||||
<i class="fa-solid fa-bars"></i> Actions
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownMenu">
|
||||
<button type="button" class="dropdown-item btn-warning" onclick="window.location.href='{{ url_for('admin.edit_user', user_username=user.username) }}'">
|
||||
<i class="fa-solid fa-edit"></i> Edit User
|
||||
</button>
|
||||
{% if not user.username==current_user.username or (current_user.role.name=='Operator' and user.role.name=='Administrator') %}
|
||||
<div class="dropdown-divider"></div>
|
||||
<button type="button"class="dropdown-item btn-secondary button_delete" id="{{ user.username }}">
|
||||
<font color="red">
|
||||
<i class="fa-solid fa-trash"></i> Delete User
|
||||
</font>
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
{% block extrascripts %}
|
||||
<script>
|
||||
<script>
|
||||
// set up user data table
|
||||
$("#tbl_users").DataTable({
|
||||
"paging": true,
|
||||
"lengthChange": true,
|
||||
"searching": true,
|
||||
"ordering": true,
|
||||
"info": false,
|
||||
"autoWidth": false,
|
||||
"lengthMenu": [
|
||||
[10, 25, 50, 100, -1],
|
||||
[10, 25, 50, 100, "All"]
|
||||
],
|
||||
"pageLength": 10
|
||||
"paging": true,
|
||||
"lengthChange": true,
|
||||
"searching": true,
|
||||
"ordering": true,
|
||||
"info": false,
|
||||
"autoWidth": false,
|
||||
"lengthMenu": [
|
||||
[10, 25, 50, 100, -1],
|
||||
[10, 25, 50, 100, "All"]
|
||||
],
|
||||
"pageLength": 10
|
||||
});
|
||||
|
||||
// handle revocation of privileges
|
||||
$(document.body).on('click', '.button_revoke', function () {
|
||||
var modal = $("#modal_revoke");
|
||||
var username = $(this).prop('id');
|
||||
var info = "Are you sure you want to revoke all privileges for " + username +
|
||||
". They will not able to access any domain.";
|
||||
modal.find('.modal-body p').text(info);
|
||||
modal.find('#button_revoke_confirm').click(function () {
|
||||
var postdata = {
|
||||
'action': 'revoke_user_privileges',
|
||||
'data': username,
|
||||
'_csrf_token': '{{ csrf_token() }}'
|
||||
}
|
||||
applyChanges(postdata, $SCRIPT_ROOT + '/admin/manage-user', true);
|
||||
modal.modal('hide');
|
||||
})
|
||||
modal.modal('show');
|
||||
var modal = $("#modal_revoke");
|
||||
var username = $(this).prop('id');
|
||||
var info = "Are you sure you want to revoke all privileges for user " + username +
|
||||
"? They will not able to access any domain.";
|
||||
modal.find('.modal-body p').text(info);
|
||||
modal.find('#button_revoke_confirm').click(function () {
|
||||
var postdata = {
|
||||
'action': 'revoke_user_privileges',
|
||||
'data': username,
|
||||
'_csrf_token': '{{ csrf_token() }}'
|
||||
}
|
||||
applyChanges(postdata, $SCRIPT_ROOT + '/admin/manage-user', true);
|
||||
modal.modal('hide');
|
||||
})
|
||||
modal.modal('show');
|
||||
});
|
||||
|
||||
// handle deletion of user
|
||||
$(document.body).on('click', '.button_delete', function () {
|
||||
var modal = $("#modal_delete");
|
||||
var username = $(this).prop('id');
|
||||
var info = "Are you sure you want to delete " + username + "?";
|
||||
modal.find('.modal-body p').text(info);
|
||||
modal.find('#button_delete_confirm').click(function () {
|
||||
var postdata = {
|
||||
'action': 'delete_user',
|
||||
'data': username,
|
||||
'_csrf_token': '{{ csrf_token() }}'
|
||||
}
|
||||
applyChanges(postdata, $SCRIPT_ROOT + '/admin/manage-user', false, true);
|
||||
modal.modal('hide');
|
||||
})
|
||||
modal.modal('show');
|
||||
|
||||
var modal = $("#modal_delete");
|
||||
var username = $(this).prop('id');
|
||||
var info = "Are you sure you want to delete user " + username + "?";
|
||||
modal.find('.modal-body p').text(info);
|
||||
modal.find('#button_delete_confirm').click(function () {
|
||||
var postdata = {
|
||||
'action': 'delete_user',
|
||||
'data': username,
|
||||
'_csrf_token': '{{ csrf_token() }}'
|
||||
}
|
||||
applyChanges(postdata, $SCRIPT_ROOT + '/admin/manage-user', false, true);
|
||||
modal.modal('hide');
|
||||
})
|
||||
modal.modal('show');
|
||||
});
|
||||
|
||||
// handle user role changing
|
||||
$(document.body).on('change', '.user_role', function () {
|
||||
var role_name = this.value;
|
||||
var username = $(this).prop('id');
|
||||
var postdata = {
|
||||
'action': 'update_user_role',
|
||||
'data': {
|
||||
'username': username,
|
||||
'role_name': role_name
|
||||
},
|
||||
'_csrf_token': '{{ csrf_token() }}'
|
||||
};
|
||||
applyChanges(postdata, $SCRIPT_ROOT + '/admin/manage-user', showResult = true);
|
||||
var role_name = this.value;
|
||||
var username = $(this).prop('id');
|
||||
var postdata = {
|
||||
'action': 'update_user_role',
|
||||
'data': {
|
||||
'username': username,
|
||||
'role_name': role_name
|
||||
},
|
||||
'_csrf_token': '{{ csrf_token() }}'
|
||||
};
|
||||
applyChanges(postdata, $SCRIPT_ROOT + '/admin/manage-user', showResult = true);
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block modals %}
|
||||
<div class="modal fade modal-warning" id="modal_revoke">
|
||||
<div class="modal fade" id="modal_revoke">
|
||||
<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_revoke_confirm">Revoke</button>
|
||||
</div>
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Confirmation</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
<div class="modal-body">
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary float-right" data-dismiss="modal">
|
||||
Close
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger float-right" id="button_revoke_confirm">
|
||||
Revoke
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
<div class="modal fade modal-warning" id="modal_delete">
|
||||
</div>
|
||||
|
||||
<div class="modal fade" 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 class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Confirmation</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
<div class="modal-body">
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default float-right" data-dismiss="modal">
|
||||
Close
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger float-right" id="button_delete_confirm">
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -1,116 +1,127 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% set active_page = "admin_console" %}
|
||||
{% block title %}<title>Admin Console - {{ SITE_NAME }}</title>{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
<title>
|
||||
Admin Console - {{ SITE_NAME }}
|
||||
</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block dashboard_stat %}
|
||||
<!-- Content Header (Page header) -->
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
PowerDNS server configuration & statistics
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}"><i class="fa fa-dashboard"></i> Home</a></li>
|
||||
<li class="active">Admin Console</li>
|
||||
</ol>
|
||||
</section>
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
PowerDNS
|
||||
<small>Server Statistics & Configuration</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item active">PowerDNS Server Statistics & Configuration</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% 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">PDNS Statistics</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<table id="tbl_statistics" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="6%">Docs</th>
|
||||
<th>Statistic</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for statistic in statistics %}
|
||||
<tr class="odd gradeX">
|
||||
<td><a href="https://doc.powerdns.com/authoritative/search.html?q={{ statistic['name'] }}"
|
||||
target="_blank" class="btn btn-flat btn-xs blue"><i
|
||||
class="fa fa-search"></i></a></td>
|
||||
<td>{{ statistic['name'] }}</td>
|
||||
<td>{{ statistic['value'] }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card shadow">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">PowerDNS Server Statistics</h3>
|
||||
</div>
|
||||
<!-- /.box -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<h3 class="box-title">PDNS Configuration</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<table id="tbl_configuration" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="6%">Docs</th>
|
||||
<th>Statistic</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for config in configs %}
|
||||
<tr class="odd gradeX">
|
||||
<td><a href="https://doc.powerdns.com/authoritative/search.html?q={{ config['name'] }}"
|
||||
target="_blank" class="btn btn-flat btn-xs blue"><i
|
||||
class="fa fa-search"></i></a></td>
|
||||
<td>{{ config['name'] }}</td>
|
||||
<td>
|
||||
{{ config['value'] }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
<div class="card-body">
|
||||
<table id="tbl_statistics" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="30%">Statistic</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for statistic in statistics %}
|
||||
<tr class="odd gradeX">
|
||||
<td>
|
||||
<a href="https://doc.powerdns.com/authoritative/search.html?q={{ statistic['name'] }}"
|
||||
target="_blank" class="btn btn-primary">
|
||||
<i class="fa fa-search"></i> {{ statistic['name'] }}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ statistic['value'] }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- /.box -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card shadow">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">PowerDNS Server Configuration</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table id="tbl_configuration" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="30%">Configuration</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for config in configs %}
|
||||
<tr class="odd gradeX">
|
||||
<td>
|
||||
<a href="https://doc.powerdns.com/authoritative/search.html?q={{ config['name'] }}"
|
||||
target="_blank" class="btn btn-primary">
|
||||
<i class="fa-solid fa-search"></i> {{ config['name'] }}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
{{ config['value'] }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
</section>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
{% block extrascripts %}
|
||||
<script>
|
||||
// set up statistics data table
|
||||
$("#tbl_statistics").DataTable({
|
||||
"paging": true,
|
||||
"lengthChange": false,
|
||||
"searching": true,
|
||||
"ordering": true,
|
||||
"info": true,
|
||||
"autoWidth": false
|
||||
});
|
||||
// set up statistics data table
|
||||
$("#tbl_statistics").DataTable({
|
||||
"paging": true,
|
||||
"lengthChange": true,
|
||||
"searching": true,
|
||||
"ordering": true,
|
||||
"info": true,
|
||||
"autoWidth": false
|
||||
});
|
||||
|
||||
// set up configuration data table
|
||||
$("#tbl_configuration").DataTable({
|
||||
"paging": true,
|
||||
"lengthChange": false,
|
||||
"searching": true,
|
||||
"ordering": true,
|
||||
"info": true,
|
||||
"autoWidth": false
|
||||
});
|
||||
// set up configuration data table
|
||||
$("#tbl_configuration").DataTable({
|
||||
"paging": true,
|
||||
"lengthChange": true,
|
||||
"searching": true,
|
||||
"ordering": true,
|
||||
"info": true,
|
||||
"autoWidth": false
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@ -1,67 +1,81 @@
|
||||
{% extends "base.html" %}
|
||||
{% set active_page = "admin_settings" %}
|
||||
{% block title %}
|
||||
<title>Authentication Settings - {{ SITE_NAME }}</title>
|
||||
{% endblock %} {% block dashboard_stat %}
|
||||
<!-- Content Header (Page header) -->
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
Settings <small>PowerDNS-Admin settings</small>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}"><i class="fa fa-dashboard"></i> Home</a></li>
|
||||
<li><a href="#">Setting</a></li>
|
||||
<li class="active">Authentication</li>
|
||||
</ol>
|
||||
<script>
|
||||
function ldapSelection() {
|
||||
if (document.getElementById('ldap').checked) {
|
||||
document.getElementById('ldap_openldap_fields').style.display = 'block';
|
||||
document.getElementById('ldap_openldap_group_filters').style.display = 'block';
|
||||
document.getElementById('ldap_ad_fields').style.display = 'none';
|
||||
} else {
|
||||
document.getElementById('ldap_openldap_fields').style.display = 'none';
|
||||
document.getElementById('ldap_openldap_group_filters').style.display = 'none';
|
||||
document.getElementById('ldap_ad_fields').style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
ldapSelection();
|
||||
}
|
||||
</script>
|
||||
</section>
|
||||
{% set active_page = "admin_settings" %}
|
||||
|
||||
{% block title %}
|
||||
<title>
|
||||
Authentication Settings - {{ SITE_NAME }}
|
||||
</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block dashboard_stat %}
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
Settings
|
||||
<small>PowerDNS-Admin Settings</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Home</a></li>
|
||||
<li class="breadcrumb-item">Settings</li>
|
||||
<li class="breadcrumb-item active">Authentication</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Authentication Settings</h3>
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Authentication Settings</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="card-body">
|
||||
{% if result %}
|
||||
<div class="alert {% if result['status'] %}alert-success{% else %}alert-danger{% endif%} alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||
{{ result['msg'] }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<!-- Custom Tabs -->
|
||||
<div class="nav-tabs-custom" id="tabs">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a href="#tabs-general" data-toggle="tab">General</a></li>
|
||||
<li><a href="#tabs-ldap" data-toggle="tab">LDAP</a></li>
|
||||
<li><a href="#tabs-google" data-toggle="tab">Google OAuth</a></li>
|
||||
<li><a href="#tabs-github" data-toggle="tab">Github OAuth</a></li>
|
||||
<li><a href="#tabs-azure" data-toggle="tab">Microsoft OAuth</a></li>
|
||||
<li><a href="#tabs-oidc" data-toggle="tab">OpenID Connect OAuth</a></li>
|
||||
<div class="nav-tabs-custom">
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="#tabs-general" data-toggle="pill" role="tab">General</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#tabs-ldap" data-toggle="pill" role="tab">LDAP</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#tabs-google" data-toggle="pill" role="tab">Google OAuth</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#tabs-github" data-toggle="pill" role="tab">Github OAuth</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#tabs-azure" data-toggle="pill" role="tab">Microsoft OAuth</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#tabs-oidc" data-toggle="pill" role="tab">OpenID Connect OAuth</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="tabs-general">
|
||||
<form role="form" method="post">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" value="general" name="config_tab" />
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Basic Settings</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<input type="checkbox" id="local_db_enabled" name="local_db_enabled" class="checkbox" {% if SETTING.get('local_db_enabled') %}checked{% endif %}>
|
||||
<label for="local_db_enabled">Local DB Authentication</label>
|
||||
@ -70,15 +84,13 @@
|
||||
<input type="checkbox" id="signup_enabled" name="signup_enabled" class="checkbox" {% if SETTING.get('signup_enabled') %}checked{% endif %}>
|
||||
<label for="signup_enabled">Allow users to sign up</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-flat btn-primary">Save</button>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="tab-pane" id="tabs-ldap">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="col-4">
|
||||
{% if error %}
|
||||
<div class="alert alert-danger alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||
@ -239,7 +251,7 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="col-8">
|
||||
<legend>Help</legend>
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Enable LDAP Authentication</dt>
|
||||
@ -333,7 +345,9 @@
|
||||
</div>
|
||||
<div class="tab-pane" id="tabs-google">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="col-4">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form role="form" method="post" data-toggle="validator">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" value="google" name="config_tab" />
|
||||
@ -377,21 +391,29 @@
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-flat btn-primary">Save</button>
|
||||
<div class="card-footer">
|
||||
<button type="submit" class="btn btn-primary float-right">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<legend>Help</legend>
|
||||
<p>Fill in all the fields in the left form.</p>
|
||||
<p>Make sure you add PDA redirection URI (e.g http://localhost:9191/google/authorized) to your Google App Credentials Restriction.</p>
|
||||
<div class="col-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Help</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Fill in all the fields in the left form.</p>
|
||||
<p>Make sure you add PDA redirection URI (e.g http://localhost:9191/google/authorized) to your Google App Credentials Restriction.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane" id="tabs-github">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="col-4">
|
||||
<form role="form" method="post" data-toggle="validator">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" value="github" name="config_tab" />
|
||||
@ -440,7 +462,7 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="col-8">
|
||||
<legend>Help</legend>
|
||||
<p>Fill in all the fields in the left form.</p>
|
||||
</div>
|
||||
@ -448,7 +470,7 @@
|
||||
</div>
|
||||
<div class="tab-pane" id="tabs-azure">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="col-4">
|
||||
<form role="form" method="post" data-toggle="validator">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" value="azure" name="config_tab" />
|
||||
@ -562,7 +584,7 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="col-8">
|
||||
<legend>Help</legend>
|
||||
<p>Fill in all the fields in the left form.</p>
|
||||
<p>You first need to define an Application Registration in your Azure Active Directory, with the appropriate HTTPS URL for this endpoint, and with the appropriate rights, as explained in the documentation.</p>
|
||||
@ -584,7 +606,7 @@
|
||||
</div>
|
||||
<div class="tab-pane" id="tabs-oidc">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="col-4">
|
||||
<form role="form" method="post" data-toggle="validator">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" value="oidc" name="config_tab" />
|
||||
@ -671,7 +693,7 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="col-8">
|
||||
<legend>Help</legend>
|
||||
<p>Fill in all the fields in the left form.</p>
|
||||
</div>
|
||||
@ -683,14 +705,31 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
{% block extrascripts %}
|
||||
{% assets "js_validation" -%}
|
||||
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
||||
{%- endassets %}
|
||||
|
||||
<script>
|
||||
function ldapSelection() {
|
||||
if (document.getElementById('ldap').checked) {
|
||||
document.getElementById('ldap_openldap_fields').style.display = 'block';
|
||||
document.getElementById('ldap_openldap_group_filters').style.display = 'block';
|
||||
document.getElementById('ldap_ad_fields').style.display = 'none';
|
||||
} else {
|
||||
document.getElementById('ldap_openldap_fields').style.display = 'none';
|
||||
document.getElementById('ldap_openldap_group_filters').style.display = 'none';
|
||||
document.getElementById('ldap_ad_fields').style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
ldapSelection();
|
||||
}
|
||||
|
||||
$(function() {
|
||||
$('#tabs').tabs({
|
||||
@ -1069,16 +1108,13 @@
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-flat btn-default pull-left" id="button_cancel" name="purge" value="OFF" data-dismiss="modal" >Cancel</button>
|
||||
<button type="button" class="btn btn-flat btn-success" id="button_confirm">Confirm</button>
|
||||
<button type="button" class="btn btn-default pull-left" id="button_cancel" name="purge" value="OFF" data-dismiss="modal" >Cancel</button>
|
||||
<button type="button" class="btn btn-success" id="button_confirm">Confirm</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal fade modal-warning" id="modal_warning" data-keyboard="false" data-backdrop="static">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
@ -1092,13 +1128,9 @@
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-flat btn-success" id="button_warning_confirm">Yes I understand</button>
|
||||
<button type="button" class="btn btn-success" id="button_warning_confirm">Yes I understand</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
@ -1,97 +1,116 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% set active_page = "admin_settings" %}
|
||||
|
||||
{% block title %}
|
||||
<title>Basic Settings - {{ SITE_NAME }}</title>
|
||||
{% endblock %} {% block dashboard_stat %}
|
||||
<!-- Content Header (Page header) -->
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
Settings <small>PowerDNS-Admin settings</small>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}"><i class="fa fa-dashboard"></i> Home</a></li>
|
||||
<li><a href="#">Setting</a></li>
|
||||
<li class="active">Basic</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">Basic Settings</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<table id="tbl_settings" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Value</th>
|
||||
<th>Change</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for setting in settings %}
|
||||
<tr class="odd ">
|
||||
<td>{{ setting }}</td>
|
||||
{% if SETTING.get(setting) in [True, False] %}
|
||||
<td>{{ SETTING.get(setting)|display_setting_state }}</td>
|
||||
<td width="6%">
|
||||
<button type="button" class="btn btn-flat btn-warning setting-toggle-button"
|
||||
id="{{ setting }}">
|
||||
Toggle <i class="fa fa-info"></i>
|
||||
</button>
|
||||
</td>
|
||||
{% else %}
|
||||
<td><input name="value" id="value" value="{{ SETTING.get(setting) }}"></td>
|
||||
<td width="6%">
|
||||
<button type="button" class="btn btn-flat btn-warning setting-save-button"
|
||||
id="{{ setting }}">
|
||||
Save <i class="fa fa-info"></i>
|
||||
</button>
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
</div>
|
||||
<!-- /.box -->
|
||||
<title>
|
||||
Basic Settings - {{ SITE_NAME }}
|
||||
</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block dashboard_stat %}
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
Settings
|
||||
<small>Basic</small>
|
||||
</h1>
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item active">Settings - Basic</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="card">
|
||||
<div class="card-header with-border">
|
||||
<h3 class="card-title">Basic Settings</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table id="tbl_settings" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Setting Name</th>
|
||||
<th>Current Value</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for setting in settings %}
|
||||
<tr class="odd">
|
||||
<td>
|
||||
{{ setting }}
|
||||
</td>
|
||||
{% if SETTING.get(setting) in [False] %}
|
||||
<td><i class="fas fa-toggle-off"></i> Off</td>
|
||||
<td width="20%">
|
||||
<button type="button" class="btn btn-success setting-toggle-button" id="{{ setting }}">
|
||||
<i class="fa-solid fa-toggle-on"></i> Turn On
|
||||
</button>
|
||||
</td>
|
||||
{% elif SETTING.get(setting) in [True] %}
|
||||
<td><i class="fas fa-toggle-on"></i> On</td>
|
||||
<td width="20%">
|
||||
<button type="button" class="btn btn-danger setting-toggle-button" id="{{ setting }}">
|
||||
<i class="fa-solid fa-toggle-off"></i> Turn Off
|
||||
</button>
|
||||
</td>
|
||||
{% else %}
|
||||
<td>
|
||||
<input name="value" id="value" value="{{ SETTING.get(setting) }}">
|
||||
</td>
|
||||
<td width="20%">
|
||||
<button type="button" class="btn btn-primary setting-save-button" id="{{ setting }}">
|
||||
<i class="fa-solid fa-save"></i> Save
|
||||
</button>
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
{% block extrascripts %}
|
||||
<script>
|
||||
// set up history data table
|
||||
<script>
|
||||
// set up settings table
|
||||
$("#tbl_settings").DataTable({
|
||||
"paging": false,
|
||||
"lengthChange": false,
|
||||
"searching": true,
|
||||
"ordering": true,
|
||||
"info": true,
|
||||
"autoWidth": false
|
||||
"paging": false,
|
||||
"lengthChange": false,
|
||||
"searching": true,
|
||||
"ordering": true,
|
||||
"info": true,
|
||||
"autoWidth": false
|
||||
});
|
||||
$(document.body).on('click', '.setting-toggle-button', function () {
|
||||
var setting = $(this).prop('id');
|
||||
applyChanges({
|
||||
'_csrf_token': '{{ csrf_token() }}'
|
||||
}, $SCRIPT_ROOT + '/admin/setting/basic/' + setting + '/toggle', false, true)
|
||||
var setting = $(this).prop('id');
|
||||
applyChanges({
|
||||
'_csrf_token': '{{ csrf_token() }}'
|
||||
}, $SCRIPT_ROOT + '/admin/setting/basic/' + setting + '/toggle', false, true)
|
||||
});
|
||||
|
||||
$(document.body).on('click', '.setting-save-button', function () {
|
||||
var setting = $(this).prop('id');
|
||||
var value = $(this).parents('tr').find('#value')[0].value;
|
||||
var postdata = {
|
||||
'value': value,
|
||||
'_csrf_token': '{{ csrf_token() }}'
|
||||
};
|
||||
applyChanges(postdata, $SCRIPT_ROOT + '/admin/setting/basic/' + setting + '/edit', false, true)
|
||||
var setting = $(this).prop('id');
|
||||
var value = $(this).parents('tr').find('#value')[0].value;
|
||||
var postdata = {
|
||||
'value': value,
|
||||
'_csrf_token': '{{ csrf_token() }}'
|
||||
};
|
||||
applyChanges(postdata, $SCRIPT_ROOT + '/admin/setting/basic/' + setting + '/edit', false, true)
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
</script>
|
||||
{% endblock %}
|
@ -1,93 +1,110 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% set active_page = "admin_settings" %}
|
||||
|
||||
{% block title %}
|
||||
<title>PDNS Settings - {{ SITE_NAME }}</title>
|
||||
{% endblock %} {% block dashboard_stat %}
|
||||
<!-- Content Header (Page header) -->
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
Settings <small>PowerDNS-Admin settings</small>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}"><i class="fa fa-dashboard"></i> Home</a></li>
|
||||
<li><a href="#">Setting</a></li>
|
||||
<li class="active">PDNS</li>
|
||||
</ol>
|
||||
</section>
|
||||
<title>
|
||||
PDNS Settings - {{ SITE_NAME }}
|
||||
</title>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<section class="content">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">PDNS Settings</h3>
|
||||
</div>
|
||||
<!-- /.box-header -->
|
||||
<!-- form start -->
|
||||
<form role="form" method="post" data-toggle="validator">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="box-body">
|
||||
{% if not SETTING.get('pdns_api_url') or not SETTING.get('pdns_api_key') or not SETTING.get('pdns_version') %}
|
||||
<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>
|
||||
Please complete your PowerDNS API configuration before continuing
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="form-group has-feedback">
|
||||
<label class="control-label" for="pdns_api_url">PDNS API URL</label>
|
||||
<input type="url" class="form-control" placeholder="PowerDNS API url" name="pdns_api_url"
|
||||
data-error="Please input a valid PowerDNS API URL" required value="{{ pdns_api_url }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group has-feedback">
|
||||
<label class="control-label" for="pdns_api_key">PDNS API KEY</label>
|
||||
<input type="password" class="form-control" placeholder="PowerDNS API key"
|
||||
name="pdns_api_key" data-error="Please input a valid PowerDNS API key" required
|
||||
value="{{ pdns_api_key }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group has-feedback">
|
||||
<label class="control-label" for="pdns_version">PDNS VERSION</label>
|
||||
<input type="text" class="form-control" placeholder="PowerDNS version" name="pdns_version"
|
||||
data-error="Please input PowerDNS version" required value="{{ pdns_version }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="submit" class="btn btn-flat btn-primary">Update</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% block dashboard_stat %}
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
Settings
|
||||
<small>PowerDNS Authoritative Server</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Help</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<dl class="dl-horizontal">
|
||||
<p>You must configure the API connection information before PowerDNS-Admin can query your
|
||||
PowerDNS data. Following fields are required:</p>
|
||||
<dt>PDNS API URL</dt>
|
||||
<dd>Your PowerDNS API URL (eg. http://127.0.0.1:8081/).</dd>
|
||||
<dt>PDNS API KEY</dt>
|
||||
<dd>Your PowerDNS API key.</dd>
|
||||
<dt>PDNS VERSION</dt>
|
||||
<dd>Your PowerDNS version number (eg. 4.1.1).</dd>
|
||||
</dl>
|
||||
<p>Find more details at <a
|
||||
href="https://doc.powerdns.com/md/httpapi/README/">https://doc.powerdns.com/md/httpapi/README/</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item active">Settings - PowerDNS Authoritative Server</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<div class="card shadow card-outline card-secondary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">PDNS Settings</h3>
|
||||
</div>
|
||||
<form role="form" method="post" data-toggle="validator">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="card-body">
|
||||
{% if not SETTING.get('pdns_api_url') or not SETTING.get('pdns_api_key') or not SETTING.get('pdns_version') %}
|
||||
<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>
|
||||
Please complete your PowerDNS API configuration before continuing
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="form-group has-feedback">
|
||||
<label class="control-label" for="pdns_api_url">PowerDNS API URL</label>
|
||||
<input type="url" class="form-control" placeholder="PowerDNS API URL" name="pdns_api_url"
|
||||
data-error="Please input a valid PowerDNS API URL" required value="{{ pdns_api_url }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group has-feedback">
|
||||
<label class="control-label" for="pdns_api_key">PowerDNS API Key</label>
|
||||
<input type="password" class="form-control" placeholder="PowerDNS API Key"
|
||||
name="pdns_api_key" data-error="Please input a valid PowerDNS API key" required
|
||||
value="{{ pdns_api_key }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group has-feedback">
|
||||
<label class="control-label" for="pdns_version">PowerDNS Version</label>
|
||||
<input type="text" class="form-control" placeholder="PowerDNS Version" name="pdns_version"
|
||||
data-error="Please input PowerDNS version" required value="{{ pdns_version }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<button type="submit" class="btn btn-primary float-right">
|
||||
<i class="fa-solid fa-save"></i> Save
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<div class="card shadow card-outline card-secondary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Help</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<dl class="dl-horizontal">
|
||||
<p>You must configure the API connection information before PowerDNS-Admin can query your
|
||||
PowerDNS data. Following fields are required:</p>
|
||||
<dt>PowerDNS API URL</dt>
|
||||
<dd>Your PowerDNS API URL (eg. http://127.0.0.1:8081/).</dd>
|
||||
<dt>PowerDNS API Key</dt>
|
||||
<dd>Your PowerDNS API key.</dd>
|
||||
<dt>PowerDNS Version</dt>
|
||||
<dd>Your PowerDNS version number (eg. 4.7.0).</dd>
|
||||
</dl>
|
||||
<p>Find more details at
|
||||
<a href="https://doc.powerdns.com/md/httpapi/README/">https://doc.powerdns.com/md/httpapi/README/</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
{% block extrascripts %}
|
||||
{% assets "js_validation" -%}
|
||||
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
||||
{%- endassets %}
|
||||
{% assets "js_validation" -%}
|
||||
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
||||
{%- endassets %}
|
||||
{% endblock %}
|
||||
|
@ -1,83 +1,101 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% set active_page = "admin_settings" %}
|
||||
|
||||
{% block title %}
|
||||
<title>DNS Records Settings - {{ SITE_NAME }}</title>
|
||||
{% endblock %} {% block dashboard_stat %}
|
||||
<!-- Content Header (Page header) -->
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
Settings <small>PowerDNS-Admin settings</small>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}"><i class="fa fa-dashboard"></i> Home</a></li>
|
||||
<li><a href="#">Setting</a></li>
|
||||
<li class="active">Records</li>
|
||||
</ol>
|
||||
</section>
|
||||
<title>
|
||||
DNS Records Settings - {{ SITE_NAME }}
|
||||
</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block dashboard_stat %}
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
Settings
|
||||
<small>Records</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item active">Settings - Records </li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-5">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">DNS record Settings</h3>
|
||||
</div>
|
||||
<!-- /.box-header -->
|
||||
<!-- form start -->
|
||||
<form role="form" method="post">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" name="create" value="{{ create }}">
|
||||
<div class="box-body">
|
||||
<table class="table table-bordered">
|
||||
<tr>
|
||||
<th style="width: 10px">#</th>
|
||||
<th style="width: 40px">Record</th>
|
||||
<th>Forward Zone</th>
|
||||
<th>Reverse Zone</th>
|
||||
</tr>
|
||||
{% for record in f_records %}
|
||||
<tr>
|
||||
<td>{{ loop.index }}</td>
|
||||
<td>{{ record }}</td>
|
||||
<td>
|
||||
<input type="checkbox" id="fr_{{ record|lower }}" name="fr_{{ record|lower }}"
|
||||
class="checkbox" {% if f_records[record] %}checked{% endif %}>
|
||||
</td>
|
||||
<td>
|
||||
<input type="checkbox" id="rr_{{ record|lower }}" name="rr_{{ record|lower }}"
|
||||
class="checkbox" {% if r_records[record] %}checked{% endif %}>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="submit" class="btn btn-flat btn-primary">Update</button>
|
||||
</div>
|
||||
</form>
|
||||
<div class="col-5">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">DNS record Settings</h3>
|
||||
</div>
|
||||
<form role="form" method="post">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" name="create" value="{{ create }}">
|
||||
<div class="card-body">
|
||||
<table class="table table-bordered">
|
||||
<tr>
|
||||
<th style="width: 10px">#</th>
|
||||
<th style="width: 40px">Record</th>
|
||||
<th>Forward Zone</th>
|
||||
<th>Reverse Zone</th>
|
||||
</tr>
|
||||
{% for record in f_records %}
|
||||
<tr>
|
||||
<td>{{ loop.index }}</td>
|
||||
<td>{{ record }}</td>
|
||||
<td>
|
||||
<input type="checkbox" id="fr_{{ record|lower }}" name="fr_{{ record|lower }}"
|
||||
class="checkbox" {% if f_records[record] %}checked{% endif %}>
|
||||
</td>
|
||||
<td>
|
||||
<input type="checkbox" id="rr_{{ record|lower }}" name="rr_{{ record|lower }}"
|
||||
class="checkbox" {% if r_records[record] %}checked{% endif %}>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Help</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<p>Select record types you allow user to edit in the forward zone and reverse zone. Take a look at
|
||||
<a href="https://doc.powerdns.com/authoritative/appendices/types.html">PowerDNS docs</a> for
|
||||
full list of supported record types.</p>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<button type="submit" class="btn btn-primary float-right">
|
||||
<i class="fa-solid fa-save"></i> Save
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-7">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Help</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Select record types you allow user to edit in the forward zone and reverse zone. Take a look at
|
||||
<a href="https://doc.powerdns.com/authoritative/appendices/types.html">PowerDNS docs</a> for
|
||||
full list of supported record types.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
{% block extrascripts %}
|
||||
<script>
|
||||
<script>
|
||||
$('.checkbox').iCheck({
|
||||
checkboxClass: 'icheckbox_square-blue',
|
||||
increaseArea: '20%'
|
||||
checkboxClass: 'icheckbox_square-blue',
|
||||
increaseArea: '20%'
|
||||
})
|
||||
</script>
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@ -1,38 +1,62 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="en" class>
|
||||
<head>
|
||||
{% block head %}
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<link rel="icon" href="{{ url_for('static', filename='img/favicon.png') }}">
|
||||
{% block title %}<title>{{ SITE_NAME }}</title>{% endblock %}
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='assets/css/style.css') }}">
|
||||
<!-- Get Google Fonts we like -->
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='assets/css/source_sans_pro.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='assets/css/roboto_mono.css') }}">
|
||||
<!-- Tell the browser to be responsive to screen width -->
|
||||
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
|
||||
<!-- Tell Safari to not recognise telephone numbers -->
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
{% assets "css_main" -%}
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<link rel="icon" href="{{ url_for('static', filename='img/favicon.png') }}">
|
||||
{% block title %}
|
||||
<title>
|
||||
{{ SITE_NAME }}
|
||||
</title>
|
||||
{% endblock %}
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='assets/css/style.css') }}">
|
||||
<!-- Get Google Fonts we like -->
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='assets/css/source_sans_pro.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='assets/css/roboto_mono.css') }}">
|
||||
<!-- Tell the browser to be responsive to screen width -->
|
||||
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
|
||||
<!-- Tell Safari to not recognise telephone numbers -->
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
{% assets "css_main" -%}
|
||||
<link rel="stylesheet" href="{{ ASSET_URL }}">
|
||||
{%- endassets %}
|
||||
{% if SETTING.get('custom_css') %}
|
||||
<link rel="stylesheet" href="/static/custom/{{ SETTING.get('custom_css') }}">
|
||||
{% endif %}
|
||||
{%- endassets %}
|
||||
{% if SETTING.get('custom_css') %}
|
||||
<link rel="stylesheet" href="/static/custom/{{ SETTING.get('custom_css') }}">
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body class="hold-transition skin-blue sidebar-mini {% if not SETTING.get('fullscreen_layout') %}layout-boxed{% endif %}">
|
||||
{% set user_image_url = url_for('user.image', username=current_user.username) %}
|
||||
<div class="wrapper">
|
||||
|
||||
<body class="hold-transition sidebar-mini {% if not SETTING.get('fullscreen_layout') %}layout-boxed{% endif %}">
|
||||
{% set user_image_url = url_for('user.image', username=current_user.username) %}
|
||||
<div class="wrapper">
|
||||
{% block pageheader %}
|
||||
<header class="main-header">
|
||||
<nav class="main-header navbar navbar-expand navbar-white navbar-light">
|
||||
<!-- Header Navbar: style can be found in header.less -->
|
||||
<!-- Sidebar toggle button-->
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-widget="pushmenu" href="#" role="button">
|
||||
<i class="fa-solid fa-bars"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="navbar-nav ml-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-widget="fullscreen" href="#" role="button">
|
||||
<i class="fa-solid fa-expand-arrows-alt"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
{% endblock %}
|
||||
<!-- Left side column. contains the logo and sidebar -->
|
||||
<aside class="main-sidebar sidebar-dark-primary">
|
||||
<!-- Logo -->
|
||||
<a href="{{ url_for('index.index') }}" class="logo">
|
||||
<!-- mini logo for sidebar mini 50x50 pixels -->
|
||||
<span class="logo-mini"><b>PD</b>A</span>
|
||||
<!-- logo for regular state and mobile devices -->
|
||||
<span class="logo-lg">
|
||||
<a href="{{ url_for('index.index') }}" class="brand-link">
|
||||
<img src="{{ url_for('static', filename='img/favicon.png') }}" alt="PowerDNS-Admin FavIcon" class="brand-image img-circle elevation-3" style="opacity: .8">
|
||||
<span class="brand-text font-weight-light">
|
||||
{% if SETTING.get('site_name') %}
|
||||
<b>{{ SITE_NAME }}</b>
|
||||
{% else %}
|
||||
@ -40,128 +64,147 @@
|
||||
{% endif %}
|
||||
</span>
|
||||
</a>
|
||||
<!-- Header Navbar: style can be found in header.less -->
|
||||
<nav class="navbar navbar-static-top">
|
||||
<!-- Sidebar toggle button-->
|
||||
<a href="#" class="sidebar-toggle" data-toggle="push-menu" role="button">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
</a>
|
||||
|
||||
<div class="navbar-custom-menu">
|
||||
{% if current_user.id is defined %}
|
||||
<ul class="nav navbar-nav">
|
||||
<!-- User Account: style can be found in dropdown.less -->
|
||||
<li class="dropdown user user-menu">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
<img src="{{ user_image_url }}" class="user-image" alt="User Image"/>
|
||||
<span class="hidden-xs">
|
||||
{{ current_user.firstname }}
|
||||
</span>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li class="user-header">
|
||||
<img src="{{ user_image_url }}" class="img-circle" alt="User Image"/>
|
||||
<p>
|
||||
{{ current_user.firstname }} {{ current_user.lastname }}
|
||||
<small>{{ current_user.role.name }}</small>
|
||||
</p>
|
||||
</li>
|
||||
|
||||
<!-- Menu Footer-->
|
||||
<li class="user-footer">
|
||||
<div class="pull-left">
|
||||
<a href="{{ url_for('user.profile') }}" class="btn btn-flat btn-primary">My Profile</a>
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
<a href="{{ url_for('index.logout') }}" class="btn btn-flat btn-warning">Log out</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
{% endblock %}
|
||||
<!-- Left side column. contains the logo and sidebar -->
|
||||
<aside class="main-sidebar">
|
||||
<!-- sidebar: style can be found in sidebar.less -->
|
||||
<section class="sidebar">
|
||||
<div class="sidebar">
|
||||
{% if current_user.id is defined %}
|
||||
<div class="user-panel">
|
||||
<div class="pull-left image">
|
||||
<img src="{{ user_image_url }}" class="img-circle" alt="User Image"/>
|
||||
<div class="user-panel mt-3 pb-3 mb-3 d-flex">
|
||||
<div class="image">
|
||||
<img src="{{ user_image_url }}" class="img-circle elevation-2" alt="User Image">
|
||||
</div>
|
||||
<div class="pull-left info">
|
||||
<p>{{ current_user.firstname }} {{ current_user.lastname }}</p>
|
||||
<a href="#"><i class="fa fa-circle text-success"></i> Logged In</a>
|
||||
<div class="info">
|
||||
<a href="{{ url_for('user.profile') }}" class="d-block">{{ current_user.firstname }} {{ current_user.lastname }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- sidebar menu: : style can be found in sidebar.less -->
|
||||
<ul class="sidebar-menu" data-widget="tree">
|
||||
<li class="header">USER ACTIONS</li>
|
||||
<li class="{{ 'active' if active_page == 'dashboard' else '' }}">
|
||||
<a href="{{ url_for('dashboard.dashboard') }}"><i class="fa fa-dashboard"></i> <span>Dashboard</span></a>
|
||||
<ul class="nav nav-pills nav-sidebar flex-column" data-widget="treeview" role="menu">
|
||||
<li class="{{ 'nav-item active' if active_page == 'user_profile' else 'nav-item' }}">
|
||||
<a href="{{ url_for('user.profile') }}" class="nav-link">
|
||||
<i class="nav-icon fa-solid fa-user"></i>
|
||||
<p>Profile</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('index.logout') }}" class="nav-link">
|
||||
<i class="nav-icon fa-solid fa-sign-out-alt"></i>
|
||||
<p>Logout</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-header">Zone Management</li>
|
||||
<li class="{{ 'nav-item active' if active_page == 'nav-item dashboard' else 'nav-item' }}">
|
||||
<a href="{{ url_for('dashboard.dashboard') }}" class="nav-link">
|
||||
<i class="nav-icon fa-solid fa-tachometer-alt"></i>
|
||||
<p>Dashboard</p>
|
||||
</a>
|
||||
</li>
|
||||
{% if SETTING.get('allow_user_create_domain') or current_user.role.name in ['Administrator', 'Operator'] %}
|
||||
<li class="{{ 'active' if active_page == 'new_domain' else '' }}">
|
||||
<a href="{{ url_for('domain.add') }}"><i class="fa fa-plus"></i> <span>New Domain</span></a>
|
||||
<li class="{{ 'nav-item active' if active_page == 'nav-item new_domain' else 'nav-item' }}">
|
||||
<a href="{{ url_for('domain.add') }}" class="nav-link">
|
||||
<i class="nav-icon fa-solid fa-plus"></i>
|
||||
<p>New Domain</p>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if SETTING.get('allow_user_remove_domain') or current_user.role.name in ['Administrator', 'Operator'] %}
|
||||
<li class="{{ 'active' if active_page == 'remove_domain' else '' }}">
|
||||
<a href="{{ url_for('domain.remove') }}"><i class="fa fa-trash-o"></i> <span>Remove Domain</span></a>
|
||||
<li class="{{ 'nav-item active' if active_page == 'remove_domain' else 'nav-item' }}">
|
||||
<a href="{{ url_for('domain.remove') }}" class="nav-link">
|
||||
<i class="nav-icon fa-solid fa-trash-alt"></i>
|
||||
<p>Remove Domain</p>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if current_user.role.name in ['Administrator', 'Operator'] %}
|
||||
<li class="header">ADMINISTRATION</li>
|
||||
<li class="{{ 'active' if active_page == 'admin_console' else '' }}">
|
||||
<a href="{{ url_for('admin.pdns_stats') }}"><i class="fa fa-info-circle"></i> <span>PDNS</span></a>
|
||||
</li>
|
||||
<li class="{{ 'active' if active_page == 'admin_global_search' else '' }}">
|
||||
<a href="{{ url_for('admin.global_search') }}"><i class="fa fa-search"></i> <span>Global Search</span></a>
|
||||
</li>
|
||||
<li class="{{ 'active' if active_page == 'admin_history' else '' }}">
|
||||
<a href="{{ url_for('admin.history') }}"><i class="fa fa-calendar"></i> <span>History</span></a>
|
||||
</li>
|
||||
<li class="{{ 'active' if active_page == 'admin_domain_template' else '' }}">
|
||||
<a href="{{ url_for('admin.templates') }}"><i class="fa fa-clone"></i> <span>Domain Templates</span></a>
|
||||
</li>
|
||||
<li class="{{ 'active' if active_page == 'admin_accounts' else '' }}">
|
||||
<a href="{{ url_for('admin.manage_account') }}"><i class="fa fa-industry"></i> <span>Accounts</span></a>
|
||||
</li>
|
||||
<li class="{{ 'active' if active_page == 'admin_users' else '' }}">
|
||||
<a href="{{ url_for('admin.manage_user') }}"><i class="fa fa-users"></i> <span>Users</span></a>
|
||||
</li>
|
||||
<li class="{{ 'active' if active_page == 'admin_keys' else '' }}">
|
||||
<a href="{{ url_for('admin.manage_keys') }}"><i class="fa fa-key"></i> <span>API Keys</span></a>
|
||||
</li>
|
||||
<li class="{{ 'treeview active' if active_page == 'admin_settings' else 'treeview' }}">
|
||||
<a href="#">
|
||||
<i class="fa fa-cog"></i> <span>Settings</span>
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-left pull-right"></i>
|
||||
</span>
|
||||
<li class="nav-header">Administration</li>
|
||||
<li class="{{ 'nav-item active' if active_page == 'admin_console' else 'nav-item' }}">
|
||||
<a href="{{ url_for('admin.pdns_stats') }}" class="nav-link">
|
||||
<i class="nav-icon fa-solid fa-info-circle"></i>
|
||||
<p>PowerDNS Info</p>
|
||||
</a>
|
||||
<ul class="treeview-menu" {% if active_page == 'admin_settings' %}style="display: block;"{% endif %}>
|
||||
<li><a href="{{ url_for('admin.setting_basic') }}"><i class="fa fa-circle-o"></i></i> <span>Basic</span></a></li>
|
||||
<li><a href="{{ url_for('admin.setting_records') }}"><i class="fa fa-circle-o"></i> <span>Records</span></a></li>
|
||||
</li>
|
||||
<li class="{{ 'nav-item active' if active_page == 'admin_global_search' else 'nav-item' }}">
|
||||
<a href="{{ url_for('admin.global_search') }}" class="nav-link">
|
||||
<i class="nav-icon fa-solid fa-search"></i>
|
||||
<p>Global Search</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="{{ 'nav-item active' if active_page == 'admin_history' else 'nav-item' }}">
|
||||
<a href="{{ url_for('admin.history') }}" class="nav-link">
|
||||
<i class="nav-icon fa-solid fa-calendar-alt"></i>
|
||||
<p>History</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="{{ 'nav-item active' if active_page == 'admin_domain_template' else 'nav-item' }}">
|
||||
<a href="{{ url_for('admin.templates') }}" class="nav-link">
|
||||
<i class="nav-icon fa-solid fa-clone"></i>
|
||||
<p>Domain Templates</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="{{ 'nav-item active' if active_page == 'admin_accounts' else 'nav-item' }}">
|
||||
<a href="{{ url_for('admin.manage_account') }}" class="nav-link">
|
||||
<i class="nav-icon fa-solid fa-industry"></i>
|
||||
<p>Accounts</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="{{ 'nav-item active' if active_page == 'admin_users' else 'nav-item' }}">
|
||||
<a href="{{ url_for('admin.manage_user') }}" class="nav-link">
|
||||
<i class="nav-icon fa-solid fa-users"></i>
|
||||
<p>Users</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="{{ 'nav-item active' if active_page == 'admin_keys' else 'nav-item' }}">
|
||||
<a href="{{ url_for('admin.manage_keys') }}" class="nav-link">
|
||||
<i class="nav-icon fa-solid fa-key"></i>
|
||||
<p>API Keys</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="{{ 'nav-item active' if active_page == 'admin_settings' else 'nav-item' }}">
|
||||
<a href="#" class="nav-link">
|
||||
<i class="nav-icon fa-solid fa-cog"></i>
|
||||
<p>
|
||||
Settings
|
||||
<i class="right fa-solid fa-angle-left"></i>
|
||||
</p>
|
||||
</a>
|
||||
<ul class="nav nav-treeview" {% if active_page == 'admin_settings' %}style="display: block;"{% endif %}>
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('admin.setting_basic') }}" class="nav-link">
|
||||
<i class="nav-icon fa-solid fa-circle"></i>
|
||||
<p>Basic</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('admin.setting_records') }}" class="nav-link">
|
||||
<i class="nav-icon fa-solid fa-circle"></i>
|
||||
<p>Records</p>
|
||||
</a>
|
||||
</li>
|
||||
{% if current_user.role.name == 'Administrator' %}
|
||||
<li><a href="{{ url_for('admin.setting_pdns') }}"><i class="fa fa-circle-o"></i> <span>PDNS</a></li>
|
||||
<li><a href="{{ url_for('admin.setting_authentication') }}"><i class="fa fa-circle-o"></i> <span>Authentication</span></a></li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('admin.setting_pdns') }}" class="nav-link">
|
||||
<i class="nav-icon fa-solid fa-circle"></i>
|
||||
<p>PowerDNS Connection</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('admin.setting_authentication') }}" class="nav-link">
|
||||
<i class="nav-icon fa-solid fa-circle"></i>
|
||||
<p>Authentication</p>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</li>
|
||||
{% elif SETTING.get('allow_user_view_history') %}
|
||||
<li class="header">ADMINISTRATION</li>
|
||||
<li class="{{ 'active' if active_page == 'admin_history' else '' }}">
|
||||
<a href="{{ url_for('admin.history') }}"><i class="fa fa-calendar"></i> <span>History</span></a>
|
||||
<li class="nav-header">Administration</li>
|
||||
<li class="{{ 'nav-item active' if active_page == 'admin_history' else 'nav-item' }}">
|
||||
<a href="{{ url_for('admin.history') }}" class="nav-link">
|
||||
<i class="nav-icon fa-solid fa-calendar-alt"></i>
|
||||
<p>History</p>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</section>
|
||||
</div>
|
||||
<!-- /.sidebar -->
|
||||
</aside>
|
||||
|
||||
@ -169,16 +212,24 @@
|
||||
<div class="content-wrapper">
|
||||
{% block dashboard_stat %}
|
||||
<!-- Content Header (Page header) -->
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
Dashboard
|
||||
<small>Control panel</small>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}"><i class="fa fa-dashboard"></i> Home</a></li>
|
||||
<li class="active">Dashboard</li>
|
||||
</ol>
|
||||
</section>
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
Dashboard
|
||||
<small>Control panel</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Home</a></li>
|
||||
<li class="breadcrumb-item active">Dashboard</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
@ -312,49 +363,43 @@
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"
|
||||
aria-label="Close">
|
||||
<h4 class="modal-title">Error</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title">Error</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-flat btn-default pull-right"
|
||||
data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-secondary float-right" data-dismiss="modal">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
<!-- /.modal -->
|
||||
<div class="modal fade modal-success" id="modal_success">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"
|
||||
aria-label="Close">
|
||||
<h4 class="modal-title">Success</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title">Success</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-flat btn-default pull-right"
|
||||
data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-secondary float-right" data-dismiss="modal">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
<!-- /.modal -->
|
||||
<!-- /.session-warning-modal -->
|
||||
<div class="modal fade modal-warning" data-backdrop="static" id="modal_session_warning">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
@ -367,19 +412,18 @@
|
||||
<p>To coninue your ssession, select <strong>Stay Signed In</strong></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-flat btn-danger pull-left button_stay_signed_in"
|
||||
data-dismiss="modal">Stay Signed In</button>
|
||||
<button type="button" class="btn btn-flat btn-default pull-right button_sign_out"
|
||||
data-dismiss="modal">Sign Out</button>
|
||||
<button type="button" class="btn btn-success float-right button_stay_signed_in" data-dismiss="modal">
|
||||
Stay Signed In
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger float-left button_sign_out" data-dismiss="modal">
|
||||
Sign Out
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.session-warning-modal-content -->
|
||||
</div>
|
||||
<!-- /.session-warning-modal-dialog -->
|
||||
</div>
|
||||
<!-- /.session-warning-modal -->
|
||||
{% endblock %}
|
||||
{% block modals %}
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
@ -1,191 +1,217 @@
|
||||
{% extends "base.html" %}
|
||||
{% set active_page = "dashboard" %}
|
||||
{% block title %}<title>Dashboard - {{ SITE_NAME }}</title>{% endblock %}
|
||||
|
||||
{% set active_page = "dashboard" %}
|
||||
|
||||
{% block title %}
|
||||
<title>
|
||||
Dashboard - {{ SITE_NAME }}
|
||||
</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block dashboard_stat %}
|
||||
<!-- Content Header (Page header) -->
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
Dashboard
|
||||
<small>Info</small>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}"><i class="fa fa-dashboard"></i> Home</a></li>
|
||||
<li class="active">Dashboard</li>
|
||||
</ol>
|
||||
</section>
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
Dashboard
|
||||
<small>Info</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% import 'applied_change_macro.html' as applied_change_macro %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Main content -->
|
||||
<section class="content">
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
{% if current_user.role.name in ['Administrator', 'Operator'] or SETTING.get('allow_user_view_history') %}
|
||||
<div class="row">
|
||||
<div class="col-xs-3">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<h3 class="box-title">Statistics</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<!-- small box -->
|
||||
<div class="small-box bg-aqua">
|
||||
<div class="inner">
|
||||
<h3>{{ domain_count }}</h3>
|
||||
<p>{% if domain_count > 1 %}Domains{% else %}Domain{% endif %}</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="fa fa-book"></i>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-3">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Statistics</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<!-- small box -->
|
||||
<div class="small-box bg-info">
|
||||
<div class="inner">
|
||||
<h3>{{ domain_count }}</h3>
|
||||
<p>{% if domain_count > 1 %}Domains{% else %}Domain{% endif %}</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="fa-solid fa-book"></i>
|
||||
</div>
|
||||
</div>
|
||||
{% if current_user.role.name in ['Administrator', 'Operator'] %}
|
||||
<div class="col-lg-6">
|
||||
</div>
|
||||
{% if current_user.role.name in ['Administrator', 'Operator'] %}
|
||||
<div class="col-6">
|
||||
<a href="{{ url_for('admin.manage_user') }}">
|
||||
<div class="small-box bg-green">
|
||||
<div class="inner">
|
||||
<h3>{{ user_num }}</h3>
|
||||
<p>{% if user_num > 1 %}Users{% else %}User{% endif %}</p>
|
||||
<div class="small-box bg-green">
|
||||
<div class="inner">
|
||||
<h3>{{ user_num }}</h3>
|
||||
<p>{% if user_num > 1 %}Users{% else %}User{% endif %}</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="fa-solid fa-users"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="fa fa-users"></i>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<a href="{{ url_for('admin.history') }}">
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<a href="{{ url_for('admin.history') }}">
|
||||
<div class="small-box bg-green">
|
||||
<div class="inner">
|
||||
<h3>{{ history_number }}</h3>
|
||||
<p>{% if history_number > 1 %}Histories{% else %}History{% endif %}</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="fa fa-calendar"></i>
|
||||
<i class="fa-solid fa-calendar"></i>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{% if current_user.role.name in ['Administrator', 'Operator'] %}
|
||||
<div class="col-lg-6">
|
||||
</a>
|
||||
</div>
|
||||
{% if current_user.role.name in ['Administrator', 'Operator'] %}
|
||||
<div class="col-6">
|
||||
<a href="{{ url_for('admin.pdns_stats') }}">
|
||||
<div class="small-box bg-green">
|
||||
<div class="inner">
|
||||
<h3><span style="font-size: 18px">{{ uptime|display_second_to_time }}</span></h3>
|
||||
<p>Uptime</p>
|
||||
<div class="small-box bg-green">
|
||||
<div class="inner">
|
||||
<h3><span style="font-size: 18px">{{ uptime|display_second_to_time }}</span></h3>
|
||||
<p>Uptime</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="fa-solid fa-clock"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="fa fa-clock-o"></i>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-9">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<h3 class="box-title">Recent History</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<table id="tbl_history" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
</div>
|
||||
<div class="col-9">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Recent History</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table id="tbl_history" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Changed By</th>
|
||||
<th>Content</th>
|
||||
<th>Time</th>
|
||||
<th>Detail</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for history in histories %}
|
||||
<tr class="odd">
|
||||
<td>{{ history.history.created_by }}</td>
|
||||
<td>{{ history.history.msg }}</td>
|
||||
<td>{{ history.history.created_on }}</td>
|
||||
<td width="6%">
|
||||
<div id="history-info-div-{{ loop.index0 }}" style="display: none;">
|
||||
{{ history.detailed_msg | safe }}
|
||||
{% if history.change_set %}
|
||||
<div class="content">
|
||||
<div id="change_index_definition"></div>
|
||||
{% call applied_change_macro.applied_change_template(history.change_set) %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<button type="button" class="btn btn-flat btn-primary history-info-button"
|
||||
{% if history.detailed_msg == "" and history.change_set is none %}
|
||||
style="visibility: hidden;"
|
||||
{% endif %} value="{{ loop.index0 }}">Info <i class="fa fa-info"></i>
|
||||
</button>
|
||||
</td>
|
||||
<td>{{ history.history.created_by }}</td>
|
||||
<td>{{ history.history.msg }}</td>
|
||||
<td>{{ history.history.created_on }}</td>
|
||||
<td width="6%">
|
||||
<div id="history-info-div-{{ loop.index0 }}" style="display: none;">
|
||||
{{ history.detailed_msg | safe }}
|
||||
{% if history.change_set %}
|
||||
<div class="content">
|
||||
<div id="change_index_definition"></div>
|
||||
{% call applied_change_macro.applied_change_template(history.change_set) %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-primary history-info-button"
|
||||
{% if history.detailed_msg == "" and history.change_set is none %}
|
||||
style="visibility: hidden;"
|
||||
{% endif %} value="{{ loop.index0 }}">
|
||||
<i class="fa-solid fa-info-circle"></i> Info
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="nav-tabs-custom">
|
||||
<ul class="nav nav-tabs card-header-tabs" id="custom-content-below-tab" role="tablist">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="#tab_{{custom_boxes.order[0]}}" data-toggle="pill" role="tab">
|
||||
Hosted Domains <b>{{custom_boxes.boxes[custom_boxes.order[0]][0]}}</b>
|
||||
</a>
|
||||
</li>
|
||||
{% for boxId in custom_boxes.order[1:] %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#tab_{{boxId}}" data-toggle="pill" role="tab">Hosted Domains <b>{{custom_boxes.boxes[boxId][0]}}</b></a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="tab-content">
|
||||
{% for boxId in custom_boxes.order %}
|
||||
<div class="tab-pane fade show" id='tab_{{boxId}}'>
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Hosted Domains <b>{{custom_boxes.boxes[boxId][0]}}</b></h3>
|
||||
{% if show_bg_domain_button %}
|
||||
<button type="button" class="btn btn-primary refresh-bg-button float-right">
|
||||
<i class="fa-solid fa-sync"></i>
|
||||
Sync Domains
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table id='tbl_domain_list_{{boxId}}' class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>DNSSEC</th>
|
||||
<th>Type</th>
|
||||
<th>Serial</th>
|
||||
<th>Primary</th>
|
||||
<th>Account</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!--SYBPATCH START-->
|
||||
<div class="nav-tabs-custom">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a href="#tab_{{custom_boxes.order[0]}}" data-toggle="tab">Hosted Domains <b>{{custom_boxes.boxes[custom_boxes.order[0]][0]}}</b></a></li>
|
||||
{% for boxId in custom_boxes.order[1:] %}
|
||||
<li><a href="#tab_{{boxId}}" data-toggle="tab">Hosted Domains <b>{{custom_boxes.boxes[boxId][0]}}</b></a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
{% for boxId in custom_boxes.order %}
|
||||
<div class="tab-pane" id='tab_{{boxId}}'>
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<h3 class="box-title">Hosted Domains <b>{{custom_boxes.boxes[boxId][0]}}</b></h3>{% if show_bg_domain_button %}<button type="button" class="btn btn-flat btn-primary refresh-bg-button pull-right"><i class="fa fa-refresh"></i> Sync domains </button>{% endif %}
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<table id='tbl_domain_list_{{boxId}}' class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>DNSSEC</th>
|
||||
<th>Type</th>
|
||||
<th>Serial</th>
|
||||
<th>Master</th>
|
||||
<th>Account</th>
|
||||
<th {% if current_user.role.name not in ['Administrator','Operator'] %}width="6%"{% else %}width="25%"{% endif %}>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div><!-- /.tab-content -->
|
||||
</div><!-- custom tabs -->
|
||||
<!--SYBPATCH END-->
|
||||
|
||||
</section>
|
||||
<!-- /.content -->
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
{% block extrascripts %}
|
||||
<script>
|
||||
//SYBPATCH START//
|
||||
@ -230,7 +256,7 @@
|
||||
"searching" : false,
|
||||
"ordering" : false,
|
||||
"info" : false,
|
||||
"autoWidth" : false,
|
||||
"autoWidth" : true,
|
||||
"columnDefs": [
|
||||
{
|
||||
"render": function ( data, type, row ) {
|
||||
@ -281,118 +307,112 @@
|
||||
});
|
||||
|
||||
{% if current_user.role.name in ['Administrator', 'Operator'] or not SETTING.get('dnssec_admins_only') %}
|
||||
$(document.body).on("click", ".button_dnssec", function() {
|
||||
$(document.body).on("click", ".button_dnssec", function() {
|
||||
var domain = $(this).prop('id');
|
||||
getdnssec($SCRIPT_ROOT + '/domain/' + domain + '/dnssec', domain);
|
||||
});
|
||||
});
|
||||
|
||||
$(document.body).on("click", ".button_dnssec_enable", function() {
|
||||
$(document.body).on("click", ".button_dnssec_enable", function() {
|
||||
var domain = $(this).prop('id');
|
||||
enable_dns_sec($SCRIPT_ROOT + '/domain/' + domain + '/dnssec/enable', '{{ csrf_token() }}');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
$(document.body).on("click", ".button_dnssec_disable", function() {
|
||||
$(document.body).on("click", ".button_dnssec_disable", function() {
|
||||
var domain = $(this).prop('id');
|
||||
enable_dns_sec($SCRIPT_ROOT + '/domain/' + domain + '/dnssec/disable', '{{ csrf_token() }}');
|
||||
|
||||
});
|
||||
});
|
||||
{% endif %}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block modals %}
|
||||
<div class="modal fade" id="modal_history_info">
|
||||
<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">History Details</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="modal-info-content"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-flat btn-default pull-right"
|
||||
data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">History Details</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="modal-info-content">
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary float-right" data-dismiss="modal">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade modal-primary" id="modal_template">
|
||||
<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">Clone to template</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-flat btn-default pull-left"
|
||||
id="button_close" data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-flat btn-primary" id="button_save">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
<!-- /.modal -->
|
||||
<div class="modal fade" id="modal_dnssec_info">
|
||||
<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">DNSSEC</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-flat btn-default pull-right"
|
||||
data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
<!-- /.modal -->
|
||||
<div class="modal fade" id="modal_bg_reload">
|
||||
<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">Sync domains from nameserver</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="overlay">
|
||||
<div id="modal_bg_reload_content"><i class="fa fa-refresh fa-spin"></i> Update in progress ..</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-flat btn-default pull-right"
|
||||
data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
<div class="modal fade modal-primary" id="modal_template">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Clone to template</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary float-right" id="button_close" data-dismiss="modal">
|
||||
Close
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary float-right" id="button_save">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="modal_dnssec_info">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">DNSSEC</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary float-right" data-dismiss="modal">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="modal_bg_reload">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Sync Domains from backend</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="modal_bg_reload_content">
|
||||
<i class="fa fa-refresh fa-spin"></i> Update in progress ..
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary float-right" data-dismiss="modal">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -4,9 +4,9 @@
|
||||
|
||||
{% macro dnssec(domain) %}
|
||||
{% if domain.dnssec %}
|
||||
<td><span style="cursor:pointer" class="label label-success button_dnssec" id="{{ domain.name }}"><i class="fa fa-lock"></i> Enabled</span></td>
|
||||
<td><span style="cursor:pointer" class="badge badge-success button_dnssec" id="{{ domain.name }}"><i class="fa-solid fa-lock"></i> Enabled</span></td>
|
||||
{% else %}
|
||||
<td><span style="cursor:pointer" class="label label-primary button_dnssec" id="{{ domain.name }}"><i class="fa fa-unlock-alt"></i> Disabled</span></td>
|
||||
<td><span style="cursor:pointer" class="badge badge-danger button_dnssec" id="{{ domain.name }}"><i class="fa-solid fa-lock-open"></i> Disabled</span></td>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
@ -29,31 +29,51 @@
|
||||
{% endmacro %}
|
||||
|
||||
{% macro actions(domain) %}
|
||||
{% if current_user.role.name in ['Administrator', 'Operator'] %}
|
||||
<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', 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.setting', domain_name=domain.name) }}'">
|
||||
Admin <i class="fa fa-cog"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-flat btn-primary" onclick="window.location.href='{{ url_for('domain.changelog', domain_name=domain.name) }}'">
|
||||
Changelog <i class="fa fa-history" aria-hidden="true"></i>
|
||||
</button>
|
||||
</td>
|
||||
{% else %}
|
||||
<td width="6%">
|
||||
<button type="button" class="btn btn-flat btn-success" onclick="window.location.href='{{ url_for('domain.domain', domain_name=domain.name) }}'">
|
||||
Manage <i class="fa fa-cog"></i>
|
||||
</button>
|
||||
{% if allow_user_view_history %}
|
||||
<button type="button" class="btn btn-flat btn-primary" onclick="window.location.href='{{ url_for('domain.changelog', domain_name=domain.name) }}'">
|
||||
Changelog <i class="fa fa-history" aria-hidden="true"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
<td width="6%">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-primary dropdown-toggle" type="button" id="dropdownMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="fa-solid fa-bars"></i> Actions
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownMenu">
|
||||
{% if current_user.role.name in ['Administrator', 'Operator'] %}
|
||||
<button class="dropdown-item btn-success button_template" id="{{ domain.name }}" type="button">
|
||||
<i class="fa-solid fa-clone"></i> Clone to Template
|
||||
</button>
|
||||
<button class="dropdown-item btn-success" type="button" onclick="window.location.href='{{ url_for('domain.domain', domain_name=domain.name) }}'">
|
||||
<i class="fa-solid fa-cog"></i> Manage Domain
|
||||
</button>
|
||||
<button class="dropdown-item btn-danger" type="button" onclick="window.location.href='{{ url_for('domain.setting', domain_name=domain.name) }}'">
|
||||
<i class="fa-solid fa-cog"></i> Admin Settings
|
||||
</button>
|
||||
<button class="dropdown-item btn-primary" type="button" onclick="window.location.href='{{ url_for('domain.changelog', domain_name=domain.name) }}'">
|
||||
<i class="fa-solid fa-history" aria-hidden="true"></i> Domain Changelog
|
||||
</button>
|
||||
<div class="dropdown-divider"></div>
|
||||
<button type="button"class="dropdown-item btn-secondary button_delete" onclick="window.location.href='{{ url_for('domain.remove') }}'">
|
||||
<font color="red">
|
||||
<i class="fa-solid fa-trash"></i> Remove Domain
|
||||
</font>
|
||||
</button>
|
||||
</div>
|
||||
{% else %}
|
||||
<button class="dropdown-item btn-success" type="button" onclick="window.location.href='{{ url_for('domain.domain', domain_name=domain.name) }}'">
|
||||
<i class="fa-solid fa-cog"></i> Manage Domain
|
||||
</button>
|
||||
{% if allow_user_view_history %}
|
||||
<button class="dropdown-item btn-primary" type="button" onclick="window.location.href='{{ url_for('domain.changelog', domain_name=domain.name) }}'">
|
||||
<i class="fa-solid fa-history" aria-hidden="true"></i> Domain Changelog
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if allow_user_remove_domain %}
|
||||
<div class="dropdown-divider"></div>
|
||||
<button type="button"class="dropdown-item btn-secondary button_delete" onclick="window.location.href='{{ url_for('domain.remove') }}'">
|
||||
<font color="red">
|
||||
<i class="fa-solid fa-trash"></i> Remove Domain
|
||||
</font>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
{% endmacro %}
|
||||
|
@ -1,133 +1,137 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}<title>{{ domain.name | pretty_domain_name }} - {{ SITE_NAME }}</title>{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
<title>
|
||||
{{ domain.name | pretty_domain_name }} - {{ SITE_NAME }}
|
||||
</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block dashboard_stat %}
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
Manage domain: <b>{{ domain.name | pretty_domain_name }}</b>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}"><i
|
||||
class="fa fa-dashboard"></i> Home</a></li>
|
||||
<li>Domain</li>
|
||||
<li class="active">{{ domain.name | pretty_domain_name }}</li>
|
||||
</ol>
|
||||
</section>
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
Domain: <b>{{ domain.name | pretty_domain_name }}</b>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item active">Domain: {{ domain.name | pretty_domain_name }}</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="box">
|
||||
<div class="box-body">
|
||||
{% if domain.type != 'Slave' %}
|
||||
<button type="button" class="btn btn-flat btn-primary pull-left button_add_record" id="{{ domain.name }}">
|
||||
Add Record <i class="fa fa-plus"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-flat btn-primary pull-right button_apply_changes" id="{{ domain.name }}" value="{{ domain.serial }}">
|
||||
Apply Changes <i class="fa fa-floppy-o"></i>
|
||||
</button>
|
||||
{% else %}
|
||||
<button type="button" class="btn btn-flat btn-primary pull-left button_update_from_master" id="{{ domain.name }}">
|
||||
Update from Master <i class="fa fa-download"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if current_user.role.name in ['Administrator', 'Operator'] %}
|
||||
<button type="button" style="position: relative; margin-left: 20px" class="btn btn-flat btn-primary pull-left btn-danger" onclick="window.location.href='{{ url_for('domain.setting', domain_name=domain.name) }}'">
|
||||
Admin <i class="fa fa-cog"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
{% if domain.type != 'Slave' %}
|
||||
<button type="button" style="position: relative; margin-left: 20px" class="btn btn-primary float-left button_add_record" id="{{ domain.name }}">
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
Add Record
|
||||
</button>
|
||||
<button type="button" style="position: relative; margin-left: 20px" class="btn btn-primary float-right button_apply_changes" id="{{ domain.name }}" value="{{ domain.serial }}">
|
||||
<i class="fa-solid fa-save"></i>
|
||||
Apply Changes
|
||||
</button>
|
||||
{% else %}
|
||||
<button type="button" style="position: relative; margin-left: 20px" class="btn btn-primary float-left button_update_from_primary" id="{{ domain.name }}">
|
||||
<i class="fa-solid fa-sync"></i>
|
||||
Update from Primary
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if current_user.role.name in ['Administrator', 'Operator'] %}
|
||||
<button type="button" style="position: relative; margin-left: 20px" class="btn btn-primary float-left btn-danger" onclick="window.location.href='{{ url_for('domain.setting', domain_name=domain.name) }}'">
|
||||
<i class="fa-solid fa-toolbox"></i>
|
||||
Admin
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if current_user.role.name in ['Administrator', 'Operator'] or SETTING.get('allow_user_view_history') %}
|
||||
<button type="button" style="position: relative; margin-left: 20px" class="btn btn-primary button_changelog" id="{{ domain.name }}">
|
||||
<i class="fa-solid fa-history" aria-hidden="true"></i>
|
||||
Changelog
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card-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>
|
||||
{% if domain.type != 'Slave' %}
|
||||
<th>Comment</th>
|
||||
<th>Edit</th>
|
||||
<th>Delete</th>
|
||||
{% if current_user.role.name in ['Administrator', 'Operator'] or SETTING.get('allow_user_view_history') %}
|
||||
<button type="button" style="position: relative; margin-left: 20px" class="btn btn-flat btn-primary button_changelog" id="{{ domain.name }}">
|
||||
Changelog <i class="fa fa-history" aria-hidden="true"></i>
|
||||
</i>
|
||||
</button>
|
||||
<th >Changelog</th>
|
||||
{% endif %}
|
||||
</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>Comment</th>
|
||||
<th>Edit</th>
|
||||
<th>Delete</th>
|
||||
{% if current_user.role.name in ['Administrator', 'Operator'] or SETTING.get('allow_user_view_history') %}
|
||||
<th >Changelog</th>
|
||||
{% endif %}
|
||||
<th>Invisible Sorting Column</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for record in records %}
|
||||
<tr class="odd row_record" id="{{ domain.name }}">
|
||||
<td>
|
||||
{{ (record.name,domain.name) | display_record_name | pretty_domain_name }}
|
||||
</td>
|
||||
<td>
|
||||
{{ record.type }}
|
||||
</td>
|
||||
<td>
|
||||
{{ record.status }}
|
||||
</td>
|
||||
<td>
|
||||
{{ record.ttl }}
|
||||
</td>
|
||||
<td>
|
||||
{{ record.data | pretty_domain_name }}
|
||||
</td>
|
||||
<td>
|
||||
{{ record.comment }}
|
||||
</td>
|
||||
{% if domain.type != 'Slave' %}
|
||||
<td width="6%">
|
||||
{% if record.is_allowed_edit() %}
|
||||
<button type="button" class="btn btn-flat btn-warning button_edit">Edit <i class="fa fa-edit"></i></button>
|
||||
{% else %}
|
||||
<button type="button" class="btn btn-flat btn-warning"> <i class="fa fa-exclamation-circle"></i></button>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td width="6%">
|
||||
{% if record.is_allowed_delete() %}
|
||||
<button type="button" class="btn btn-flat btn-danger button_delete">Delete <i class="fa fa-trash"></i></button>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% else %}
|
||||
<td width="6%">
|
||||
<button type="button" class="btn btn-flat btn-warning"> <i class="fa fa-exclamation-circle"></i> </button>
|
||||
</td>
|
||||
<td width="6%">
|
||||
<button type="button" class="btn btn-flat btn-warning"> <i class="fa fa-exclamation-circle"></i> </button>
|
||||
</td>
|
||||
{% endif %}
|
||||
{% if current_user.role.name in ['Administrator', 'Operator'] or SETTING.get('allow_user_view_history') %}
|
||||
<td width="6%">
|
||||
<button type="button" onclick="show_record_changelog('{{record.name}}','{{record.type}}',event)" class="btn btn-flat btn-primary">
|
||||
<i class="fa fa-history" aria-hidden="true"></i>
|
||||
</button>
|
||||
</td>
|
||||
{% endif %}
|
||||
<!-- hidden column that we can sort on -->
|
||||
<td>1</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
</div>
|
||||
<!-- /.box -->
|
||||
{% else %}
|
||||
<th>Invisible Sorting Column</th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for record in records %}
|
||||
<tr class="odd row_record" id="{{ domain.name }}">
|
||||
<td>{{ (record.name,domain.name) | display_record_name | pretty_domain_name }}</td>
|
||||
<td>{{ record.type }}</td>
|
||||
<td>{{ record.status }}</td>
|
||||
<td>{{ record.ttl }}</td>
|
||||
<td>{{ record.data | pretty_domain_name }}</td>
|
||||
{% if domain.type != 'Secondary' %}
|
||||
<td>{{ record.comment }}</td>
|
||||
<td width="6%">
|
||||
{% if record.is_allowed_edit() %}
|
||||
<button type="button" class="btn btn-warning button_edit">
|
||||
<i class="fa-solid fa-edit"></i>
|
||||
</button>
|
||||
{% else %}
|
||||
<button type="button" class="btn btn-warning">
|
||||
<i class="fa-solid fa-exclamation-circle"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td width="6%">
|
||||
{% if record.is_allowed_delete() %}
|
||||
<button type="button" class="btn btn-danger button_delete">
|
||||
<i class="fa-solid fa-trash"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% if current_user.role.name in ['Administrator', 'Operator'] or SETTING.get('allow_user_view_history') %}
|
||||
<td width="6%">
|
||||
<button type="button" onclick="show_record_changelog('{{record.name}}','{{record.type}}',event)" class="btn btn-primary">
|
||||
<i class="fa-solid fa-history" aria-hidden="true"></i>
|
||||
</button>
|
||||
</td>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<!-- hidden column that we can sort on -->
|
||||
<td>1</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
{% block extrascripts %}
|
||||
<script>
|
||||
// superglobals
|
||||
@ -169,10 +173,14 @@
|
||||
// hidden column so that we can add new records on top
|
||||
// regardless of whatever sorting is done. See orderFixed
|
||||
visible: false,
|
||||
{% if current_user.role.name in ['Administrator', 'Operator'] or SETTING.get('allow_user_view_history') %}
|
||||
targets: [ 9 ]
|
||||
{% if domain.type != 'Slave' %}
|
||||
{% if current_user.role.name in ['Administrator', 'Operator'] or SETTING.get('allow_user_view_history') %}
|
||||
targets: [ 9 ]
|
||||
{% else %}
|
||||
targets: [ 8 ]
|
||||
{% endif %}
|
||||
{% else %}
|
||||
targets: [ 8 ]
|
||||
targets: [ 5 ]
|
||||
{% endif %}
|
||||
},
|
||||
{
|
||||
@ -180,10 +188,14 @@
|
||||
targets: [ 4, 5 ]
|
||||
}
|
||||
],
|
||||
{% if current_user.role.name in ['Administrator', 'Operator'] or SETTING.get('allow_user_view_history') %}
|
||||
"orderFixed": [[9, 'asc']]
|
||||
{% if domain.type != 'Slave' %}
|
||||
{% if current_user.role.name in ['Administrator', 'Operator'] or SETTING.get('allow_user_view_history') %}
|
||||
"orderFixed": [[9, 'asc']]
|
||||
{% else %}
|
||||
"orderFixed": [[8, 'asc']]
|
||||
{% endif %}
|
||||
{% else %}
|
||||
"orderFixed": [[8, 'asc']]
|
||||
"orderFixed": [[5, 'asc']]
|
||||
{% endif %}
|
||||
});
|
||||
|
||||
@ -219,7 +231,8 @@
|
||||
});
|
||||
});
|
||||
// handle edit button and record click
|
||||
$(document.body).on("click", ".button_edit{% if quick_edit %}, .row_record{% endif %}", function(e) {
|
||||
{% if domain.type != 'Slave' %}
|
||||
$(document.body).on("click", ".button_edit{% if quick_edit %}, .row_record{% endif %}", function(e) {
|
||||
e.stopPropagation();
|
||||
if ($(this).is('tr')) {
|
||||
var nRow = $(this)[0];
|
||||
@ -246,7 +259,8 @@
|
||||
editRow(table, nRow);
|
||||
nEditing = nRow;
|
||||
}
|
||||
});
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
// handle apply changes button
|
||||
$(document.body).on("click",".button_apply_changes", function() {
|
||||
@ -284,9 +298,9 @@
|
||||
// add new row
|
||||
var default_type = records_allow_edit[0]
|
||||
{% if current_user.role.name in ['Administrator', 'Operator'] or SETTING.get('allow_user_view_history') %}
|
||||
var nRow = jQuery('#tbl_records').dataTable().fnAddData(['', default_type, 'Active', window.ttl_options[0][0], '', '', '', '', '', '0']);
|
||||
var nRow = jQuery('#tbl_records').dataTable().fnAddData(['', default_type, 'Active', window.ttl_options[0][0], '', '', '', '', '', '0']);
|
||||
{% else %}
|
||||
var nRow = jQuery('#tbl_records').dataTable().fnAddData(['', default_type, 'Active', window.ttl_options[0][0], '', '', '', '', '0']);
|
||||
var nRow = jQuery('#tbl_records').dataTable().fnAddData(['', default_type, 'Active', window.ttl_options[0][0], '', '', '', '', '0']);
|
||||
{% endif %}
|
||||
editRow($("#tbl_records").DataTable(), nRow);
|
||||
document.getElementById("edit-row-focus").focus();
|
||||
@ -317,8 +331,8 @@
|
||||
nNew = false;
|
||||
});
|
||||
|
||||
//handle update_from_master button
|
||||
$(document.body).on("click", ".button_update_from_master", function (e) {
|
||||
//handle update_from_primary button
|
||||
$(document.body).on("click", ".button_update_from_primary", function (e) {
|
||||
var domain = $(this).prop('id');
|
||||
applyChanges({'domain': domain, '_csrf_token': '{{ csrf_token() }}'}, $SCRIPT_ROOT + '/domain/' + domain + '/update', true);
|
||||
});
|
||||
@ -551,74 +565,84 @@
|
||||
}
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
window.onload = function() {
|
||||
document.getElementById("loading-spinner").style.display = "none";
|
||||
}
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block modals %}
|
||||
<div class="modal fade modal-warning" id="modal_delete">
|
||||
<div class="modal fade" 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" id="button_delete_cancel"
|
||||
data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-flat btn-danger" id="button_delete_confirm">Delete</button>
|
||||
</div>
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Confirmation</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
<div class="modal-body">
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary pull-left" id="button_delete_cancel" data-dismiss="modal">
|
||||
<i class="fa-solid fa-window-close"></i> Close
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger" id="button_delete_confirm">
|
||||
<i class="fa-solid fa-trash"></i> Delete
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
<div class="modal fade modal-primary" id="modal_apply_changes">
|
||||
</div>
|
||||
|
||||
<div class="modal fade" 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 class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Confirmation</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
<div class="modal-body">
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">
|
||||
<i class="fa-solid fa-window-close"></i> Close
|
||||
</button>
|
||||
<button type="button" class="btn btn-success" id="button_apply_confirm">
|
||||
<i class="fa-solid fa-save"></i> Apply Changes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
<div class="modal fade modal-primary" id="modal_custom_record">
|
||||
</div>
|
||||
|
||||
<div class="modal fade bg-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 class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Custom Record</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
<div class="modal-body">
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">
|
||||
<i class="fa-solid fa-window-close"></i> Close
|
||||
</button>
|
||||
<button type="button" class="btn btn-success" id="button_save">
|
||||
<i class="fa-solid fa-save"></i> Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -1,221 +1,239 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% set active_page = "new_domain" %}
|
||||
{% block title %}<title>Add Domain - {{ SITE_NAME }}</title>{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
<title>
|
||||
Add Domain - {{ SITE_NAME }}
|
||||
</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block dashboard_stat %}
|
||||
<!-- Content Header (Page header) -->
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
Domain
|
||||
<small>Create new</small>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}"><i class="fa fa-dashboard"></i>Home</a></li>
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}">Domain</a></li>
|
||||
<li class="active">Add Domain</li>
|
||||
</ol>
|
||||
</section>
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
Domain
|
||||
<small>New Domain</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item active">Domain - New Domain</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<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 domain</h3>
|
||||
<div class="col-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Create new domain</h3>
|
||||
</div>
|
||||
<form role="form" method="post" action="{{ url_for('domain.add') }}">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" name="domain_name" id="domain_name" placeholder="Enter a valid domain name (required)">
|
||||
</div>
|
||||
{% if domain_override_toggle == True %}
|
||||
<div class="form-group">
|
||||
<label>Domain Override Record</label>
|
||||
<input type="checkbox" id="domain_override" name="domain_override" class="checkbox">
|
||||
</div>
|
||||
<!-- /.box-header -->
|
||||
<!-- form start -->
|
||||
<form role="form" method="post" action="{{ url_for('domain.add') }}">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="box-body">
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" name="domain_name" id="domain_name"
|
||||
placeholder="Enter a valid domain name (required)">
|
||||
</div>
|
||||
{% if domain_override_toggle == True %}
|
||||
<div class="form-group">
|
||||
<label>Domain Override Record</label>
|
||||
<input type="checkbox" id="domain_override" name="domain_override"
|
||||
class="checkbox">
|
||||
</div>
|
||||
{% endif %}
|
||||
<select name="accountid" class="form-control" style="width:15em;">
|
||||
<option value="0">- No Account -</option>
|
||||
{% for account in accounts %}
|
||||
<option value="{{ account.id }}">{{ account.name }}</option>
|
||||
{% endfor %}
|
||||
</select><br />
|
||||
<div class="form-group">
|
||||
<label>Type</label>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" name="radio_type" id="radio_type_native" value="native" checked>
|
||||
Native
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<input type="radio" name="radio_type" id="radio_type_master" value="master"> Master
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<input type="radio" name="radio_type" id="radio_type_slave" value="slave">Slave
|
||||
</label>
|
||||
</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">
|
||||
<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 class="form-group">
|
||||
<label>SOA-EDIT-API</label>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" name="radio_type_soa_edit_api" id="radio_default"
|
||||
value="DEFAULT" checked> DEFAULT
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" name="radio_type_soa_edit_api" id="radio_increase"
|
||||
value="INCREASE"> INCREASE
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" name="radio_type_soa_edit_api" id="radio_epoch" value="EPOCH">
|
||||
EPOCH
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" name="radio_type_soa_edit_api" id="radio_off" value="OFF"> OFF
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
|
||||
<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('dashboard.dashboard') }}'">Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
<select name="accountid" class="form-control" style="width:15em;">
|
||||
<option value="0">- No Account -</option>
|
||||
{% for account in accounts %}
|
||||
<option value="{{ account.id }}">{{ account.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<br />
|
||||
<div class="form-group">
|
||||
<label>Type</label>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" name="radio_type" id="radio_type_native" value="native" checked>
|
||||
Native
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" name="radio_type" id="radio_type_primary" value="primary">
|
||||
Primary
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" name="radio_type" id="radio_type_secondary" value="secondary">
|
||||
Secondary
|
||||
</label>
|
||||
</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_primary_address_div">
|
||||
<input type="text" class="form-control" name="domain_primary_address" id="domain_primary_address" placeholder="Enter valid Primary Server IP addresses (separated by commas)">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>SOA-EDIT-API</label>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" name="radio_type_soa_edit_api" id="radio_default" value="DEFAULT" checked>
|
||||
DEFAULT
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" name="radio_type_soa_edit_api" id="radio_increase" value="INCREASE">
|
||||
INCREASE
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" name="radio_type_soa_edit_api" id="radio_epoch" value="EPOCH">
|
||||
EPOCH
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" name="radio_type_soa_edit_api" id="radio_off" value="OFF"> OFF
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</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 domain</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Domain name</dt>
|
||||
<dd>Enter your domain name in the format of name.tld (eg. powerdns-admin.com). You can also
|
||||
enter sub-domains to create a sub-root zone (eg. sub.powerdns-admin.com) in case you want to
|
||||
delegate sub-domain management to specific users.</dd>
|
||||
<dt>Type</dt>
|
||||
<dd>The type decides how the domain will be replicated across multiple DNS servers.
|
||||
<ul>
|
||||
<li>
|
||||
Native - PowerDNS will not perform any replication. Use this if you only have one
|
||||
PowerDNS server or you handle replication via your backend.
|
||||
</li>
|
||||
<li>
|
||||
Master - This PowerDNS server will serve as the master and will send zone transfers
|
||||
(AXFRs) to other servers configured as slaves.
|
||||
</li>
|
||||
<li>
|
||||
Slave - This PowerDNS server will serve as the slave and will request and receive
|
||||
zone transfers (AXFRs) from other servers configured as masters.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dt>SOA-EDIT-API</dt>
|
||||
<dd>The SOA-EDIT-API setting defines how the SOA serial number will be updated after a change is
|
||||
made to the domain.
|
||||
<ul>
|
||||
<li>
|
||||
DEFAULT - Generate a soa serial of YYYYMMDD01. If the current serial is lower than
|
||||
the generated serial, use the generated serial. If the current serial is higher or
|
||||
equal to the generated serial, increase the current serial by 1.
|
||||
</li>
|
||||
<li>
|
||||
INCREASE - Increase the current serial by 1.
|
||||
</li>
|
||||
<li>
|
||||
EPOCH - Change the serial to the number of seconds since the EPOCH, aka unixtime.
|
||||
</li>
|
||||
<li>
|
||||
OFF - Disable automatic updates of the SOA serial.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
<p>Find more details at <a href="https://docs.powerdns.com/md/">https://docs.powerdns.com/md/</a>
|
||||
</p>
|
||||
</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 %}
|
||||
{% block modals %}
|
||||
<script>
|
||||
{% if domain_override_message %}
|
||||
$(document.body).ready(function () {
|
||||
var modal = $("#modal_warning");
|
||||
var info = "{{ domain_override_message }}";
|
||||
modal.find('.modal-body p').text(info);
|
||||
modal.modal('show');
|
||||
});
|
||||
{% endif %}
|
||||
</script>
|
||||
</div>
|
||||
<div class="modal fade" id="modal_warning">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content modal-sm">
|
||||
<div class="modal-header alert-danger">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close" id="button_close_warn_modal">
|
||||
<span aria-hidden="true">×</span>
|
||||
<div class="card-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="window.location.href='{{ url_for('dashboard.dashboard') }}'">
|
||||
<i class="fa-solid fa-window-close"></i> Cancel
|
||||
</button>
|
||||
<h4 class="modal-title">WARNING</h4>
|
||||
<button type="submit" class="btn btn-primary float-right">
|
||||
<i class="fa-solid fa-save"></i> Create
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Help with creating a new domain</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-flat btn-primary center-block" data-dismiss="modal" id="button_confirm_warn_modal">
|
||||
CLOSE</button>
|
||||
<div class="card-body">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Domain name</dt>
|
||||
<dd>Enter your domain name in the format of name.tld (eg. powerdns-admin.com). You can also
|
||||
enter sub-domains to create a sub-root zone (eg. sub.powerdns-admin.com) in case you want to
|
||||
delegate sub-domain management to specific users.
|
||||
</dd>
|
||||
<dt>Type</dt>
|
||||
<dd>The type decides how the domain will be replicated across multiple DNS servers.
|
||||
<ul>
|
||||
<li>
|
||||
<b>Native - </b>{{ SITE_NAME }} will not perform any Primary or Secondary zone functions.
|
||||
</li>
|
||||
<li>
|
||||
<b>Primary - </b>{{ SITE_NAME }} will serve as the Primary and will send zone transfers
|
||||
(AXFRs) to other servers configured as secondaries.
|
||||
</li>
|
||||
<li>
|
||||
<b>Secondary - </b>{{ SITE_NAME }} will serve as the Secondary and will request and receive
|
||||
zone transfers (AXFRs) from other servers configured as Primaries.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dt>SOA-EDIT-API</dt>
|
||||
<dd>The SOA-EDIT-API setting defines how the SOA serial number will be updated after a change is
|
||||
made to the domain.
|
||||
<ul>
|
||||
<li>
|
||||
<b>DEFAULT - </b>Generate a soa serial of YYYYMMDD01. If the current serial is lower than
|
||||
the generated serial, use the generated serial. If the current serial is higher or
|
||||
equal to the generated serial, increase the current serial by 1.
|
||||
</li>
|
||||
<li>
|
||||
<b>INCREASE - </b>Increase the current serial by 1.
|
||||
</li>
|
||||
<li>
|
||||
<b>EPOCH - </b>Change the serial to the number of seconds since the EPOCH, aka unixtime.
|
||||
</li>
|
||||
<li>
|
||||
<b>OFF - </b>Disable automatic updates of the SOA serial.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
<p>
|
||||
Find more details at <a href="https://docs.powerdns.com/md/">https://docs.powerdns.com/md/</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block extrascripts %}
|
||||
<script>
|
||||
$("input[name=radio_type]").change(function () {
|
||||
var type = $(this).val();
|
||||
if (type == "secondary") {
|
||||
$("#domain_primary_address_div").show();
|
||||
} else {
|
||||
$("#domain_primary_address_div").hide();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block modals %}
|
||||
<script>
|
||||
{% if domain_override_message %}
|
||||
$(document.body).ready(function () {
|
||||
var modal = $("#modal_warning");
|
||||
var info = "{{ domain_override_message }}";
|
||||
modal.find('.modal-body p').text(info);
|
||||
modal.modal('show');
|
||||
});
|
||||
{% endif %}
|
||||
</script>
|
||||
|
||||
<div class="modal fade" id="modal_warning">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content modal-sm">
|
||||
<div class="modal-header alert-danger">
|
||||
<h4 class="modal-title">
|
||||
WARNING
|
||||
</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close" id="button_close_warn_modal">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary center-block" data-dismiss="modal" id="button_confirm_warn_modal">
|
||||
CLOSE
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
@ -1,73 +1,85 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}<title>{{ domain.name | pretty_domain_name }} - {{ SITE_NAME }}</title>{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
<title>
|
||||
{{ domain.name | pretty_domain_name }} - {{ SITE_NAME }}
|
||||
</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block dashboard_stat %}
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
{% if record_name and record_type %}
|
||||
Record changelog: <b>{{ record_name}}   {{ record_type }}</b>
|
||||
{% else %}
|
||||
Domain changelog: <b>{{ domain.name | pretty_domain_name }}</b>
|
||||
{% endif %}
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}"><i class="fa fa-dashboard"></i> Home</a></li>
|
||||
<li>Domain</li>
|
||||
<li class="active">{{ domain.name | pretty_domain_name }}</li>
|
||||
</ol>
|
||||
</section>
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
{% if record_name and record_type %}
|
||||
Record changelog: <b>{{ record_name}}   {{ record_type }}</b>
|
||||
{% else %}
|
||||
Domain changelog: <b>{{ domain.name | pretty_domain_name }}</b>
|
||||
{% endif %}
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item active">Changelog: {{ domain.name | pretty_domain_name }}</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% import 'applied_change_macro.html' as applied_change_macro %}
|
||||
|
||||
{% block content %}
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="box">
|
||||
<div class="box-body">
|
||||
<button type="button" class="btn btn-flat btn-primary pull-left button_show_records"
|
||||
id="{{ domain.name }}">
|
||||
Manage <i class="fa fa-arrow-left"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
|
||||
<table id="tbl_changelog" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Changed on</th>
|
||||
<th>Changed by</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for applied_change in allHistoryChanges %}
|
||||
|
||||
<tr class="odd row_record" id="{{ domain.name }}">
|
||||
<td id="changed_on" class="changed_on">
|
||||
{{ allHistoryChanges[applied_change][0].history_entry.created_on }}
|
||||
</td>
|
||||
<td>
|
||||
{{allHistoryChanges[applied_change][0].history_entry.created_by }}
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Nested Table -->
|
||||
<tr style='visibility:collapse'>
|
||||
<td colspan="2">
|
||||
<div class="content">
|
||||
{% call applied_change_macro.applied_change_template(allHistoryChanges[applied_change]) %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- end nested table -->
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<button type="button" class="btn btn-primary float-left button_show_records" id="{{ domain.name }}">
|
||||
<i class="fa-solid fa-arrow-left"></i>
|
||||
Manage Domain {{ domain.name }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table id="tbl_changelog" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Changed on</th>
|
||||
<th>Changed by</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for applied_change in allHistoryChanges %}
|
||||
<tr class="odd row_record" id="{{ domain.name }}">
|
||||
<td id="changed_on" class="changed_on">
|
||||
{{ allHistoryChanges[applied_change][0].history_entry.created_on }}
|
||||
</td>
|
||||
<td>
|
||||
{{allHistoryChanges[applied_change][0].history_entry.created_by }}
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Nested Table -->
|
||||
<tr style='visibility:collapse'>
|
||||
<td colspan="2">
|
||||
<div class="content">
|
||||
{% call applied_change_macro.applied_change_template(allHistoryChanges[applied_change]) %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- end nested table -->
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
|
@ -1,127 +1,137 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% set active_page = "remove_domain" %}
|
||||
{% block title %}<title>Remove Domain - {{ SITE_NAME }}</title>{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
<title>
|
||||
Remove Domain - {{ SITE_NAME }}
|
||||
</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block dashboard_stat %}
|
||||
<!-- Content Header (Page header) -->
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
Domain
|
||||
<small>Remove existing</small>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}"><i class="fa fa-dashboard"></i>Home</a></li>
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}">Domain</a></li>
|
||||
<li class="active">Remove Domain</li>
|
||||
</ol>
|
||||
</section>
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
Domain
|
||||
<small>Remove</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item active">Domain - Remove Domain</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Remove domain</h3>
|
||||
</div>
|
||||
<!-- /.box-header -->
|
||||
<!-- form start -->
|
||||
<form role="form" method="post" action="{{ url_for('domain.remove') }}">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="box-body">
|
||||
<select id=domainid class="form-control" style="width:15em;">
|
||||
<option value="0">- Select Domain -</option>
|
||||
{% for domain in domainss %}
|
||||
<option value="{{ domain.id }}">{{ domain.name }}</option>
|
||||
{% endfor %}
|
||||
</select><br />
|
||||
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
|
||||
<div class="box-footer">
|
||||
<button type="button" class="btn btn-flat btn-danger button_delete">Remove</button>
|
||||
<button type="button" class="btn btn-flat btn-default"
|
||||
onclick="window.location.href='{{ url_for('dashboard.dashboard') }}'">Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
<div class="col-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Remove Domain</h3>
|
||||
</div>
|
||||
<form role="form" method="post" action="{{ url_for('domain.remove') }}">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="card-body">
|
||||
<select id=domainid class="form-control" style="width:15em;">
|
||||
<option value="0">- Select Domain -</option>
|
||||
{% for domain in domainss %}
|
||||
<option value="{{ domain.id }}">{{ domain.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<br />
|
||||
</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 removing a new domain</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Domain name</dt>
|
||||
<dd>Select domain you wish to remove from DNS.</dd>
|
||||
</dl>
|
||||
<p>Find more details at <a href="https://docs.powerdns.com/md/">https://docs.powerdns.com/md/</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="window.location.href='{{ url_for('dashboard.dashboard') }}'">
|
||||
<i class="fa-solid fa-window-close"></i> Cancel
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger button_delete float-right">
|
||||
<i class="fa-solid fa-trash-alt"></i> Remove
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Help with removing a domain</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Domain Name</dt>
|
||||
<dd>Select the domain you wish to remove from the system.</dd>
|
||||
</dl>
|
||||
<p>Find more details at <a href="https://docs.powerdns.com/md/">https://docs.powerdns.com/md/</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
{% block extrascripts %}
|
||||
<script>
|
||||
// handle delete button
|
||||
$(document.body).on("click", ".button_delete", function(e) {
|
||||
e.stopPropagation();
|
||||
if ( $("#domainid").val() == 0 ){
|
||||
showErrorModal("Please select domain to remove.");
|
||||
return;
|
||||
}
|
||||
// handle delete button
|
||||
$(document.body).on("click", ".button_delete", function(e) {
|
||||
e.stopPropagation();
|
||||
if ( $("#domainid").val() == 0 ){
|
||||
showErrorModal("Please select domain to remove.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var modal = $("#modal_delete");
|
||||
var domain = $("#domainid option:selected").text();
|
||||
var info = "Are you sure you want to delete " + domain + "?";
|
||||
modal.find('.modal-body p').text(info);
|
||||
modal.find('#button_delete_confirm').click(function () {
|
||||
$.post($SCRIPT_ROOT + '/domain/remove' , {
|
||||
'_csrf_token': '{{ csrf_token() }}',
|
||||
'domainid': domain,
|
||||
}, function () {
|
||||
window.location.href = '{{ url_for('dashboard.dashboard') }}';
|
||||
});
|
||||
modal.modal('hide');
|
||||
})
|
||||
modal.modal('show');
|
||||
|
||||
$("#button_delete_cancel").unbind().one('click', function(e) {
|
||||
modal.modal('hide');
|
||||
});
|
||||
var modal = $("#modal_delete");
|
||||
var domain = $("#domainid option:selected").text();
|
||||
var info = "Are you sure you want to delete " + domain + "?";
|
||||
modal.find('.modal-body p').text(info);
|
||||
modal.find('#button_delete_confirm').click(function () {
|
||||
$.post($SCRIPT_ROOT + '/domain/remove' , {
|
||||
'_csrf_token': '{{ csrf_token() }}',
|
||||
'domainid': domain,
|
||||
}, function () {
|
||||
window.location.href = '{{ url_for('dashboard.dashboard') }}';
|
||||
});
|
||||
modal.modal('hide');
|
||||
})
|
||||
modal.modal('show');
|
||||
$("#button_delete_cancel").unbind().one('click', function(e) {
|
||||
modal.modal('hide');
|
||||
});
|
||||
});
|
||||
</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" id="button_delete_cancel"
|
||||
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 class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Confirmation</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" id="button_delete_cancel" data-dismiss="modal">
|
||||
<i class="fa-solid fa-window-close"></i> Cancel
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger float-right" id="button_delete_confirm">
|
||||
<i class="fa-solid fa-trash-alt"></i> Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -1,46 +1,66 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}<title>Domain Management - {{ SITE_NAME }}</title>{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
<title>
|
||||
Domain Management - {{ SITE_NAME }}
|
||||
</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block dashboard_stat %}
|
||||
|
||||
{% if status %}
|
||||
{% if status.get('status') == 'ok' %}
|
||||
<div class="alert alert-success">
|
||||
<strong>Success!</strong> {{ status.get('msg') }}
|
||||
</div>
|
||||
{% elif status.get('status') == 'error' %}
|
||||
<div class="alert alert-danger">
|
||||
{% if status.get('msg') != None %}
|
||||
<strong>Error!</strong> {{ status.get('msg') }}
|
||||
{% else %}
|
||||
<strong>Error!</strong> An undefined error occurred.
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if status.get('status') == 'ok' %}
|
||||
<div class="alert alert-success">
|
||||
<strong>Success!</strong> {{ status.get('msg') }}
|
||||
</div>
|
||||
{% elif status.get('status') == 'error' %}
|
||||
<div class="alert alert-danger">
|
||||
{% if status.get('msg') != None %}
|
||||
<strong>Error!</strong>
|
||||
{{ status.get('msg') }}
|
||||
{% else %}
|
||||
<strong>Error!</strong>
|
||||
An undefined error occurred.
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
Manage domain <small>{{ domain.name | pretty_domain_name }}</small>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}"><i class="fa fa-dashboard"></i> Home</a></li>
|
||||
<li class="active">Domain Management</li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
Domain Settings
|
||||
<small>{{ domain.name | pretty_domain_name }}</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item active">Domain Settings: {{ domain.name | pretty_domain_name }}</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="box">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<form method="post" action="{{ url_for('domain.setting', domain_name=domain.name) }}">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="box-header">
|
||||
<h3 class="box-title">Domain Access Control</h3>
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Domain Access Control</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-xs-2">
|
||||
<div class="col-2">
|
||||
<p>Users on the right have access to manage the records in
|
||||
the {{ domain.name | pretty_domain_name }} domain.</p>
|
||||
<p>Click on users to move from between columns.</p>
|
||||
@ -49,7 +69,7 @@
|
||||
and already have access to <b>ALL</b> domains.
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group col-xs-2">
|
||||
<div class="form-group col-2">
|
||||
<select multiple="multiple" class="form-control" id="domain_multi_user"
|
||||
name="domain_multi_user[]">
|
||||
{% for user in users %}
|
||||
@ -60,11 +80,13 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="col-xs-offset-2">
|
||||
<div class="card-body">
|
||||
<div class="col-offset-2">
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-flat btn-primary"><i class="fa fa-check"></i>
|
||||
Save</button>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fa-solid fa-check"></i>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -74,13 +96,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<h3 class="box-title">Account</h3>
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Account</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="col-xs-12">
|
||||
<div class="card-body">
|
||||
<div class="col-12">
|
||||
<div class="form-group">
|
||||
<form method="post"
|
||||
action="{{ url_for('domain.change_account', domain_name=domain.name) }}">
|
||||
@ -94,7 +116,7 @@
|
||||
{% endfor %}
|
||||
</select><br />
|
||||
<button type="submit" class="btn btn-flat btn-primary" id="change_soa_edit_api">
|
||||
<i class="fa fa-check"></i> Change account for {{ domain.name | pretty_domain_name }}
|
||||
<i class="fa-solid fa-check"></i> Change account for {{ domain.name | pretty_domain_name }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
@ -104,12 +126,12 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<h3 class="box-title">Auto PTR creation</h3>
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Auto PTR creation</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="card-body">
|
||||
<p><input type="checkbox" id="{{ domain.name }}" class="auto_ptr_toggle"
|
||||
{% for setting in domain.settings %}{% if setting.setting=='auto_ptr' and setting.value=='True' %}checked{% endif %}{% endfor %}
|
||||
{% if SETTING.get('auto_ptr') %}disabled="True" {% endif %}>
|
||||
@ -123,12 +145,12 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<h3 class="box-title">DynDNS 2 Settings</h3>
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">DynDNS 2 Settings</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="card-body">
|
||||
<p><input type="checkbox" id="{{ domain.name }}" class="dyndns_on_demand_toggle"
|
||||
{% for setting in domain.settings %}{% if setting.setting=='create_via_dyndns' and setting.value=='True' %}checked{% endif %}{% endfor %}>
|
||||
Allow on-demand creation of records via DynDNS updates?</p>
|
||||
@ -137,12 +159,12 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<h3 class="box-title">Change Type</h3>
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Change Type</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="card-body">
|
||||
<p>The type decides how the domain will be replicated across multiple DNS servers.</p>
|
||||
<ul>
|
||||
<li>
|
||||
@ -150,12 +172,12 @@
|
||||
PowerDNS server or you handle replication via your backend.
|
||||
</li>
|
||||
<li>
|
||||
Master - This PowerDNS server will serve as the master and will send zone transfers
|
||||
(AXFRs) to other servers configured as slaves.
|
||||
Primary - This PowerDNS server will serve as the primary and will send zone transfers
|
||||
(AXFRs) to other servers configured as secondaries.
|
||||
</li>
|
||||
<li>
|
||||
Slave - This PowerDNS server will serve as the slave and will request and receive
|
||||
zone transfers (AXFRs) from other servers configured as masters.
|
||||
Secondary - This PowerDNS server will serve as the secondaries and will request and receive
|
||||
zone transfers (AXFRs) from other servers configured as primaries.
|
||||
</li>
|
||||
</ul>
|
||||
<b>New Domain Type Setting:</b>
|
||||
@ -164,16 +186,16 @@
|
||||
<select name="domain_type" class="form-control" style="width:15em;">
|
||||
<option selected value="0">- Unchanged -</option>
|
||||
<option value="native">Native</option>
|
||||
<option value="master">Master</option>
|
||||
<option value="slave">Slave</option>
|
||||
<option value="priamry">Primary</option>
|
||||
<option value="secondary">Secondary</option>
|
||||
</select><br />
|
||||
<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)">
|
||||
<div class="form-group" style="display: none;" id="domain_primary_address_div">
|
||||
<input type="text" class="form-control" name="domain_primary_address"
|
||||
id="domain_primary_address"
|
||||
placeholder="Enter valid Primary Server IP addresses (separated by commas)">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-flat btn-primary" id="change_type">
|
||||
<i class="fa fa-check"></i> Change type for {{ domain.name | pretty_domain_name }}
|
||||
<button type="submit" class="btn btn-primary" id="change_type">
|
||||
<i class="fa-solid fa-check"></i> Change type for {{ domain.name | pretty_domain_name }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
@ -181,12 +203,12 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<h3 class="box-title">Change SOA-EDIT-API</h3>
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Change SOA-EDIT-API</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="card-body">
|
||||
<p>The SOA-EDIT-API setting defines how the SOA serial number will be updated after a change is made
|
||||
to the domain.</p>
|
||||
<ul>
|
||||
@ -215,8 +237,8 @@
|
||||
<option>EPOCH</option>
|
||||
<option>OFF</option>
|
||||
</select><br />
|
||||
<button type="submit" class="btn btn-flat btn-primary" id="change_soa_edit_api">
|
||||
<i class="fa fa-check"></i> Change SOA-EDIT-API setting for {{ domain.name | pretty_domain_name }}
|
||||
<button type="submit" class="btn btn-primary" id="change_soa_edit_api">
|
||||
<i class="fa-solid fa-check"></i> Change SOA-EDIT-API setting for {{ domain.name | pretty_domain_name }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
@ -224,23 +246,24 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<h3 class="box-title">Domain Deletion</h3>
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Domain Deletion</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="card-body">
|
||||
<p>This function is used to remove a domain from PowerDNS-Admin <b>AND</b> PowerDNS. All records and
|
||||
user privileges associated with this domain will also be removed. This change cannot be
|
||||
reverted.</p>
|
||||
<button type="button" class="btn btn-flat btn-danger pull-left delete_domain"
|
||||
<button type="button" class="btn btn-danger float-left delete_domain"
|
||||
id="{{ domain.name }}">
|
||||
<i class="fa fa-trash"></i> DELETE DOMAIN {{ domain.name | pretty_domain_name }}
|
||||
<i class="fa-solid fa-trash"></i> DELETE DOMAIN {{ domain.name | pretty_domain_name }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
{% block extrascripts %}
|
||||
@ -336,39 +359,37 @@
|
||||
modal.modal('show');
|
||||
});
|
||||
|
||||
// domain master address input handeling
|
||||
// domain primary address input handeling
|
||||
$("select[name=domain_type]").change(function () {
|
||||
var type = $(this).val();
|
||||
if (type == "slave") {
|
||||
$("#domain_master_address_div").show();
|
||||
if (type == "secondary") {
|
||||
$("#domain_primary_address_div").show();
|
||||
} else {
|
||||
$("#domain_master_address_div").hide();
|
||||
$("#domain_primary_address_div").hide();
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% block modals %}
|
||||
<div class="modal fade modal-warning" id="modal_delete_domain">
|
||||
<div class="modal fade" id="modal_delete_domain">
|
||||
<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>
|
||||
<h4 class="modal-title">Confirmation</h4>
|
||||
<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">
|
||||
<button type="button" class="btn btn-secondary float-left" data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-danger" id="button_delete_confirm">
|
||||
Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -1,41 +1,52 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}<title>PowerDNS-Admin - 400 Error</title>{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
<title>
|
||||
400 Error - {{ SITE_NAME }}
|
||||
</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block dashboard_stat %}
|
||||
<!-- Content Header (Page header) -->
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
400
|
||||
<small>Error</small>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}"><i class="fa fa-dashboard"></i>Home</a></li>
|
||||
<li>400</li>
|
||||
</ol>
|
||||
</section>
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
400
|
||||
<small>Error</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item active">400 Error</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Main content -->
|
||||
<section class="content">
|
||||
<section class="content">
|
||||
<div class="error-page">
|
||||
<h2 class="headline text-yellow">400</h2>
|
||||
<div class="error-content">
|
||||
<h3>
|
||||
<i class="fa fa-warning text-yellow"></i> Oops! Bad request
|
||||
</h3>
|
||||
<p>
|
||||
{% if msg %}
|
||||
{{ msg }}
|
||||
{% else %}
|
||||
The server refused to process your request and return a 400 error.
|
||||
{% endif %}
|
||||
<br/>You may <a href="{{ url_for('dashboard.dashboard') }}">return to the dashboard</a>.
|
||||
</p>
|
||||
</div>
|
||||
<!-- /.error-content -->
|
||||
<h2 class="headline text-yellow">
|
||||
400
|
||||
</h2>
|
||||
<div class="error-content">
|
||||
<h3>
|
||||
<i class="fa-solid fa-exclamation-triangle text-yellow"></i>
|
||||
Oops! Bad request
|
||||
</h3>
|
||||
<p>
|
||||
{% if msg %}
|
||||
{{ msg }}
|
||||
{% else %}
|
||||
The server refused to process your request and return a 400 error.
|
||||
{% endif %}
|
||||
<br/>You may <a href="{{ url_for('dashboard.dashboard') }}">return to the dashboard</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.error-page -->
|
||||
</section>
|
||||
<!-- /.content -->
|
||||
{% endblock %}
|
||||
</section>
|
||||
{% endblock %}
|
@ -1,37 +1,48 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}<title>PowerDNS-Admin - 403 Error</title>{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
<title>
|
||||
403 Error - {{ SITE_NAME }}
|
||||
</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block dashboard_stat %}
|
||||
<!-- Content Header (Page header) -->
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
403
|
||||
<small>Error</small>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}"><i class="fa fa-dashboard"></i>Home</a></li>
|
||||
<li>403</li>
|
||||
</ol>
|
||||
</section>
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
403
|
||||
<small>Error</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item active">403 Error</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Main content -->
|
||||
<section class="content">
|
||||
<section class="content">
|
||||
<div class="error-page">
|
||||
<h2 class="headline text-yellow">403</h2>
|
||||
<div class="error-content">
|
||||
<h3>
|
||||
<i class="fa fa-warning text-yellow"></i> Oops! Access denied
|
||||
</h3>
|
||||
<p>
|
||||
You don't have permission to access this page
|
||||
You may <a href="{{ url_for('dashboard.dashboard') }}">return to the dashboard</a>.
|
||||
</p>
|
||||
</div>
|
||||
<!-- /.error-content -->
|
||||
<h2 class="headline text-yellow">
|
||||
403
|
||||
</h2>
|
||||
<div class="error-content">
|
||||
<h3>
|
||||
<i class="fa-solid fa-exclamation-triangle text-yellow"></i>
|
||||
Oops! Access denied
|
||||
</h3>
|
||||
<p>
|
||||
You don't have permission to access this page
|
||||
You may <a href="{{ url_for('dashboard.dashboard') }}">return to the dashboard</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.error-page -->
|
||||
</section>
|
||||
<!-- /.content -->
|
||||
{% endblock %}
|
||||
</section>
|
||||
{% endblock %}
|
@ -1,37 +1,48 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}<title>PowerDNS-Admin - 404 Error</title>{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
<title>
|
||||
404 Error - {{ SITE_NAME }}
|
||||
</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block dashboard_stat %}
|
||||
<!-- Content Header (Page header) -->
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
404
|
||||
<small>Error</small>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}"><i class="fa fa-dashboard"></i>Home</a></li>
|
||||
<li>404</li>
|
||||
</ol>
|
||||
</section>
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
404
|
||||
<small>Error</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item active">404 Error</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Main content -->
|
||||
<section class="content">
|
||||
<section class="content">
|
||||
<div class="error-page">
|
||||
<h2 class="headline text-yellow">404</h2>
|
||||
<div class="error-content">
|
||||
<h3>
|
||||
<i class="fa fa-warning text-yellow"></i> Oops! You're lost
|
||||
</h3>
|
||||
<p>
|
||||
The page you requested could not be found.
|
||||
You may <a href="{{ url_for('dashboard.dashboard') }}">return to the dashboard</a>.
|
||||
</p>
|
||||
</div>
|
||||
<!-- /.error-content -->
|
||||
<h2 class="headline text-yellow">
|
||||
404
|
||||
</h2>
|
||||
<div class="error-content">
|
||||
<h3>
|
||||
<i class="fa-solid fa-exclamation-triangle text-yellow"></i>
|
||||
Oops! You're lost
|
||||
</h3>
|
||||
<p>
|
||||
The page you requested could not be found.
|
||||
You may <a href="{{ url_for('dashboard.dashboard') }}">return to the dashboard</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.error-page -->
|
||||
</section>
|
||||
<!-- /.content -->
|
||||
{% endblock %}
|
||||
</section>
|
||||
{% endblock %}
|
@ -1,37 +1,48 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}<title>PowerDNS-Admin - 500 Error</title>{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
<title>
|
||||
500 Error - {{ SITE_NAME }}
|
||||
</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block dashboard_stat %}
|
||||
<!-- Content Header (Page header) -->
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
500
|
||||
<small>Error</small>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}"><i class="fa fa-dashboard"></i>Home</a></li>
|
||||
<li>500</li>
|
||||
</ol>
|
||||
</section>
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
500
|
||||
<small>Error</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item active">500 Error</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Main content -->
|
||||
<section class="content">
|
||||
<section class="content">
|
||||
<div class="error-page">
|
||||
<h2 class="headline text-yellow">500</h2>
|
||||
<div class="error-content">
|
||||
<h3>
|
||||
<i class="fa fa-warning text-yellow"></i> Oops! Something went wrong
|
||||
</h3>
|
||||
<p>
|
||||
Try again later.
|
||||
You may <a href="{{ url_for('dashboard.dashboard') }}">return to the dashboard</a>.
|
||||
</p>
|
||||
</div>
|
||||
<!-- /.error-content -->
|
||||
<h2 class="headline text-yellow">
|
||||
500
|
||||
</h2>
|
||||
<div class="error-content">
|
||||
<h3>
|
||||
<i class="fa-solid fa-exclamation-triangle text-yellow"></i>
|
||||
Oops! Something went wrong
|
||||
</h3>
|
||||
<p>
|
||||
Try again later.
|
||||
You may <a href="{{ url_for('dashboard.dashboard') }}">return to the dashboard</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.error-page -->
|
||||
</section>
|
||||
<!-- /.content -->
|
||||
{% endblock %}
|
||||
</section>
|
||||
{% endblock %}
|
@ -1,45 +1,61 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}<title>PowerDNS-Admin - SAML Authentication Error</title>{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
<title>
|
||||
SAML Authentication Error - {{ SITE_NAME }}
|
||||
</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block dashboard_stat %}
|
||||
<!-- Content Header (Page header) -->
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
SAML
|
||||
<small>Error</small>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}"><i class="fa fa-dashboard"></i>Home</a></li>
|
||||
<li>SAML</li>
|
||||
</ol>
|
||||
</section>
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
SAML
|
||||
<small>Error</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item active">SAML Authentication Error</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Main content -->
|
||||
<section class="content">
|
||||
<section class="content">
|
||||
<div class="error-page">
|
||||
<div>
|
||||
<h1 class="headline text-yellow" style="font-size:46px;">SAML Authentication Error</h1></div><br/><br/>
|
||||
<div class="error-content">
|
||||
<h3>
|
||||
<i class="fa fa-warning text-yellow"></i> Oops! Something went wrong
|
||||
</h3><br>
|
||||
<p>
|
||||
Login failed.<br>
|
||||
Error(s) when processing SAML Response:<br>
|
||||
<ul>
|
||||
{% for error in errors %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
You may <a href="{{ url_for('dashboard.dashboard') }}">return to the dashboard</a>.
|
||||
</p>
|
||||
</div>
|
||||
<!-- /.error-content -->
|
||||
<div>
|
||||
<h1 class="headline text-yellow" style="font-size:46px;">
|
||||
SAML Authentication Error
|
||||
</h1>
|
||||
</div>
|
||||
<br/>
|
||||
<br/>
|
||||
<div class="error-content">
|
||||
<h3>
|
||||
<i class="fa-solid fa-exclamation-triangle text-yellow"></i>
|
||||
Oops! Something went wrong
|
||||
</h3>
|
||||
<br>
|
||||
<p>
|
||||
Login failed.
|
||||
<br>
|
||||
Error(s) when processing SAML Response:
|
||||
<br>
|
||||
<ul>
|
||||
{% for error in errors %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
You may <a href="{{ url_for('dashboard.dashboard') }}">return to the dashboard</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.error-page -->
|
||||
</section>
|
||||
<!-- /.content -->
|
||||
{% endblock %}
|
||||
</section>
|
||||
{% endblock %}
|
@ -1,166 +1,170 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Log In - {{ SITE_NAME }}</title>
|
||||
<link rel="icon" href="{{ url_for('static', filename='img/favicon.png') }}">
|
||||
<!-- Tell the browser to be responsive to screen width -->
|
||||
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
|
||||
<META HTTP-EQUIV="REFRESH" CONTENT="{{ 60 * SETTING.get('session_timeout') }}">
|
||||
{% assets "css_login" -%}
|
||||
<link rel="stylesheet" href="{{ ASSET_URL }}">
|
||||
{%- endassets %}
|
||||
{% if SETTING.get('custom_css') %}
|
||||
<link rel="stylesheet" href="/static/custom/{{ SETTING.get('custom_css') }}">
|
||||
{% endif %}
|
||||
</head>
|
||||
|
||||
<body class="hold-transition login-page">
|
||||
<div class="login-box">
|
||||
<div class="login-logo">
|
||||
<a href="{{ url_for('index.index') }}">
|
||||
{% if SETTING.get('site_name') %}
|
||||
<b>{{ SITE_NAME }}</b>
|
||||
{% else %}
|
||||
<b>PowerDNS</b>-Admin
|
||||
{% endif %}
|
||||
</a>
|
||||
</div>
|
||||
<!-- /.login-logo -->
|
||||
<div class="login-box-body">
|
||||
{% if error %}
|
||||
<div class="alert alert-danger alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||
{{ error }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if SETTING.get('ldap_enabled') or SETTING.get('local_db_enabled') %}
|
||||
<form action="" method="post" data-toggle="validator">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" placeholder="Username" name="username"
|
||||
data-error="Please input your username" required {% if username %}value="{{ username }}" {% endif %}>
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="password" class="form-control" placeholder="Password" name="password"
|
||||
data-error="Please input your password" required {% if password %}value="{{ password }}" {% endif %}>
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
{% if SETTING.get('otp_field_enabled') %}
|
||||
<div class="form-group">
|
||||
<input type="otptoken" class="form-control" placeholder="OTP Token" name="otptoken" autocomplete="off">
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if SETTING.get('ldap_enabled') and SETTING.get('local_db_enabled') %}
|
||||
<div class="form-group">
|
||||
<select class="form-control" name="auth_method">
|
||||
<option value="LOCAL">LOCAL Authentication</option>
|
||||
{% if SETTING.get('login_ldap_first') %}
|
||||
<option value="LDAP" selected="selected">LDAP Authentication</option>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Log In - {{ SITE_NAME }}</title>
|
||||
<link rel="icon" href="{{ url_for('static', filename='img/favicon.png') }}">
|
||||
<!-- Tell the browser to be responsive to screen width -->
|
||||
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
|
||||
<META HTTP-EQUIV="REFRESH" CONTENT="{{ 60 * SETTING.get('session_timeout') }}">
|
||||
{% assets "css_login" -%}
|
||||
<link rel="stylesheet" href="{{ ASSET_URL }}">
|
||||
{%- endassets %}
|
||||
{% if SETTING.get('custom_css') %}
|
||||
<link rel="stylesheet" href="/static/custom/{{ SETTING.get('custom_css') }}">
|
||||
{% endif %}
|
||||
</head>
|
||||
<body class="hold-transition login-page">
|
||||
<div class="login-box">
|
||||
<div class="card card-outline card-primary">
|
||||
<div class="card-header text-center">
|
||||
<a href="{{ url_for('index.index') }}" class="h3">
|
||||
{% if SETTING.get('site_name') %}
|
||||
{{ SITE_NAME }}
|
||||
{% else %}
|
||||
<option value="LDAP">LDAP Authentication</option>
|
||||
<b>PowerDNS</b>-Admin
|
||||
{% endif %}
|
||||
</select>
|
||||
</a>
|
||||
</div>
|
||||
{% elif SETTING.get('ldap_enabled') and not SETTING.get('local_db_enabled') %}
|
||||
<div class="form-group">
|
||||
<input type="hidden" name="auth_method" value="LDAP">
|
||||
</div>
|
||||
{% elif SETTING.get('local_db_enabled') and not SETTING.get('ldap_enabled') %}
|
||||
<div class="form-group">
|
||||
<input type="hidden" name="auth_method" value="LOCAL">
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="form-group">
|
||||
<input type="hidden" name="auth_method" value="LOCAL">
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-8">
|
||||
<div class="checkbox icheck">
|
||||
<label>
|
||||
<input type="checkbox" name="remember"> Remember Me
|
||||
</label>
|
||||
<div class="card-body login-card-body">
|
||||
{% if error %}
|
||||
<div class="alert alert-danger alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||
{{ error }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
<div class="col-xs-4">
|
||||
<button type="submit" class="btn btn-flat btn-primary btn-block">Sign In</button>
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
{% endif %}
|
||||
{% if SETTING.get('ldap_enabled') or SETTING.get('local_db_enabled') %}
|
||||
<form action="" method="post" data-toggle="validator">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" placeholder="Username" name="username" data-error="Please input your username" required {% if username %}value="{{ username }}" {% endif %}>
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="password" class="form-control" placeholder="Password" name="password" data-error="Please input your password" required {% if password %}value="{{ password }}" {% endif %}>
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
{% if SETTING.get('otp_field_enabled') %}
|
||||
<div class="form-group">
|
||||
<input type="otptoken" class="form-control" placeholder="OTP Token" name="otptoken" autocomplete="off">
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if SETTING.get('ldap_enabled') and SETTING.get('local_db_enabled') %}
|
||||
<div class="form-group">
|
||||
<select class="form-control" name="auth_method">
|
||||
<option value="LOCAL">
|
||||
LOCAL Authentication
|
||||
</option>
|
||||
{% if SETTING.get('login_ldap_first') %}
|
||||
<option value="LDAP" selected="selected">
|
||||
LDAP Authentication
|
||||
</option>
|
||||
{% else %}
|
||||
<option value="LDAP">
|
||||
LDAP Authentication
|
||||
</option>
|
||||
{% endif %}
|
||||
</select>
|
||||
</div>
|
||||
{% elif SETTING.get('ldap_enabled') and not SETTING.get('local_db_enabled') %}
|
||||
<div class="form-group">
|
||||
<input type="hidden" name="auth_method" value="LDAP">
|
||||
</div>
|
||||
{% elif SETTING.get('local_db_enabled') and not SETTING.get('ldap_enabled') %}
|
||||
<div class="form-group">
|
||||
<input type="hidden" name="auth_method" value="LOCAL">
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="form-group">
|
||||
<input type="hidden" name="auth_method" value="LOCAL">
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
<div class="icheck-primary">
|
||||
<input type="checkbox" id="remember">
|
||||
<label for="remember">
|
||||
Remember Me
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<button type="submit" class="btn btn-primary btn-block">Sign In</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% if SETTING.get('google_oauth_enabled') or SETTING.get('github_oauth_enabled') or SETTING.get('oidc_oauth_enabled') or SETTING.get('azure_oauth_enabled') %}
|
||||
<div class="social-auth-links text-center">
|
||||
{% if SETTING.get('ldap_enabled') or SETTING.get('local_db_enabled') %}
|
||||
<p>- OR -</p>
|
||||
{% endif %}
|
||||
{% if SETTING.get('oidc_oauth_enabled') %}
|
||||
<a href="{{ url_for('index.oidc_login') }}" class="btn btn-block btn-social btn-openid btn-flat">
|
||||
<i class="fa-brands fa-openid"></i>
|
||||
Sign in using OpenID Connect
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if SETTING.get('github_oauth_enabled') %}
|
||||
<a href="{{ url_for('index.github_login') }}" class="btn btn-block btn-social btn-github btn-flat">
|
||||
<i class="fa-brands fa-github"></i>
|
||||
Sign in using Github
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if SETTING.get('google_oauth_enabled') %}
|
||||
<a href="{{ url_for('index.google_login') }}" class="btn btn-block btn-social btn-google btn-flat">
|
||||
<i class="fa-brands fa-google"></i>
|
||||
Sign in using Google
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if SETTING.get('azure_oauth_enabled') %}
|
||||
<a href="{{ url_for('index.azure_login') }}" class="btn btn-block btn-social btn-microsoft btn-flat">
|
||||
<i class="fa-brands fa-windows"></i>
|
||||
Sign in using Microsoft Azure
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if saml_enabled %}
|
||||
<a href="{{ url_for('index.saml_login') }}">
|
||||
SAML login
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if SETTING.get('signup_enabled') %}
|
||||
<br>
|
||||
<a href="{{ url_for('index.register') }}" class="text-center">
|
||||
Create an account
|
||||
</a>
|
||||
{% if SETTING.get('verify_user_email') %}
|
||||
<br/>
|
||||
<a href="{{ url_for('index.resend_confirmation_email') }}" class="text-center">
|
||||
Resend confirmation email
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% if SETTING.get('google_oauth_enabled') or SETTING.get('github_oauth_enabled') or SETTING.get('oidc_oauth_enabled') or SETTING.get('azure_oauth_enabled') %}
|
||||
<div class="social-auth-links text-center">
|
||||
{% if SETTING.get('ldap_enabled') or SETTING.get('local_db_enabled') %}
|
||||
<p>- OR -</p>
|
||||
{% endif %}
|
||||
{% if SETTING.get('oidc_oauth_enabled') %}
|
||||
<a href="{{ url_for('index.oidc_login') }}" class="btn btn-block btn-social btn-openid btn-flat"><i
|
||||
class="fa fa-openid"></i> Sign in using
|
||||
OpenID Connect</a>
|
||||
{% endif %}
|
||||
{% if SETTING.get('github_oauth_enabled') %}
|
||||
<a href="{{ url_for('index.github_login') }}" class="btn btn-block btn-social btn-github btn-flat"><i
|
||||
class="fa fa-github"></i> Sign in using
|
||||
Github</a>
|
||||
{% endif %}
|
||||
{% if SETTING.get('google_oauth_enabled') %}
|
||||
<a href="{{ url_for('index.google_login') }}" class="btn btn-block btn-social btn-google btn-flat"><i
|
||||
class="fa fa-google"></i> Sign in using
|
||||
Google</a>
|
||||
{% endif %}
|
||||
{% if SETTING.get('azure_oauth_enabled') %}
|
||||
<a href="{{ url_for('index.azure_login') }}" class="btn btn-block btn-social btn-microsoft btn-flat"><i
|
||||
class="fa fa-windows"></i> Sign in using
|
||||
Microsoft Azure</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if saml_enabled %}
|
||||
<a href="{{ url_for('index.saml_login') }}">SAML login</a>
|
||||
{% endif %}
|
||||
|
||||
{% if SETTING.get('signup_enabled') %}
|
||||
<br>
|
||||
<a href="{{ url_for('index.register') }}" class="text-center">Create an account </a>
|
||||
{% if SETTING.get('verify_user_email') %}
|
||||
<br/>
|
||||
<a href="{{ url_for('index.resend_confirmation_email') }}" class="text-center">Resend confirmation email</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<div class="login-box-footer">
|
||||
<center>
|
||||
<p>Powered by <a href="https://github.com/PowerDNS-Admin/PowerDNS-Admin">PowerDNS-Admin</a></p>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.login-box-body -->
|
||||
<div class="login-box-footer">
|
||||
<center>
|
||||
<p>Powered by <a href="https://github.com/PowerDNS-Admin/PowerDNS-Admin">PowerDNS-Admin</a></p>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.login-box -->
|
||||
|
||||
{% assets "js_login" -%}
|
||||
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
||||
{%- endassets %}
|
||||
{% assets "js_validation" -%}
|
||||
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
||||
{%- endassets %}
|
||||
|
||||
<script>
|
||||
$(function () {
|
||||
$('input').iCheck({
|
||||
checkboxClass: 'icheckbox_square-blue',
|
||||
radioClass: 'iradio_square-blue',
|
||||
increaseArea: '20%' // optional
|
||||
{% assets "js_login" -%}
|
||||
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
||||
{%- endassets %}
|
||||
{% assets "js_validation" -%}
|
||||
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
||||
{%- endassets %}
|
||||
<script>
|
||||
$(function () {
|
||||
$('input').iCheck({
|
||||
checkboxClass: 'icheckbox_square-blue',
|
||||
radioClass: 'iradio_square-blue',
|
||||
increaseArea: '20%' // optional
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,102 +1,192 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Register - {{ SITE_NAME }}</title>
|
||||
<link rel="icon" href="{{ url_for('static', filename='img/favicon.png') }}">
|
||||
<!-- Tell the browser to be responsive to screen width -->
|
||||
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
|
||||
{% assets "css_login" -%}
|
||||
<link rel="stylesheet" href="{{ ASSET_URL }}">
|
||||
{%- endassets %}
|
||||
</head>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Register - {{ SITE_NAME }}</title>
|
||||
<link rel="icon" href="{{ url_for('static', filename='img/favicon.png') }}">
|
||||
<!-- Tell the browser to be responsive to screen width -->
|
||||
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css">
|
||||
{% assets "css_login" -%}
|
||||
<link rel="stylesheet" href="{{ ASSET_URL }}">
|
||||
{%- endassets %}
|
||||
</head>
|
||||
|
||||
<body class="hold-transition register-page">
|
||||
<div class="register-box">
|
||||
<div class="register-logo">
|
||||
<a href="{{ url_for('index.index') }}"><b>PowerDNS</b>-Admin</a>
|
||||
</div>
|
||||
<div class="register-box-body">
|
||||
{% if error %}
|
||||
<div class="alert alert-danger alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||
{{ error }}
|
||||
<body class="hold-transition register-page">
|
||||
<div class="register-box">
|
||||
<div class="card card-outline card-primary">
|
||||
<div class="card-header text-center">
|
||||
<a href="{{ url_for('index.index') }}" class="h3">
|
||||
{% if SETTING.get('site_name') %}
|
||||
{{ SITE_NAME }}
|
||||
{% else %}
|
||||
<b>PowerDNS</b>-Admin
|
||||
{% endif %}
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if error %}
|
||||
<div class="alert alert-danger alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||
{{ error }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<p class="login-box-msg">Enter your personal details below</p>
|
||||
<form action="{{ url_for('index.register') }}" method="post" validator>
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">
|
||||
<i class="fas fa-user"></i>
|
||||
</span>
|
||||
</div>
|
||||
<input type="text" class="form-control {{ 'is-invalid' if 'firstname' in error_messages else '' }}" placeholder="First Name" name="firstname" id="firstname" value="{{ request.form.firstname }}" required>
|
||||
{% if 'firstname' in error_messages %}
|
||||
<div class="invalid-feedback">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
{{ error_messages['firstname'] }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">
|
||||
<i class="fas fa-user"></i>
|
||||
</span>
|
||||
</div>
|
||||
<input type="text" class="form-control {{ 'is-invalid' if 'lastname' in error_messages else '' }}" placeholder="Last name" name="lastname" id="lastname" value="{{ request.form.lastname }}" required>
|
||||
{% if 'lastname' in error_messages %}
|
||||
<div class="invalid-feedback">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
{{ error_messages['lastname'] }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">
|
||||
<i class="fas fa-envelope"></i>
|
||||
</span>
|
||||
</div>
|
||||
<input type="email" class="form-control {{ 'is-invalid' if 'email' in error_messages else '' }}" placeholder="Email" name="email" id="email" value="{{ request.form.email }}" required>
|
||||
{% if 'email' in error_messages %}
|
||||
<div class="invalid-feedback">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
{{ error_messages['email'] }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="login-box-msg">Enter your account details below</p>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">
|
||||
<i class="fas fa-user"></i>
|
||||
</span>
|
||||
</div>
|
||||
<input type="text" class="form-control {{ 'is-invalid' if 'username' in error_messages else '' }}" placeholder="Username" name="username" id="username" value="{{ request.form.username }}" required>
|
||||
{% if 'username' in error_messages %}
|
||||
<div class="invalid-feedback">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
{{ error_messages['username'] }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">
|
||||
<i class="fas fa-lock"></i>
|
||||
</span>
|
||||
</div>
|
||||
<input type="password" class="form-control {{ 'is-invalid' if 'password' in error_messages else '' }}" placeholder="Password" id="password" name="password" required>
|
||||
{% if 'password' in error_messages %}
|
||||
<div class="invalid-feedback">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
{{ error_messages['password'] }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">
|
||||
<i class="fas fa-lock"></i>
|
||||
</span>
|
||||
</div>
|
||||
<input type="password" class="form-control {{ 'is-invalid' if 'rpassword' in error_messages else '' }}" placeholder="Retype password" id="rpassword" name="rpassword" required>
|
||||
{% if 'rpassword' in error_messages %}
|
||||
<div class="invalid-feedback">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
{{ error_messages['rpassword'] }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if captcha_enable %}
|
||||
<p class="login-box-msg">Please complete the CAPTCHA below</p>
|
||||
<div class="form-group">
|
||||
{{ captcha() }}
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">
|
||||
<i class="fas fa-shield-alt"></i>
|
||||
</span>
|
||||
</div>
|
||||
<input type="text" class="form-control {{ 'is-invalid' if 'captcha_result' in error_messages else '' }}" placeholder="CAPTCHA" id="captcha" name="captcha" required>
|
||||
{% if 'captcha_result' in error_messages %}
|
||||
<div class="invalid-feedback">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
{{ error_messages['captcha_result'] }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="row">
|
||||
<div class="d-flex justify-content-between mx-auto mt-3">
|
||||
<button type="button" class="btn btn-secondary" id="button_back">Back</button>
|
||||
<button type="submit" class="btn btn-primary btn-block mx-3">Register</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="login-box-footer">
|
||||
<center>
|
||||
<p>Powered by <a href="https://github.com/PowerDNS-Admin/PowerDNS-Admin">PowerDNS-Admin</a></p>
|
||||
</center>
|
||||
</div>
|
||||
{% endif %}
|
||||
<p class="login-box-msg">Enter your personal details below</p>
|
||||
<form action="{{ url_for('index.register') }}" method="post" data-toggle="validator">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="form-group has-feedback">
|
||||
<input type="text" class="form-control" placeholder="First Name" name="firstname"
|
||||
data-error="Please input your first name" required>
|
||||
<span class="glyphicon glyphicon-user form-control-feedback"></span>
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group has-feedback">
|
||||
<input type="text" class="form-control" placeholder="Last name" name="lastname"
|
||||
data-error="Please input your last name" required>
|
||||
<span class="glyphicon glyphicon-user form-control-feedback"></span>
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group has-feedback">
|
||||
<input type="email" class="form-control" placeholder="Email" name="email"
|
||||
data-error="Please input your valid email address" required>
|
||||
<span class="glyphicon glyphicon-envelope form-control-feedback"></span>
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<p class="login-box-msg">Enter your account details below</p>
|
||||
<div class="form-group has-feedback">
|
||||
<input type="text" class="form-control" placeholder="Username" name="username"
|
||||
data-error="Please input your username" required>
|
||||
<span class="glyphicon glyphicon-user form-control-feedback"></span>
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group has-feedback">
|
||||
<input type="password" class="form-control" placeholder="Password" id="password" name="password"
|
||||
data-error="Please input your password" required>
|
||||
<span class="glyphicon glyphicon-lock form-control-feedback"></span>
|
||||
</div>
|
||||
<div class="form-group has-feedback">
|
||||
<input type="password" class="form-control" placeholder="Retype password" name="rpassword"
|
||||
data-match="#password" data-match-error="Password confirmation does not match" required>
|
||||
<span class="glyphicon glyphicon-log-in form-control-feedback"></span>
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-4 pull-left">
|
||||
<button type="button" class="btn btn-flat btn-block" id="button_back">Back</button>
|
||||
</div>
|
||||
<div class="col-xs-4 pull-right">
|
||||
<button type="submit" class="btn btn-flat btn-primary btn-block">Register</button>
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<!-- /.form-box -->
|
||||
<div class="login-box-footer">
|
||||
<center>
|
||||
<p>Powered by <a href="https://github.com/PowerDNS-Admin/PowerDNS-Admin">PowerDNS-Admin</a></p>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.login-box -->
|
||||
|
||||
{% assets "js_login" -%}
|
||||
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
||||
{%- endassets %}
|
||||
{% assets "js_validation" -%}
|
||||
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
||||
{%- endassets %}
|
||||
<script>
|
||||
$(function () {
|
||||
$('#button_back').click(function () {
|
||||
window.location.href = '{{ url_for('index.login') }}';
|
||||
})
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
{% assets "js_login" -%}
|
||||
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
||||
{%- endassets %}
|
||||
{% assets "js_validation" -%}
|
||||
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
||||
{%- endassets %}
|
||||
<script>
|
||||
$(function () {
|
||||
$('#button_back').click(function () {
|
||||
window.location.href = '{{ url_for('index.login') }}';
|
||||
})
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -69,7 +69,7 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-4">
|
||||
<button type="submit" class="btn btn-flat btn-primary btn-block">Continue</button>
|
||||
<button type="submit" class="btn btn-primary btn-block">Continue</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -1,106 +1,129 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% set active_page = "admin_domain_template" %}
|
||||
{% block title %}<title>Templates - {{ SITE_NAME }}</title>{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
<title>
|
||||
Templates - {{ SITE_NAME }}
|
||||
</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('admin.templates') }}"><i class="fa fa-dashboard"></i> Templates</a></li>
|
||||
<li class="active">List</li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
Templates
|
||||
<small>List</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item active">Templates - List</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% 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 class="container-fluid">
|
||||
{% with errors = get_flashed_messages(category_filter=["error"]) %}
|
||||
{% if errors %}
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="alert alert-danger alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||
<h4>
|
||||
<i class="fa-solid 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-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Templates</h3>
|
||||
<a href="{{ url_for('admin.create_template') }}">
|
||||
<button type="button" class="btn btn-primary float-right">
|
||||
<i class="fa-solid fa-plus"></i> Create Template
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-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%">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for template in templates %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ url_for('admin.edit_template', template=template.name) }}">
|
||||
<strong>{{ template.name }}</strong>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
{{ template.description }}
|
||||
</td>
|
||||
<td>
|
||||
{{ template.records|count }}
|
||||
</td>
|
||||
<td>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-primary dropdown-toggle" type="button" id="dropdownMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="fa-solid fa-bars"></i> Actions
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownMenu">
|
||||
<button type="button" class="dropdown-item btn-warning" onclick="window.location.href='{{ url_for('admin.edit_template', template=template.name) }}'">
|
||||
<i class="fa-solid fa-edit"></i> Edit Template
|
||||
</button>
|
||||
<div class="dropdown-divider"></div>
|
||||
<button type="button"class="dropdown-item btn-secondary button_delete" id="{{template.name}}">
|
||||
<font color="red">
|
||||
<i class="fa-solid fa-trash"></i> Delete Template
|
||||
</font>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</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('admin.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('admin.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('admin.edit_template', template=template.name) }}">
|
||||
<button type="button" class="btn btn-flat btn-warning button_edit" id="btn_edit">
|
||||
Edit <i class="fa fa-edit"></i>
|
||||
</button>
|
||||
</a>
|
||||
<button type="button" class="btn btn-flat btn-danger button_delete" id="{{template.name}}">
|
||||
Delete <i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</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
|
||||
// set up template data table
|
||||
$("#tbl_template_list").DataTable({
|
||||
"paging": true,
|
||||
"lengthChange": true,
|
||||
@ -109,6 +132,7 @@
|
||||
"info": false,
|
||||
"autoWidth": false
|
||||
});
|
||||
|
||||
// handle delete button
|
||||
$(document.body).on("click", ".button_delete", function (e) {
|
||||
var template = $(this).prop('id');
|
||||
@ -117,9 +141,9 @@
|
||||
}, function () {
|
||||
window.location.href = '{{ url_for('admin.templates') }}';
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block modals %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
@ -1,103 +1,118 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% set active_page = "admin_domain_template" %}
|
||||
{% block title %}<title>Create Template - {{ SITE_NAME }}</title>{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
<title>
|
||||
Create Template - {{ SITE_NAME }}
|
||||
</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('admin.templates') }}"><i class="fa fa-dashboard"></i> Templates</a></li>
|
||||
<li class="active">Create</li>
|
||||
</ol>
|
||||
</section>
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
Template
|
||||
<small>Create</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('admin.templates') }}">Templates</a></li>
|
||||
<li class="breadcrumb-item active">Create</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% 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('admin.create_template') }}">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<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('admin.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>
|
||||
<div class="container-fluid">
|
||||
{% with errors = get_flashed_messages(category_filter=["error"]) %}
|
||||
{% if errors %}
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="alert alert-danger alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||
<h4>
|
||||
<i class="fa-solid 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>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Create new template</h3>
|
||||
</div>
|
||||
<form role="form" method="post" action="{{ url_for('admin.create_template') }}">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="card-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="card-footer">
|
||||
<button type="button" class="btn btn-secondary float-left" onclick="window.location.href='{{ url_for('admin.templates') }}'">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary float-right">
|
||||
<i class="fa-solid fa-save"></i> Save
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Help with creating a new template</h3>
|
||||
</div>
|
||||
<div class="card-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>
|
||||
</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();
|
||||
}
|
||||
});
|
||||
$("input[name=radio_type]").change(function () {
|
||||
var type = $(this).val();
|
||||
if (type == "secondary") {
|
||||
$("#domain_primary_address_div").show();
|
||||
} else {
|
||||
$("#domain_primary_address_div").hide();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@ -1,38 +1,52 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% set active_page = "admin_domain_template" %}
|
||||
{% block title %}<title>Edit Template - {{ SITE_NAME }}</title>{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
<title>
|
||||
Edit Template - {{ SITE_NAME }}
|
||||
</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.dashboard') }}"><i
|
||||
class="fa fa-dashboard"></i> Home</a></li>
|
||||
<li>Templates</li>
|
||||
<li class="active">{{ template }}</li>
|
||||
</ol>
|
||||
</section>
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
Template Edit
|
||||
<small>{{ template }}</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('admin.templates') }}">Templates</a></li>
|
||||
<li class="breadcrumb-item active">Template Edit: {{ template }}</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<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 class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-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>
|
||||
<div class="card-body">
|
||||
<button type="button" class="btn btn-primary float-left button_add_record" id="{{ template }}">
|
||||
<i class="fa-solid fa-plus"></i> Add Record
|
||||
</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 type="button" class="btn btn-primary float-right button_apply_changes" id="{{ template }}">
|
||||
<i class="fa-solid fa-save"></i> Save Changes
|
||||
</button>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="card-body">
|
||||
<table id="tbl_records" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
@ -69,17 +83,17 @@
|
||||
{{ record.comment }}
|
||||
</td>
|
||||
<td width="6%">
|
||||
<button type="button" class="btn btn-flat btn-warning button_edit">
|
||||
Edit <i class="fa fa-edit"></i>
|
||||
<button type="button" class="btn btn-warning button_edit">
|
||||
<i class="fa-solid fa-edit"></i> Edit
|
||||
</button>
|
||||
</td>
|
||||
<td width="6%">
|
||||
<button type="button" class="btn btn-flat btn-danger button_delete">
|
||||
Delete <i class="fa fa-trash"></i>
|
||||
<button type="button" class="btn btn-danger button_delete">
|
||||
<i class="fa-solid fa-trash"></i> Delete
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
{{ record.id }}
|
||||
{{ record.id }}
|
||||
</td>
|
||||
</td>
|
||||
</tr>
|
||||
@ -90,13 +104,14 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
{% block extrascripts %}
|
||||
<script>
|
||||
// superglobals
|
||||
window.records_allow_edit = {{ editable_records|tojson }};
|
||||
window.ttl_options = {{ ttl_options|tojson }};
|
||||
window.records_allow_edit = {{ editable_records | tojson }};
|
||||
window.ttl_options = {{ ttl_options | tojson }};
|
||||
window.nEditing = null;
|
||||
window.nNew = false;
|
||||
|
||||
@ -223,7 +238,7 @@
|
||||
|
||||
// add new row
|
||||
var default_type = records_allow_edit[0]
|
||||
var nRow = jQuery('#tbl_records').dataTable().fnAddData(['', default_type, 'Active', 3600, '', '', '', '', '0']);
|
||||
var nRow = jQuery('#tbl_records').dataTable().fnAddData(['', default_type, 'Active', window.ttl_options[0][0], '', '', '', '', '0']);
|
||||
editRow($("#tbl_records").DataTable(), nRow);
|
||||
document.getElementById("edit-row-focus").focus();
|
||||
nEditing = nRow;
|
||||
@ -415,67 +430,67 @@
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"
|
||||
<h4 class="modal-title">Confirmation</h4>
|
||||
<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" id="button_delete_cancel"
|
||||
data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-flat btn-danger" id="button_delete_confirm">Delete</button>
|
||||
<button type="button" class="btn btn-secondary float-left" id="button_delete_cancel" data-dismiss="modal">
|
||||
Close
|
||||
</button>
|
||||
<button type="button" class="btn 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"
|
||||
<h4 class="modal-title">Confirmation</h4>
|
||||
<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>
|
||||
<button type="button" class="btn btn-secondary float-left" data-dismiss="modal">
|
||||
Close
|
||||
</button>
|
||||
<button type="button" class="btn 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"
|
||||
<h4 class="modal-title">Custom Record</h4>
|
||||
<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>
|
||||
<button type="button" class="btn btn-primary" id="button_save">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -1,146 +1,169 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}<title>My Profile - {{ SITE_NAME }}</title>{% endblock %}
|
||||
{% block dashboard_stat %}
|
||||
<!-- Content Header (Page header) -->
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
Profile
|
||||
<small>Edit my profile</small>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ url_for('dashboard.dashboard') }}"><i class="fa fa-dashboard"></i>Home</a></li>
|
||||
<li class="active">My Profile</li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
{% set active_page = "user_profile" %}
|
||||
|
||||
{% block title %}
|
||||
<title>
|
||||
My Profile - {{ SITE_NAME }}
|
||||
</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block dashboard_stat %}
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
User
|
||||
<small>My Profile</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item active">My Profile</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Edit my profile{% if session['authentication_type'] != 'LOCAL' %} [Disabled -
|
||||
Authenticated externally]{% endif %}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<!-- Custom Tabs -->
|
||||
<div class="nav-tabs-custom" id="tabs">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a href="#tabs-personal" data-toggle="tab">Personal
|
||||
Info</a></li>
|
||||
{% if session['authentication_type'] == 'LOCAL' %}
|
||||
<li><a href="#tabs-password" data-toggle="tab">Change Password</a></li>
|
||||
{% endif %}
|
||||
{% if session['authentication_type'] in ['LOCAL', 'LDAP'] %}
|
||||
<li><a href="#tabs-authentication" data-toggle="tab">Authentication</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="tabs-personal">
|
||||
<form role="form" method="post" action="{{ user_profile }}">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="form-group">
|
||||
<label for="firstname">First Name</label> <input type="text"
|
||||
class="form-control" name="firstname" id="firstname"
|
||||
placeholder="{{ current_user.firstname }}"
|
||||
{% if session['authentication_type'] != 'LOCAL' %}disabled{% endif %}>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="lastname">Last Name</label> <input type="text" class="form-control"
|
||||
name="lastname" id="lastname" placeholder="{{ current_user.lastname }}"
|
||||
{% if session['authentication_type'] != 'LOCAL' %}disabled{% endif %}>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email">E-mail</label> <input type="email" class="form-control"
|
||||
name="email" id="email" placeholder="{{ current_user.email }}"
|
||||
{% if session['authentication_type'] != 'LOCAL' %}disabled{% endif %}>
|
||||
</div>{% if session['authentication_type'] == 'LOCAL' %}
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-flat btn-primary">Submit</button>
|
||||
</div>{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
{% if session['authentication_type'] == 'LOCAL' %}
|
||||
<div class="tab-pane" id="tabs-password">
|
||||
{% if not current_user.password %}
|
||||
Your account password is managed via LDAP which isn't supported to change here.
|
||||
{% else %}
|
||||
<form action="{{ user_profile }}" method="post">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="form-group">
|
||||
<label for="password">New Password</label> <input type="password"
|
||||
class="form-control" name="password" id="newpassword" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="rpassword">Re-type New Password</label> <input type="password"
|
||||
class="form-control" name="rpassword" id="rpassword" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-flat btn-primary">Change Password</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<!-- {% if session['authentication_type'] in ['LOCAL', 'LDAP'] %} -->
|
||||
<div class="tab-pane" id="tabs-authentication">
|
||||
<form action="{{ user_profile }}" method="post">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="form-group">
|
||||
<input type="checkbox" id="otp_toggle" class="otp_toggle"
|
||||
{% if current_user.otp_secret %}checked{% endif %}>
|
||||
<label for="otp_toggle">Enable Two Factor Authentication</label>
|
||||
{% if current_user.otp_secret %}
|
||||
<div id="token_information">
|
||||
<p><img id="qrcode" src="{{ url_for('user.qrcode') }}"></p>
|
||||
<div style="position: relative; left: 15px">
|
||||
Your secret key is: <br />
|
||||
<form>
|
||||
<input type=text id="otp_secret" value={{current_user.otp_secret}} readonly>
|
||||
<button type=button style="position:relative; right:28px" onclick="copy_otp_secret_to_clipboard()"> <i class="fa fa-clipboard"></i> </button>
|
||||
<br /><font color="red" id="copy_tooltip" style="visibility:collapse">Copied.</font>
|
||||
</form>
|
||||
</div>
|
||||
You can use Google Authenticator (<a target="_blank"
|
||||
href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2">Android</a>
|
||||
- <a target="_blank"
|
||||
href="https://apps.apple.com/us/app/google-authenticator/id388497605">iOS</a>)
|
||||
or FreeOTP (<a target="_blank"
|
||||
href="https://play.google.com/store/apps/details?id=org.fedorahosted.freeotp&hl=en">Android</a>
|
||||
- <a target="_blank"
|
||||
href="https://itunes.apple.com/en/app/freeotp-authenticator/id872559395?mt=8">iOS</a>)
|
||||
on your smartphone to scan the QR code.
|
||||
<br />
|
||||
<font color="red"><strong><i>Make sure only you can see this QR Code and secret key and
|
||||
nobody can capture them.</i></strong></font>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<!-- {% endif %} -->
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="nav-tabs-custom">
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="#tabs-personal" data-toggle="tab">
|
||||
Personal Info
|
||||
</a>
|
||||
</li>
|
||||
{% if session['authentication_type'] == 'LOCAL' %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#tabs-password" data-toggle="tab">
|
||||
Change Password
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if session['authentication_type'] in ['LOCAL', 'LDAP'] %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#tabs-authentication" data-toggle="tab">
|
||||
Authentication
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
<div class="card-body">
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane fade show active" id="tabs-personal">
|
||||
<form role="form" method="post" action="{{ user_profile }}">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="form-group">
|
||||
<label for="firstname">First Name</label>
|
||||
<input type="text" class="form-control" name="firstname" id="firstname" placeholder="{{ current_user.firstname }}"
|
||||
{% if session['authentication_type'] != 'LOCAL' %}disabled{% endif %}>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="lastname">Last Name</label>
|
||||
<input type="text" class="form-control" name="lastname" id="lastname" placeholder="{{ current_user.lastname }}"
|
||||
{% if session['authentication_type'] != 'LOCAL' %}disabled{% endif %}>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email">E-Mail</label> <input type="email" class="form-control" name="email" id="email" placeholder="{{ current_user.email }}"
|
||||
{% if session['authentication_type'] != 'LOCAL' %}disabled{% endif %}>
|
||||
</div>
|
||||
{% if session['authentication_type'] == 'LOCAL' %}
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fa-solid fa-floppy-disk"></i> Save
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
{% if session['authentication_type'] == 'LOCAL' %}
|
||||
<div class="tab-pane fade" id="tabs-password">
|
||||
{% if not current_user.password %}
|
||||
Your account password is managed via LDAP which isn't supported to change here.
|
||||
{% else %}
|
||||
<form action="{{ user_profile }}" method="post">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="form-group">
|
||||
<label for="password">New Password</label>
|
||||
<input type="password" class="form-control" name="password" id="newpassword">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="rpassword">Re-type New Password</label>
|
||||
<input type="password" class="form-control" name="rpassword" id="rpassword">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fa-solid fa-floppy-disk"></i> Save
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="tab-pane fade" id="tabs-authentication">
|
||||
<form action="{{ user_profile }}" method="post">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="form-group">
|
||||
<input type="checkbox" id="otp_toggle" class="otp_toggle" {% if current_user.otp_secret %}checked{% endif %}>
|
||||
<label for="otp_toggle">Enable Two Factor Authentication</label>
|
||||
{% if current_user.otp_secret %}
|
||||
<div id="token_information">
|
||||
<p>
|
||||
<img id="qrcode" src="{{ url_for('user.qrcode') }}">
|
||||
</p>
|
||||
<div style="position: relative; left: 15px">
|
||||
Your secret key is:
|
||||
<br />
|
||||
<form>
|
||||
<input type=text id="otp_secret" value={{current_user.otp_secret}} readonly>
|
||||
<button type=button style="position:relative; right:28px" onclick="copy_otp_secret_to_clipboard()">
|
||||
<i class="fa-solid fa-clipboard"></i>
|
||||
</button>
|
||||
<br />
|
||||
<font color="red" id="copy_tooltip" style="visibility:collapse">Copied.</font>
|
||||
</form>
|
||||
</div>
|
||||
You can use Google Authenticator
|
||||
(<a target="_blank" href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2"><i class="fa-brands fa-google-play"></i>Android</a>
|
||||
- <a target="_blank" href="https://apps.apple.com/us/app/google-authenticator/id388497605"><i class="fa-brands fa-app-store-ios"></i>iOS</a>)
|
||||
or FreeOTP
|
||||
(<a target="_blank" href="https://play.google.com/store/apps/details?id=org.fedorahosted.freeotp&hl=en"><i class="fa-brands fa-google-play"></i>Android</a>
|
||||
- <a target="_blank" href="https://itunes.apple.com/en/app/freeotp-authenticator/id872559395?mt=8"><i class="fa-brands fa-app-store-ios"></i>iOS</a>)
|
||||
on your smartphone to scan the QR code.
|
||||
<br />
|
||||
<font color="red">
|
||||
<strong>
|
||||
<i>Make sure only you can see this QR Code and secret key and
|
||||
nobody can capture them.
|
||||
</i>
|
||||
</strong>
|
||||
</font>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
{% block extrascripts %}
|
||||
<!-- TODO: add password and password confirmation comparison check -->
|
||||
<script>
|
||||
$(function () {
|
||||
$('#tabs').tabs({
|
||||
// add url anchor tags
|
||||
activate: function (event, ui) {
|
||||
window.location.hash = ui.newPanel.attr('id');
|
||||
}
|
||||
});
|
||||
// re-set active tab (ui)
|
||||
var activeTabIdx = $('#tabs').tabs('option', 'active');
|
||||
$('#tabs li:eq(' + activeTabIdx + ')').tab('show')
|
||||
});
|
||||
|
||||
// initialize pretty checkboxes
|
||||
$('.otp_toggle').iCheck({
|
||||
@ -149,16 +172,19 @@
|
||||
});
|
||||
|
||||
// handle checkbox toggling
|
||||
$('.otp_toggle').on('ifToggled', function (event) {
|
||||
var enable_otp = $(this).prop('checked');
|
||||
var postdata = {
|
||||
'action': 'enable_otp',
|
||||
'data': {
|
||||
'enable_otp': enable_otp
|
||||
},
|
||||
'_csrf_token': '{{ csrf_token() }}'
|
||||
};
|
||||
applyChanges(postdata, $SCRIPT_ROOT + '/user/profile', false, true);
|
||||
$('.otp_toggle').on('ifToggled', function(event) {
|
||||
var enable_otp = $(this).prop('checked');
|
||||
var postdata = {
|
||||
'action': 'enable_otp',
|
||||
'data': {
|
||||
'enable_otp': enable_otp
|
||||
},
|
||||
'_csrf_token': '{{ csrf_token() }}'
|
||||
};
|
||||
applyChanges(postdata, $SCRIPT_ROOT + '/user/profile', false, true, function() {
|
||||
window.location.reload();
|
||||
$('#tabs li:eq(2) a').tab('show');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@ -1,33 +1,44 @@
|
||||
Flask==1.1.2
|
||||
Authlib==1.2.0
|
||||
Flask-Assets==2.0
|
||||
Flask-Login==0.5.0
|
||||
Flask-SQLAlchemy==2.4.4
|
||||
Flask-Migrate==2.5.3
|
||||
SQLAlchemy==1.3.19
|
||||
mysqlclient==2.0.1
|
||||
configobj==5.0.6
|
||||
bcrypt>=3.1.7
|
||||
requests==2.24.0
|
||||
python-ldap==3.4.2
|
||||
pyotp==2.4.0
|
||||
qrcode==6.1
|
||||
dnspython>=1.16.0
|
||||
gunicorn==20.0.4
|
||||
python3-saml
|
||||
pytz==2020.1
|
||||
cssmin==0.2.0
|
||||
rjsmin==1.2.0
|
||||
Authlib==0.15
|
||||
Flask-SeaSurf==1.1.1
|
||||
bravado-core==5.17.0
|
||||
lima==0.5
|
||||
pytest==6.1.1
|
||||
pytimeparse==1.1.8
|
||||
PyYAML==5.4
|
||||
Flask-SSLify==0.1.5
|
||||
Flask-Login==0.6.2
|
||||
Flask-Mail==0.9.1
|
||||
flask-session==0.3.2
|
||||
Jinja2==3.0.3
|
||||
itsdangerous==2.0.1
|
||||
werkzeug==2.0.3
|
||||
Flask-Migrate==2.5.3
|
||||
Flask-SQLAlchemy==2.5.1
|
||||
Flask-SSLify==0.1.5
|
||||
Flask-SeaSurf==1.1.1
|
||||
Flask-Session==0.4.0
|
||||
Flask==2.1.3
|
||||
Jinja2==3.1.2
|
||||
PyYAML==5.4
|
||||
SQLAlchemy==1.3.24
|
||||
#alembic==1.9.0
|
||||
bcrypt==4.0.1
|
||||
bravado-core==5.17.1
|
||||
certifi==2022.12.7
|
||||
cffi==1.15.1
|
||||
configobj==5.0.8
|
||||
cryptography==36.0.2
|
||||
cssmin==0.2.0
|
||||
dnspython>=2.3.0
|
||||
flask_session_captcha==1.3.0
|
||||
gunicorn==20.1.0
|
||||
itsdangerous==2.1.2
|
||||
jsonschema[format]>=2.5.1,<4.0.0 # until https://github.com/Yelp/bravado-core/pull/385
|
||||
lima==0.5
|
||||
lxml==4.6.5
|
||||
mysqlclient==2.0.1
|
||||
passlib==1.7.4
|
||||
#pyOpenSSL==22.1.0
|
||||
pyasn1==0.4.8
|
||||
pyotp==2.8.0
|
||||
pytest==7.2.1
|
||||
python-ldap==3.4.3
|
||||
python3-saml==1.14.0
|
||||
pytimeparse==1.1.8
|
||||
pytz==2022.7.1
|
||||
qrcode==7.3.1
|
||||
requests==2.28.2
|
||||
rjsmin==1.2.1
|
||||
webcolors==1.12
|
||||
werkzeug==2.1.2
|
||||
zipp==3.11.0
|
||||
|
2
run.py
2
run.py
@ -3,4 +3,4 @@ from powerdnsadmin import create_app
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = create_app()
|
||||
app.run(debug = True, host=app.config.get('BIND_ADDRESS', '127.0.0.1'), port=app.config.get('PORT', '9191'))
|
||||
app.run(debug=True, host=app.config.get('BIND_ADDRESS', '127.0.0.1'), port=app.config.get('PORT', '9191'))
|
||||
|
@ -1,26 +1,28 @@
|
||||
import os
|
||||
import pytest
|
||||
import flask_migrate
|
||||
from base64 import b64encode
|
||||
|
||||
from powerdnsadmin import create_app
|
||||
from powerdnsadmin.models.base import db
|
||||
from powerdnsadmin.models.user import User
|
||||
from powerdnsadmin.models.setting import Setting
|
||||
from powerdnsadmin.models.api_key import ApiKey
|
||||
import pytest
|
||||
from flask_migrate import upgrade as flask_migrate_upgrade
|
||||
|
||||
app = create_app('../configs/test.py')
|
||||
ctx = app.app_context()
|
||||
ctx.push()
|
||||
from powerdnsadmin import create_app
|
||||
from powerdnsadmin.models.api_key import ApiKey
|
||||
from powerdnsadmin.models.base import db
|
||||
from powerdnsadmin.models.setting import Setting
|
||||
from powerdnsadmin.models.user import User
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def app():
|
||||
app = create_app('../configs/test.py')
|
||||
yield app
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
def client(app):
|
||||
app.config['TESTING'] = True
|
||||
client = app.test_client()
|
||||
yield client
|
||||
|
||||
|
||||
def load_data(setting_name, *args, **kwargs):
|
||||
if setting_name == 'maintenance':
|
||||
return 0
|
||||
@ -36,20 +38,22 @@ def load_data(setting_name, *args, **kwargs):
|
||||
return 10
|
||||
if setting_name == 'allow_user_create_domain':
|
||||
return True
|
||||
if setting_name == 'allow_user_remove_domain':
|
||||
return True
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def test_admin_user():
|
||||
def test_admin_user(app):
|
||||
return app.config.get('TEST_ADMIN_USER')
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def test_user():
|
||||
def test_user(app):
|
||||
return app.config.get('TEST_USER')
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def basic_auth_admin_headers():
|
||||
def basic_auth_admin_headers(app):
|
||||
test_admin_user = app.config.get('TEST_ADMIN_USER')
|
||||
test_admin_pass = app.config.get('TEST_ADMIN_PASSWORD')
|
||||
user_pass = "{0}:{1}".format(test_admin_user, test_admin_pass)
|
||||
@ -61,7 +65,7 @@ def basic_auth_admin_headers():
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def basic_auth_user_headers():
|
||||
def basic_auth_user_headers(app):
|
||||
test_user = app.config.get('TEST_USER')
|
||||
test_user_pass = app.config.get('TEST_USER_PASSWORD')
|
||||
user_pass = "{0}:{1}".format(test_user, test_user_pass)
|
||||
@ -73,7 +77,8 @@ def basic_auth_user_headers():
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def initial_data():
|
||||
def initial_data(app):
|
||||
|
||||
pdns_proto = os.environ['PDNS_PROTO']
|
||||
pdns_host = os.environ['PDNS_HOST']
|
||||
pdns_port = os.environ['PDNS_PORT']
|
||||
@ -83,46 +88,44 @@ def initial_data():
|
||||
api_key_setting = Setting('pdns_api_key', os.environ['PDNS_API_KEY'])
|
||||
allow_create_domain_setting = Setting('allow_user_create_domain', True)
|
||||
|
||||
try:
|
||||
flask_migrate.upgrade()
|
||||
with app.app_context():
|
||||
try:
|
||||
flask_migrate_upgrade(directory="migrations")
|
||||
db.session.add(api_url_setting)
|
||||
db.session.add(api_key_setting)
|
||||
db.session.add(allow_create_domain_setting)
|
||||
|
||||
db.session.add(api_url_setting)
|
||||
db.session.add(api_key_setting)
|
||||
db.session.add(allow_create_domain_setting)
|
||||
test_user = app.config.get('TEST_USER')
|
||||
test_user_pass = app.config.get('TEST_USER_PASSWORD')
|
||||
test_admin_user = app.config.get('TEST_ADMIN_USER')
|
||||
test_admin_pass = app.config.get('TEST_ADMIN_PASSWORD')
|
||||
|
||||
test_user = app.config.get('TEST_USER')
|
||||
test_user_pass = app.config.get('TEST_USER_PASSWORD')
|
||||
test_admin_user = app.config.get('TEST_ADMIN_USER')
|
||||
test_admin_pass = app.config.get('TEST_ADMIN_PASSWORD')
|
||||
admin_user = User(username=test_admin_user,
|
||||
plain_text_password=test_admin_pass,
|
||||
email="admin@admin.com")
|
||||
ret = admin_user.create_local_user()
|
||||
|
||||
admin_user = User(username=test_admin_user,
|
||||
plain_text_password=test_admin_pass,
|
||||
email="admin@admin.com")
|
||||
msg = admin_user.create_local_user()
|
||||
if not ret['status']:
|
||||
raise Exception("Error occurred creating user {0}".format(ret['msg']))
|
||||
|
||||
if not msg:
|
||||
raise Exception("Error occurred creating user {0}".format(msg))
|
||||
ordinary_user = User(username=test_user,
|
||||
plain_text_password=test_user_pass,
|
||||
email="test@test.com")
|
||||
ret = ordinary_user.create_local_user()
|
||||
|
||||
ordinary_user = User(username=test_user,
|
||||
plain_text_password=test_user_pass,
|
||||
email="test@test.com")
|
||||
msg = ordinary_user.create_local_user()
|
||||
if not ret['status']:
|
||||
raise Exception("Error occurred creating user {0}".format(ret['msg']))
|
||||
|
||||
if not msg:
|
||||
raise Exception("Error occurred creating user {0}".format(msg))
|
||||
|
||||
except Exception as e:
|
||||
print("Unexpected ERROR: {0}".format(e))
|
||||
raise e
|
||||
except Exception as e:
|
||||
print("Unexpected ERROR: {0}".format(e))
|
||||
raise e
|
||||
|
||||
yield
|
||||
|
||||
db.session.close()
|
||||
os.unlink(app.config['TEST_DB_LOCATION'])
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def initial_apikey_data():
|
||||
def initial_apikey_data(app):
|
||||
pdns_proto = os.environ['PDNS_PROTO']
|
||||
pdns_host = os.environ['PDNS_HOST']
|
||||
pdns_port = os.environ['PDNS_PORT']
|
||||
@ -131,42 +134,42 @@ def initial_apikey_data():
|
||||
api_url_setting = Setting('pdns_api_url', pdns_api_url)
|
||||
api_key_setting = Setting('pdns_api_key', os.environ['PDNS_API_KEY'])
|
||||
allow_create_domain_setting = Setting('allow_user_create_domain', True)
|
||||
allow_remove_domain_setting = Setting('allow_user_remove_domain', True)
|
||||
|
||||
try:
|
||||
flask_migrate.upgrade()
|
||||
with app.app_context():
|
||||
try:
|
||||
flask_migrate_upgrade(directory="migrations")
|
||||
db.session.add(api_url_setting)
|
||||
db.session.add(api_key_setting)
|
||||
db.session.add(allow_create_domain_setting)
|
||||
db.session.add(allow_remove_domain_setting)
|
||||
|
||||
db.session.add(api_url_setting)
|
||||
db.session.add(api_key_setting)
|
||||
db.session.add(allow_create_domain_setting)
|
||||
test_user_apikey = app.config.get('TEST_USER_APIKEY')
|
||||
test_admin_apikey = app.config.get('TEST_ADMIN_APIKEY')
|
||||
|
||||
test_user_apikey = app.config.get('TEST_USER_APIKEY')
|
||||
test_admin_apikey = app.config.get('TEST_ADMIN_APIKEY')
|
||||
dummy_apikey = ApiKey(desc="dummy", role_name="Administrator")
|
||||
|
||||
dummy_apikey = ApiKey(desc="dummy", role_name="Administrator")
|
||||
admin_key = dummy_apikey.get_hashed_password(
|
||||
plain_text_password=test_admin_apikey).decode('utf-8')
|
||||
|
||||
admin_key = dummy_apikey.get_hashed_password(
|
||||
plain_text_password=test_admin_apikey).decode('utf-8')
|
||||
admin_apikey = ApiKey(key=admin_key,
|
||||
desc="test admin apikey",
|
||||
role_name="Administrator")
|
||||
admin_apikey.create()
|
||||
|
||||
admin_apikey = ApiKey(key=admin_key,
|
||||
desc="test admin apikey",
|
||||
role_name="Administrator")
|
||||
admin_apikey.create()
|
||||
user_key = dummy_apikey.get_hashed_password(
|
||||
plain_text_password=test_user_apikey).decode('utf-8')
|
||||
|
||||
user_key = dummy_apikey.get_hashed_password(
|
||||
plain_text_password=test_user_apikey).decode('utf-8')
|
||||
user_apikey = ApiKey(key=user_key,
|
||||
desc="test user apikey",
|
||||
role_name="User")
|
||||
user_apikey.create()
|
||||
|
||||
user_apikey = ApiKey(key=user_key,
|
||||
desc="test user apikey",
|
||||
role_name="User")
|
||||
user_apikey.create()
|
||||
|
||||
except Exception as e:
|
||||
print("Unexpected ERROR: {0}".format(e))
|
||||
raise e
|
||||
except Exception as e:
|
||||
print("Unexpected ERROR: {0}".format(e))
|
||||
raise e
|
||||
|
||||
yield
|
||||
|
||||
db.session.close()
|
||||
os.unlink(app.config['TEST_DB_LOCATION'])
|
||||
|
||||
|
||||
@ -183,61 +186,51 @@ def zone_data():
|
||||
@pytest.fixture
|
||||
def created_zone_data():
|
||||
data = {
|
||||
'url':
|
||||
'/api/v1/servers/localhost/zones/example.org.',
|
||||
'soa_edit_api':
|
||||
'DEFAULT',
|
||||
'last_check':
|
||||
0,
|
||||
'url': '/api/v1/servers/localhost/zones/example.org.',
|
||||
'soa_edit_api': 'DEFAULT',
|
||||
'last_check': 0,
|
||||
'masters': [],
|
||||
'dnssec':
|
||||
False,
|
||||
'notified_serial':
|
||||
0,
|
||||
'nsec3narrow':
|
||||
False,
|
||||
'serial':
|
||||
2019013101,
|
||||
'nsec3param':
|
||||
'',
|
||||
'soa_edit':
|
||||
'',
|
||||
'api_rectify':
|
||||
False,
|
||||
'kind':
|
||||
'Native',
|
||||
'dnssec': False,
|
||||
'notified_serial': 0,
|
||||
'nsec3narrow': False,
|
||||
'serial': 2019013101,
|
||||
'nsec3param': '',
|
||||
'soa_edit': '',
|
||||
'api_rectify': False,
|
||||
'kind': 'Native',
|
||||
'rrsets': [{
|
||||
'comments': [],
|
||||
'type':
|
||||
'SOA',
|
||||
'name':
|
||||
'example.org.',
|
||||
'ttl':
|
||||
3600,
|
||||
'type': 'SOA',
|
||||
'name': 'example.org.',
|
||||
'ttl': 3600,
|
||||
'records': [{
|
||||
'content':
|
||||
'a.misconfigured.powerdns.server. hostmaster.example.org. 2019013101 10800 3600 604800 3600',
|
||||
'content': 'a.misconfigured.powerdns.server. hostmaster.example.org. 2019013101 10800 3600 604800 3600',
|
||||
'disabled': False
|
||||
}]
|
||||
}, {
|
||||
'comments': [],
|
||||
'type':
|
||||
'NS',
|
||||
'name':
|
||||
'example.org.',
|
||||
'ttl':
|
||||
3600,
|
||||
'type': 'NS',
|
||||
'name': 'example.org.',
|
||||
'ttl': 3600,
|
||||
'records': [{
|
||||
'content': 'ns1.example.org.',
|
||||
'disabled': False
|
||||
}]
|
||||
}],
|
||||
'name':
|
||||
'example.org.',
|
||||
'account':
|
||||
'',
|
||||
'id':
|
||||
'example.org.'
|
||||
'name': 'example.org.',
|
||||
'account': '',
|
||||
'id': 'example.org.'
|
||||
}
|
||||
return data
|
||||
|
||||
|
||||
def user_data(app):
|
||||
test_user = app.config.get('TEST_USER')
|
||||
test_user_pass = app.config.get('TEST_USER_PASSWORD')
|
||||
data = {
|
||||
"username": test_user,
|
||||
"plain_text_password": test_user_pass,
|
||||
"email": "test@test.com"
|
||||
}
|
||||
return data
|
||||
|
||||
@ -257,37 +250,39 @@ def admin_apikey_data():
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def user_apikey_integration():
|
||||
def user_apikey_integration(app):
|
||||
test_user_apikey = app.config.get('TEST_USER_APIKEY')
|
||||
headers = create_apikey_headers(test_user_apikey)
|
||||
return headers
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def admin_apikey_integration():
|
||||
def admin_apikey_integration(app):
|
||||
test_user_apikey = app.config.get('TEST_ADMIN_APIKEY')
|
||||
headers = create_apikey_headers(test_user_apikey)
|
||||
return headers
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def user_apikey():
|
||||
data = user_apikey_data()
|
||||
api_key = ApiKey(desc=data['description'],
|
||||
role_name=data['role'],
|
||||
domains=[])
|
||||
headers = create_apikey_headers(api_key.plain_key)
|
||||
return headers
|
||||
def user_apikey(app):
|
||||
with app.app_context():
|
||||
data = user_apikey_data()
|
||||
api_key = ApiKey(desc=data['description'],
|
||||
role_name=data['role'],
|
||||
domains=[])
|
||||
headers = create_apikey_headers(api_key.plain_key)
|
||||
return headers
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def admin_apikey():
|
||||
data = admin_apikey_data()
|
||||
api_key = ApiKey(desc=data['description'],
|
||||
role_name=data['role'],
|
||||
domains=[])
|
||||
headers = create_apikey_headers(api_key.plain_key)
|
||||
return headers
|
||||
def admin_apikey(app):
|
||||
with app.app_context():
|
||||
data = admin_apikey_data()
|
||||
api_key = ApiKey(desc=data['description'],
|
||||
role_name=data['role'],
|
||||
domains=[])
|
||||
headers = create_apikey_headers(api_key.plain_key)
|
||||
return headers
|
||||
|
||||
|
||||
def create_apikey_headers(passw):
|
@ -4,12 +4,11 @@ from collections import namedtuple
|
||||
|
||||
from powerdnsadmin.lib.validators import validate_apikey
|
||||
from powerdnsadmin.lib.schema import ApiKeySchema
|
||||
from tests.fixtures import client, initial_data, basic_auth_admin_headers
|
||||
from tests.fixtures import user_apikey_data, admin_apikey_data, zone_data
|
||||
from tests.conftest import user_apikey_data, admin_apikey_data
|
||||
|
||||
|
||||
class TestIntegrationApiApiKeyAdminUser(object):
|
||||
def test_empty_get(self, client, initial_data, basic_auth_admin_headers):
|
||||
def test_empty_get(self, initial_data, client, basic_auth_admin_headers):
|
||||
res = client.get("/api/v1/pdnsadmin/apikeys",
|
||||
headers=basic_auth_admin_headers)
|
||||
data = res.get_json(force=True)
|
||||
@ -19,7 +18,7 @@ class TestIntegrationApiApiKeyAdminUser(object):
|
||||
@pytest.mark.parametrize(
|
||||
"apikey_data",
|
||||
[user_apikey_data(), admin_apikey_data()])
|
||||
def test_create_apikey(self, client, initial_data, apikey_data, zone_data,
|
||||
def test_create_apikey(self, initial_data, client, apikey_data, zone_data,
|
||||
basic_auth_admin_headers):
|
||||
res = client.post("/api/v1/pdnsadmin/zones",
|
||||
headers=basic_auth_admin_headers,
|
||||
@ -39,7 +38,7 @@ class TestIntegrationApiApiKeyAdminUser(object):
|
||||
assert res.status_code == 201
|
||||
|
||||
apikey_url_format = "/api/v1/pdnsadmin/apikeys/{0}"
|
||||
apikey_url = apikey_url_format.format(data[0]['id'])
|
||||
apikey_url = apikey_url_format.format(data['id'])
|
||||
|
||||
res = client.delete(apikey_url, headers=basic_auth_admin_headers)
|
||||
|
||||
@ -54,7 +53,7 @@ class TestIntegrationApiApiKeyAdminUser(object):
|
||||
@pytest.mark.parametrize(
|
||||
"apikey_data",
|
||||
[user_apikey_data(), admin_apikey_data()])
|
||||
def test_get_multiple_apikey(self, client, initial_data, apikey_data,
|
||||
def test_get_multiple_apikey(self, initial_data, client, apikey_data,
|
||||
zone_data, basic_auth_admin_headers):
|
||||
res = client.post("/api/v1/pdnsadmin/zones",
|
||||
headers=basic_auth_admin_headers,
|
||||
@ -103,7 +102,7 @@ class TestIntegrationApiApiKeyAdminUser(object):
|
||||
@pytest.mark.parametrize(
|
||||
"apikey_data",
|
||||
[user_apikey_data(), admin_apikey_data()])
|
||||
def test_delete_apikey(self, client, initial_data, apikey_data, zone_data,
|
||||
def test_delete_apikey(self, initial_data, client, apikey_data, zone_data,
|
||||
basic_auth_admin_headers):
|
||||
res = client.post("/api/v1/pdnsadmin/zones",
|
||||
headers=basic_auth_admin_headers,
|
||||
@ -123,7 +122,7 @@ class TestIntegrationApiApiKeyAdminUser(object):
|
||||
assert res.status_code == 201
|
||||
|
||||
apikey_url_format = "/api/v1/pdnsadmin/apikeys/{0}"
|
||||
apikey_url = apikey_url_format.format(data[0]['id'])
|
||||
apikey_url = apikey_url_format.format(data['id'])
|
||||
res = client.delete(apikey_url, headers=basic_auth_admin_headers)
|
||||
|
||||
assert res.status_code == 204
|
||||
|
@ -1,14 +1,11 @@
|
||||
import pytest
|
||||
import json
|
||||
from collections import namedtuple
|
||||
|
||||
from powerdnsadmin.lib.validators import validate_zone
|
||||
from powerdnsadmin.lib.schema import DomainSchema
|
||||
from tests.fixtures import client, initial_data, basic_auth_user_headers
|
||||
from tests.fixtures import zone_data
|
||||
|
||||
|
||||
class TestIntegrationApiZoneUser(object):
|
||||
class TestIntegrationApiApiKeyUser(object):
|
||||
def test_empty_get(self, initial_data, client, basic_auth_user_headers):
|
||||
res = client.get("/api/v1/pdnsadmin/zones",
|
||||
headers=basic_auth_user_headers)
|
||||
|
@ -14,8 +14,10 @@ class IntegrationApiManagement(object):
|
||||
assert res.status_code == status_code
|
||||
if res.status_code == 200:
|
||||
data = res.get_json(force=True)
|
||||
assert len(data) == 1
|
||||
return data[0]
|
||||
assert isinstance(data, dict)
|
||||
assert len(data) == 7
|
||||
assert data.get('id', None)
|
||||
return data
|
||||
return None
|
||||
|
||||
def check_account(self, cmpdata, data=None):
|
||||
@ -37,8 +39,10 @@ class IntegrationApiManagement(object):
|
||||
assert res.status_code == status_code
|
||||
if status_code == 200:
|
||||
data = res.get_json(force=True)
|
||||
assert len(data) == 1
|
||||
return data[0]
|
||||
assert isinstance(data, dict)
|
||||
assert len(data) == 7
|
||||
assert data.get('id', None)
|
||||
return data
|
||||
return None
|
||||
|
||||
def check_user(self, cmpdata, data=None):
|
||||
@ -50,5 +54,5 @@ class IntegrationApiManagement(object):
|
||||
elif key == 'role':
|
||||
assert data[key]['name'] == cmpdata['role_name']
|
||||
else:
|
||||
assert key in ("id",)
|
||||
assert key in ("id","accounts",)
|
||||
return data
|
||||
|
@ -1,9 +1,5 @@
|
||||
|
||||
import json
|
||||
from tests.fixtures import ( # noqa: F401
|
||||
client, initial_data, basic_auth_admin_headers,
|
||||
test_admin_user, test_user, account_data, user1_data,
|
||||
)
|
||||
|
||||
from . import IntegrationApiManagement
|
||||
|
||||
|
||||
@ -89,8 +85,9 @@ class TestIntegrationApiManagementAdminUser(IntegrationApiManagement):
|
||||
)
|
||||
data = res.get_json(force=True)
|
||||
assert res.status_code == 200
|
||||
assert len(data) == 1
|
||||
data = data[0]
|
||||
assert isinstance(data, dict)
|
||||
assert len(data) == 7
|
||||
assert data.get('id', None)
|
||||
account_id = data["id"]
|
||||
for key, value in account_data.items():
|
||||
assert data[key] == value
|
||||
@ -142,10 +139,12 @@ class TestIntegrationApiManagementAdminUser(IntegrationApiManagement):
|
||||
)
|
||||
data = res.get_json(force=True)
|
||||
assert res.status_code == 201
|
||||
assert len(data) == 1
|
||||
assert isinstance(data, dict)
|
||||
assert len(data) == 6
|
||||
assert data.get('id', None)
|
||||
|
||||
# Check user
|
||||
user1 = self.check_user(user1_data, data[0])
|
||||
user1 = self.check_user(user1_data, data)
|
||||
user1_id = user1["id"]
|
||||
|
||||
updated = user1_data.copy()
|
||||
@ -240,10 +239,12 @@ class TestIntegrationApiManagementAdminUser(IntegrationApiManagement):
|
||||
)
|
||||
data = res.get_json(force=True)
|
||||
assert res.status_code == 201
|
||||
assert len(data) == 1
|
||||
assert isinstance(data, dict)
|
||||
assert len(data) == 6
|
||||
assert data.get('id', None)
|
||||
|
||||
# Check user
|
||||
user1 = self.check_user(user1_data, data[0])
|
||||
user1 = self.check_user(user1_data, data)
|
||||
user1_id = user1["id"]
|
||||
|
||||
# Assert test account has no users
|
||||
|
@ -1,66 +1,61 @@
|
||||
|
||||
import json
|
||||
|
||||
from tests.fixtures import ( # noqa: F401
|
||||
client, initial_data, basic_auth_admin_headers, basic_auth_user_headers,
|
||||
test_admin_user, test_user, account_data, user1_data,
|
||||
)
|
||||
from . import IntegrationApiManagement
|
||||
|
||||
|
||||
class TestIntegrationApiManagementUser(IntegrationApiManagement):
|
||||
|
||||
def test_accounts_empty_get(
|
||||
self, client, initial_data, # noqa: F811
|
||||
basic_auth_user_headers): # noqa: F811
|
||||
def test_accounts_empty_get(self, initial_data, client, # noqa: F811
|
||||
basic_auth_user_headers): # noqa: F811
|
||||
res = client.get("/api/v1/pdnsadmin/accounts",
|
||||
headers=basic_auth_user_headers)
|
||||
assert res.status_code == 401
|
||||
|
||||
def test_users_empty_get(
|
||||
self, client, initial_data, # noqa: F811
|
||||
test_admin_user, test_user, # noqa: F811
|
||||
basic_auth_user_headers): # noqa: F811
|
||||
def test_users_empty_get(self, initial_data, client, # noqa: F811
|
||||
test_admin_user, test_user, # noqa: F811
|
||||
basic_auth_user_headers): # noqa: F811
|
||||
res = client.get("/api/v1/pdnsadmin/users",
|
||||
headers=basic_auth_user_headers)
|
||||
assert res.status_code == 401
|
||||
|
||||
def test_self_get(
|
||||
self, initial_data, client, test_user, # noqa: F811
|
||||
basic_auth_user_headers): # noqa: F811
|
||||
self.user = None
|
||||
def test_self_get(self, initial_data, client, basic_auth_user_headers, test_user): # noqa: F811
|
||||
res = client.get("/api/v1/pdnsadmin/users/{}".format(test_user),
|
||||
headers=basic_auth_user_headers)
|
||||
data = res.get_json(force=True)
|
||||
assert res.status_code == 200
|
||||
assert len(data) == 1, data
|
||||
self.user = data
|
||||
assert data
|
||||
|
||||
def test_accounts(
|
||||
self, client, initial_data, # noqa: F811
|
||||
account_data, # noqa: F811
|
||||
basic_auth_admin_headers, basic_auth_user_headers): # noqa: F811
|
||||
def test_create_account_fail(self, client, initial_data, account_data, # noqa: F811
|
||||
basic_auth_user_headers): # noqa: F811
|
||||
|
||||
# Create account (should fail)
|
||||
res = client.post("/api/v1/pdnsadmin/accounts",
|
||||
headers=basic_auth_user_headers,
|
||||
data=json.dumps(account_data),
|
||||
content_type="application/json")
|
||||
assert res.status_code == 401
|
||||
|
||||
def test_create_account_as_admin(self, app, initial_data, client, account_data, # noqa: F811
|
||||
basic_auth_admin_headers): # noqa: F811
|
||||
self.client = client
|
||||
self.basic_auth_admin_headers = basic_auth_admin_headers
|
||||
|
||||
# Create account (should fail)
|
||||
res = client.post(
|
||||
"/api/v1/pdnsadmin/accounts",
|
||||
headers=basic_auth_user_headers,
|
||||
data=json.dumps(account_data),
|
||||
content_type="application/json",
|
||||
)
|
||||
assert res.status_code == 401
|
||||
with app.test_request_context():
|
||||
# Create account (as admin)
|
||||
res = client.post("/api/v1/pdnsadmin/accounts",
|
||||
headers=basic_auth_admin_headers,
|
||||
data=json.dumps(account_data),
|
||||
content_type="application/json")
|
||||
data = res.get_json(force=True)
|
||||
assert res.status_code == 201
|
||||
|
||||
# Create account (as admin)
|
||||
res = client.post(
|
||||
"/api/v1/pdnsadmin/accounts",
|
||||
headers=basic_auth_admin_headers,
|
||||
data=json.dumps(account_data),
|
||||
content_type="application/json",
|
||||
)
|
||||
data = res.get_json(force=True)
|
||||
assert res.status_code == 201
|
||||
def test_update_account_fail(
|
||||
self, initial_data, client, # noqa: F811
|
||||
account_data, # noqa: F811
|
||||
basic_auth_user_headers,
|
||||
basic_auth_admin_headers): # noqa: F811
|
||||
self.client = client
|
||||
self.basic_auth_admin_headers = basic_auth_admin_headers
|
||||
|
||||
# Check account
|
||||
data = self.check_account(account_data)
|
||||
@ -75,6 +70,18 @@ class TestIntegrationApiManagementUser(IntegrationApiManagement):
|
||||
)
|
||||
assert res.status_code == 401
|
||||
|
||||
def test_delete_account_fail(
|
||||
self, initial_data, client, # noqa: F811
|
||||
account_data, # noqa: F811
|
||||
basic_auth_user_headers,
|
||||
basic_auth_admin_headers): # noqa: F811
|
||||
self.client = client
|
||||
self.basic_auth_admin_headers = basic_auth_admin_headers
|
||||
|
||||
# Check account
|
||||
data = self.check_account(account_data)
|
||||
account_id = data["id"]
|
||||
|
||||
# Delete account (should fail)
|
||||
res = client.delete(
|
||||
"/api/v1/pdnsadmin/accounts/{}".format(account_id),
|
||||
@ -84,6 +91,17 @@ class TestIntegrationApiManagementUser(IntegrationApiManagement):
|
||||
)
|
||||
assert res.status_code == 401
|
||||
|
||||
def test_delete_account_as_admin(
|
||||
self, client, initial_data, # noqa: F811
|
||||
account_data, # noqa: F811
|
||||
basic_auth_admin_headers): # noqa: F811
|
||||
self.client = client
|
||||
self.basic_auth_admin_headers = basic_auth_admin_headers
|
||||
|
||||
# Check account
|
||||
data = self.check_account(account_data)
|
||||
account_id = data["id"]
|
||||
|
||||
# Cleanup (delete account as admin)
|
||||
res = client.delete(
|
||||
"/api/v1/pdnsadmin/accounts/{}".format(account_id),
|
||||
@ -94,8 +112,8 @@ class TestIntegrationApiManagementUser(IntegrationApiManagement):
|
||||
assert res.status_code == 204
|
||||
|
||||
def test_users(
|
||||
self, client, initial_data, # noqa: F811
|
||||
user1_data, # noqa: F811
|
||||
self, client, initial_data, # noqa: F811
|
||||
user1_data, # noqa: F811
|
||||
basic_auth_admin_headers, basic_auth_user_headers): # noqa: F811
|
||||
self.client = client
|
||||
self.basic_auth_admin_headers = basic_auth_admin_headers
|
||||
@ -118,10 +136,12 @@ class TestIntegrationApiManagementUser(IntegrationApiManagement):
|
||||
)
|
||||
data = res.get_json(force=True)
|
||||
assert res.status_code == 201
|
||||
assert len(data) == 1
|
||||
assert isinstance(data, dict)
|
||||
assert len(data) == 6
|
||||
assert data.get('id', None)
|
||||
|
||||
# Check user
|
||||
user1 = self.check_user(user1_data, data[0])
|
||||
user1 = self.check_user(user1_data, data)
|
||||
user1_id = user1["id"]
|
||||
|
||||
# Update to defaults (should fail)
|
||||
@ -152,8 +172,8 @@ class TestIntegrationApiManagementUser(IntegrationApiManagement):
|
||||
assert res.status_code == 204
|
||||
|
||||
def test_account_users(
|
||||
self, client, initial_data, # noqa: F811
|
||||
account_data, user1_data, # noqa: F811
|
||||
self, client, initial_data, # noqa: F811
|
||||
account_data, user1_data, # noqa: F811
|
||||
basic_auth_admin_headers, basic_auth_user_headers): # noqa: F811
|
||||
self.client = client
|
||||
self.basic_auth_admin_headers = basic_auth_admin_headers
|
||||
@ -181,10 +201,12 @@ class TestIntegrationApiManagementUser(IntegrationApiManagement):
|
||||
)
|
||||
data = res.get_json(force=True)
|
||||
assert res.status_code == 201
|
||||
assert len(data) == 1
|
||||
assert isinstance(data, dict)
|
||||
assert len(data) == 6
|
||||
assert data.get('id', None)
|
||||
|
||||
# Check user
|
||||
user1 = self.check_user(user1_data, data[0])
|
||||
user1 = self.check_user(user1_data, data)
|
||||
user1_id = user1["id"]
|
||||
|
||||
# Assert test account has no users
|
||||
|
@ -1,11 +1,8 @@
|
||||
import pytest
|
||||
import json
|
||||
from collections import namedtuple
|
||||
|
||||
from powerdnsadmin.lib.validators import validate_zone
|
||||
from powerdnsadmin.lib.schema import DomainSchema
|
||||
from tests.fixtures import client, initial_data, basic_auth_admin_headers
|
||||
from tests.fixtures import zone_data
|
||||
|
||||
|
||||
class TestIntegrationApiZoneAdminUser(object):
|
||||
|
@ -3,9 +3,6 @@ from collections import namedtuple
|
||||
|
||||
from powerdnsadmin.lib.validators import validate_zone
|
||||
from powerdnsadmin.lib.schema import DomainSchema
|
||||
from tests.fixtures import client
|
||||
from tests.fixtures import zone_data, initial_apikey_data
|
||||
from tests.fixtures import admin_apikey_integration
|
||||
|
||||
|
||||
class TestIntegrationApiZoneAdminApiKey(object):
|
||||
|
@ -3,9 +3,6 @@ from collections import namedtuple
|
||||
|
||||
from powerdnsadmin.lib.validators import validate_zone
|
||||
from powerdnsadmin.lib.schema import DomainSchema
|
||||
from tests.fixtures import client
|
||||
from tests.fixtures import zone_data, initial_apikey_data
|
||||
from tests.fixtures import user_apikey_integration
|
||||
|
||||
class TestIntegrationApiZoneUserApiKey(object):
|
||||
|
||||
|
@ -3,8 +3,6 @@ from collections import namedtuple
|
||||
|
||||
from powerdnsadmin.lib.validators import validate_zone
|
||||
from powerdnsadmin.lib.schema import DomainSchema
|
||||
from tests.fixtures import client, initial_data, basic_auth_user_headers
|
||||
from tests.fixtures import zone_data
|
||||
|
||||
|
||||
class TestIntegrationApiZoneUser(object):
|
||||
|
@ -2,9 +2,6 @@ import json
|
||||
import pytest
|
||||
from unittest.mock import patch
|
||||
from collections import namedtuple
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.getcwd())
|
||||
|
||||
import powerdnsadmin
|
||||
from powerdnsadmin.models.setting import Setting
|
||||
@ -12,79 +9,108 @@ from powerdnsadmin.models.domain import Domain
|
||||
from powerdnsadmin.models.api_key import ApiKey
|
||||
from powerdnsadmin.models.role import Role
|
||||
from powerdnsadmin.lib.validators import validate_zone
|
||||
from powerdnsadmin.lib.schema import DomainSchema, ApiKeySchema
|
||||
from tests.fixtures import client, initial_data, created_zone_data
|
||||
from tests.fixtures import user_apikey, admin_apikey, zone_data
|
||||
from tests.fixtures import admin_apikey_data, load_data
|
||||
from powerdnsadmin.lib.schema import DomainSchema
|
||||
from tests.conftest import admin_apikey_data, load_data
|
||||
|
||||
|
||||
class TestUnitApiZoneAdminApiKey(object):
|
||||
@pytest.fixture
|
||||
def common_data_mock(self):
|
||||
self.google_setting_patcher = patch(
|
||||
'powerdnsadmin.services.google.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.github_setting_patcher = patch(
|
||||
'powerdnsadmin.services.github.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.oidc_setting_patcher = patch(
|
||||
'powerdnsadmin.services.oidc.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.helpers_setting_patcher = patch(
|
||||
'powerdnsadmin.lib.helper.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.models_setting_patcher = patch(
|
||||
'powerdnsadmin.models.setting.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.domain_model_setting_patcher = patch(
|
||||
'powerdnsadmin.models.domain.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.record_model_setting_patcher = patch(
|
||||
'powerdnsadmin.models.record.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.server_model_setting_patcher = patch(
|
||||
'powerdnsadmin.models.server.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.mock_apikey_patcher = patch(
|
||||
'powerdnsadmin.decorators.ApiKey',
|
||||
spec=powerdnsadmin.models.api_key.ApiKey)
|
||||
self.mock_hist_patcher = patch(
|
||||
'powerdnsadmin.routes.api.History',
|
||||
spec=powerdnsadmin.models.history.History)
|
||||
self.mock_setting_patcher = patch(
|
||||
'powerdnsadmin.routes.api.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
def common_data_mock(self, app):
|
||||
with app.app_context():
|
||||
self.google_setting_patcher = patch(
|
||||
'powerdnsadmin.services.google.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.github_setting_patcher = patch(
|
||||
'powerdnsadmin.services.github.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.azure_setting_patcher = patch(
|
||||
'powerdnsadmin.services.azure.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.oidc_setting_patcher = patch(
|
||||
'powerdnsadmin.services.oidc.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.helpers_setting_patcher = patch(
|
||||
'powerdnsadmin.lib.helper.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.models_setting_patcher = patch(
|
||||
'powerdnsadmin.models.setting.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.domain_model_setting_patcher = patch(
|
||||
'powerdnsadmin.models.domain.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.record_model_setting_patcher = patch(
|
||||
'powerdnsadmin.models.record.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.server_model_setting_patcher = patch(
|
||||
'powerdnsadmin.models.server.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.mock_apikey_patcher = patch(
|
||||
'powerdnsadmin.decorators.ApiKey',
|
||||
spec=powerdnsadmin.models.api_key.ApiKey)
|
||||
self.mock_hist_patcher = patch(
|
||||
'powerdnsadmin.routes.api.History',
|
||||
spec=powerdnsadmin.models.history.History)
|
||||
self.mock_setting_patcher = patch(
|
||||
'powerdnsadmin.routes.api.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.mock_decorators_setting_patcher = patch(
|
||||
'powerdnsadmin.decorators.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
|
||||
data = admin_apikey_data()
|
||||
api_key = ApiKey(desc=data['description'],
|
||||
role_name=data['role'],
|
||||
domains=[])
|
||||
api_key.role = Role(name=data['role'])
|
||||
data = admin_apikey_data()
|
||||
api_key = ApiKey(desc=data['description'],
|
||||
role_name=data['role'],
|
||||
domains=[])
|
||||
api_key.role = Role(name=data['role'])
|
||||
|
||||
self.mock_google_setting = self.google_setting_patcher.start()
|
||||
self.mock_github_setting = self.github_setting_patcher.start()
|
||||
self.mock_oidc_setting = self.oidc_setting_patcher.start()
|
||||
self.mock_helpers_setting = self.helpers_setting_patcher.start()
|
||||
self.mock_models_setting = self.models_setting_patcher.start()
|
||||
self.mock_domain_model_setting = self.domain_model_setting_patcher.start(
|
||||
)
|
||||
self.mock_record_model_setting = self.record_model_setting_patcher.start(
|
||||
)
|
||||
self.mock_server_model_setting = self.server_model_setting_patcher.start(
|
||||
)
|
||||
self.mock_apikey = self.mock_apikey_patcher.start()
|
||||
self.mock_hist = self.mock_hist_patcher.start()
|
||||
self.mock_setting = self.mock_setting_patcher.start()
|
||||
self.mock_google_setting = self.google_setting_patcher.start()
|
||||
self.mock_github_setting = self.github_setting_patcher.start()
|
||||
self.mock_azure_setting = self.azure_setting_patcher.start()
|
||||
self.mock_oidc_setting = self.oidc_setting_patcher.start()
|
||||
self.mock_helpers_setting = self.helpers_setting_patcher.start()
|
||||
self.mock_models_setting = self.models_setting_patcher.start()
|
||||
self.mock_domain_model_setting = self.domain_model_setting_patcher.start(
|
||||
)
|
||||
self.mock_record_model_setting = self.record_model_setting_patcher.start(
|
||||
)
|
||||
self.mock_server_model_setting = self.server_model_setting_patcher.start(
|
||||
)
|
||||
self.mock_apikey = self.mock_apikey_patcher.start()
|
||||
self.mock_hist = self.mock_hist_patcher.start()
|
||||
self.mock_setting = self.mock_setting_patcher.start()
|
||||
self.mock_decorators_setting = self.mock_decorators_setting_patcher.start()
|
||||
|
||||
self.mock_google_setting.return_value.get.side_effect = load_data
|
||||
self.mock_github_setting.return_value.get.side_effect = load_data
|
||||
self.mock_azure_setting.return_value.get.side_effect = load_data
|
||||
self.mock_oidc_setting.return_value.get.side_effect = load_data
|
||||
self.mock_helpers_setting.return_value.get.side_effect = load_data
|
||||
self.mock_models_setting.return_value.get.side_effect = load_data
|
||||
self.mock_domain_model_setting.return_value.get.side_effect = load_data
|
||||
self.mock_record_model_setting.return_value.get.side_effect = load_data
|
||||
self.mock_server_model_setting.return_value.get.side_effect = load_data
|
||||
self.mock_decorators_setting.return_value.get.side_effect = load_data
|
||||
self.mock_apikey.return_value.is_validate.return_value = api_key
|
||||
|
||||
yield
|
||||
|
||||
for patcher in [
|
||||
self.google_setting_patcher,
|
||||
self.github_setting_patcher,
|
||||
self.azure_setting_patcher,
|
||||
self.oidc_setting_patcher,
|
||||
self.helpers_setting_patcher,
|
||||
self.models_setting_patcher,
|
||||
self.domain_model_setting_patcher,
|
||||
self.record_model_setting_patcher,
|
||||
self.server_model_setting_patcher,
|
||||
self.mock_apikey_patcher,
|
||||
self.mock_hist_patcher,
|
||||
self.mock_setting_patcher,
|
||||
self.mock_decorators_setting_patcher,
|
||||
]:
|
||||
patcher.stop()
|
||||
|
||||
self.mock_google_setting.return_value.get.side_effect = load_data
|
||||
self.mock_github_setting.return_value.get.side_effect = load_data
|
||||
self.mock_oidc_setting.return_value.get.side_effect = load_data
|
||||
self.mock_helpers_setting.return_value.get.side_effect = load_data
|
||||
self.mock_models_setting.return_value.get.side_effect = load_data
|
||||
self.mock_domain_model_setting.return_value.get.side_effect = load_data
|
||||
self.mock_record_model_setting.return_value.get.side_effect = load_data
|
||||
self.mock_server_model_setting.return_value.get.side_effect = load_data
|
||||
self.mock_apikey.return_value.is_validate.return_value = api_key
|
||||
|
||||
def test_empty_get(self, client, common_data_mock, admin_apikey):
|
||||
with patch('powerdnsadmin.routes.api.Domain') as mock_domain, \
|
||||
|
@ -5,37 +5,33 @@ from collections import namedtuple
|
||||
|
||||
import powerdnsadmin
|
||||
from powerdnsadmin.models.user import User
|
||||
from powerdnsadmin.models.role import Role
|
||||
from powerdnsadmin.models.domain import Domain
|
||||
from powerdnsadmin.lib.validators import validate_zone
|
||||
from powerdnsadmin.lib.schema import DomainSchema
|
||||
from tests.fixtures import client, basic_auth_admin_headers
|
||||
from tests.fixtures import zone_data, created_zone_data, load_data
|
||||
from tests.conftest import load_data
|
||||
|
||||
|
||||
class TestUnitApiZoneAdminUser(object):
|
||||
@pytest.fixture
|
||||
def common_data_mock(self):
|
||||
def common_data_mock(self, app, initial_data):
|
||||
|
||||
self.google_setting_patcher = patch(
|
||||
'powerdnsadmin.services.google.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.github_setting_patcher = patch(
|
||||
'powerdnsadmin.services.github.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.azure_setting_patcher = patch(
|
||||
'powerdnsadmin.services.azure.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.oidc_setting_patcher = patch(
|
||||
'powerdnsadmin.services.oidc.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.api_setting_patcher = patch(
|
||||
'powerdnsadmin.routes.api.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.base_route_user_patcher = patch(
|
||||
'powerdnsadmin.routes.base.User',
|
||||
spec=powerdnsadmin.models.user.User)
|
||||
self.helpers_setting_patcher = patch(
|
||||
'powerdnsadmin.lib.helper.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.models_setting_patcher = patch(
|
||||
'powerdnsadmin.models.Setting',
|
||||
'powerdnsadmin.models.setting.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.domain_model_setting_patcher = patch(
|
||||
'powerdnsadmin.models.domain.Setting',
|
||||
@ -46,50 +42,81 @@ class TestUnitApiZoneAdminUser(object):
|
||||
self.server_model_setting_patcher = patch(
|
||||
'powerdnsadmin.models.server.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.decorators_setting_patcher = patch(
|
||||
'powerdnsadmin.decorators.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.mock_user_patcher = patch('powerdnsadmin.decorators.User',
|
||||
spec=powerdnsadmin.models.user.User)
|
||||
self.mock_user_patcher = patch(
|
||||
'powerdnsadmin.decorators.User',
|
||||
spec=powerdnsadmin.models.user.User)
|
||||
self.mock_hist_patcher = patch(
|
||||
'powerdnsadmin.routes.api.History',
|
||||
spec=powerdnsadmin.models.history.History)
|
||||
self.mock_setting_patcher = patch(
|
||||
'powerdnsadmin.routes.api.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.mock_decorators_setting_patcher = patch(
|
||||
'powerdnsadmin.decorators.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.base_route_user_patcher = patch(
|
||||
'powerdnsadmin.routes.base.User',
|
||||
spec=powerdnsadmin.models.user.User)
|
||||
|
||||
self.mock_google_setting = self.google_setting_patcher.start()
|
||||
self.mock_github_setting = self.github_setting_patcher.start()
|
||||
self.mock_oidc_setting = self.oidc_setting_patcher.start()
|
||||
self.mock_base_route_user = self.base_route_user_patcher.start()
|
||||
self.mock_helpers_setting = self.helpers_setting_patcher.start()
|
||||
self.mock_models_setting = self.models_setting_patcher.start()
|
||||
self.mock_domain_model_setting = self.domain_model_setting_patcher.start(
|
||||
)
|
||||
self.mock_record_model_setting = self.record_model_setting_patcher.start(
|
||||
)
|
||||
self.mock_server_model_setting = self.server_model_setting_patcher.start(
|
||||
)
|
||||
self.decorators_setting = self.decorators_setting_patcher.start()
|
||||
self.api_setting = self.api_setting_patcher.start()
|
||||
self.mock_user = self.mock_user_patcher.start()
|
||||
self.mock_hist = self.mock_hist_patcher.start()
|
||||
with app.app_context():
|
||||
self.mock_google_setting = self.google_setting_patcher.start()
|
||||
self.mock_github_setting = self.github_setting_patcher.start()
|
||||
self.mock_azure_setting = self.azure_setting_patcher.start()
|
||||
self.mock_oidc_setting = self.oidc_setting_patcher.start()
|
||||
self.mock_base_route_user = self.base_route_user_patcher.start()
|
||||
self.mock_helpers_setting = self.helpers_setting_patcher.start()
|
||||
self.mock_models_setting = self.models_setting_patcher.start()
|
||||
self.mock_domain_model_setting = self.domain_model_setting_patcher.start(
|
||||
)
|
||||
self.mock_record_model_setting = self.record_model_setting_patcher.start(
|
||||
)
|
||||
self.mock_server_model_setting = self.server_model_setting_patcher.start(
|
||||
)
|
||||
self.mock_user = self.mock_user_patcher.start()
|
||||
self.mock_hist = self.mock_hist_patcher.start()
|
||||
self.mock_setting = self.mock_setting_patcher.start()
|
||||
self.mock_decorators_setting = self.mock_decorators_setting_patcher.start()
|
||||
|
||||
self.mock_google_setting.return_value.get.side_effect = load_data
|
||||
self.mock_github_setting.return_value.get.side_effect = load_data
|
||||
self.mock_oidc_setting.return_value.get.side_effect = load_data
|
||||
self.mock_helpers_setting.return_value.get.side_effect = load_data
|
||||
self.mock_models_setting.return_value.get.side_effect = load_data
|
||||
self.mock_domain_model_setting.return_value.get.side_effect = load_data
|
||||
self.mock_record_model_setting.return_value.get.side_effect = load_data
|
||||
self.mock_server_model_setting.return_value.get.side_effect = load_data
|
||||
self.decorators_setting.return_value.get.side_effect = load_data
|
||||
self.api_setting.return_value.get.side_effect = load_data
|
||||
self.mock_google_setting.return_value.get.side_effect = load_data
|
||||
self.mock_github_setting.return_value.get.side_effect = load_data
|
||||
self.mock_azure_setting.return_value.get.side_effect = load_data
|
||||
self.mock_oidc_setting.return_value.get.side_effect = load_data
|
||||
self.mock_helpers_setting.return_value.get.side_effect = load_data
|
||||
self.mock_models_setting.return_value.get.side_effect = load_data
|
||||
self.mock_domain_model_setting.return_value.get.side_effect = load_data
|
||||
self.mock_record_model_setting.return_value.get.side_effect = load_data
|
||||
self.mock_server_model_setting.return_value.get.side_effect = load_data
|
||||
self.mock_decorators_setting.return_value.get.side_effect = load_data
|
||||
self.mock_setting.return_value.get.side_effect = load_data
|
||||
|
||||
self.mockk = MagicMock()
|
||||
self.mockk.role.name = "Administrator"
|
||||
self.mockk = MagicMock()
|
||||
self.mockk.role.name = "Administrator"
|
||||
|
||||
self.mock_user.query.filter.return_value.first.return_value = self.mockk
|
||||
self.mock_user.return_value.is_validate.return_value = True
|
||||
self.mock_base_route_user.query.filter.return_value.first.return_value = self.mockk
|
||||
self.mock_base_route_user.return_value.is_validate.return_value = True
|
||||
|
||||
yield
|
||||
|
||||
for patcher in [
|
||||
self.google_setting_patcher,
|
||||
self.github_setting_patcher,
|
||||
self.azure_setting_patcher,
|
||||
self.oidc_setting_patcher,
|
||||
self.base_route_user_patcher,
|
||||
self.helpers_setting_patcher,
|
||||
self.models_setting_patcher,
|
||||
self.domain_model_setting_patcher,
|
||||
self.record_model_setting_patcher,
|
||||
self.server_model_setting_patcher,
|
||||
self.mock_user_patcher,
|
||||
self.mock_hist_patcher,
|
||||
self.mock_setting_patcher,
|
||||
self.mock_decorators_setting_patcher,
|
||||
]:
|
||||
patcher.stop()
|
||||
|
||||
self.mock_user.query.filter.return_value.first.return_value = self.mockk
|
||||
self.mock_user.return_value.is_validate.return_value = True
|
||||
self.mock_base_route_user.query.filter.return_value.first.return_value = self.mockk
|
||||
self.mock_base_route_user.return_value.is_validate.return_value = True
|
||||
|
||||
def test_empty_get(self, client, common_data_mock,
|
||||
basic_auth_admin_headers):
|
||||
@ -135,8 +162,7 @@ class TestUnitApiZoneAdminUser(object):
|
||||
headers=basic_auth_admin_headers)
|
||||
data = res.get_json(force=True)
|
||||
|
||||
fake_domain = namedtuple("Domain",
|
||||
data[0].keys())(*data[0].values())
|
||||
fake_domain = namedtuple("Domain", data[0].keys())(*data[0].values())
|
||||
domain_schema = DomainSchema(many=True)
|
||||
|
||||
json.dumps(domain_schema.dump([fake_domain]))
|
||||
|
@ -5,37 +5,33 @@ from collections import namedtuple
|
||||
|
||||
import powerdnsadmin
|
||||
from powerdnsadmin.models.user import User
|
||||
from powerdnsadmin.models.role import Role
|
||||
from powerdnsadmin.models.domain import Domain
|
||||
from powerdnsadmin.lib.validators import validate_zone
|
||||
from powerdnsadmin.lib.schema import DomainSchema
|
||||
from tests.fixtures import client, basic_auth_user_headers
|
||||
from tests.fixtures import zone_data, created_zone_data, load_data
|
||||
from tests.conftest import load_data
|
||||
|
||||
|
||||
class TestUnitApiZoneUser(object):
|
||||
@pytest.fixture
|
||||
def common_data_mock(self):
|
||||
def common_data_mock(self, app, initial_data):
|
||||
|
||||
self.google_setting_patcher = patch(
|
||||
'powerdnsadmin.services.google.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.github_setting_patcher = patch(
|
||||
'powerdnsadmin.services.github.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.azure_setting_patcher = patch(
|
||||
'powerdnsadmin.services.azure.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.oidc_setting_patcher = patch(
|
||||
'powerdnsadmin.services.oidc.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.api_setting_patcher = patch(
|
||||
'powerdnsadmin.routes.api.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.base_route_user_patcher = patch(
|
||||
'powerdnsadmin.routes.base.User',
|
||||
spec=powerdnsadmin.models.user.User)
|
||||
self.helpers_setting_patcher = patch(
|
||||
'powerdnsadmin.lib.helper.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.models_setting_patcher = patch(
|
||||
'powerdnsadmin.models.Setting',
|
||||
'powerdnsadmin.models.setting.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.domain_model_setting_patcher = patch(
|
||||
'powerdnsadmin.models.domain.Setting',
|
||||
@ -46,55 +42,86 @@ class TestUnitApiZoneUser(object):
|
||||
self.server_model_setting_patcher = patch(
|
||||
'powerdnsadmin.models.server.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.decorators_setting_patcher = patch(
|
||||
'powerdnsadmin.decorators.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.mock_user_patcher = patch('powerdnsadmin.decorators.User',
|
||||
spec=powerdnsadmin.models.user.User)
|
||||
self.mock_user_patcher = patch(
|
||||
'powerdnsadmin.decorators.User',
|
||||
spec=powerdnsadmin.models.user.User)
|
||||
self.mock_hist_patcher = patch(
|
||||
'powerdnsadmin.routes.api.History',
|
||||
spec=powerdnsadmin.models.history.History)
|
||||
self.mock_setting_patcher = patch(
|
||||
'powerdnsadmin.routes.api.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.mock_decorators_setting_patcher = patch(
|
||||
'powerdnsadmin.decorators.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.base_route_user_patcher = patch(
|
||||
'powerdnsadmin.routes.base.User',
|
||||
spec=powerdnsadmin.models.user.User)
|
||||
|
||||
self.mock_google_setting = self.google_setting_patcher.start()
|
||||
self.mock_github_setting = self.github_setting_patcher.start()
|
||||
self.mock_oidc_setting = self.oidc_setting_patcher.start()
|
||||
self.mock_base_route_user = self.base_route_user_patcher.start()
|
||||
self.mock_helpers_setting = self.helpers_setting_patcher.start()
|
||||
self.mock_models_setting = self.models_setting_patcher.start()
|
||||
self.mock_domain_model_setting = self.domain_model_setting_patcher.start(
|
||||
)
|
||||
self.mock_record_model_setting = self.record_model_setting_patcher.start(
|
||||
)
|
||||
self.mock_server_model_setting = self.server_model_setting_patcher.start(
|
||||
)
|
||||
self.decorators_setting = self.decorators_setting_patcher.start()
|
||||
self.api_setting = self.api_setting_patcher.start()
|
||||
self.mock_user = self.mock_user_patcher.start()
|
||||
self.mock_hist = self.mock_hist_patcher.start()
|
||||
with app.app_context():
|
||||
self.mock_google_setting = self.google_setting_patcher.start()
|
||||
self.mock_github_setting = self.github_setting_patcher.start()
|
||||
self.mock_azure_setting = self.azure_setting_patcher.start()
|
||||
self.mock_oidc_setting = self.oidc_setting_patcher.start()
|
||||
self.mock_base_route_user = self.base_route_user_patcher.start()
|
||||
self.mock_helpers_setting = self.helpers_setting_patcher.start()
|
||||
self.mock_models_setting = self.models_setting_patcher.start()
|
||||
self.mock_domain_model_setting = self.domain_model_setting_patcher.start(
|
||||
)
|
||||
self.mock_record_model_setting = self.record_model_setting_patcher.start(
|
||||
)
|
||||
self.mock_server_model_setting = self.server_model_setting_patcher.start(
|
||||
)
|
||||
self.mock_user = self.mock_user_patcher.start()
|
||||
self.mock_hist = self.mock_hist_patcher.start()
|
||||
self.mock_setting = self.mock_setting_patcher.start()
|
||||
self.mock_decorators_setting = self.mock_decorators_setting_patcher.start()
|
||||
|
||||
self.mock_google_setting.return_value.get.side_effect = load_data
|
||||
self.mock_github_setting.return_value.get.side_effect = load_data
|
||||
self.mock_oidc_setting.return_value.get.side_effect = load_data
|
||||
self.mock_helpers_setting.return_value.get.side_effect = load_data
|
||||
self.mock_models_setting.return_value.get.side_effect = load_data
|
||||
self.mock_domain_model_setting.return_value.get.side_effect = load_data
|
||||
self.mock_record_model_setting.return_value.get.side_effect = load_data
|
||||
self.mock_server_model_setting.return_value.get.side_effect = load_data
|
||||
self.decorators_setting.return_value.get.side_effect = load_data
|
||||
self.api_setting.return_value.get.side_effect = load_data
|
||||
self.mock_google_setting.return_value.get.side_effect = load_data
|
||||
self.mock_github_setting.return_value.get.side_effect = load_data
|
||||
self.mock_azure_setting.return_value.get.side_effect = load_data
|
||||
self.mock_oidc_setting.return_value.get.side_effect = load_data
|
||||
self.mock_helpers_setting.return_value.get.side_effect = load_data
|
||||
self.mock_models_setting.return_value.get.side_effect = load_data
|
||||
self.mock_domain_model_setting.return_value.get.side_effect = load_data
|
||||
self.mock_record_model_setting.return_value.get.side_effect = load_data
|
||||
self.mock_server_model_setting.return_value.get.side_effect = load_data
|
||||
self.mock_decorators_setting.return_value.get.side_effect = load_data
|
||||
self.mock_setting.return_value.get.side_effect = load_data
|
||||
|
||||
self.mockk = MagicMock()
|
||||
self.mockk.role.name = "User"
|
||||
self.mockk = MagicMock()
|
||||
self.mockk.role.name = "User"
|
||||
|
||||
self.mock_user.query.filter.return_value.first.return_value = self.mockk
|
||||
self.mock_user.return_value.is_validate.return_value = True
|
||||
self.mock_base_route_user.query.filter.return_value.first.return_value = self.mockk
|
||||
self.mock_base_route_user.return_value.is_validate.return_value = True
|
||||
|
||||
yield
|
||||
|
||||
for patcher in [
|
||||
self.google_setting_patcher,
|
||||
self.github_setting_patcher,
|
||||
self.azure_setting_patcher,
|
||||
self.oidc_setting_patcher,
|
||||
self.base_route_user_patcher,
|
||||
self.helpers_setting_patcher,
|
||||
self.models_setting_patcher,
|
||||
self.domain_model_setting_patcher,
|
||||
self.record_model_setting_patcher,
|
||||
self.server_model_setting_patcher,
|
||||
self.mock_user_patcher,
|
||||
self.mock_hist_patcher,
|
||||
self.mock_setting_patcher,
|
||||
self.mock_decorators_setting_patcher,
|
||||
]:
|
||||
patcher.stop()
|
||||
|
||||
self.mock_user.query.filter.return_value.first.return_value = self.mockk
|
||||
self.mock_user.return_value.is_validate.return_value = True
|
||||
self.mock_base_route_user.query.filter.return_value.first.return_value = self.mockk
|
||||
self.mock_base_route_user.return_value.is_validate.return_value = True
|
||||
|
||||
def test_create_zone(self, client, common_data_mock, zone_data,
|
||||
basic_auth_user_headers, created_zone_data):
|
||||
with patch('powerdnsadmin.lib.helper.requests.request') as mock_post, \
|
||||
patch('powerdnsadmin.routes.api.Domain') as mock_domain:
|
||||
patch('powerdnsadmin.routes.api.Domain') as mock_domain:
|
||||
mock_post.return_value.status_code = 201
|
||||
mock_post.return_value.content = json.dumps(created_zone_data)
|
||||
mock_post.return_value.headers = {}
|
||||
@ -115,8 +142,9 @@ class TestUnitApiZoneUser(object):
|
||||
with patch('powerdnsadmin.routes.api.get_user_domains') as mock_user_domains:
|
||||
test_domain = Domain(1, name=zone_data['name'].rstrip("."))
|
||||
mock_user_domains.return_value = [test_domain]
|
||||
|
||||
res = client.get("/api/v1/pdnsadmin/zones",
|
||||
headers=basic_auth_user_headers)
|
||||
headers=basic_auth_user_headers)
|
||||
data = res.get_json(force=True)
|
||||
|
||||
fake_domain = namedtuple("Domain", data[0].keys())(*data[0].values())
|
||||
@ -127,9 +155,9 @@ class TestUnitApiZoneUser(object):
|
||||
|
||||
def test_delete_zone(self, client, common_data_mock, zone_data,
|
||||
basic_auth_user_headers):
|
||||
with patch('powerdnsadmin.lib.utils.requests.request') as mock_delete, \
|
||||
patch('powerdnsadmin.routes.api.Domain') as mock_domain, \
|
||||
patch('powerdnsadmin.routes.api.get_user_domains') as mock_user_domains:
|
||||
with patch('powerdnsadmin.lib.helper.requests.request') as mock_delete, \
|
||||
patch('powerdnsadmin.routes.api.Domain') as mock_domain, \
|
||||
patch('powerdnsadmin.routes.api.get_user_domains') as mock_user_domains:
|
||||
test_domain = Domain(1, name=zone_data['name'].rstrip("."))
|
||||
|
||||
mock_domain.return_value.update.return_value = True
|
||||
|
@ -1,7 +1,6 @@
|
||||
import json
|
||||
import pytest
|
||||
from unittest.mock import patch
|
||||
from base64 import b64encode
|
||||
from collections import namedtuple
|
||||
|
||||
import powerdnsadmin
|
||||
@ -10,78 +9,111 @@ from powerdnsadmin.models.domain import Domain
|
||||
from powerdnsadmin.models.api_key import ApiKey
|
||||
from powerdnsadmin.models.role import Role
|
||||
from powerdnsadmin.lib.validators import validate_zone
|
||||
from powerdnsadmin.lib.schema import DomainSchema, ApiKeySchema
|
||||
from tests.fixtures import client, initial_data, created_zone_data
|
||||
from tests.fixtures import user_apikey, zone_data
|
||||
from tests.fixtures import user_apikey_data, load_data
|
||||
from powerdnsadmin.lib.schema import DomainSchema
|
||||
from tests.conftest import user_apikey_data, load_data
|
||||
|
||||
|
||||
class TestUnitApiZoneUserApiKey(object):
|
||||
@pytest.fixture
|
||||
def common_data_mock(self):
|
||||
self.google_setting_patcher = patch(
|
||||
'powerdnsadmin.services.google.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.github_setting_patcher = patch(
|
||||
'powerdnsadmin.services.github.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.oidc_setting_patcher = patch(
|
||||
'powerdnsadmin.services.oidc.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.helpers_setting_patcher = patch(
|
||||
'powerdnsadmin.lib.helper.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.models_setting_patcher = patch(
|
||||
'powerdnsadmin.models.setting.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.domain_model_setting_patcher = patch(
|
||||
'powerdnsadmin.models.domain.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.record_model_setting_patcher = patch(
|
||||
'powerdnsadmin.models.record.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.server_model_setting_patcher = patch(
|
||||
'powerdnsadmin.models.server.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.mock_apikey_patcher = patch(
|
||||
'powerdnsadmin.decorators.ApiKey',
|
||||
spec=powerdnsadmin.models.api_key.ApiKey)
|
||||
self.mock_hist_patcher = patch(
|
||||
'powerdnsadmin.routes.api.History',
|
||||
spec=powerdnsadmin.models.history.History)
|
||||
def common_data_mock(self, app):
|
||||
with app.app_context():
|
||||
self.google_setting_patcher = patch(
|
||||
'powerdnsadmin.services.google.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.github_setting_patcher = patch(
|
||||
'powerdnsadmin.services.github.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.azure_setting_patcher = patch(
|
||||
'powerdnsadmin.services.azure.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.oidc_setting_patcher = patch(
|
||||
'powerdnsadmin.services.oidc.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.helpers_setting_patcher = patch(
|
||||
'powerdnsadmin.lib.helper.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.models_setting_patcher = patch(
|
||||
'powerdnsadmin.models.setting.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.domain_model_setting_patcher = patch(
|
||||
'powerdnsadmin.models.domain.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.record_model_setting_patcher = patch(
|
||||
'powerdnsadmin.models.record.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.server_model_setting_patcher = patch(
|
||||
'powerdnsadmin.models.server.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.mock_apikey_patcher = patch(
|
||||
'powerdnsadmin.decorators.ApiKey',
|
||||
spec=powerdnsadmin.models.api_key.ApiKey)
|
||||
self.mock_hist_patcher = patch(
|
||||
'powerdnsadmin.routes.api.History',
|
||||
spec=powerdnsadmin.models.history.History)
|
||||
self.mock_setting_patcher = patch(
|
||||
'powerdnsadmin.routes.api.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
self.mock_decorators_setting_patcher = patch(
|
||||
'powerdnsadmin.decorators.Setting',
|
||||
spec=powerdnsadmin.models.setting.Setting)
|
||||
|
||||
self.mock_google_setting = self.google_setting_patcher.start()
|
||||
self.mock_github_setting = self.github_setting_patcher.start()
|
||||
self.mock_oidc_setting = self.oidc_setting_patcher.start()
|
||||
self.mock_helpers_setting = self.helpers_setting_patcher.start()
|
||||
self.mock_models_setting = self.models_setting_patcher.start()
|
||||
self.mock_domain_model_setting = self.domain_model_setting_patcher.start(
|
||||
)
|
||||
self.mock_record_model_setting = self.record_model_setting_patcher.start(
|
||||
)
|
||||
self.mock_server_model_setting = self.server_model_setting_patcher.start(
|
||||
)
|
||||
self.mock_apikey = self.mock_apikey_patcher.start()
|
||||
self.mock_hist = self.mock_hist_patcher.start()
|
||||
self.mock_google_setting = self.google_setting_patcher.start()
|
||||
self.mock_github_setting = self.github_setting_patcher.start()
|
||||
self.mock_azure_setting = self.azure_setting_patcher.start()
|
||||
self.mock_oidc_setting = self.oidc_setting_patcher.start()
|
||||
self.mock_helpers_setting = self.helpers_setting_patcher.start()
|
||||
self.mock_models_setting = self.models_setting_patcher.start()
|
||||
self.mock_domain_model_setting = self.domain_model_setting_patcher.start(
|
||||
)
|
||||
self.mock_record_model_setting = self.record_model_setting_patcher.start(
|
||||
)
|
||||
self.mock_server_model_setting = self.server_model_setting_patcher.start(
|
||||
)
|
||||
self.mock_apikey = self.mock_apikey_patcher.start()
|
||||
self.mock_hist = self.mock_hist_patcher.start()
|
||||
self.mock_setting = self.mock_setting_patcher.start()
|
||||
self.mock_decorators_setting = self.mock_decorators_setting_patcher.start()
|
||||
|
||||
self.mock_google_setting.return_value.get.side_effect = load_data
|
||||
self.mock_github_setting.return_value.get.side_effect = load_data
|
||||
self.mock_oidc_setting.return_value.get.side_effect = load_data
|
||||
self.mock_helpers_setting.return_value.get.side_effect = load_data
|
||||
self.mock_models_setting.return_value.get.side_effect = load_data
|
||||
self.mock_domain_model_setting.return_value.get.side_effect = load_data
|
||||
self.mock_record_model_setting.return_value.get.side_effect = load_data
|
||||
self.mock_server_model_setting.return_value.get.side_effect = load_data
|
||||
self.mock_google_setting.return_value.get.side_effect = load_data
|
||||
self.mock_github_setting.return_value.get.side_effect = load_data
|
||||
self.mock_azure_setting.return_value.get.side_effect = load_data
|
||||
self.mock_oidc_setting.return_value.get.side_effect = load_data
|
||||
self.mock_helpers_setting.return_value.get.side_effect = load_data
|
||||
self.mock_models_setting.return_value.get.side_effect = load_data
|
||||
self.mock_domain_model_setting.return_value.get.side_effect = load_data
|
||||
self.mock_record_model_setting.return_value.get.side_effect = load_data
|
||||
self.mock_server_model_setting.return_value.get.side_effect = load_data
|
||||
self.mock_decorators_setting.return_value.get.side_effect = load_data
|
||||
|
||||
data = user_apikey_data()
|
||||
domain = Domain(name=data['domains'][0])
|
||||
data = user_apikey_data()
|
||||
domain = Domain(name=data['domains'][0])
|
||||
|
||||
api_key = ApiKey(desc=data['description'],
|
||||
role_name=data['role'],
|
||||
domains=[domain])
|
||||
api_key.role = Role(name=data['role'])
|
||||
api_key = ApiKey(desc=data['description'],
|
||||
role_name=data['role'],
|
||||
domains=[domain])
|
||||
api_key.role = Role(name=data['role'])
|
||||
|
||||
self.mock_apikey.return_value.is_validate.return_value = api_key
|
||||
|
||||
yield
|
||||
|
||||
for patcher in [
|
||||
self.google_setting_patcher,
|
||||
self.github_setting_patcher,
|
||||
self.azure_setting_patcher,
|
||||
self.oidc_setting_patcher,
|
||||
self.helpers_setting_patcher,
|
||||
self.models_setting_patcher,
|
||||
self.domain_model_setting_patcher,
|
||||
self.record_model_setting_patcher,
|
||||
self.server_model_setting_patcher,
|
||||
self.mock_apikey_patcher,
|
||||
self.mock_hist_patcher,
|
||||
self.mock_setting_patcher,
|
||||
self.mock_decorators_setting_patcher,
|
||||
]:
|
||||
patcher.stop()
|
||||
|
||||
self.mock_apikey.return_value.is_validate.return_value = api_key
|
||||
|
||||
def test_create_zone(self, client, common_data_mock, zone_data,
|
||||
user_apikey, created_zone_data):
|
||||
@ -112,8 +144,7 @@ class TestUnitApiZoneUserApiKey(object):
|
||||
headers=user_apikey)
|
||||
data = res.get_json(force=True)
|
||||
|
||||
fake_domain = namedtuple("Domain",
|
||||
data[0].keys())(*data[0].values())
|
||||
fake_domain = namedtuple("Domain", data[0].keys())(*data[0].values())
|
||||
domain_schema = DomainSchema(many=True)
|
||||
|
||||
json.dumps(domain_schema.dump([fake_domain]))
|
||||
|
Loading…
Reference in New Issue
Block a user