mirror of
https://github.com/cwinfo/powerdns-admin.git
synced 2025-06-14 12:06:06 +00:00
Update docker stuff and bug fixes
This commit is contained in:
@ -2,58 +2,9 @@ import os
|
||||
from werkzeug.contrib.fixers import ProxyFix
|
||||
from flask import Flask
|
||||
from flask_seasurf import SeaSurf
|
||||
from flask_sslify import SSLify
|
||||
|
||||
from .lib import utils
|
||||
|
||||
# from flask_login import LoginManager
|
||||
# from flask_sqlalchemy import SQLAlchemy as SA
|
||||
# from flask_migrate import Migrate
|
||||
# from authlib.flask.client import OAuth as AuthlibOAuth
|
||||
# from sqlalchemy.exc import OperationalError
|
||||
|
||||
# from app.assets import assets
|
||||
|
||||
# ### SYBPATCH ###
|
||||
# from app.customboxes import customBoxes
|
||||
### SYBPATCH ###
|
||||
|
||||
# subclass SQLAlchemy to enable pool_pre_ping
|
||||
# class SQLAlchemy(SA):
|
||||
# def apply_pool_defaults(self, app, options):
|
||||
# SA.apply_pool_defaults(self, app, options)
|
||||
# options["pool_pre_ping"] = True
|
||||
|
||||
# app = Flask(__name__)
|
||||
# app.config.from_object('config')
|
||||
# app.wsgi_app = ProxyFix(app.wsgi_app)
|
||||
# csrf = SeaSurf(app)
|
||||
|
||||
# assets.init_app(app)
|
||||
|
||||
# #### CONFIGURE LOGGER ####
|
||||
# from app.lib.log import logger
|
||||
# logging = logger('powerdns-admin', app.config['LOG_LEVEL'], app.config['LOG_FILE']).config()
|
||||
|
||||
# login_manager = LoginManager()
|
||||
# login_manager.init_app(app)
|
||||
# db = SQLAlchemy(app) # database
|
||||
# migrate = Migrate(app, db) # flask-migrate
|
||||
# authlib_oauth_client = AuthlibOAuth(app) # authlib oauth
|
||||
|
||||
# if app.config.get('SAML_ENABLED') and app.config.get('SAML_ENCRYPT'):
|
||||
# from app.lib import certutil
|
||||
# if not certutil.check_certificate():
|
||||
# certutil.create_self_signed_cert()
|
||||
|
||||
# from app import models
|
||||
|
||||
# from app.blueprints.api import api_blueprint
|
||||
|
||||
# app.register_blueprint(api_blueprint, url_prefix='/api/v1')
|
||||
|
||||
# from app import views
|
||||
|
||||
|
||||
def create_app(config=None):
|
||||
from . import models, routes, services
|
||||
@ -63,9 +14,6 @@ def create_app(config=None):
|
||||
# Proxy
|
||||
app.wsgi_app = ProxyFix(app.wsgi_app)
|
||||
|
||||
# HSTS enabled
|
||||
_sslify = SSLify(app)
|
||||
|
||||
# CSRF protection
|
||||
csrf = SeaSurf(app)
|
||||
csrf.exempt(routes.index.dyndns_checkip)
|
||||
@ -80,10 +28,14 @@ def create_app(config=None):
|
||||
csrf.exempt(routes.api.api_zone_forward)
|
||||
csrf.exempt(routes.api.api_create_zone)
|
||||
|
||||
# Load default configuration
|
||||
app.config.from_object('powerdnsadmin.default_config')
|
||||
# Load config from env variables if using docker
|
||||
if os.path.exists(os.path.join(app.root_path, 'docker_config.py')):
|
||||
app.config.from_object('powerdnsadmin.docker_config')
|
||||
else:
|
||||
# Load default configuration
|
||||
app.config.from_object('powerdnsadmin.default_config')
|
||||
|
||||
# Load environment configuration
|
||||
# Load config file from FLASK_CONF env variable
|
||||
if 'FLASK_CONF' in os.environ:
|
||||
app.config.from_envvar('FLASK_CONF')
|
||||
|
||||
@ -94,6 +46,11 @@ def create_app(config=None):
|
||||
elif config.endswith('.py'):
|
||||
app.config.from_pyfile(config)
|
||||
|
||||
# HSTS
|
||||
if app.config.get('HSTS_ENABLED'):
|
||||
from flask_sslify import SSLify
|
||||
_sslify = SSLify(app)
|
||||
|
||||
# Load app's components
|
||||
assets.init_app(app)
|
||||
models.init_app(app)
|
||||
|
@ -1,102 +1,25 @@
|
||||
import os
|
||||
basedir = os.path.abspath(os.path.dirname(__file__))
|
||||
basedir = os.path.abspath(os.path.abspath(os.path.dirname(__file__)))
|
||||
|
||||
### BASIC APP CONFIG
|
||||
SALT = '$2b$12$yLUMTIfl21FKJQpTkRQXCu'
|
||||
|
||||
# BASIC APP CONFIG
|
||||
SECRET_KEY = 'We are the world'
|
||||
SECRET_KEY = 'e951e5a1f4b94151b360f47edf596dd2'
|
||||
BIND_ADDRESS = '0.0.0.0'
|
||||
PORT = 9191
|
||||
HSTS_ENABLED = False
|
||||
|
||||
# TIMEOUT - for large zones
|
||||
TIMEOUT = 10
|
||||
|
||||
# LOG CONFIG
|
||||
# - For docker, LOG_FILE=''
|
||||
LOG_LEVEL = 'DEBUG'
|
||||
|
||||
# DATABASE CONFIG
|
||||
### DATABASE CONFIG
|
||||
SQLA_DB_USER = 'pda'
|
||||
SQLA_DB_PASSWORD = 'changeme'
|
||||
SQLA_DB_HOST = '127.0.0.1'
|
||||
SQLA_DB_NAME = 'pda'
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = True
|
||||
|
||||
# DATBASE - MySQL
|
||||
### DATBASE - MySQL
|
||||
SQLALCHEMY_DATABASE_URI = 'mysql://'+SQLA_DB_USER+':'+SQLA_DB_PASSWORD+'@'+SQLA_DB_HOST+'/'+SQLA_DB_NAME
|
||||
|
||||
# DATABSE - SQLite
|
||||
#SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'pdns.db')
|
||||
### DATABSE - SQLite
|
||||
# SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'pdns.db')
|
||||
|
||||
# SAML Authnetication
|
||||
SAML_ENABLED = False
|
||||
# SAML_DEBUG = True
|
||||
# SAML_PATH = os.path.join(os.path.dirname(__file__), 'saml')
|
||||
# ##Example for ADFS Metadata-URL
|
||||
# SAML_METADATA_URL = 'https://<hostname>/FederationMetadata/2007-06/FederationMetadata.xml'
|
||||
# #Cache Lifetime in Seconds
|
||||
# SAML_METADATA_CACHE_LIFETIME = 1
|
||||
|
||||
# # SAML SSO binding format to use
|
||||
# ## Default: library default (urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect)
|
||||
# #SAML_IDP_SSO_BINDING = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
|
||||
|
||||
# ## EntityID of the IdP to use. Only needed if more than one IdP is
|
||||
# ## in the SAML_METADATA_URL
|
||||
# ### Default: First (only) IdP in the SAML_METADATA_URL
|
||||
# ### Example: https://idp.example.edu/idp
|
||||
# #SAML_IDP_ENTITY_ID = 'https://idp.example.edu/idp'
|
||||
# ## NameID format to request
|
||||
# ### Default: The SAML NameID Format in the metadata if present,
|
||||
# ### otherwise urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
|
||||
# ### Example: urn:oid:0.9.2342.19200300.100.1.1
|
||||
# #SAML_NAMEID_FORMAT = 'urn:oid:0.9.2342.19200300.100.1.1'
|
||||
|
||||
# ## Attribute to use for Email address
|
||||
# ### Default: email
|
||||
# ### Example: urn:oid:0.9.2342.19200300.100.1.3
|
||||
# #SAML_ATTRIBUTE_EMAIL = 'urn:oid:0.9.2342.19200300.100.1.3'
|
||||
|
||||
# ## Attribute to use for Given name
|
||||
# ### Default: givenname
|
||||
# ### Example: urn:oid:2.5.4.42
|
||||
# #SAML_ATTRIBUTE_GIVENNAME = 'urn:oid:2.5.4.42'
|
||||
|
||||
# ## Attribute to use for Surname
|
||||
# ### Default: surname
|
||||
# ### Example: urn:oid:2.5.4.4
|
||||
# #SAML_ATTRIBUTE_SURNAME = 'urn:oid:2.5.4.4'
|
||||
|
||||
# ## Attribute to use for username
|
||||
# ### Default: Use NameID instead
|
||||
# ### Example: urn:oid:0.9.2342.19200300.100.1.1
|
||||
# #SAML_ATTRIBUTE_USERNAME = 'urn:oid:0.9.2342.19200300.100.1.1'
|
||||
|
||||
# ## Attribute to get admin status from
|
||||
# ### Default: Don't control admin with SAML attribute
|
||||
# ### Example: https://example.edu/pdns-admin
|
||||
# ### If set, look for the value 'true' to set a user as an administrator
|
||||
# ### If not included in assertion, or set to something other than 'true',
|
||||
# ### the user is set as a non-administrator user.
|
||||
# #SAML_ATTRIBUTE_ADMIN = 'https://example.edu/pdns-admin'
|
||||
|
||||
# ## Attribute to get account names from
|
||||
# ### Default: Don't control accounts with SAML attribute
|
||||
# ### If set, the user will be added and removed from accounts to match
|
||||
# ### what's in the login assertion. Accounts that don't exist will
|
||||
# ### be created and the user added to them.
|
||||
# SAML_ATTRIBUTE_ACCOUNT = 'https://example.edu/pdns-account'
|
||||
|
||||
# SAML_SP_ENTITY_ID = 'http://<SAML SP Entity ID>'
|
||||
# SAML_SP_CONTACT_NAME = '<contact name>'
|
||||
# SAML_SP_CONTACT_MAIL = '<contact mail>'
|
||||
# #Cofigures if SAML tokens should be encrypted.
|
||||
# #If enabled a new app certificate will be generated on restart
|
||||
# SAML_SIGN_REQUEST = False
|
||||
# #Use SAML standard logout mechanism retreived from idp metadata
|
||||
# #If configured false don't care about SAML session on logout.
|
||||
# #Logout from PowerDNS-Admin only and keep SAML session authenticated.
|
||||
# SAML_LOGOUT = False
|
||||
# #Configure to redirect to a different url then PowerDNS-Admin login after SAML logout
|
||||
# #for example redirect to google.com after successful saml logout
|
||||
# #SAML_LOGOUT_URL = 'https://google.com'
|
||||
|
@ -12,44 +12,6 @@ from datetime import datetime, timedelta
|
||||
from threading import Thread
|
||||
|
||||
from .certutil import KEY_FILE, CERT_FILE
|
||||
# import logging as logger
|
||||
|
||||
# logging = logger.getLogger(__name__)
|
||||
|
||||
# if app.config['SAML_ENABLED']:
|
||||
# from onelogin.saml2.auth import OneLogin_Saml2_Auth
|
||||
# from onelogin.saml2.idp_metadata_parser import OneLogin_Saml2_IdPMetadataParser
|
||||
# idp_timestamp = datetime(1970, 1, 1)
|
||||
# idp_data = None
|
||||
# if 'SAML_IDP_ENTITY_ID' in app.config:
|
||||
# idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote(app.config['SAML_METADATA_URL'], entity_id=app.config.get('SAML_IDP_ENTITY_ID', None), required_sso_binding=app.config['SAML_IDP_SSO_BINDING'])
|
||||
# else:
|
||||
# idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote(app.config['SAML_METADATA_URL'], entity_id=app.config.get('SAML_IDP_ENTITY_ID', None))
|
||||
# if idp_data is None:
|
||||
# print('SAML: IDP Metadata initial load failed')
|
||||
# exit(-1)
|
||||
# idp_timestamp = datetime.now()
|
||||
|
||||
# def get_idp_data():
|
||||
# global idp_data, idp_timestamp
|
||||
# lifetime = timedelta(minutes=app.config['SAML_METADATA_CACHE_LIFETIME'])
|
||||
# if idp_timestamp+lifetime < datetime.now():
|
||||
# background_thread = Thread(target=retrieve_idp_data)
|
||||
# background_thread.start()
|
||||
# return idp_data
|
||||
|
||||
# def retrieve_idp_data():
|
||||
# global idp_data, idp_timestamp
|
||||
# if 'SAML_IDP_SSO_BINDING' in app.config:
|
||||
# new_idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote(app.config['SAML_METADATA_URL'], entity_id=app.config.get('SAML_IDP_ENTITY_ID', None), required_sso_binding=app.config['SAML_IDP_SSO_BINDING'])
|
||||
# else:
|
||||
# new_idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote(app.config['SAML_METADATA_URL'], entity_id=app.config.get('SAML_IDP_ENTITY_ID', None))
|
||||
# if new_idp_data is not None:
|
||||
# idp_data = new_idp_data
|
||||
# idp_timestamp = datetime.now()
|
||||
# print("SAML: IDP Metadata successfully retrieved from: " + app.config['SAML_METADATA_URL'])
|
||||
# else:
|
||||
# print("SAML: IDP Metadata could not be retrieved")
|
||||
|
||||
|
||||
def auth_from_url(url):
|
||||
@ -115,6 +77,8 @@ def fetch_json(remote_url, method='GET', data=None, params=None, headers=None, t
|
||||
|
||||
if r.status_code == 204:
|
||||
return {}
|
||||
elif r.status_code == 409:
|
||||
return {'error': 'Resource already exists or conflict', 'http_code': r.status_code}
|
||||
|
||||
try:
|
||||
assert ('json' in r.headers['content-type'])
|
||||
|
@ -24,7 +24,7 @@ bravado_config = {
|
||||
'use_models': True,
|
||||
}
|
||||
|
||||
dir_path = os.path.dirname(os.path.abspath(__file__))
|
||||
dir_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
spec_path = os.path.join(dir_path, "swagger-spec.yaml")
|
||||
spec_dict = get_swagger_spec(spec_path)
|
||||
spec = Spec.from_dict(spec_dict, config=bravado_config)
|
@ -1,26 +0,0 @@
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import ldap
|
||||
import ldap.filter
|
||||
import base64
|
||||
import bcrypt
|
||||
import itertools
|
||||
import traceback
|
||||
import pyotp
|
||||
import dns.reversename
|
||||
import dns.inet
|
||||
import dns.name
|
||||
import pytimeparse
|
||||
import random
|
||||
import string
|
||||
|
||||
from ast import literal_eval
|
||||
from datetime import datetime
|
||||
from urllib.parse import urljoin
|
||||
from distutils.util import strtobool
|
||||
from distutils.version import StrictVersion
|
||||
from flask_login import AnonymousUserMixin
|
||||
from app import db, app
|
||||
from app.lib import utils
|
||||
from app.lib.log import logging
|
@ -243,6 +243,8 @@ class Domain(db.Model):
|
||||
data=post_data)
|
||||
if 'error' in jdata.keys():
|
||||
current_app.logger.error(jdata['error'])
|
||||
if jdata.get('http_code') == 409:
|
||||
return {'status': 'error', 'msg': 'Domain already exists'}
|
||||
return {'status': 'error', 'msg': jdata['error']}
|
||||
else:
|
||||
current_app.logger.info(
|
||||
|
@ -281,7 +281,7 @@ def api_get_apikeys(domain_name):
|
||||
if current_user.role.name not in ['Administrator', 'Operator']:
|
||||
if domain_name:
|
||||
msg = "Check if domain {0} exists and \
|
||||
is allowed for user." .format(domain_name)
|
||||
is allowed for user." .format(domain_name)
|
||||
current_app.logger.debug(msg)
|
||||
apikeys = current_user.get_apikeys(domain_name)
|
||||
|
||||
@ -502,14 +502,18 @@ def api_create_zone(server_id):
|
||||
@api_bp.route('/servers/<string:server_id>/zones', methods=['GET'])
|
||||
@apikey_auth
|
||||
def api_get_zones(server_id):
|
||||
if g.apikey.role.name not in ['Administrator', 'Operator']:
|
||||
domain_obj_list = g.apikey.domains
|
||||
if server_id == 'powerdns-admin':
|
||||
if g.apikey.role.name not in ['Administrator', 'Operator']:
|
||||
domain_obj_list = g.apikey.domains
|
||||
else:
|
||||
domain_obj_list = Domain.query.all()
|
||||
return json.dumps(domain_schema.dump(domain_obj_list)), 200
|
||||
else:
|
||||
domain_obj_list = Domain.query.all()
|
||||
return json.dumps(domain_schema.dump(domain_obj_list)), 200
|
||||
resp = helper.forward_request()
|
||||
return resp.content, resp.status_code, resp.headers.items()
|
||||
|
||||
|
||||
#endpoint to snychronize Domains in background
|
||||
# The endpoint to snychronize Domains in background
|
||||
@api_bp.route('/sync_domains', methods=['GET'])
|
||||
@apikey_auth
|
||||
def sync_domains():
|
||||
|
@ -1,4 +1,4 @@
|
||||
from flask import Blueprint, render_template, make_response, url_for, current_app, request, jsonify
|
||||
from flask import Blueprint, render_template, make_response, url_for, current_app, request, jsonify, redirect
|
||||
from flask_login import login_required, current_user
|
||||
from sqlalchemy import not_, or_
|
||||
|
||||
@ -120,7 +120,7 @@ def domains_custom(boxId):
|
||||
def dashboard():
|
||||
if not Setting().get('pdns_api_url') or not Setting().get(
|
||||
'pdns_api_key') or not Setting().get('pdns_version'):
|
||||
return redirect(url_for('admin_setting_pdns'))
|
||||
return redirect(url_for('admin.setting_pdns'))
|
||||
|
||||
BG_DOMAIN_UPDATE = Setting().get('bg_domain_updates')
|
||||
if not BG_DOMAIN_UPDATE:
|
||||
|
@ -111,7 +111,7 @@ def add():
|
||||
|
||||
if ' ' in domain_name or not domain_name or not domain_type:
|
||||
return render_template('errors/400.html',
|
||||
msg="Please correct your input"), 400
|
||||
msg="Please enter a valid domain name"), 400
|
||||
|
||||
if domain_type == 'slave':
|
||||
if request.form.getlist('domain_master_address'):
|
||||
|
@ -25,8 +25,12 @@
|
||||
<i class="fa fa-warning text-yellow"></i> Oops! Bad request
|
||||
</h3>
|
||||
<p>
|
||||
The server refused to process your request and return a 400 error.
|
||||
You may <a href="{{ url_for('dashboard.dashboard') }}">return to the dashboard</a>.
|
||||
{% 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 -->
|
||||
|
Reference in New Issue
Block a user