mirror of
https://github.com/cwinfo/powerdns-admin.git
synced 2024-12-04 11:05:32 +00:00
Overhauled app settings implementation to remove redundancy of definitions. Additionally, re-factored settings initialization code to allow for every setting to be defined by environment variable for both bare metal and Docker container deployments.
This commit is contained in:
parent
9ddfde02b8
commit
c842d09195
@ -1,151 +1,2 @@
|
|||||||
# import everything from environment variables
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
|
|
||||||
# Defaults for Docker image
|
|
||||||
BIND_ADDRESS = '0.0.0.0'
|
|
||||||
PORT = 80
|
PORT = 80
|
||||||
SERVER_EXTERNAL_SSL = os.getenv('SERVER_EXTERNAL_SSL', True)
|
SQLALCHEMY_DATABASE_URI = 'sqlite:////data/powerdns-admin.db'
|
||||||
SQLALCHEMY_DATABASE_URI = 'sqlite:////data/powerdns-admin.db'
|
|
||||||
SESSION_COOKIE_SAMESITE = 'Lax'
|
|
||||||
CSRF_COOKIE_HTTPONLY = True
|
|
||||||
SESSION_TYPE = 'sqlalchemy'
|
|
||||||
|
|
||||||
legal_envvars = (
|
|
||||||
'SECRET_KEY',
|
|
||||||
'OIDC_OAUTH_ENABLED',
|
|
||||||
'OIDC_OAUTH_KEY',
|
|
||||||
'OIDC_OAUTH_SECRET',
|
|
||||||
'OIDC_OAUTH_API_URL',
|
|
||||||
'OIDC_OAUTH_TOKEN_URL',
|
|
||||||
'OIDC_OAUTH_AUTHORIZE_URL',
|
|
||||||
'OIDC_OAUTH_METADATA_URL',
|
|
||||||
'OIDC_OAUTH_LOGOUT_URL',
|
|
||||||
'OIDC_OAUTH_SCOPE',
|
|
||||||
'OIDC_OAUTH_USERNAME',
|
|
||||||
'OIDC_OAUTH_FIRSTNAME',
|
|
||||||
'OIDC_OAUTH_LAST_NAME',
|
|
||||||
'OIDC_OAUTH_EMAIL',
|
|
||||||
'BIND_ADDRESS',
|
|
||||||
'PORT',
|
|
||||||
'SERVER_EXTERNAL_SSL',
|
|
||||||
'LOG_LEVEL',
|
|
||||||
'SALT',
|
|
||||||
'SQLALCHEMY_TRACK_MODIFICATIONS',
|
|
||||||
'SQLALCHEMY_DATABASE_URI',
|
|
||||||
'SQLALCHEMY_ENGINE_OPTIONS',
|
|
||||||
'MAIL_SERVER',
|
|
||||||
'MAIL_PORT',
|
|
||||||
'MAIL_DEBUG',
|
|
||||||
'MAIL_USE_TLS',
|
|
||||||
'MAIL_USE_SSL',
|
|
||||||
'MAIL_USERNAME',
|
|
||||||
'MAIL_PASSWORD',
|
|
||||||
'MAIL_DEFAULT_SENDER',
|
|
||||||
'SAML_ENABLED',
|
|
||||||
'SAML_DEBUG',
|
|
||||||
'SAML_PATH',
|
|
||||||
'SAML_METADATA_URL',
|
|
||||||
'SAML_METADATA_CACHE_LIFETIME',
|
|
||||||
'SAML_IDP_SSO_BINDING',
|
|
||||||
'SAML_IDP_ENTITY_ID',
|
|
||||||
'SAML_NAMEID_FORMAT',
|
|
||||||
'SAML_ATTRIBUTE_EMAIL',
|
|
||||||
'SAML_ATTRIBUTE_GIVENNAME',
|
|
||||||
'SAML_ATTRIBUTE_SURNAME',
|
|
||||||
'SAML_ATTRIBUTE_NAME',
|
|
||||||
'SAML_ATTRIBUTE_USERNAME',
|
|
||||||
'SAML_ATTRIBUTE_ADMIN',
|
|
||||||
'SAML_ATTRIBUTE_GROUP',
|
|
||||||
'SAML_GROUP_ADMIN_NAME',
|
|
||||||
'SAML_GROUP_TO_ACCOUNT_MAPPING',
|
|
||||||
'SAML_ATTRIBUTE_ACCOUNT',
|
|
||||||
'SAML_SP_ENTITY_ID',
|
|
||||||
'SAML_SP_CONTACT_NAME',
|
|
||||||
'SAML_SP_CONTACT_MAIL',
|
|
||||||
'SAML_SIGN_REQUEST',
|
|
||||||
'SAML_WANT_MESSAGE_SIGNED',
|
|
||||||
'SAML_LOGOUT',
|
|
||||||
'SAML_LOGOUT_URL',
|
|
||||||
'SAML_ASSERTION_ENCRYPTED',
|
|
||||||
'REMOTE_USER_LOGOUT_URL',
|
|
||||||
'REMOTE_USER_COOKIES',
|
|
||||||
'SIGNUP_ENABLED',
|
|
||||||
'LOCAL_DB_ENABLED',
|
|
||||||
'LDAP_ENABLED',
|
|
||||||
'SAML_CERT',
|
|
||||||
'SAML_KEY',
|
|
||||||
'SESSION_TYPE',
|
|
||||||
'SESSION_COOKIE_SECURE',
|
|
||||||
'CSRF_COOKIE_SECURE',
|
|
||||||
'CAPTCHA_ENABLE',
|
|
||||||
)
|
|
||||||
|
|
||||||
legal_envvars_int = ('PORT', 'MAIL_PORT', 'SAML_METADATA_CACHE_LIFETIME')
|
|
||||||
|
|
||||||
legal_envvars_bool = (
|
|
||||||
'SQLALCHEMY_TRACK_MODIFICATIONS',
|
|
||||||
'HSTS_ENABLED',
|
|
||||||
'MAIL_DEBUG',
|
|
||||||
'MAIL_USE_TLS',
|
|
||||||
'MAIL_USE_SSL',
|
|
||||||
'OIDC_OAUTH_ENABLED',
|
|
||||||
'SAML_ENABLED',
|
|
||||||
'SAML_DEBUG',
|
|
||||||
'SAML_SIGN_REQUEST',
|
|
||||||
'SAML_WANT_MESSAGE_SIGNED',
|
|
||||||
'SAML_LOGOUT',
|
|
||||||
'SAML_ASSERTION_ENCRYPTED',
|
|
||||||
'REMOTE_USER_ENABLED',
|
|
||||||
'SIGNUP_ENABLED',
|
|
||||||
'LOCAL_DB_ENABLED',
|
|
||||||
'LDAP_ENABLED',
|
|
||||||
'SESSION_COOKIE_SECURE',
|
|
||||||
'CSRF_COOKIE_SECURE',
|
|
||||||
'CAPTCHA_ENABLE',
|
|
||||||
'SERVER_EXTERNAL_SSL',
|
|
||||||
)
|
|
||||||
|
|
||||||
legal_envvars_dict = (
|
|
||||||
'SQLALCHEMY_ENGINE_OPTIONS',
|
|
||||||
)
|
|
||||||
|
|
||||||
def str2bool(v):
|
|
||||||
return v.lower() in ("true", "yes", "1")
|
|
||||||
|
|
||||||
|
|
||||||
def dictfromstr(v, ret):
|
|
||||||
try:
|
|
||||||
return json.loads(ret)
|
|
||||||
except Exception as e:
|
|
||||||
print('Cannot parse json {} for variable {}'.format(ret, v))
|
|
||||||
print(e)
|
|
||||||
raise ValueError
|
|
||||||
|
|
||||||
|
|
||||||
for v in legal_envvars:
|
|
||||||
|
|
||||||
ret = None
|
|
||||||
# _FILE suffix will allow to read value from file, useful for Docker containers.
|
|
||||||
# secrets feature
|
|
||||||
if v + '_FILE' in os.environ:
|
|
||||||
if v in os.environ:
|
|
||||||
raise AttributeError(
|
|
||||||
"Both {} and {} are set but are exclusive.".format(
|
|
||||||
v, v + '_FILE'))
|
|
||||||
with open(os.environ[v + '_FILE']) as f:
|
|
||||||
ret = f.read()
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
elif v in os.environ:
|
|
||||||
ret = os.environ[v]
|
|
||||||
|
|
||||||
if ret is not None:
|
|
||||||
if v in legal_envvars_bool:
|
|
||||||
ret = str2bool(ret)
|
|
||||||
if v in legal_envvars_int:
|
|
||||||
ret = int(ret)
|
|
||||||
if v in legal_envvars_dict:
|
|
||||||
ret = dictfromstr(v, ret)
|
|
||||||
sys.modules[__name__].__dict__[v] = ret
|
|
@ -4,11 +4,11 @@ from flask import Flask
|
|||||||
from flask_mail import Mail
|
from flask_mail import Mail
|
||||||
from werkzeug.middleware.proxy_fix import ProxyFix
|
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||||
from flask_session import Session
|
from flask_session import Session
|
||||||
|
|
||||||
from .lib import utils
|
from .lib import utils
|
||||||
|
|
||||||
|
|
||||||
def create_app(config=None):
|
def create_app(config=None):
|
||||||
|
from powerdnsadmin.lib.settings import AppSettings
|
||||||
from . import models, routes, services
|
from . import models, routes, services
|
||||||
from .assets import assets
|
from .assets import assets
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
@ -50,6 +50,9 @@ def create_app(config=None):
|
|||||||
elif config.endswith('.py'):
|
elif config.endswith('.py'):
|
||||||
app.config.from_pyfile(config)
|
app.config.from_pyfile(config)
|
||||||
|
|
||||||
|
# Load any settings defined with environment variables
|
||||||
|
AppSettings.load_environment(app)
|
||||||
|
|
||||||
# HSTS
|
# HSTS
|
||||||
if app.config.get('HSTS_ENABLED'):
|
if app.config.get('HSTS_ENABLED'):
|
||||||
from flask_sslify import SSLify
|
from flask_sslify import SSLify
|
||||||
|
@ -13,6 +13,7 @@ def admin_role_required(f):
|
|||||||
"""
|
"""
|
||||||
Grant access if user is in Administrator role
|
Grant access if user is in Administrator role
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
if current_user.role.name != 'Administrator':
|
if current_user.role.name != 'Administrator':
|
||||||
@ -26,6 +27,7 @@ def operator_role_required(f):
|
|||||||
"""
|
"""
|
||||||
Grant access if user is in Operator role or higher
|
Grant access if user is in Operator role or higher
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
if current_user.role.name not in ['Administrator', 'Operator']:
|
if current_user.role.name not in ['Administrator', 'Operator']:
|
||||||
@ -39,6 +41,7 @@ def history_access_required(f):
|
|||||||
"""
|
"""
|
||||||
Grant access if user is in Operator role or higher, or Users can view history
|
Grant access if user is in Operator role or higher, or Users can view history
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
if current_user.role.name not in [
|
if current_user.role.name not in [
|
||||||
@ -57,6 +60,7 @@ def can_access_domain(f):
|
|||||||
- user is in granted Account, or
|
- user is in granted Account, or
|
||||||
- user is in granted Domain
|
- user is in granted Domain
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
if current_user.role.name not in ['Administrator', 'Operator']:
|
if current_user.role.name not in ['Administrator', 'Operator']:
|
||||||
@ -83,10 +87,11 @@ def can_configure_dnssec(f):
|
|||||||
- user is in Operator role or higher, or
|
- user is in Operator role or higher, or
|
||||||
- dnssec_admins_only is off
|
- dnssec_admins_only is off
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
if current_user.role.name not in [
|
if current_user.role.name not in [
|
||||||
'Administrator', 'Operator'
|
'Administrator', 'Operator'
|
||||||
] and Setting().get('dnssec_admins_only'):
|
] and Setting().get('dnssec_admins_only'):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
@ -94,16 +99,18 @@ def can_configure_dnssec(f):
|
|||||||
|
|
||||||
return decorated_function
|
return decorated_function
|
||||||
|
|
||||||
|
|
||||||
def can_remove_domain(f):
|
def can_remove_domain(f):
|
||||||
"""
|
"""
|
||||||
Grant access if:
|
Grant access if:
|
||||||
- user is in Operator role or higher, or
|
- user is in Operator role or higher, or
|
||||||
- allow_user_remove_domain is on
|
- allow_user_remove_domain is on
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
if current_user.role.name not in [
|
if current_user.role.name not in [
|
||||||
'Administrator', 'Operator'
|
'Administrator', 'Operator'
|
||||||
] and not Setting().get('allow_user_remove_domain'):
|
] and not Setting().get('allow_user_remove_domain'):
|
||||||
abort(403)
|
abort(403)
|
||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
@ -111,17 +118,17 @@ def can_remove_domain(f):
|
|||||||
return decorated_function
|
return decorated_function
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def can_create_domain(f):
|
def can_create_domain(f):
|
||||||
"""
|
"""
|
||||||
Grant access if:
|
Grant access if:
|
||||||
- user is in Operator role or higher, or
|
- user is in Operator role or higher, or
|
||||||
- allow_user_create_domain is on
|
- allow_user_create_domain is on
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
if current_user.role.name not in [
|
if current_user.role.name not in [
|
||||||
'Administrator', 'Operator'
|
'Administrator', 'Operator'
|
||||||
] and not Setting().get('allow_user_create_domain'):
|
] and not Setting().get('allow_user_create_domain'):
|
||||||
abort(403)
|
abort(403)
|
||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
@ -144,11 +151,12 @@ def api_basic_auth(f):
|
|||||||
|
|
||||||
# Remove "Basic " from the header value
|
# Remove "Basic " from the header value
|
||||||
auth_header = auth_header[6:]
|
auth_header = auth_header[6:]
|
||||||
|
auth_components = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
auth_header = str(base64.b64decode(auth_header), 'utf-8')
|
auth_header = str(base64.b64decode(auth_header), 'utf-8')
|
||||||
# NK: We use auth_components here as we don't know if we'll have a :, we split it maximum 1 times to grab the
|
# NK: We use auth_components here as we don't know if we'll have a colon,
|
||||||
# username, the rest of the string would be the password.
|
# we split it maximum 1 times to grab the username, the rest of the string would be the password.
|
||||||
auth_components = auth_header.split(':', maxsplit=1)
|
auth_components = auth_header.split(':', maxsplit=1)
|
||||||
except (binascii.Error, UnicodeDecodeError) as e:
|
except (binascii.Error, UnicodeDecodeError) as e:
|
||||||
current_app.logger.error(
|
current_app.logger.error(
|
||||||
@ -211,16 +219,19 @@ def callback_if_request_body_contains_key(callback, http_methods=[], keys=[]):
|
|||||||
If request body contains one or more of specified keys, call
|
If request body contains one or more of specified keys, call
|
||||||
:param callback
|
:param callback
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def decorator(f):
|
def decorator(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
check_current_http_method = not http_methods or request.method in http_methods
|
check_current_http_method = not http_methods or request.method in http_methods
|
||||||
if (check_current_http_method and
|
if (check_current_http_method and
|
||||||
set(request.get_json(force=True).keys()).intersection(set(keys))
|
set(request.get_json(force=True).keys()).intersection(set(keys))
|
||||||
):
|
):
|
||||||
callback(*args, **kwargs)
|
callback(*args, **kwargs)
|
||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
|
|
||||||
return decorated_function
|
return decorated_function
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
@ -246,16 +257,18 @@ def api_role_can(action, roles=None, allow_self=False):
|
|||||||
except:
|
except:
|
||||||
username = None
|
username = None
|
||||||
if (
|
if (
|
||||||
(current_user.role.name in roles) or
|
(current_user.role.name in roles) or
|
||||||
(allow_self and user_id and current_user.id == user_id) or
|
(allow_self and user_id and current_user.id == user_id) or
|
||||||
(allow_self and username and current_user.username == username)
|
(allow_self and username and current_user.username == username)
|
||||||
):
|
):
|
||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
msg = (
|
msg = (
|
||||||
"User {} with role {} does not have enough privileges to {}"
|
"User {} with role {} does not have enough privileges to {}"
|
||||||
).format(current_user.username, current_user.role.name, action)
|
).format(current_user.username, current_user.role.name, action)
|
||||||
raise NotEnoughPrivileges(message=msg)
|
raise NotEnoughPrivileges(message=msg)
|
||||||
|
|
||||||
return decorated_function
|
return decorated_function
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
@ -265,15 +278,16 @@ def api_can_create_domain(f):
|
|||||||
- user is in Operator role or higher, or
|
- user is in Operator role or higher, or
|
||||||
- allow_user_create_domain is on
|
- allow_user_create_domain is on
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
if current_user.role.name not in [
|
if current_user.role.name not in [
|
||||||
'Administrator', 'Operator'
|
'Administrator', 'Operator'
|
||||||
] and not Setting().get('allow_user_create_domain'):
|
] and not Setting().get('allow_user_create_domain'):
|
||||||
msg = "User {0} does not have enough privileges to create zone"
|
msg = "User {0} does not have enough privileges to create zone"
|
||||||
current_app.logger.error(msg.format(current_user.username))
|
current_app.logger.error(msg.format(current_user.username))
|
||||||
raise NotEnoughPrivileges()
|
raise NotEnoughPrivileges()
|
||||||
|
|
||||||
if Setting().get('deny_domain_override'):
|
if Setting().get('deny_domain_override'):
|
||||||
req = request.get_json(force=True)
|
req = request.get_json(force=True)
|
||||||
domain = Domain()
|
domain = Domain()
|
||||||
@ -294,10 +308,11 @@ def apikey_can_create_domain(f):
|
|||||||
- deny_domain_override is off or
|
- deny_domain_override is off or
|
||||||
- override_domain is true (from request)
|
- override_domain is true (from request)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
if g.apikey.role.name not in [
|
if g.apikey.role.name not in [
|
||||||
'Administrator', 'Operator'
|
'Administrator', 'Operator'
|
||||||
] and not Setting().get('allow_user_create_domain'):
|
] and not Setting().get('allow_user_create_domain'):
|
||||||
msg = "ApiKey #{0} does not have enough privileges to create zone"
|
msg = "ApiKey #{0} does not have enough privileges to create zone"
|
||||||
current_app.logger.error(msg.format(g.apikey.id))
|
current_app.logger.error(msg.format(g.apikey.id))
|
||||||
@ -320,20 +335,23 @@ def apikey_can_remove_domain(http_methods=[]):
|
|||||||
- user is in Operator role or higher, or
|
- user is in Operator role or higher, or
|
||||||
- allow_user_remove_domain is on
|
- allow_user_remove_domain is on
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def decorator(f):
|
def decorator(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
check_current_http_method = not http_methods or request.method in http_methods
|
check_current_http_method = not http_methods or request.method in http_methods
|
||||||
|
|
||||||
if (check_current_http_method and
|
if (check_current_http_method and
|
||||||
g.apikey.role.name not in ['Administrator', 'Operator'] and
|
g.apikey.role.name not in ['Administrator', 'Operator'] and
|
||||||
not Setting().get('allow_user_remove_domain')
|
not Setting().get('allow_user_remove_domain')
|
||||||
):
|
):
|
||||||
msg = "ApiKey #{0} does not have enough privileges to remove zone"
|
msg = "ApiKey #{0} does not have enough privileges to remove zone"
|
||||||
current_app.logger.error(msg.format(g.apikey.id))
|
current_app.logger.error(msg.format(g.apikey.id))
|
||||||
raise NotEnoughPrivileges()
|
raise NotEnoughPrivileges()
|
||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
|
|
||||||
return decorated_function
|
return decorated_function
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
@ -341,6 +359,7 @@ def apikey_is_admin(f):
|
|||||||
"""
|
"""
|
||||||
Grant access if user is in Administrator role
|
Grant access if user is in Administrator role
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
if g.apikey.role.name != 'Administrator':
|
if g.apikey.role.name != 'Administrator':
|
||||||
@ -358,6 +377,7 @@ def apikey_can_access_domain(f):
|
|||||||
- user has Operator role or higher, or
|
- user has Operator role or higher, or
|
||||||
- user has explicitly been granted access to domain
|
- user has explicitly been granted access to domain
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
if g.apikey.role.name not in ['Administrator', 'Operator']:
|
if g.apikey.role.name not in ['Administrator', 'Operator']:
|
||||||
@ -382,22 +402,26 @@ def apikey_can_configure_dnssec(http_methods=[]):
|
|||||||
- user is in Operator role or higher, or
|
- user is in Operator role or higher, or
|
||||||
- dnssec_admins_only is off
|
- dnssec_admins_only is off
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def decorator(f=None):
|
def decorator(f=None):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
check_current_http_method = not http_methods or request.method in http_methods
|
check_current_http_method = not http_methods or request.method in http_methods
|
||||||
|
|
||||||
if (check_current_http_method and
|
if (check_current_http_method and
|
||||||
g.apikey.role.name not in ['Administrator', 'Operator'] and
|
g.apikey.role.name not in ['Administrator', 'Operator'] and
|
||||||
Setting().get('dnssec_admins_only')
|
Setting().get('dnssec_admins_only')
|
||||||
):
|
):
|
||||||
msg = "ApiKey #{0} does not have enough privileges to configure dnssec"
|
msg = "ApiKey #{0} does not have enough privileges to configure dnssec"
|
||||||
current_app.logger.error(msg.format(g.apikey.id))
|
current_app.logger.error(msg.format(g.apikey.id))
|
||||||
raise DomainAccessForbidden(message=msg)
|
raise DomainAccessForbidden(message=msg)
|
||||||
return f(*args, **kwargs) if f else None
|
return f(*args, **kwargs) if f else None
|
||||||
|
|
||||||
return decorated_function
|
return decorated_function
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
def allowed_record_types(f):
|
def allowed_record_types(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
@ -423,6 +447,7 @@ def allowed_record_types(f):
|
|||||||
|
|
||||||
return decorated_function
|
return decorated_function
|
||||||
|
|
||||||
|
|
||||||
def allowed_record_ttl(f):
|
def allowed_record_ttl(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
@ -431,12 +456,12 @@ def allowed_record_ttl(f):
|
|||||||
|
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
|
|
||||||
if g.apikey.role.name in ['Administrator', 'Operator']:
|
if g.apikey.role.name in ['Administrator', 'Operator']:
|
||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
|
|
||||||
allowed_ttls = Setting().get_ttl_options()
|
allowed_ttls = Setting().get_ttl_options()
|
||||||
allowed_numeric_ttls = [ ttl[0] for ttl in allowed_ttls ]
|
allowed_numeric_ttls = [ttl[0] for ttl in allowed_ttls]
|
||||||
content = request.get_json()
|
content = request.get_json()
|
||||||
try:
|
try:
|
||||||
for record in content['rrsets']:
|
for record in content['rrsets']:
|
||||||
@ -497,6 +522,7 @@ def dyndns_login_required(f):
|
|||||||
|
|
||||||
return decorated_function
|
return decorated_function
|
||||||
|
|
||||||
|
|
||||||
def apikey_or_basic_auth(f):
|
def apikey_or_basic_auth(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
@ -505,4 +531,5 @@ def apikey_or_basic_auth(f):
|
|||||||
return apikey_auth(f)(*args, **kwargs)
|
return apikey_auth(f)(*args, **kwargs)
|
||||||
else:
|
else:
|
||||||
return api_basic_auth(f)(*args, **kwargs)
|
return api_basic_auth(f)(*args, **kwargs)
|
||||||
|
|
||||||
return decorated_function
|
return decorated_function
|
||||||
|
@ -1,44 +1,32 @@
|
|||||||
import os
|
import os
|
||||||
import urllib.parse
|
|
||||||
basedir = os.path.abspath(os.path.dirname(__file__))
|
basedir = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
|
||||||
### BASIC APP CONFIG
|
|
||||||
SALT = '$2b$12$yLUMTIfl21FKJQpTkRQXCu'
|
|
||||||
SECRET_KEY = 'e951e5a1f4b94151b360f47edf596dd2'
|
|
||||||
BIND_ADDRESS = '0.0.0.0'
|
BIND_ADDRESS = '0.0.0.0'
|
||||||
PORT = 9191
|
|
||||||
HSTS_ENABLED = False
|
|
||||||
SERVER_EXTERNAL_SSL = os.getenv('SERVER_EXTERNAL_SSL', True)
|
|
||||||
|
|
||||||
SESSION_TYPE = 'sqlalchemy'
|
|
||||||
SESSION_COOKIE_SAMESITE = 'Lax'
|
|
||||||
CSRF_COOKIE_HTTPONLY = True
|
|
||||||
|
|
||||||
#CAPTCHA Config
|
|
||||||
CAPTCHA_ENABLE = True
|
CAPTCHA_ENABLE = True
|
||||||
CAPTCHA_LENGTH = 6
|
|
||||||
CAPTCHA_WIDTH = 160
|
|
||||||
CAPTCHA_HEIGHT = 60
|
CAPTCHA_HEIGHT = 60
|
||||||
|
CAPTCHA_LENGTH = 6
|
||||||
CAPTCHA_SESSION_KEY = 'captcha_image'
|
CAPTCHA_SESSION_KEY = 'captcha_image'
|
||||||
|
CAPTCHA_WIDTH = 160
|
||||||
### DATABASE CONFIG
|
CSRF_COOKIE_HTTPONLY = True
|
||||||
SQLA_DB_USER = 'pda'
|
HSTS_ENABLED = False
|
||||||
SQLA_DB_PASSWORD = 'changeme'
|
PORT = 9191
|
||||||
SQLA_DB_HOST = '127.0.0.1'
|
SALT = '$2b$12$yLUMTIfl21FKJQpTkRQXCu'
|
||||||
SQLA_DB_NAME = 'pda'
|
SAML_ASSERTION_ENCRYPTED = True
|
||||||
|
SAML_ENABLED = False
|
||||||
|
SECRET_KEY = 'e951e5a1f4b94151b360f47edf596dd2'
|
||||||
|
SERVER_EXTERNAL_SSL = os.getenv('SERVER_EXTERNAL_SSL', True)
|
||||||
|
SESSION_COOKIE_SAMESITE = 'Lax'
|
||||||
|
SESSION_TYPE = 'sqlalchemy'
|
||||||
|
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'pdns.db')
|
||||||
SQLALCHEMY_TRACK_MODIFICATIONS = True
|
SQLALCHEMY_TRACK_MODIFICATIONS = True
|
||||||
|
# SQLA_DB_USER = 'pda'
|
||||||
### DATABASE - MySQL
|
# SQLA_DB_PASSWORD = 'changeme'
|
||||||
|
# SQLA_DB_HOST = '127.0.0.1'
|
||||||
|
# SQLA_DB_NAME = 'pda'
|
||||||
# SQLALCHEMY_DATABASE_URI = 'mysql://{}:{}@{}/{}'.format(
|
# SQLALCHEMY_DATABASE_URI = 'mysql://{}:{}@{}/{}'.format(
|
||||||
# urllib.parse.quote_plus(SQLA_DB_USER),
|
# urllib.parse.quote_plus(SQLA_DB_USER),
|
||||||
# urllib.parse.quote_plus(SQLA_DB_PASSWORD),
|
# urllib.parse.quote_plus(SQLA_DB_PASSWORD),
|
||||||
# SQLA_DB_HOST,
|
# SQLA_DB_HOST,
|
||||||
# SQLA_DB_NAME
|
# SQLA_DB_NAME
|
||||||
# )
|
# )
|
||||||
|
|
||||||
### DATABASE - SQLite
|
|
||||||
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'pdns.db')
|
|
||||||
|
|
||||||
# SAML Authnetication
|
|
||||||
SAML_ENABLED = False
|
|
||||||
SAML_ASSERTION_ENCRYPTED = True
|
|
||||||
|
629
powerdnsadmin/lib/settings.py
Normal file
629
powerdnsadmin/lib/settings.py
Normal file
@ -0,0 +1,629 @@
|
|||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
basedir = os.path.abspath(Path(os.path.dirname(__file__)).parent)
|
||||||
|
|
||||||
|
class AppSettings(object):
|
||||||
|
|
||||||
|
defaults = {
|
||||||
|
# Flask Settings
|
||||||
|
'bind_address': '0.0.0.0',
|
||||||
|
'csrf_cookie_secure': False,
|
||||||
|
'log_level': 'WARNING',
|
||||||
|
'port': 9191,
|
||||||
|
'salt': '$2b$12$yLUMTIfl21FKJQpTkRQXCu',
|
||||||
|
'secret_key': 'e951e5a1f4b94151b360f47edf596dd2',
|
||||||
|
'session_cookie_secure': False,
|
||||||
|
'session_type': 'sqlalchemy',
|
||||||
|
'sqlalchemy_track_modifications': True,
|
||||||
|
'sqlalchemy_database_uri': 'sqlite:///' + os.path.join(basedir, 'pdns.db'),
|
||||||
|
'sqlalchemy_engine_options': {},
|
||||||
|
|
||||||
|
# General Settings
|
||||||
|
'captcha_enable': True,
|
||||||
|
'captcha_height': 60,
|
||||||
|
'captcha_length': 6,
|
||||||
|
'captcha_session_key': 'captcha_image',
|
||||||
|
'captcha_width': 160,
|
||||||
|
'mail_server': 'localhost',
|
||||||
|
'mail_port': 25,
|
||||||
|
'mail_debug': False,
|
||||||
|
'mail_use_ssl': False,
|
||||||
|
'mail_use_tls': False,
|
||||||
|
'mail_username': '',
|
||||||
|
'mail_password': '',
|
||||||
|
'mail_default_sender': '',
|
||||||
|
'remote_user_enabled': False,
|
||||||
|
'remote_user_cookies': [],
|
||||||
|
'remote_user_logout_url': '',
|
||||||
|
'server_external_ssl': True,
|
||||||
|
'maintenance': False,
|
||||||
|
'fullscreen_layout': True,
|
||||||
|
'record_helper': True,
|
||||||
|
'login_ldap_first': True,
|
||||||
|
'default_record_table_size': 15,
|
||||||
|
'default_domain_table_size': 10,
|
||||||
|
'auto_ptr': False,
|
||||||
|
'record_quick_edit': True,
|
||||||
|
'pretty_ipv6_ptr': False,
|
||||||
|
'dnssec_admins_only': False,
|
||||||
|
'allow_user_create_domain': False,
|
||||||
|
'allow_user_remove_domain': False,
|
||||||
|
'allow_user_view_history': False,
|
||||||
|
'custom_history_header': '',
|
||||||
|
'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,
|
||||||
|
'warn_session_timeout': True,
|
||||||
|
'pdns_api_url': '',
|
||||||
|
'pdns_api_key': '',
|
||||||
|
'pdns_api_timeout': 30,
|
||||||
|
'pdns_version': '4.1.1',
|
||||||
|
'verify_ssl_connections': True,
|
||||||
|
'verify_user_email': False,
|
||||||
|
'enforce_api_ttl': False,
|
||||||
|
'ttl_options': '1 minute,5 minutes,30 minutes,60 minutes,24 hours',
|
||||||
|
'otp_field_enabled': True,
|
||||||
|
'custom_css': '',
|
||||||
|
'otp_force': False,
|
||||||
|
'max_history_records': 1000,
|
||||||
|
'deny_domain_override': False,
|
||||||
|
'account_name_extra_chars': False,
|
||||||
|
'gravatar_enabled': False,
|
||||||
|
|
||||||
|
# Local Authentication Settings
|
||||||
|
'local_db_enabled': True,
|
||||||
|
'signup_enabled': True,
|
||||||
|
'pwd_enforce_characters': False,
|
||||||
|
'pwd_min_len': 10,
|
||||||
|
'pwd_min_lowercase': 3,
|
||||||
|
'pwd_min_uppercase': 2,
|
||||||
|
'pwd_min_digits': 2,
|
||||||
|
'pwd_min_special': 1,
|
||||||
|
'pwd_enforce_complexity': False,
|
||||||
|
'pwd_min_complexity': 11,
|
||||||
|
|
||||||
|
# LDAP Authentication Settings
|
||||||
|
'ldap_enabled': False,
|
||||||
|
'ldap_type': 'ldap',
|
||||||
|
'ldap_uri': '',
|
||||||
|
'ldap_base_dn': '',
|
||||||
|
'ldap_admin_username': '',
|
||||||
|
'ldap_admin_password': '',
|
||||||
|
'ldap_domain': '',
|
||||||
|
'ldap_filter_basic': '',
|
||||||
|
'ldap_filter_username': '',
|
||||||
|
'ldap_filter_group': '',
|
||||||
|
'ldap_filter_groupname': '',
|
||||||
|
'ldap_sg_enabled': False,
|
||||||
|
'ldap_admin_group': '',
|
||||||
|
'ldap_operator_group': '',
|
||||||
|
'ldap_user_group': '',
|
||||||
|
'autoprovisioning': False,
|
||||||
|
'autoprovisioning_attribute': '',
|
||||||
|
'urn_value': '',
|
||||||
|
'purge': False,
|
||||||
|
|
||||||
|
# Google OAuth Settings
|
||||||
|
'google_oauth_enabled': False,
|
||||||
|
'google_oauth_client_id': '',
|
||||||
|
'google_oauth_client_secret': '',
|
||||||
|
'google_oauth_scope': 'openid email profile',
|
||||||
|
'google_base_url': 'https://www.googleapis.com/oauth2/v3/',
|
||||||
|
'google_oauth_auto_configure': True,
|
||||||
|
'google_oauth_metadata_url': 'https://accounts.google.com/.well-known/openid-configuration',
|
||||||
|
'google_token_url': 'https://oauth2.googleapis.com/token',
|
||||||
|
'google_authorize_url': 'https://accounts.google.com/o/oauth2/v2/auth',
|
||||||
|
|
||||||
|
# GitHub OAuth Settings
|
||||||
|
'github_oauth_enabled': False,
|
||||||
|
'github_oauth_key': '',
|
||||||
|
'github_oauth_secret': '',
|
||||||
|
'github_oauth_scope': 'email',
|
||||||
|
'github_oauth_api_url': 'https://api.github.com/user',
|
||||||
|
'github_oauth_auto_configure': False,
|
||||||
|
'github_oauth_metadata_url': '',
|
||||||
|
'github_oauth_token_url': 'https://github.com/login/oauth/access_token',
|
||||||
|
'github_oauth_authorize_url': 'https://github.com/login/oauth/authorize',
|
||||||
|
|
||||||
|
# Azure OAuth Settings
|
||||||
|
'azure_oauth_enabled': False,
|
||||||
|
'azure_oauth_key': '',
|
||||||
|
'azure_oauth_secret': '',
|
||||||
|
'azure_oauth_scope': 'User.Read openid email profile',
|
||||||
|
'azure_oauth_api_url': 'https://graph.microsoft.com/v1.0/',
|
||||||
|
'azure_oauth_auto_configure': True,
|
||||||
|
'azure_oauth_metadata_url': '',
|
||||||
|
'azure_oauth_token_url': '',
|
||||||
|
'azure_oauth_authorize_url': '',
|
||||||
|
'azure_sg_enabled': False,
|
||||||
|
'azure_admin_group': '',
|
||||||
|
'azure_operator_group': '',
|
||||||
|
'azure_user_group': '',
|
||||||
|
'azure_group_accounts_enabled': False,
|
||||||
|
'azure_group_accounts_name': 'displayName',
|
||||||
|
'azure_group_accounts_name_re': '',
|
||||||
|
'azure_group_accounts_description': 'description',
|
||||||
|
'azure_group_accounts_description_re': '',
|
||||||
|
|
||||||
|
# OIDC OAuth Settings
|
||||||
|
'oidc_oauth_enabled': False,
|
||||||
|
'oidc_oauth_key': '',
|
||||||
|
'oidc_oauth_secret': '',
|
||||||
|
'oidc_oauth_scope': 'email',
|
||||||
|
'oidc_oauth_api_url': '',
|
||||||
|
'oidc_oauth_auto_configure': True,
|
||||||
|
'oidc_oauth_metadata_url': '',
|
||||||
|
'oidc_oauth_token_url': '',
|
||||||
|
'oidc_oauth_authorize_url': '',
|
||||||
|
'oidc_oauth_logout_url': '',
|
||||||
|
'oidc_oauth_username': 'preferred_username',
|
||||||
|
'oidc_oauth_email': 'email',
|
||||||
|
'oidc_oauth_firstname': 'given_name',
|
||||||
|
'oidc_oauth_last_name': 'family_name',
|
||||||
|
'oidc_oauth_account_name_property': '',
|
||||||
|
'oidc_oauth_account_description_property': '',
|
||||||
|
|
||||||
|
# SAML Authentication Settings
|
||||||
|
'saml_enabled': False,
|
||||||
|
'saml_debug': False,
|
||||||
|
'saml_path': os.path.join(basedir, 'saml'),
|
||||||
|
'saml_metadata_url': None,
|
||||||
|
'saml_metadata_cache_lifetime': 1,
|
||||||
|
'saml_idp_sso_binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
|
||||||
|
'saml_idp_entity_id': None,
|
||||||
|
'saml_nameid_format': None,
|
||||||
|
'saml_attribute_account': None,
|
||||||
|
'saml_attribute_email': 'email',
|
||||||
|
'saml_attribute_givenname': 'givenname',
|
||||||
|
'saml_attribute_surname': 'surname',
|
||||||
|
'saml_attribute_name': None,
|
||||||
|
'saml_attribute_username': None,
|
||||||
|
'saml_attribute_admin': None,
|
||||||
|
'saml_attribute_group': None,
|
||||||
|
'saml_group_admin_name': None,
|
||||||
|
'saml_group_operator_name': None,
|
||||||
|
'saml_group_to_account_mapping': None,
|
||||||
|
'saml_sp_entity_id': None,
|
||||||
|
'saml_sp_contact_name': None,
|
||||||
|
'saml_sp_contact_mail': None,
|
||||||
|
'saml_sign_request': False,
|
||||||
|
'saml_want_message_signed': True,
|
||||||
|
'saml_logout': True,
|
||||||
|
'saml_logout_url': None,
|
||||||
|
'saml_assertion_encrypted': True,
|
||||||
|
'saml_cert': None,
|
||||||
|
'saml_key': None,
|
||||||
|
|
||||||
|
# Zone Record Settings
|
||||||
|
'forward_records_allow_edit': {
|
||||||
|
'A': True,
|
||||||
|
'AAAA': True,
|
||||||
|
'AFSDB': False,
|
||||||
|
'ALIAS': False,
|
||||||
|
'CAA': True,
|
||||||
|
'CERT': False,
|
||||||
|
'CDNSKEY': False,
|
||||||
|
'CDS': False,
|
||||||
|
'CNAME': True,
|
||||||
|
'DNSKEY': False,
|
||||||
|
'DNAME': False,
|
||||||
|
'DS': False,
|
||||||
|
'HINFO': False,
|
||||||
|
'KEY': False,
|
||||||
|
'LOC': True,
|
||||||
|
'LUA': False,
|
||||||
|
'MX': True,
|
||||||
|
'NAPTR': False,
|
||||||
|
'NS': True,
|
||||||
|
'NSEC': False,
|
||||||
|
'NSEC3': False,
|
||||||
|
'NSEC3PARAM': False,
|
||||||
|
'OPENPGPKEY': False,
|
||||||
|
'PTR': True,
|
||||||
|
'RP': False,
|
||||||
|
'RRSIG': False,
|
||||||
|
'SOA': False,
|
||||||
|
'SPF': True,
|
||||||
|
'SSHFP': False,
|
||||||
|
'SRV': True,
|
||||||
|
'TKEY': False,
|
||||||
|
'TSIG': False,
|
||||||
|
'TLSA': False,
|
||||||
|
'SMIMEA': False,
|
||||||
|
'TXT': True,
|
||||||
|
'URI': False
|
||||||
|
},
|
||||||
|
'reverse_records_allow_edit': {
|
||||||
|
'A': False,
|
||||||
|
'AAAA': False,
|
||||||
|
'AFSDB': False,
|
||||||
|
'ALIAS': False,
|
||||||
|
'CAA': False,
|
||||||
|
'CERT': False,
|
||||||
|
'CDNSKEY': False,
|
||||||
|
'CDS': False,
|
||||||
|
'CNAME': False,
|
||||||
|
'DNSKEY': False,
|
||||||
|
'DNAME': False,
|
||||||
|
'DS': False,
|
||||||
|
'HINFO': False,
|
||||||
|
'KEY': False,
|
||||||
|
'LOC': True,
|
||||||
|
'LUA': False,
|
||||||
|
'MX': False,
|
||||||
|
'NAPTR': False,
|
||||||
|
'NS': True,
|
||||||
|
'NSEC': False,
|
||||||
|
'NSEC3': False,
|
||||||
|
'NSEC3PARAM': False,
|
||||||
|
'OPENPGPKEY': False,
|
||||||
|
'PTR': True,
|
||||||
|
'RP': False,
|
||||||
|
'RRSIG': False,
|
||||||
|
'SOA': False,
|
||||||
|
'SPF': False,
|
||||||
|
'SSHFP': False,
|
||||||
|
'SRV': False,
|
||||||
|
'TKEY': False,
|
||||||
|
'TSIG': False,
|
||||||
|
'TLSA': False,
|
||||||
|
'SMIMEA': False,
|
||||||
|
'TXT': True,
|
||||||
|
'URI': False
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
types = {
|
||||||
|
# Flask Settings
|
||||||
|
'bind_address': str,
|
||||||
|
'csrf_cookie_secure': bool,
|
||||||
|
'log_level': str,
|
||||||
|
'port': int,
|
||||||
|
'salt': str,
|
||||||
|
'secret_key': str,
|
||||||
|
'session_cookie_secure': bool,
|
||||||
|
'session_type': str,
|
||||||
|
'sqlalchemy_track_modifications': bool,
|
||||||
|
'sqlalchemy_database_uri': str,
|
||||||
|
'sqlalchemy_engine_options': dict,
|
||||||
|
|
||||||
|
# General Settings
|
||||||
|
'captcha_enable': bool,
|
||||||
|
'captcha_height': int,
|
||||||
|
'captcha_length': int,
|
||||||
|
'captcha_session_key': str,
|
||||||
|
'captcha_width': int,
|
||||||
|
'mail_server': str,
|
||||||
|
'mail_port': int,
|
||||||
|
'mail_debug': bool,
|
||||||
|
'mail_use_ssl': bool,
|
||||||
|
'mail_use_tls': bool,
|
||||||
|
'mail_username': str,
|
||||||
|
'mail_password': str,
|
||||||
|
'mail_default_sender': str,
|
||||||
|
'remote_user_enabled': bool,
|
||||||
|
'remote_user_cookies': list,
|
||||||
|
'remote_user_logout_url': str,
|
||||||
|
'maintenance': bool,
|
||||||
|
'fullscreen_layout': bool,
|
||||||
|
'record_helper': bool,
|
||||||
|
'login_ldap_first': bool,
|
||||||
|
'default_record_table_size': int,
|
||||||
|
'default_domain_table_size': int,
|
||||||
|
'auto_ptr': bool,
|
||||||
|
'record_quick_edit': bool,
|
||||||
|
'pretty_ipv6_ptr': bool,
|
||||||
|
'dnssec_admins_only': bool,
|
||||||
|
'allow_user_create_domain': bool,
|
||||||
|
'allow_user_remove_domain': bool,
|
||||||
|
'allow_user_view_history': bool,
|
||||||
|
'custom_history_header': str,
|
||||||
|
'delete_sso_accounts': bool,
|
||||||
|
'bg_domain_updates': bool,
|
||||||
|
'enable_api_rr_history': bool,
|
||||||
|
'preserve_history': bool,
|
||||||
|
'site_name': str,
|
||||||
|
'site_url': str,
|
||||||
|
'session_timeout': int,
|
||||||
|
'warn_session_timeout': bool,
|
||||||
|
'pdns_api_url': str,
|
||||||
|
'pdns_api_key': str,
|
||||||
|
'pdns_api_timeout': int,
|
||||||
|
'pdns_version': str,
|
||||||
|
'verify_ssl_connections': bool,
|
||||||
|
'verify_user_email': bool,
|
||||||
|
'enforce_api_ttl': bool,
|
||||||
|
'ttl_options': str,
|
||||||
|
'otp_field_enabled': bool,
|
||||||
|
'custom_css': str,
|
||||||
|
'otp_force': bool,
|
||||||
|
'max_history_records': int,
|
||||||
|
'deny_domain_override': bool,
|
||||||
|
'account_name_extra_chars': bool,
|
||||||
|
'gravatar_enabled': bool,
|
||||||
|
'forward_records_allow_edit': dict,
|
||||||
|
'reverse_records_allow_edit': dict,
|
||||||
|
|
||||||
|
# Local Authentication Settings
|
||||||
|
'local_db_enabled': bool,
|
||||||
|
'signup_enabled': bool,
|
||||||
|
'pwd_enforce_characters': bool,
|
||||||
|
'pwd_min_len': int,
|
||||||
|
'pwd_min_lowercase': int,
|
||||||
|
'pwd_min_uppercase': int,
|
||||||
|
'pwd_min_digits': int,
|
||||||
|
'pwd_min_special': int,
|
||||||
|
'pwd_enforce_complexity': bool,
|
||||||
|
'pwd_min_complexity': int,
|
||||||
|
|
||||||
|
# LDAP Authentication Settings
|
||||||
|
'ldap_enabled': bool,
|
||||||
|
'ldap_type': str,
|
||||||
|
'ldap_uri': str,
|
||||||
|
'ldap_base_dn': str,
|
||||||
|
'ldap_admin_username': str,
|
||||||
|
'ldap_admin_password': str,
|
||||||
|
'ldap_domain': str,
|
||||||
|
'ldap_filter_basic': str,
|
||||||
|
'ldap_filter_username': str,
|
||||||
|
'ldap_filter_group': str,
|
||||||
|
'ldap_filter_groupname': str,
|
||||||
|
'ldap_sg_enabled': bool,
|
||||||
|
'ldap_admin_group': str,
|
||||||
|
'ldap_operator_group': str,
|
||||||
|
'ldap_user_group': str,
|
||||||
|
'autoprovisioning': bool,
|
||||||
|
'autoprovisioning_attribute': str,
|
||||||
|
'urn_value': str,
|
||||||
|
'purge': bool,
|
||||||
|
|
||||||
|
# Google OAuth Settings
|
||||||
|
'google_oauth_enabled': bool,
|
||||||
|
'google_oauth_client_id': str,
|
||||||
|
'google_oauth_client_secret': str,
|
||||||
|
'google_oauth_scope': str,
|
||||||
|
'google_base_url': str,
|
||||||
|
'google_oauth_auto_configure': bool,
|
||||||
|
'google_oauth_metadata_url': str,
|
||||||
|
'google_token_url': str,
|
||||||
|
'google_authorize_url': str,
|
||||||
|
|
||||||
|
# GitHub OAuth Settings
|
||||||
|
'github_oauth_enabled': bool,
|
||||||
|
'github_oauth_key': str,
|
||||||
|
'github_oauth_secret': str,
|
||||||
|
'github_oauth_scope': str,
|
||||||
|
'github_oauth_api_url': str,
|
||||||
|
'github_oauth_auto_configure': bool,
|
||||||
|
'github_oauth_metadata_url': str,
|
||||||
|
'github_oauth_token_url': str,
|
||||||
|
'github_oauth_authorize_url': str,
|
||||||
|
|
||||||
|
# Azure OAuth Settings
|
||||||
|
'azure_oauth_enabled': bool,
|
||||||
|
'azure_oauth_key': str,
|
||||||
|
'azure_oauth_secret': str,
|
||||||
|
'azure_oauth_scope': str,
|
||||||
|
'azure_oauth_api_url': str,
|
||||||
|
'azure_oauth_auto_configure': bool,
|
||||||
|
'azure_oauth_metadata_url': str,
|
||||||
|
'azure_oauth_token_url': str,
|
||||||
|
'azure_oauth_authorize_url': str,
|
||||||
|
'azure_sg_enabled': bool,
|
||||||
|
'azure_admin_group': str,
|
||||||
|
'azure_operator_group': str,
|
||||||
|
'azure_user_group': str,
|
||||||
|
'azure_group_accounts_enabled': bool,
|
||||||
|
'azure_group_accounts_name': str,
|
||||||
|
'azure_group_accounts_name_re': str,
|
||||||
|
'azure_group_accounts_description': str,
|
||||||
|
'azure_group_accounts_description_re': str,
|
||||||
|
|
||||||
|
# OIDC OAuth Settings
|
||||||
|
'oidc_oauth_enabled': bool,
|
||||||
|
'oidc_oauth_key': str,
|
||||||
|
'oidc_oauth_secret': str,
|
||||||
|
'oidc_oauth_scope': str,
|
||||||
|
'oidc_oauth_api_url': str,
|
||||||
|
'oidc_oauth_auto_configure': bool,
|
||||||
|
'oidc_oauth_metadata_url': str,
|
||||||
|
'oidc_oauth_token_url': str,
|
||||||
|
'oidc_oauth_authorize_url': str,
|
||||||
|
'oidc_oauth_logout_url': str,
|
||||||
|
'oidc_oauth_username': str,
|
||||||
|
'oidc_oauth_email': str,
|
||||||
|
'oidc_oauth_firstname': str,
|
||||||
|
'oidc_oauth_last_name': str,
|
||||||
|
'oidc_oauth_account_name_property': str,
|
||||||
|
'oidc_oauth_account_description_property': str,
|
||||||
|
|
||||||
|
# SAML Authentication Settings
|
||||||
|
'saml_enabled': bool,
|
||||||
|
'saml_debug': bool,
|
||||||
|
'saml_path': str,
|
||||||
|
'saml_metadata_url': str,
|
||||||
|
'saml_metadata_cache_lifetime': int,
|
||||||
|
'saml_idp_sso_binding': str,
|
||||||
|
'saml_idp_entity_id': str,
|
||||||
|
'saml_nameid_format': str,
|
||||||
|
'saml_attribute_account': str,
|
||||||
|
'saml_attribute_email': str,
|
||||||
|
'saml_attribute_givenname': str,
|
||||||
|
'saml_attribute_surname': str,
|
||||||
|
'saml_attribute_name': str,
|
||||||
|
'saml_attribute_username': str,
|
||||||
|
'saml_attribute_admin': str,
|
||||||
|
'saml_attribute_group': str,
|
||||||
|
'saml_group_admin_name': str,
|
||||||
|
'saml_group_operator_name': str,
|
||||||
|
'saml_group_to_account_mapping': str,
|
||||||
|
'saml_sp_entity_id': str,
|
||||||
|
'saml_sp_contact_name': str,
|
||||||
|
'saml_sp_contact_mail': str,
|
||||||
|
'saml_sign_request': bool,
|
||||||
|
'saml_want_message_signed': bool,
|
||||||
|
'saml_logout': bool,
|
||||||
|
'saml_logout_url': str,
|
||||||
|
'saml_assertion_encrypted': bool,
|
||||||
|
'saml_cert': str,
|
||||||
|
'saml_key': str,
|
||||||
|
}
|
||||||
|
|
||||||
|
groups = {
|
||||||
|
'authentication': [
|
||||||
|
# Local Authentication Settings
|
||||||
|
'local_db_enabled',
|
||||||
|
'signup_enabled',
|
||||||
|
'pwd_enforce_characters',
|
||||||
|
'pwd_min_len',
|
||||||
|
'pwd_min_lowercase',
|
||||||
|
'pwd_min_uppercase',
|
||||||
|
'pwd_min_digits',
|
||||||
|
'pwd_min_special',
|
||||||
|
'pwd_enforce_complexity',
|
||||||
|
'pwd_min_complexity',
|
||||||
|
|
||||||
|
# LDAP Authentication Settings
|
||||||
|
'ldap_enabled',
|
||||||
|
'ldap_type',
|
||||||
|
'ldap_uri',
|
||||||
|
'ldap_base_dn',
|
||||||
|
'ldap_admin_username',
|
||||||
|
'ldap_admin_password',
|
||||||
|
'ldap_domain',
|
||||||
|
'ldap_filter_basic',
|
||||||
|
'ldap_filter_username',
|
||||||
|
'ldap_filter_group',
|
||||||
|
'ldap_filter_groupname',
|
||||||
|
'ldap_sg_enabled',
|
||||||
|
'ldap_admin_group',
|
||||||
|
'ldap_operator_group',
|
||||||
|
'ldap_user_group',
|
||||||
|
'autoprovisioning',
|
||||||
|
'autoprovisioning_attribute',
|
||||||
|
'urn_value',
|
||||||
|
'purge',
|
||||||
|
|
||||||
|
# Google OAuth Settings
|
||||||
|
'google_oauth_enabled',
|
||||||
|
'google_oauth_client_id',
|
||||||
|
'google_oauth_client_secret',
|
||||||
|
'google_oauth_scope',
|
||||||
|
'google_base_url',
|
||||||
|
'google_oauth_auto_configure',
|
||||||
|
'google_oauth_metadata_url',
|
||||||
|
'google_token_url',
|
||||||
|
'google_authorize_url',
|
||||||
|
|
||||||
|
# GitHub OAuth Settings
|
||||||
|
'github_oauth_enabled',
|
||||||
|
'github_oauth_key',
|
||||||
|
'github_oauth_secret',
|
||||||
|
'github_oauth_scope',
|
||||||
|
'github_oauth_api_url',
|
||||||
|
'github_oauth_auto_configure',
|
||||||
|
'github_oauth_metadata_url',
|
||||||
|
'github_oauth_token_url',
|
||||||
|
'github_oauth_authorize_url',
|
||||||
|
|
||||||
|
# Azure OAuth Settings
|
||||||
|
'azure_oauth_enabled',
|
||||||
|
'azure_oauth_key',
|
||||||
|
'azure_oauth_secret',
|
||||||
|
'azure_oauth_scope',
|
||||||
|
'azure_oauth_api_url',
|
||||||
|
'azure_oauth_auto_configure',
|
||||||
|
'azure_oauth_metadata_url',
|
||||||
|
'azure_oauth_token_url',
|
||||||
|
'azure_oauth_authorize_url',
|
||||||
|
'azure_sg_enabled',
|
||||||
|
'azure_admin_group',
|
||||||
|
'azure_operator_group',
|
||||||
|
'azure_user_group',
|
||||||
|
'azure_group_accounts_enabled',
|
||||||
|
'azure_group_accounts_name',
|
||||||
|
'azure_group_accounts_name_re',
|
||||||
|
'azure_group_accounts_description',
|
||||||
|
'azure_group_accounts_description_re',
|
||||||
|
|
||||||
|
# OIDC OAuth Settings
|
||||||
|
'oidc_oauth_enabled',
|
||||||
|
'oidc_oauth_key',
|
||||||
|
'oidc_oauth_secret',
|
||||||
|
'oidc_oauth_scope',
|
||||||
|
'oidc_oauth_api_url',
|
||||||
|
'oidc_oauth_auto_configure',
|
||||||
|
'oidc_oauth_metadata_url',
|
||||||
|
'oidc_oauth_token_url',
|
||||||
|
'oidc_oauth_authorize_url',
|
||||||
|
'oidc_oauth_logout_url',
|
||||||
|
'oidc_oauth_username',
|
||||||
|
'oidc_oauth_email',
|
||||||
|
'oidc_oauth_firstname',
|
||||||
|
'oidc_oauth_last_name',
|
||||||
|
'oidc_oauth_account_name_property',
|
||||||
|
'oidc_oauth_account_description_property',
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def convert_type(name, value):
|
||||||
|
import json
|
||||||
|
from json import JSONDecodeError
|
||||||
|
if name in AppSettings.types:
|
||||||
|
var_type = AppSettings.types[name]
|
||||||
|
|
||||||
|
# Handle boolean values
|
||||||
|
if var_type == bool and isinstance(value, str):
|
||||||
|
if value.lower() in ['True', 'true', '1'] or value is True:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Handle float values
|
||||||
|
if var_type == float:
|
||||||
|
return float(value)
|
||||||
|
|
||||||
|
# Handle integer values
|
||||||
|
if var_type == int:
|
||||||
|
return int(value)
|
||||||
|
|
||||||
|
if (var_type == dict or var_type == list) and isinstance(value, str) and len(value) > 0:
|
||||||
|
try:
|
||||||
|
return json.loads(value)
|
||||||
|
except JSONDecodeError as e:
|
||||||
|
raise ValueError('Cannot parse json {} for variable {}'.format(value, name))
|
||||||
|
|
||||||
|
if var_type == str:
|
||||||
|
return str(value)
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load_environment(app):
|
||||||
|
""" Load app settings from environment variables when defined. """
|
||||||
|
import os
|
||||||
|
|
||||||
|
for var_name, default_value in AppSettings.defaults.items():
|
||||||
|
env_name = var_name.upper()
|
||||||
|
current_value = None
|
||||||
|
|
||||||
|
if env_name + '_FILE' in os.environ:
|
||||||
|
if env_name in os.environ:
|
||||||
|
raise AttributeError(
|
||||||
|
"Both {} and {} are set but are exclusive.".format(
|
||||||
|
env_name, env_name + '_FILE'))
|
||||||
|
with open(os.environ[env_name + '_FILE']) as f:
|
||||||
|
current_value = f.read()
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
elif env_name in os.environ:
|
||||||
|
current_value = os.environ[env_name]
|
||||||
|
|
||||||
|
if current_value is not None:
|
||||||
|
app.config[env_name] = AppSettings.convert_type(var_name, current_value)
|
@ -1,450 +1,19 @@
|
|||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
import pytimeparse
|
import pytimeparse
|
||||||
from ast import literal_eval
|
from ast import literal_eval
|
||||||
from distutils.util import strtobool
|
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
||||||
from .base import db
|
from .base import db
|
||||||
|
from powerdnsadmin.lib.settings import AppSettings
|
||||||
|
|
||||||
|
|
||||||
class Setting(db.Model):
|
class Setting(db.Model):
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
name = db.Column(db.String(64), unique=True, index=True)
|
name = db.Column(db.String(64), unique=True, index=True)
|
||||||
value = db.Column(db.Text())
|
value = db.Column(db.Text())
|
||||||
|
|
||||||
types = {
|
|
||||||
'maintenance': bool,
|
|
||||||
'fullscreen_layout': bool,
|
|
||||||
'record_helper': bool,
|
|
||||||
'login_ldap_first': bool,
|
|
||||||
'default_record_table_size': int,
|
|
||||||
'default_domain_table_size': int,
|
|
||||||
'auto_ptr': bool,
|
|
||||||
'record_quick_edit': bool,
|
|
||||||
'pretty_ipv6_ptr': bool,
|
|
||||||
'dnssec_admins_only': bool,
|
|
||||||
'allow_user_create_domain': bool,
|
|
||||||
'allow_user_remove_domain': bool,
|
|
||||||
'allow_user_view_history': bool,
|
|
||||||
'custom_history_header': str,
|
|
||||||
'delete_sso_accounts': bool,
|
|
||||||
'bg_domain_updates': bool,
|
|
||||||
'enable_api_rr_history': bool,
|
|
||||||
'preserve_history': bool,
|
|
||||||
'site_name': str,
|
|
||||||
'site_url': str,
|
|
||||||
'session_timeout': int,
|
|
||||||
'warn_session_timeout': bool,
|
|
||||||
'pdns_api_url': str,
|
|
||||||
'pdns_api_key': str,
|
|
||||||
'pdns_api_timeout': int,
|
|
||||||
'pdns_version': str,
|
|
||||||
'verify_ssl_connections': bool,
|
|
||||||
'verify_user_email': bool,
|
|
||||||
'enforce_api_ttl': bool,
|
|
||||||
'ttl_options': str,
|
|
||||||
'otp_field_enabled': bool,
|
|
||||||
'custom_css': str,
|
|
||||||
'otp_force': bool,
|
|
||||||
'max_history_records': int,
|
|
||||||
'deny_domain_override': bool,
|
|
||||||
'account_name_extra_chars': bool,
|
|
||||||
'gravatar_enabled': bool,
|
|
||||||
'forward_records_allow_edit': dict,
|
|
||||||
'reverse_records_allow_edit': dict,
|
|
||||||
'local_db_enabled': bool,
|
|
||||||
'signup_enabled': bool,
|
|
||||||
'pwd_enforce_characters': bool,
|
|
||||||
'pwd_min_len': int,
|
|
||||||
'pwd_min_lowercase': int,
|
|
||||||
'pwd_min_uppercase': int,
|
|
||||||
'pwd_min_digits': int,
|
|
||||||
'pwd_min_special': int,
|
|
||||||
'pwd_enforce_complexity': bool,
|
|
||||||
'pwd_min_complexity': int,
|
|
||||||
'ldap_enabled': bool,
|
|
||||||
'ldap_type': str,
|
|
||||||
'ldap_uri': str,
|
|
||||||
'ldap_base_dn': str,
|
|
||||||
'ldap_admin_username': str,
|
|
||||||
'ldap_admin_password': str,
|
|
||||||
'ldap_domain': str,
|
|
||||||
'ldap_filter_basic': str,
|
|
||||||
'ldap_filter_username': str,
|
|
||||||
'ldap_filter_group': str,
|
|
||||||
'ldap_filter_groupname': str,
|
|
||||||
'ldap_sg_enabled': bool,
|
|
||||||
'ldap_admin_group': str,
|
|
||||||
'ldap_operator_group': str,
|
|
||||||
'ldap_user_group': str,
|
|
||||||
'autoprovisioning': bool,
|
|
||||||
'autoprovisioning_attribute': str,
|
|
||||||
'urn_value': str,
|
|
||||||
'purge': bool,
|
|
||||||
'google_oauth_enabled': bool,
|
|
||||||
'google_oauth_client_id': str,
|
|
||||||
'google_oauth_client_secret': str,
|
|
||||||
'google_oauth_scope': str,
|
|
||||||
'google_base_url': str,
|
|
||||||
'google_oauth_auto_configure': bool,
|
|
||||||
'google_oauth_metadata_url': str,
|
|
||||||
'google_token_url': str,
|
|
||||||
'google_authorize_url': str,
|
|
||||||
'github_oauth_enabled': bool,
|
|
||||||
'github_oauth_key': str,
|
|
||||||
'github_oauth_secret': str,
|
|
||||||
'github_oauth_scope': str,
|
|
||||||
'github_oauth_api_url': str,
|
|
||||||
'github_oauth_auto_configure': bool,
|
|
||||||
'github_oauth_metadata_url': str,
|
|
||||||
'github_oauth_token_url': str,
|
|
||||||
'github_oauth_authorize_url': str,
|
|
||||||
'azure_oauth_enabled': bool,
|
|
||||||
'azure_oauth_key': str,
|
|
||||||
'azure_oauth_secret': str,
|
|
||||||
'azure_oauth_scope': str,
|
|
||||||
'azure_oauth_api_url': str,
|
|
||||||
'azure_oauth_auto_configure': bool,
|
|
||||||
'azure_oauth_metadata_url': str,
|
|
||||||
'azure_oauth_token_url': str,
|
|
||||||
'azure_oauth_authorize_url': str,
|
|
||||||
'azure_sg_enabled': bool,
|
|
||||||
'azure_admin_group': str,
|
|
||||||
'azure_operator_group': str,
|
|
||||||
'azure_user_group': str,
|
|
||||||
'azure_group_accounts_enabled': bool,
|
|
||||||
'azure_group_accounts_name': str,
|
|
||||||
'azure_group_accounts_name_re': str,
|
|
||||||
'azure_group_accounts_description': str,
|
|
||||||
'azure_group_accounts_description_re': str,
|
|
||||||
'oidc_oauth_enabled': bool,
|
|
||||||
'oidc_oauth_key': str,
|
|
||||||
'oidc_oauth_secret': str,
|
|
||||||
'oidc_oauth_scope': str,
|
|
||||||
'oidc_oauth_api_url': str,
|
|
||||||
'oidc_oauth_auto_configure': bool,
|
|
||||||
'oidc_oauth_metadata_url': str,
|
|
||||||
'oidc_oauth_token_url': str,
|
|
||||||
'oidc_oauth_authorize_url': str,
|
|
||||||
'oidc_oauth_logout_url': str,
|
|
||||||
'oidc_oauth_username': str,
|
|
||||||
'oidc_oauth_email': str,
|
|
||||||
'oidc_oauth_firstname': str,
|
|
||||||
'oidc_oauth_last_name': str,
|
|
||||||
'oidc_oauth_account_name_property': str,
|
|
||||||
'oidc_oauth_account_description_property': str,
|
|
||||||
}
|
|
||||||
|
|
||||||
defaults = {
|
|
||||||
# General Settings
|
|
||||||
'maintenance': False,
|
|
||||||
'fullscreen_layout': True,
|
|
||||||
'record_helper': True,
|
|
||||||
'login_ldap_first': True,
|
|
||||||
'default_record_table_size': 15,
|
|
||||||
'default_domain_table_size': 10,
|
|
||||||
'auto_ptr': False,
|
|
||||||
'record_quick_edit': True,
|
|
||||||
'pretty_ipv6_ptr': False,
|
|
||||||
'dnssec_admins_only': False,
|
|
||||||
'allow_user_create_domain': False,
|
|
||||||
'allow_user_remove_domain': False,
|
|
||||||
'allow_user_view_history': False,
|
|
||||||
'custom_history_header': '',
|
|
||||||
'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,
|
|
||||||
'warn_session_timeout': True,
|
|
||||||
'pdns_api_url': '',
|
|
||||||
'pdns_api_key': '',
|
|
||||||
'pdns_api_timeout': 30,
|
|
||||||
'pdns_version': '4.1.1',
|
|
||||||
'verify_ssl_connections': True,
|
|
||||||
'verify_user_email': False,
|
|
||||||
'enforce_api_ttl': False,
|
|
||||||
'ttl_options': '1 minute,5 minutes,30 minutes,60 minutes,24 hours',
|
|
||||||
'otp_field_enabled': True,
|
|
||||||
'custom_css': '',
|
|
||||||
'otp_force': False,
|
|
||||||
'max_history_records': 1000,
|
|
||||||
'deny_domain_override': False,
|
|
||||||
'account_name_extra_chars': False,
|
|
||||||
'gravatar_enabled': False,
|
|
||||||
|
|
||||||
# Local Authentication Settings
|
ZONE_TYPE_FORWARD = 'forward'
|
||||||
'local_db_enabled': True,
|
ZONE_TYPE_REVERSE = 'reverse'
|
||||||
'signup_enabled': True,
|
|
||||||
'pwd_enforce_characters': False,
|
|
||||||
'pwd_min_len': 10,
|
|
||||||
'pwd_min_lowercase': 3,
|
|
||||||
'pwd_min_uppercase': 2,
|
|
||||||
'pwd_min_digits': 2,
|
|
||||||
'pwd_min_special': 1,
|
|
||||||
'pwd_enforce_complexity': False,
|
|
||||||
'pwd_min_complexity': 11,
|
|
||||||
|
|
||||||
# LDAP Authentication Settings
|
|
||||||
'ldap_enabled': False,
|
|
||||||
'ldap_type': 'ldap',
|
|
||||||
'ldap_uri': '',
|
|
||||||
'ldap_base_dn': '',
|
|
||||||
'ldap_admin_username': '',
|
|
||||||
'ldap_admin_password': '',
|
|
||||||
'ldap_domain': '',
|
|
||||||
'ldap_filter_basic': '',
|
|
||||||
'ldap_filter_username': '',
|
|
||||||
'ldap_filter_group': '',
|
|
||||||
'ldap_filter_groupname': '',
|
|
||||||
'ldap_sg_enabled': False,
|
|
||||||
'ldap_admin_group': '',
|
|
||||||
'ldap_operator_group': '',
|
|
||||||
'ldap_user_group': '',
|
|
||||||
'autoprovisioning': False,
|
|
||||||
'autoprovisioning_attribute': '',
|
|
||||||
'urn_value': '',
|
|
||||||
'purge': False,
|
|
||||||
|
|
||||||
# Google OAuth2 Settings
|
|
||||||
'google_oauth_enabled': False,
|
|
||||||
'google_oauth_client_id': '',
|
|
||||||
'google_oauth_client_secret': '',
|
|
||||||
'google_oauth_scope': 'openid email profile',
|
|
||||||
'google_base_url': 'https://www.googleapis.com/oauth2/v3/',
|
|
||||||
'google_oauth_auto_configure': True,
|
|
||||||
'google_oauth_metadata_url': 'https://accounts.google.com/.well-known/openid-configuration',
|
|
||||||
'google_token_url': 'https://oauth2.googleapis.com/token',
|
|
||||||
'google_authorize_url': 'https://accounts.google.com/o/oauth2/v2/auth',
|
|
||||||
|
|
||||||
# GitHub OAuth2 Settings
|
|
||||||
'github_oauth_enabled': False,
|
|
||||||
'github_oauth_key': '',
|
|
||||||
'github_oauth_secret': '',
|
|
||||||
'github_oauth_scope': 'email',
|
|
||||||
'github_oauth_api_url': 'https://api.github.com/user',
|
|
||||||
'github_oauth_auto_configure': False,
|
|
||||||
'github_oauth_metadata_url': '',
|
|
||||||
'github_oauth_token_url': 'https://github.com/login/oauth/access_token',
|
|
||||||
'github_oauth_authorize_url': 'https://github.com/login/oauth/authorize',
|
|
||||||
|
|
||||||
# Azure OAuth2 Settings
|
|
||||||
'azure_oauth_enabled': False,
|
|
||||||
'azure_oauth_key': '',
|
|
||||||
'azure_oauth_secret': '',
|
|
||||||
'azure_oauth_scope': 'User.Read openid email profile',
|
|
||||||
'azure_oauth_api_url': 'https://graph.microsoft.com/v1.0/',
|
|
||||||
'azure_oauth_auto_configure': True,
|
|
||||||
'azure_oauth_metadata_url': '',
|
|
||||||
'azure_oauth_token_url': '',
|
|
||||||
'azure_oauth_authorize_url': '',
|
|
||||||
'azure_sg_enabled': False,
|
|
||||||
'azure_admin_group': '',
|
|
||||||
'azure_operator_group': '',
|
|
||||||
'azure_user_group': '',
|
|
||||||
'azure_group_accounts_enabled': False,
|
|
||||||
'azure_group_accounts_name': 'displayName',
|
|
||||||
'azure_group_accounts_name_re': '',
|
|
||||||
'azure_group_accounts_description': 'description',
|
|
||||||
'azure_group_accounts_description_re': '',
|
|
||||||
|
|
||||||
# OIDC OAuth2 Settings
|
|
||||||
'oidc_oauth_enabled': False,
|
|
||||||
'oidc_oauth_key': '',
|
|
||||||
'oidc_oauth_secret': '',
|
|
||||||
'oidc_oauth_scope': 'email',
|
|
||||||
'oidc_oauth_api_url': '',
|
|
||||||
'oidc_oauth_auto_configure': True,
|
|
||||||
'oidc_oauth_metadata_url': '',
|
|
||||||
'oidc_oauth_token_url': '',
|
|
||||||
'oidc_oauth_authorize_url': '',
|
|
||||||
'oidc_oauth_logout_url': '',
|
|
||||||
'oidc_oauth_username': 'preferred_username',
|
|
||||||
'oidc_oauth_email': 'email',
|
|
||||||
'oidc_oauth_firstname': 'given_name',
|
|
||||||
'oidc_oauth_last_name': 'family_name',
|
|
||||||
'oidc_oauth_account_name_property': '',
|
|
||||||
'oidc_oauth_account_description_property': '',
|
|
||||||
|
|
||||||
# Zone Record Settings
|
|
||||||
'forward_records_allow_edit': {
|
|
||||||
'A': True,
|
|
||||||
'AAAA': True,
|
|
||||||
'AFSDB': False,
|
|
||||||
'ALIAS': False,
|
|
||||||
'CAA': True,
|
|
||||||
'CERT': False,
|
|
||||||
'CDNSKEY': False,
|
|
||||||
'CDS': False,
|
|
||||||
'CNAME': True,
|
|
||||||
'DNSKEY': False,
|
|
||||||
'DNAME': False,
|
|
||||||
'DS': False,
|
|
||||||
'HINFO': False,
|
|
||||||
'KEY': False,
|
|
||||||
'LOC': True,
|
|
||||||
'LUA': False,
|
|
||||||
'MX': True,
|
|
||||||
'NAPTR': False,
|
|
||||||
'NS': True,
|
|
||||||
'NSEC': False,
|
|
||||||
'NSEC3': False,
|
|
||||||
'NSEC3PARAM': False,
|
|
||||||
'OPENPGPKEY': False,
|
|
||||||
'PTR': True,
|
|
||||||
'RP': False,
|
|
||||||
'RRSIG': False,
|
|
||||||
'SOA': False,
|
|
||||||
'SPF': True,
|
|
||||||
'SSHFP': False,
|
|
||||||
'SRV': True,
|
|
||||||
'TKEY': False,
|
|
||||||
'TSIG': False,
|
|
||||||
'TLSA': False,
|
|
||||||
'SMIMEA': False,
|
|
||||||
'TXT': True,
|
|
||||||
'URI': False
|
|
||||||
},
|
|
||||||
'reverse_records_allow_edit': {
|
|
||||||
'A': False,
|
|
||||||
'AAAA': False,
|
|
||||||
'AFSDB': False,
|
|
||||||
'ALIAS': False,
|
|
||||||
'CAA': False,
|
|
||||||
'CERT': False,
|
|
||||||
'CDNSKEY': False,
|
|
||||||
'CDS': False,
|
|
||||||
'CNAME': False,
|
|
||||||
'DNSKEY': False,
|
|
||||||
'DNAME': False,
|
|
||||||
'DS': False,
|
|
||||||
'HINFO': False,
|
|
||||||
'KEY': False,
|
|
||||||
'LOC': True,
|
|
||||||
'LUA': False,
|
|
||||||
'MX': False,
|
|
||||||
'NAPTR': False,
|
|
||||||
'NS': True,
|
|
||||||
'NSEC': False,
|
|
||||||
'NSEC3': False,
|
|
||||||
'NSEC3PARAM': False,
|
|
||||||
'OPENPGPKEY': False,
|
|
||||||
'PTR': True,
|
|
||||||
'RP': False,
|
|
||||||
'RRSIG': False,
|
|
||||||
'SOA': False,
|
|
||||||
'SPF': False,
|
|
||||||
'SSHFP': False,
|
|
||||||
'SRV': False,
|
|
||||||
'TKEY': False,
|
|
||||||
'TSIG': False,
|
|
||||||
'TLSA': False,
|
|
||||||
'SMIMEA': False,
|
|
||||||
'TXT': True,
|
|
||||||
'URI': False
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
groups = {
|
|
||||||
'authentication': [
|
|
||||||
# Local Authentication Settings
|
|
||||||
'local_db_enabled',
|
|
||||||
'signup_enabled',
|
|
||||||
'pwd_enforce_characters',
|
|
||||||
'pwd_min_len',
|
|
||||||
'pwd_min_lowercase',
|
|
||||||
'pwd_min_uppercase',
|
|
||||||
'pwd_min_digits',
|
|
||||||
'pwd_min_special',
|
|
||||||
'pwd_enforce_complexity',
|
|
||||||
'pwd_min_complexity',
|
|
||||||
|
|
||||||
# LDAP Authentication Settings
|
|
||||||
'ldap_enabled',
|
|
||||||
'ldap_type',
|
|
||||||
'ldap_uri',
|
|
||||||
'ldap_base_dn',
|
|
||||||
'ldap_admin_username',
|
|
||||||
'ldap_admin_password',
|
|
||||||
'ldap_domain',
|
|
||||||
'ldap_filter_basic',
|
|
||||||
'ldap_filter_username',
|
|
||||||
'ldap_filter_group',
|
|
||||||
'ldap_filter_groupname',
|
|
||||||
'ldap_sg_enabled',
|
|
||||||
'ldap_admin_group',
|
|
||||||
'ldap_operator_group',
|
|
||||||
'ldap_user_group',
|
|
||||||
'autoprovisioning',
|
|
||||||
'autoprovisioning_attribute',
|
|
||||||
'urn_value',
|
|
||||||
'purge',
|
|
||||||
|
|
||||||
# Google OAuth2 Settings
|
|
||||||
'google_oauth_enabled',
|
|
||||||
'google_oauth_client_id',
|
|
||||||
'google_oauth_client_secret',
|
|
||||||
'google_oauth_scope',
|
|
||||||
'google_base_url',
|
|
||||||
'google_oauth_auto_configure',
|
|
||||||
'google_oauth_metadata_url',
|
|
||||||
'google_token_url',
|
|
||||||
'google_authorize_url',
|
|
||||||
|
|
||||||
# GitHub OAuth2 Settings
|
|
||||||
'github_oauth_enabled',
|
|
||||||
'github_oauth_key',
|
|
||||||
'github_oauth_secret',
|
|
||||||
'github_oauth_scope',
|
|
||||||
'github_oauth_api_url',
|
|
||||||
'github_oauth_auto_configure',
|
|
||||||
'github_oauth_metadata_url',
|
|
||||||
'github_oauth_token_url',
|
|
||||||
'github_oauth_authorize_url',
|
|
||||||
|
|
||||||
# Azure OAuth2 Settings
|
|
||||||
'azure_oauth_enabled',
|
|
||||||
'azure_oauth_key',
|
|
||||||
'azure_oauth_secret',
|
|
||||||
'azure_oauth_scope',
|
|
||||||
'azure_oauth_api_url',
|
|
||||||
'azure_oauth_auto_configure',
|
|
||||||
'azure_oauth_metadata_url',
|
|
||||||
'azure_oauth_token_url',
|
|
||||||
'azure_oauth_authorize_url',
|
|
||||||
'azure_sg_enabled',
|
|
||||||
'azure_admin_group',
|
|
||||||
'azure_operator_group',
|
|
||||||
'azure_user_group',
|
|
||||||
'azure_group_accounts_enabled',
|
|
||||||
'azure_group_accounts_name',
|
|
||||||
'azure_group_accounts_name_re',
|
|
||||||
'azure_group_accounts_description',
|
|
||||||
'azure_group_accounts_description_re',
|
|
||||||
|
|
||||||
# OIDC OAuth2 Settings
|
|
||||||
'oidc_oauth_enabled',
|
|
||||||
'oidc_oauth_key',
|
|
||||||
'oidc_oauth_secret',
|
|
||||||
'oidc_oauth_scope',
|
|
||||||
'oidc_oauth_api_url',
|
|
||||||
'oidc_oauth_auto_configure',
|
|
||||||
'oidc_oauth_metadata_url',
|
|
||||||
'oidc_oauth_token_url',
|
|
||||||
'oidc_oauth_authorize_url',
|
|
||||||
'oidc_oauth_logout_url',
|
|
||||||
'oidc_oauth_username',
|
|
||||||
'oidc_oauth_email',
|
|
||||||
'oidc_oauth_firstname',
|
|
||||||
'oidc_oauth_last_name',
|
|
||||||
'oidc_oauth_account_name_property',
|
|
||||||
'oidc_oauth_account_description_property',
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, id=None, name=None, value=None):
|
def __init__(self, id=None, name=None, value=None):
|
||||||
self.id = id
|
self.id = id
|
||||||
@ -457,44 +26,12 @@ class Setting(db.Model):
|
|||||||
self.name = name
|
self.name = name
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
def convert_type(self, name, value):
|
|
||||||
import json
|
|
||||||
from json import JSONDecodeError
|
|
||||||
if name in self.types:
|
|
||||||
var_type = self.types[name]
|
|
||||||
|
|
||||||
# Handle boolean values
|
|
||||||
if var_type == bool:
|
|
||||||
if value == 'True' or value == 'true' or value == '1' or value is True:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Handle float values
|
|
||||||
if var_type == float:
|
|
||||||
return float(value)
|
|
||||||
|
|
||||||
# Handle integer values
|
|
||||||
if var_type == int:
|
|
||||||
return int(value)
|
|
||||||
|
|
||||||
if (var_type == dict or var_type == list) and isinstance(value, str) and len(value) > 0:
|
|
||||||
try:
|
|
||||||
return json.loads(value)
|
|
||||||
except JSONDecodeError as e:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if var_type == str:
|
|
||||||
return str(value)
|
|
||||||
|
|
||||||
return value
|
|
||||||
|
|
||||||
def set_maintenance(self, mode):
|
def set_maintenance(self, mode):
|
||||||
maintenance = Setting.query.filter(
|
maintenance = Setting.query.filter(
|
||||||
Setting.name == 'maintenance').first()
|
Setting.name == 'maintenance').first()
|
||||||
|
|
||||||
if maintenance is None:
|
if maintenance is None:
|
||||||
value = self.defaults['maintenance']
|
value = AppSettings.defaults['maintenance']
|
||||||
maintenance = Setting(name='maintenance', value=str(value))
|
maintenance = Setting(name='maintenance', value=str(value))
|
||||||
db.session.add(maintenance)
|
db.session.add(maintenance)
|
||||||
|
|
||||||
@ -516,7 +53,7 @@ class Setting(db.Model):
|
|||||||
current_setting = Setting.query.filter(Setting.name == setting).first()
|
current_setting = Setting.query.filter(Setting.name == setting).first()
|
||||||
|
|
||||||
if current_setting is None:
|
if current_setting is None:
|
||||||
value = self.defaults[setting]
|
value = AppSettings.defaults[setting]
|
||||||
current_setting = Setting(name=setting, value=str(value))
|
current_setting = Setting(name=setting, value=str(value))
|
||||||
db.session.add(current_setting)
|
db.session.add(current_setting)
|
||||||
|
|
||||||
@ -541,21 +78,20 @@ class Setting(db.Model):
|
|||||||
current_setting = Setting(name=setting, value=None)
|
current_setting = Setting(name=setting, value=None)
|
||||||
db.session.add(current_setting)
|
db.session.add(current_setting)
|
||||||
|
|
||||||
value = str(self.convert_type(setting, value))
|
value = str(AppSettings.convert_type(setting, value))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
current_setting.value = value
|
current_setting.value = value
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
current_app.logger.error('Cannot edit setting {0}. DETAIL: {1}'.format(
|
current_app.logger.error('Cannot edit setting {0}. DETAIL: {1}'.format(setting, e))
|
||||||
setting, e))
|
|
||||||
current_app.logger.debug(traceback.format_exec())
|
current_app.logger.debug(traceback.format_exec())
|
||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get(self, setting):
|
def get(self, setting):
|
||||||
if setting in self.defaults:
|
if setting in AppSettings.defaults:
|
||||||
|
|
||||||
if setting.upper() in current_app.config:
|
if setting.upper() in current_app.config:
|
||||||
result = current_app.config[setting.upper()]
|
result = current_app.config[setting.upper()]
|
||||||
@ -566,51 +102,45 @@ class Setting(db.Model):
|
|||||||
if hasattr(result, 'value'):
|
if hasattr(result, 'value'):
|
||||||
result = result.value
|
result = result.value
|
||||||
|
|
||||||
return self.convert_type(setting, result)
|
return AppSettings.convert_type(setting, result)
|
||||||
else:
|
else:
|
||||||
return self.defaults[setting]
|
return AppSettings.defaults[setting]
|
||||||
else:
|
else:
|
||||||
current_app.logger.error('Unknown setting queried: {0}'.format(setting))
|
current_app.logger.error('Unknown setting queried: {0}'.format(setting))
|
||||||
|
|
||||||
def get_group(self, group):
|
def get_group(self, group):
|
||||||
if not isinstance(group, list):
|
if not isinstance(group, list):
|
||||||
group = self.groups[group]
|
group = AppSettings.groups[group]
|
||||||
|
|
||||||
result = {}
|
result = {}
|
||||||
records = self.query.all()
|
|
||||||
|
|
||||||
for record in records:
|
for var_name, default_value in AppSettings.defaults.items():
|
||||||
if record.name in group:
|
if var_name in group:
|
||||||
result[record.name] = self.convert_type(record.name, record.value)
|
result[var_name] = self.get(var_name)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def get_records_allow_to_edit(self):
|
def get_records_allow_to_edit(self):
|
||||||
return list(
|
return list(
|
||||||
set(self.get_forward_records_allow_to_edit() +
|
set(self.get_supported_record_types(self.ZONE_TYPE_FORWARD) +
|
||||||
self.get_reverse_records_allow_to_edit()))
|
self.get_supported_record_types(self.ZONE_TYPE_REVERSE)))
|
||||||
|
|
||||||
def get_forward_records_allow_to_edit(self):
|
def get_supported_record_types(self, zone_type):
|
||||||
records = self.get('forward_records_allow_edit')
|
setting_value = []
|
||||||
f_records = literal_eval(records) if isinstance(records,
|
|
||||||
str) else records
|
if zone_type == self.ZONE_TYPE_FORWARD:
|
||||||
r_name = [r for r in f_records if f_records[r]]
|
setting_value = self.get('forward_records_allow_edit')
|
||||||
# Sort alphabetically if python version is smaller than 3.6
|
elif zone_type == self.ZONE_TYPE_REVERSE:
|
||||||
if sys.version_info[0] < 3 or (sys.version_info[0] == 3
|
setting_value = self.get('reverse_records_allow_edit')
|
||||||
and sys.version_info[1] < 6):
|
|
||||||
r_name.sort()
|
records = literal_eval(setting_value) if isinstance(setting_value, str) else setting_value
|
||||||
return r_name
|
types = [r for r in records if records[r]]
|
||||||
|
|
||||||
def get_reverse_records_allow_to_edit(self):
|
|
||||||
records = self.get('reverse_records_allow_edit')
|
|
||||||
r_records = literal_eval(records) if isinstance(records,
|
|
||||||
str) else records
|
|
||||||
r_name = [r for r in r_records if r_records[r]]
|
|
||||||
# Sort alphabetically if python version is smaller than 3.6
|
# Sort alphabetically if python version is smaller than 3.6
|
||||||
if sys.version_info[0] < 3 or (sys.version_info[0] == 3
|
if sys.version_info[0] < 3 or (sys.version_info[0] == 3 and sys.version_info[1] < 6):
|
||||||
and sys.version_info[1] < 6):
|
types.sort()
|
||||||
r_name.sort()
|
|
||||||
return r_name
|
return types
|
||||||
|
|
||||||
def get_ttl_options(self):
|
def get_ttl_options(self):
|
||||||
return [(pytimeparse.parse(ttl), ttl)
|
return [(pytimeparse.parse(ttl), ttl)
|
||||||
|
@ -1460,6 +1460,7 @@ def setting_pdns():
|
|||||||
@login_required
|
@login_required
|
||||||
@operator_role_required
|
@operator_role_required
|
||||||
def setting_records():
|
def setting_records():
|
||||||
|
from powerdnsadmin.lib.settings import AppSettings
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
_fr = Setting().get('forward_records_allow_edit')
|
_fr = Setting().get('forward_records_allow_edit')
|
||||||
_rr = Setting().get('reverse_records_allow_edit')
|
_rr = Setting().get('reverse_records_allow_edit')
|
||||||
@ -1472,7 +1473,7 @@ def setting_records():
|
|||||||
elif request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
fr = {}
|
fr = {}
|
||||||
rr = {}
|
rr = {}
|
||||||
records = Setting().defaults['forward_records_allow_edit']
|
records = AppSettings.defaults['forward_records_allow_edit']
|
||||||
for r in records:
|
for r in records:
|
||||||
fr[r] = True if request.form.get('fr_{0}'.format(
|
fr[r] = True if request.form.get('fr_{0}'.format(
|
||||||
r.lower())) else False
|
r.lower())) else False
|
||||||
@ -1517,6 +1518,7 @@ def setting_authentication():
|
|||||||
@login_required
|
@login_required
|
||||||
@admin_role_required
|
@admin_role_required
|
||||||
def setting_authentication_api():
|
def setting_authentication_api():
|
||||||
|
from powerdnsadmin.lib.settings import AppSettings
|
||||||
result = {'status': 1, 'messages': [], 'data': {}}
|
result = {'status': 1, 'messages': [], 'data': {}}
|
||||||
|
|
||||||
if request.form.get('commit') == '1':
|
if request.form.get('commit') == '1':
|
||||||
@ -1524,7 +1526,7 @@ def setting_authentication_api():
|
|||||||
data = json.loads(request.form.get('data'))
|
data = json.loads(request.form.get('data'))
|
||||||
|
|
||||||
for key, value in data.items():
|
for key, value in data.items():
|
||||||
if key in model.groups['authentication']:
|
if key in AppSettings.groups['authentication']:
|
||||||
model.set(key, value)
|
model.set(key, value)
|
||||||
|
|
||||||
result['data'] = Setting().get_group('authentication')
|
result['data'] = Setting().get_group('authentication')
|
||||||
|
@ -72,9 +72,9 @@ def domain(domain_name):
|
|||||||
quick_edit = Setting().get('record_quick_edit')
|
quick_edit = Setting().get('record_quick_edit')
|
||||||
records_allow_to_edit = Setting().get_records_allow_to_edit()
|
records_allow_to_edit = Setting().get_records_allow_to_edit()
|
||||||
forward_records_allow_to_edit = Setting(
|
forward_records_allow_to_edit = Setting(
|
||||||
).get_forward_records_allow_to_edit()
|
).get_supported_record_types(Setting().ZONE_TYPE_FORWARD)
|
||||||
reverse_records_allow_to_edit = Setting(
|
reverse_records_allow_to_edit = Setting(
|
||||||
).get_reverse_records_allow_to_edit()
|
).get_supported_record_types(Setting().ZONE_TYPE_REVERSE)
|
||||||
ttl_options = Setting().get_ttl_options()
|
ttl_options = Setting().get_ttl_options()
|
||||||
records = []
|
records = []
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user