mirror of
https://github.com/cwinfo/powerdns-admin.git
synced 2024-11-09 15:10:27 +00:00
Feature Update: OAuth Settings Management / Dual-Schema Support (#1515)
This commit is contained in:
commit
4e54a2bb3f
@ -1,3 +1,8 @@
|
|||||||
|
# import everything from environment variables
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
|
||||||
# Defaults for Docker image
|
# Defaults for Docker image
|
||||||
BIND_ADDRESS = '0.0.0.0'
|
BIND_ADDRESS = '0.0.0.0'
|
||||||
PORT = 80
|
PORT = 80
|
||||||
@ -23,6 +28,7 @@ legal_envvars = (
|
|||||||
'OIDC_OAUTH_EMAIL',
|
'OIDC_OAUTH_EMAIL',
|
||||||
'BIND_ADDRESS',
|
'BIND_ADDRESS',
|
||||||
'PORT',
|
'PORT',
|
||||||
|
'SERVER_EXTERNAL_SSL',
|
||||||
'LOG_LEVEL',
|
'LOG_LEVEL',
|
||||||
'SALT',
|
'SALT',
|
||||||
'SQLALCHEMY_TRACK_MODIFICATIONS',
|
'SQLALCHEMY_TRACK_MODIFICATIONS',
|
||||||
@ -97,21 +103,18 @@ legal_envvars_bool = (
|
|||||||
'SESSION_COOKIE_SECURE',
|
'SESSION_COOKIE_SECURE',
|
||||||
'CSRF_COOKIE_SECURE',
|
'CSRF_COOKIE_SECURE',
|
||||||
'CAPTCHA_ENABLE',
|
'CAPTCHA_ENABLE',
|
||||||
|
'SERVER_EXTERNAL_SSL',
|
||||||
)
|
)
|
||||||
|
|
||||||
legal_envvars_dict = (
|
legal_envvars_dict = (
|
||||||
'SQLALCHEMY_ENGINE_OPTIONS',
|
'SQLALCHEMY_ENGINE_OPTIONS',
|
||||||
)
|
)
|
||||||
|
|
||||||
# import everything from environment variables
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
|
|
||||||
def str2bool(v):
|
def str2bool(v):
|
||||||
return v.lower() in ("true", "yes", "1")
|
return v.lower() in ("true", "yes", "1")
|
||||||
|
|
||||||
def dictfromstr(v,ret):
|
|
||||||
|
def dictfromstr(v, ret):
|
||||||
try:
|
try:
|
||||||
return json.loads(ret)
|
return json.loads(ret)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -119,10 +122,11 @@ def dictfromstr(v,ret):
|
|||||||
print(e)
|
print(e)
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
|
|
||||||
for v in legal_envvars:
|
for v in legal_envvars:
|
||||||
|
|
||||||
ret = None
|
ret = None
|
||||||
# _FILE suffix will allow to read value from file, usefull for Docker's
|
# _FILE suffix will allow to read value from file, useful for Docker containers.
|
||||||
# secrets feature
|
# secrets feature
|
||||||
if v + '_FILE' in os.environ:
|
if v + '_FILE' in os.environ:
|
||||||
if v in os.environ:
|
if v in os.environ:
|
||||||
|
@ -1,64 +1,65 @@
|
|||||||
# Supported environment variables
|
# Supported environment variables
|
||||||
|
|
||||||
| Variable | Description | Required | Default value |
|
| Variable | Description | Required | Default value |
|
||||||
| ---------| ----------- | -------- | ------------- |
|
|--------------------------------|--------------------------------------------------------------------------|------------|---------------|
|
||||||
| BIND_ADDRESS |
|
| BIND_ADDRESS |
|
||||||
| CSRF_COOKIE_SECURE |
|
| CSRF_COOKIE_SECURE |
|
||||||
| SESSION_TYPE | null|filesystem|sqlalchemy | | filesystem |
|
| SESSION_TYPE | null | filesystem | sqlalchemy | | filesystem |
|
||||||
| LDAP_ENABLED |
|
| LDAP_ENABLED |
|
||||||
| LOCAL_DB_ENABLED |
|
| LOCAL_DB_ENABLED |
|
||||||
| LOG_LEVEL |
|
| LOG_LEVEL |
|
||||||
| MAIL_DEBUG |
|
| MAIL_DEBUG |
|
||||||
| MAIL_DEFAULT_SENDER |
|
| MAIL_DEFAULT_SENDER |
|
||||||
| MAIL_PASSWORD |
|
| MAIL_PASSWORD |
|
||||||
| MAIL_PORT |
|
| MAIL_PORT |
|
||||||
| MAIL_SERVER |
|
| MAIL_SERVER |
|
||||||
| MAIL_USERNAME |
|
| MAIL_USERNAME |
|
||||||
| MAIL_USE_SSL |
|
| MAIL_USE_SSL |
|
||||||
| MAIL_USE_TLS |
|
| MAIL_USE_TLS |
|
||||||
| OFFLINE_MODE |
|
| OFFLINE_MODE |
|
||||||
| OIDC_OAUTH_API_URL | | | |
|
| OIDC_OAUTH_API_URL | | | |
|
||||||
| OIDC_OAUTH_AUTHORIZE_URL |
|
| OIDC_OAUTH_AUTHORIZE_URL |
|
||||||
| OIDC_OAUTH_TOKEN_URL | | | |
|
| OIDC_OAUTH_TOKEN_URL | | | |
|
||||||
| OIDC_OAUTH_METADATA_URL | | | |
|
| OIDC_OAUTH_METADATA_URL | | | |
|
||||||
| PORT |
|
| PORT |
|
||||||
| REMOTE_USER_COOKIES |
|
| SERVER_EXTERNAL_SSL | Forceful override of URL schema detection when using the url_for method. | False | None |
|
||||||
| REMOTE_USER_LOGOUT_URL |
|
| REMOTE_USER_COOKIES |
|
||||||
| SALT |
|
| REMOTE_USER_LOGOUT_URL |
|
||||||
| SAML_ASSERTION_ENCRYPTED |
|
| SALT |
|
||||||
| SAML_ATTRIBUTE_ACCOUNT |
|
| SAML_ASSERTION_ENCRYPTED |
|
||||||
| SAML_ATTRIBUTE_ADMIN |
|
| SAML_ATTRIBUTE_ACCOUNT |
|
||||||
| SAML_ATTRIBUTE_EMAIL |
|
| SAML_ATTRIBUTE_ADMIN |
|
||||||
| SAML_ATTRIBUTE_GIVENNAME |
|
| SAML_ATTRIBUTE_EMAIL |
|
||||||
| SAML_ATTRIBUTE_GROUP |
|
| SAML_ATTRIBUTE_GIVENNAME |
|
||||||
| SAML_ATTRIBUTE_NAME |
|
| SAML_ATTRIBUTE_GROUP |
|
||||||
| SAML_ATTRIBUTE_SURNAME |
|
| SAML_ATTRIBUTE_NAME |
|
||||||
| SAML_ATTRIBUTE_USERNAME |
|
| SAML_ATTRIBUTE_SURNAME |
|
||||||
| SAML_CERT |
|
| SAML_ATTRIBUTE_USERNAME |
|
||||||
| SAML_DEBUG |
|
| SAML_CERT |
|
||||||
| SAML_ENABLED |
|
| SAML_DEBUG |
|
||||||
| SAML_GROUP_ADMIN_NAME |
|
| SAML_ENABLED |
|
||||||
| SAML_GROUP_TO_ACCOUNT_MAPPING |
|
| SAML_GROUP_ADMIN_NAME |
|
||||||
| SAML_IDP_SSO_BINDING |
|
| SAML_GROUP_TO_ACCOUNT_MAPPING |
|
||||||
| SAML_IDP_ENTITY_ID |
|
| SAML_IDP_SSO_BINDING |
|
||||||
| SAML_KEY |
|
| SAML_IDP_ENTITY_ID |
|
||||||
| SAML_LOGOUT |
|
| SAML_KEY |
|
||||||
| SAML_LOGOUT_URL |
|
| SAML_LOGOUT |
|
||||||
| SAML_METADATA_CACHE_LIFETIME |
|
| SAML_LOGOUT_URL |
|
||||||
| SAML_METADATA_URL |
|
| SAML_METADATA_CACHE_LIFETIME |
|
||||||
| SAML_NAMEID_FORMAT |
|
| SAML_METADATA_URL |
|
||||||
| SAML_PATH |
|
| SAML_NAMEID_FORMAT |
|
||||||
| SAML_SIGN_REQUEST |
|
| SAML_PATH |
|
||||||
| SAML_SP_CONTACT_MAIL |
|
| SAML_SIGN_REQUEST |
|
||||||
| SAML_SP_CONTACT_NAME |
|
| SAML_SP_CONTACT_MAIL |
|
||||||
| SAML_SP_ENTITY_ID |
|
| SAML_SP_CONTACT_NAME |
|
||||||
| SAML_WANT_MESSAGE_SIGNED |
|
| SAML_SP_ENTITY_ID |
|
||||||
| SECRET_KEY | Flask secret key [^1] | Y | no default |
|
| SAML_WANT_MESSAGE_SIGNED |
|
||||||
| SESSION_COOKIE_SECURE |
|
| SECRET_KEY | Flask secret key [^1] | Y | no default |
|
||||||
| SIGNUP_ENABLED |
|
| SESSION_COOKIE_SECURE |
|
||||||
| SQLALCHEMY_DATABASE_URI | SQL Alchemy URI to connect to database | N | no default |
|
| SIGNUP_ENABLED |
|
||||||
|
| SQLALCHEMY_DATABASE_URI | SQL Alchemy URI to connect to database | N | no default |
|
||||||
| SQLALCHEMY_TRACK_MODIFICATIONS |
|
| SQLALCHEMY_TRACK_MODIFICATIONS |
|
||||||
| SQLALCHEMY_ENGINE_OPTIONS | json string. e.g. '{"pool_recycle":600,"echo":1}' [^2] |
|
| SQLALCHEMY_ENGINE_OPTIONS | json string. e.g. '{"pool_recycle":600,"echo":1}' [^2] |
|
||||||
|
|
||||||
[^1]: Flask secret key (see https://flask.palletsprojects.com/en/1.1.x/config/#SECRET_KEY for how to generate)
|
[^1]: Flask secret key (see https://flask.palletsprojects.com/en/1.1.x/config/#SECRET_KEY for how to generate)
|
||||||
[^2]: See Flask-SQLAlchemy Documentation for all engine options.
|
[^2]: See Flask-SQLAlchemy Documentation for all engine options.
|
||||||
|
@ -11,7 +11,9 @@
|
|||||||
"jquery-sparkline": "^2.4.0",
|
"jquery-sparkline": "^2.4.0",
|
||||||
"jquery-ui-dist": "^1.13.2",
|
"jquery-ui-dist": "^1.13.2",
|
||||||
"jquery.quicksearch": "^2.4.0",
|
"jquery.quicksearch": "^2.4.0",
|
||||||
|
"jquery-validation": "^1.19.5",
|
||||||
"jtimeout": "^3.2.0",
|
"jtimeout": "^3.2.0",
|
||||||
|
"knockout": "^3.5.1",
|
||||||
"multiselect": "^0.9.12"
|
"multiselect": "^0.9.12"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
|
@ -20,6 +20,7 @@ js_login = Bundle(
|
|||||||
'node_modules/jquery/dist/jquery.js',
|
'node_modules/jquery/dist/jquery.js',
|
||||||
'node_modules/bootstrap/dist/js/bootstrap.js',
|
'node_modules/bootstrap/dist/js/bootstrap.js',
|
||||||
'node_modules/icheck/icheck.js',
|
'node_modules/icheck/icheck.js',
|
||||||
|
'node_modules/knockout/build/output/knockout-latest.js',
|
||||||
'custom/js/custom.js',
|
'custom/js/custom.js',
|
||||||
filters=(ConcatFilter, 'rjsmin'),
|
filters=(ConcatFilter, 'rjsmin'),
|
||||||
output='generated/login.js')
|
output='generated/login.js')
|
||||||
@ -47,6 +48,7 @@ js_main = Bundle(
|
|||||||
'node_modules/datatables.net-bs4/js/dataTables.bootstrap4.js',
|
'node_modules/datatables.net-bs4/js/dataTables.bootstrap4.js',
|
||||||
'node_modules/jquery-sparkline/jquery.sparkline.js',
|
'node_modules/jquery-sparkline/jquery.sparkline.js',
|
||||||
'node_modules/jquery-slimscroll/jquery.slimscroll.js',
|
'node_modules/jquery-slimscroll/jquery.slimscroll.js',
|
||||||
|
'node_modules/jquery-validation/dist/jquery.validate.js',
|
||||||
'node_modules/icheck/icheck.js',
|
'node_modules/icheck/icheck.js',
|
||||||
'node_modules/fastclick/lib/fastclick.js',
|
'node_modules/fastclick/lib/fastclick.js',
|
||||||
'node_modules/moment/moment.js',
|
'node_modules/moment/moment.js',
|
||||||
@ -55,6 +57,8 @@ js_main = Bundle(
|
|||||||
'node_modules/datatables.net-plugins/sorting/natural.js',
|
'node_modules/datatables.net-plugins/sorting/natural.js',
|
||||||
'node_modules/jtimeout/src/jTimeout.js',
|
'node_modules/jtimeout/src/jTimeout.js',
|
||||||
'node_modules/jquery.quicksearch/src/jquery.quicksearch.js',
|
'node_modules/jquery.quicksearch/src/jquery.quicksearch.js',
|
||||||
|
'node_modules/knockout/build/output/knockout-latest.js',
|
||||||
|
'custom/js/app-authentication-settings-editor.js',
|
||||||
'custom/js/custom.js',
|
'custom/js/custom.js',
|
||||||
'node_modules/bootstrap-datepicker/dist/js/bootstrap-datepicker.js',
|
'node_modules/bootstrap-datepicker/dist/js/bootstrap-datepicker.js',
|
||||||
filters=(ConcatFilter, 'rjsmin'),
|
filters=(ConcatFilter, 'rjsmin'),
|
||||||
|
@ -8,6 +8,7 @@ SECRET_KEY = 'e951e5a1f4b94151b360f47edf596dd2'
|
|||||||
BIND_ADDRESS = '0.0.0.0'
|
BIND_ADDRESS = '0.0.0.0'
|
||||||
PORT = 9191
|
PORT = 9191
|
||||||
HSTS_ENABLED = False
|
HSTS_ENABLED = False
|
||||||
|
SERVER_EXTERNAL_SSL = None
|
||||||
|
|
||||||
SESSION_TYPE = 'sqlalchemy'
|
SESSION_TYPE = 'sqlalchemy'
|
||||||
SESSION_COOKIE_SAMESITE = 'Lax'
|
SESSION_COOKIE_SAMESITE = 'Lax'
|
||||||
|
@ -15,6 +15,7 @@ class Setting(db.Model):
|
|||||||
value = db.Column(db.Text())
|
value = db.Column(db.Text())
|
||||||
|
|
||||||
defaults = {
|
defaults = {
|
||||||
|
# General Settings
|
||||||
'maintenance': False,
|
'maintenance': False,
|
||||||
'fullscreen_layout': True,
|
'fullscreen_layout': True,
|
||||||
'record_helper': True,
|
'record_helper': True,
|
||||||
@ -42,56 +43,82 @@ class Setting(db.Model):
|
|||||||
'pdns_api_timeout': 30,
|
'pdns_api_timeout': 30,
|
||||||
'pdns_version': '4.1.1',
|
'pdns_version': '4.1.1',
|
||||||
'verify_ssl_connections': True,
|
'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,
|
'local_db_enabled': True,
|
||||||
'signup_enabled': True,
|
'signup_enabled': True,
|
||||||
'autoprovisioning': False,
|
'pwd_enforce_characters': False,
|
||||||
'urn_value': '',
|
'pwd_min_len': 10,
|
||||||
'autoprovisioning_attribute': '',
|
'pwd_min_lowercase': 3,
|
||||||
'purge': False,
|
'pwd_min_uppercase': 2,
|
||||||
'verify_user_email': False,
|
'pwd_min_digits': 2,
|
||||||
|
'pwd_min_special': 1,
|
||||||
|
'pwd_enforce_complexity': False,
|
||||||
|
'pwd_min_complexity': 11,
|
||||||
|
|
||||||
|
# LDAP Authentication Settings
|
||||||
'ldap_enabled': False,
|
'ldap_enabled': False,
|
||||||
'ldap_type': 'ldap',
|
'ldap_type': 'ldap',
|
||||||
'ldap_uri': '',
|
'ldap_uri': '',
|
||||||
'ldap_base_dn': '',
|
'ldap_base_dn': '',
|
||||||
'ldap_admin_username': '',
|
'ldap_admin_username': '',
|
||||||
'ldap_admin_password': '',
|
'ldap_admin_password': '',
|
||||||
|
'ldap_domain': '',
|
||||||
'ldap_filter_basic': '',
|
'ldap_filter_basic': '',
|
||||||
'ldap_filter_group': '',
|
|
||||||
'ldap_filter_username': '',
|
'ldap_filter_username': '',
|
||||||
|
'ldap_filter_group': '',
|
||||||
'ldap_filter_groupname': '',
|
'ldap_filter_groupname': '',
|
||||||
'ldap_sg_enabled': False,
|
'ldap_sg_enabled': False,
|
||||||
'ldap_admin_group': '',
|
'ldap_admin_group': '',
|
||||||
'ldap_operator_group': '',
|
'ldap_operator_group': '',
|
||||||
'ldap_user_group': '',
|
'ldap_user_group': '',
|
||||||
'ldap_domain': '',
|
'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_enabled': False,
|
||||||
'github_oauth_key': '',
|
'github_oauth_key': '',
|
||||||
'github_oauth_secret': '',
|
'github_oauth_secret': '',
|
||||||
'github_oauth_scope': 'email',
|
'github_oauth_scope': 'email',
|
||||||
'github_oauth_api_url': 'https://api.github.com/user',
|
'github_oauth_api_url': 'https://api.github.com/user',
|
||||||
'github_oauth_token_url':
|
'github_oauth_auto_configure': False,
|
||||||
'https://github.com/login/oauth/access_token',
|
|
||||||
'github_oauth_authorize_url':
|
|
||||||
'https://github.com/login/oauth/authorize',
|
|
||||||
'github_oauth_metadata_url': '',
|
'github_oauth_metadata_url': '',
|
||||||
'google_oauth_enabled': False,
|
'github_oauth_token_url': 'https://github.com/login/oauth/access_token',
|
||||||
'google_oauth_client_id': '',
|
'github_oauth_authorize_url': 'https://github.com/login/oauth/authorize',
|
||||||
'google_oauth_client_secret': '',
|
|
||||||
'google_token_url': 'https://oauth2.googleapis.com/token',
|
# Azure OAuth2 Settings
|
||||||
'google_oauth_scope': 'openid email profile',
|
|
||||||
'google_authorize_url': 'https://accounts.google.com/o/oauth2/v2/auth',
|
|
||||||
'google_oauth_metadata_url': '',
|
|
||||||
'google_base_url': 'https://www.googleapis.com/oauth2/v3/',
|
|
||||||
'azure_oauth_enabled': False,
|
'azure_oauth_enabled': False,
|
||||||
'azure_oauth_key': '',
|
'azure_oauth_key': '',
|
||||||
'azure_oauth_secret': '',
|
'azure_oauth_secret': '',
|
||||||
'azure_oauth_scope': 'User.Read openid email profile',
|
'azure_oauth_scope': 'User.Read openid email profile',
|
||||||
'azure_oauth_api_url': 'https://graph.microsoft.com/v1.0/',
|
'azure_oauth_api_url': 'https://graph.microsoft.com/v1.0/',
|
||||||
'azure_oauth_token_url':
|
'azure_oauth_auto_configure': True,
|
||||||
'https://login.microsoftonline.com/[tenancy]/oauth2/v2.0/token',
|
|
||||||
'azure_oauth_authorize_url':
|
|
||||||
'https://login.microsoftonline.com/[tenancy]/oauth2/v2.0/authorize',
|
|
||||||
'azure_oauth_metadata_url': '',
|
'azure_oauth_metadata_url': '',
|
||||||
|
'azure_oauth_token_url': '',
|
||||||
|
'azure_oauth_authorize_url': '',
|
||||||
'azure_sg_enabled': False,
|
'azure_sg_enabled': False,
|
||||||
'azure_admin_group': '',
|
'azure_admin_group': '',
|
||||||
'azure_operator_group': '',
|
'azure_operator_group': '',
|
||||||
@ -101,22 +128,26 @@ class Setting(db.Model):
|
|||||||
'azure_group_accounts_name_re': '',
|
'azure_group_accounts_name_re': '',
|
||||||
'azure_group_accounts_description': 'description',
|
'azure_group_accounts_description': 'description',
|
||||||
'azure_group_accounts_description_re': '',
|
'azure_group_accounts_description_re': '',
|
||||||
|
|
||||||
|
# OIDC OAuth2 Settings
|
||||||
'oidc_oauth_enabled': False,
|
'oidc_oauth_enabled': False,
|
||||||
'oidc_oauth_key': '',
|
'oidc_oauth_key': '',
|
||||||
'oidc_oauth_secret': '',
|
'oidc_oauth_secret': '',
|
||||||
'oidc_oauth_scope': 'email',
|
'oidc_oauth_scope': 'email',
|
||||||
'oidc_oauth_api_url': '',
|
'oidc_oauth_api_url': '',
|
||||||
|
'oidc_oauth_auto_configure': True,
|
||||||
|
'oidc_oauth_metadata_url': '',
|
||||||
'oidc_oauth_token_url': '',
|
'oidc_oauth_token_url': '',
|
||||||
'oidc_oauth_authorize_url': '',
|
'oidc_oauth_authorize_url': '',
|
||||||
'oidc_oauth_metadata_url': '',
|
|
||||||
'oidc_oauth_logout_url': '',
|
'oidc_oauth_logout_url': '',
|
||||||
'oidc_oauth_username': 'preferred_username',
|
'oidc_oauth_username': 'preferred_username',
|
||||||
|
'oidc_oauth_email': 'email',
|
||||||
'oidc_oauth_firstname': 'given_name',
|
'oidc_oauth_firstname': 'given_name',
|
||||||
'oidc_oauth_last_name': 'family_name',
|
'oidc_oauth_last_name': 'family_name',
|
||||||
'oidc_oauth_email': 'email',
|
|
||||||
'oidc_oauth_account_name_property': '',
|
'oidc_oauth_account_name_property': '',
|
||||||
'oidc_oauth_account_description_property': '',
|
'oidc_oauth_account_description_property': '',
|
||||||
'enforce_api_ttl': False,
|
|
||||||
|
# Zone Record Settings
|
||||||
'forward_records_allow_edit': {
|
'forward_records_allow_edit': {
|
||||||
'A': True,
|
'A': True,
|
||||||
'AAAA': True,
|
'AAAA': True,
|
||||||
@ -193,22 +224,103 @@ class Setting(db.Model):
|
|||||||
'TXT': True,
|
'TXT': True,
|
||||||
'URI': False
|
'URI': False
|
||||||
},
|
},
|
||||||
'ttl_options': '1 minute,5 minutes,30 minutes,60 minutes,24 hours',
|
}
|
||||||
'otp_field_enabled': True,
|
|
||||||
'custom_css': '',
|
groups = {
|
||||||
'otp_force': False,
|
'authentication': [
|
||||||
'max_history_records': 1000,
|
# Local Authentication Settings
|
||||||
'deny_domain_override': False,
|
'local_db_enabled',
|
||||||
'account_name_extra_chars': False,
|
'signup_enabled',
|
||||||
'gravatar_enabled': False,
|
'pwd_enforce_characters',
|
||||||
'pwd_enforce_characters': False,
|
'pwd_min_len',
|
||||||
'pwd_min_len': 10,
|
'pwd_min_lowercase',
|
||||||
'pwd_min_lowercase': 3,
|
'pwd_min_uppercase',
|
||||||
'pwd_min_uppercase': 2,
|
'pwd_min_digits',
|
||||||
'pwd_min_digits': 2,
|
'pwd_min_special',
|
||||||
'pwd_min_special': 1,
|
'pwd_enforce_complexity',
|
||||||
'pwd_enforce_complexity': False,
|
'pwd_min_complexity',
|
||||||
'pwd_min_complexity': 11
|
|
||||||
|
# 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):
|
||||||
@ -306,6 +418,28 @@ class Setting(db.Model):
|
|||||||
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):
|
||||||
|
if isinstance(group, str):
|
||||||
|
group = self.groups[group]
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
records = self.query.all()
|
||||||
|
|
||||||
|
for record in records:
|
||||||
|
if record.name in group:
|
||||||
|
value = record.value
|
||||||
|
|
||||||
|
if value in ['True', 'False']:
|
||||||
|
value = strtobool(value)
|
||||||
|
elif value.isdecimal() and '.' in value:
|
||||||
|
value = float(value)
|
||||||
|
elif value.isnumeric():
|
||||||
|
value = int(value)
|
||||||
|
|
||||||
|
result[record.name] = value
|
||||||
|
|
||||||
|
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_forward_records_allow_to_edit() +
|
||||||
|
@ -72,8 +72,8 @@ def get_record_changes(del_rrset, add_rrset):
|
|||||||
"""For the given record, return the state dict."""
|
"""For the given record, return the state dict."""
|
||||||
return {
|
return {
|
||||||
"disabled": record['disabled'],
|
"disabled": record['disabled'],
|
||||||
"content": record['content'],
|
"content": record['content'],
|
||||||
"comment": record.get('comment', ''),
|
"comment": record.get('comment', ''),
|
||||||
}
|
}
|
||||||
|
|
||||||
add_records = get_records(add_rrset)
|
add_records = get_records(add_rrset)
|
||||||
@ -149,8 +149,8 @@ def extract_changelogs_from_a_history_entry(out_changes, history_entry, change_n
|
|||||||
# Sort them by the record name
|
# Sort them by the record name
|
||||||
if change_num in out_changes:
|
if change_num in out_changes:
|
||||||
out_changes[change_num].sort(key=lambda change:
|
out_changes[change_num].sort(key=lambda change:
|
||||||
change.del_rrset['name'] if change.del_rrset else change.add_rrset['name']
|
change.del_rrset['name'] if change.del_rrset else change.add_rrset['name']
|
||||||
)
|
)
|
||||||
|
|
||||||
# only used for changelog per record
|
# only used for changelog per record
|
||||||
if record_name != None and record_type != None: # then get only the records with the specific (record_name, record_type) tuple
|
if record_name != None and record_type != None: # then get only the records with the specific (record_name, record_type) tuple
|
||||||
@ -897,7 +897,8 @@ class DetailedHistory():
|
|||||||
description=DetailedHistory.get_key_val(detail_dict,
|
description=DetailedHistory.get_key_val(detail_dict,
|
||||||
"description"))
|
"description"))
|
||||||
|
|
||||||
elif any(msg in history.msg for msg in ['Change zone','Change domain']) and 'access control' in history.msg: # added or removed a user from a zone
|
elif any(msg in history.msg for msg in ['Change zone',
|
||||||
|
'Change domain']) and 'access control' in history.msg: # added or removed a user from a zone
|
||||||
users_with_access = DetailedHistory.get_key_val(detail_dict, "user_has_access")
|
users_with_access = DetailedHistory.get_key_val(detail_dict, "user_has_access")
|
||||||
self.detailed_msg = render_template_string("""
|
self.detailed_msg = render_template_string("""
|
||||||
<table class="table table-bordered table-striped">
|
<table class="table table-bordered table-striped">
|
||||||
@ -942,7 +943,7 @@ class DetailedHistory():
|
|||||||
linked_domains=DetailedHistory.get_key_val(detail_dict,
|
linked_domains=DetailedHistory.get_key_val(detail_dict,
|
||||||
"domains"))
|
"domains"))
|
||||||
|
|
||||||
elif any(msg in history.msg for msg in ['Update type for zone','Update type for domain']):
|
elif any(msg in history.msg for msg in ['Update type for zone', 'Update type for domain']):
|
||||||
self.detailed_msg = render_template_string("""
|
self.detailed_msg = render_template_string("""
|
||||||
<table class="table table-bordered table-striped">
|
<table class="table table-bordered table-striped">
|
||||||
<tr><td>Zone: </td><td>{{ domain }}</td></tr>
|
<tr><td>Zone: </td><td>{{ domain }}</td></tr>
|
||||||
@ -977,7 +978,8 @@ class DetailedHistory():
|
|||||||
'status'),
|
'status'),
|
||||||
history_msg=DetailedHistory.get_key_val(detail_dict, 'msg'))
|
history_msg=DetailedHistory.get_key_val(detail_dict, 'msg'))
|
||||||
|
|
||||||
elif any(msg in history.msg for msg in ['Update zone','Update domain']) and 'associate account' in history.msg: # When an account gets associated or dissociate with zones
|
elif any(msg in history.msg for msg in ['Update zone',
|
||||||
|
'Update domain']) and 'associate account' in history.msg: # When an account gets associated or dissociate with zones
|
||||||
self.detailed_msg = render_template_string('''
|
self.detailed_msg = render_template_string('''
|
||||||
<table class="table table-bordered table-striped">
|
<table class="table table-bordered table-striped">
|
||||||
<tr><td>Associate: </td><td>{{ history_assoc_account }}</td></tr>
|
<tr><td>Associate: </td><td>{{ history_assoc_account }}</td></tr>
|
||||||
@ -1231,8 +1233,10 @@ def history_table(): # ajax call data
|
|||||||
.filter(
|
.filter(
|
||||||
db.and_(
|
db.and_(
|
||||||
db.or_(
|
db.or_(
|
||||||
History.msg.like("%domain " + domain_name) if domain_name != "*" else History.msg.like("%domain%"),
|
History.msg.like("%domain " + domain_name) if domain_name != "*" else History.msg.like(
|
||||||
History.msg.like("%zone " + domain_name) if domain_name != "*" else History.msg.like("%zone%"),
|
"%domain%"),
|
||||||
|
History.msg.like("%zone " + domain_name) if domain_name != "*" else History.msg.like(
|
||||||
|
"%zone%"),
|
||||||
History.msg.like(
|
History.msg.like(
|
||||||
"%domain " + domain_name + " access control") if domain_name != "*" else History.msg.like(
|
"%domain " + domain_name + " access control") if domain_name != "*" else History.msg.like(
|
||||||
"%domain%access control"),
|
"%domain%access control"),
|
||||||
@ -1540,287 +1544,34 @@ def has_an_auth_method(local_db_enabled=None,
|
|||||||
oidc_oauth_enabled = Setting().get('oidc_oauth_enabled')
|
oidc_oauth_enabled = Setting().get('oidc_oauth_enabled')
|
||||||
if azure_oauth_enabled is None:
|
if azure_oauth_enabled is None:
|
||||||
azure_oauth_enabled = Setting().get('azure_oauth_enabled')
|
azure_oauth_enabled = Setting().get('azure_oauth_enabled')
|
||||||
return local_db_enabled or ldap_enabled or google_oauth_enabled or github_oauth_enabled or oidc_oauth_enabled or azure_oauth_enabled
|
return local_db_enabled or ldap_enabled or google_oauth_enabled or github_oauth_enabled or oidc_oauth_enabled \
|
||||||
|
or azure_oauth_enabled
|
||||||
|
|
||||||
|
|
||||||
@admin_bp.route('/setting/authentication', methods=['GET', 'POST'])
|
@admin_bp.route('/setting/authentication', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@admin_role_required
|
@admin_role_required
|
||||||
def setting_authentication():
|
def setting_authentication():
|
||||||
if request.method == 'GET':
|
return render_template('admin_setting_authentication.html')
|
||||||
return render_template('admin_setting_authentication.html')
|
|
||||||
elif request.method == 'POST':
|
|
||||||
conf_type = request.form.get('config_tab')
|
|
||||||
result = None
|
|
||||||
|
|
||||||
if conf_type == 'general':
|
|
||||||
local_db_enabled = True if request.form.get(
|
|
||||||
'local_db_enabled') else False
|
|
||||||
signup_enabled = True if request.form.get(
|
|
||||||
'signup_enabled') else False
|
|
||||||
|
|
||||||
pwd_enforce_characters = True if request.form.get('pwd_enforce_characters') else False
|
@admin_bp.route('/setting/authentication/api', methods=['POST'])
|
||||||
pwd_min_len = safe_cast(request.form.get('pwd_min_len', Setting().defaults["pwd_min_len"]), int,
|
@login_required
|
||||||
Setting().defaults["pwd_min_len"])
|
@admin_role_required
|
||||||
pwd_min_lowercase = safe_cast(request.form.get('pwd_min_lowercase', Setting().defaults["pwd_min_lowercase"]), int,
|
def setting_authentication_api():
|
||||||
Setting().defaults["pwd_min_lowercase"])
|
result = {'status': 1, 'messages': [], 'data': {}}
|
||||||
pwd_min_uppercase = safe_cast(request.form.get('pwd_min_uppercase', Setting().defaults["pwd_min_uppercase"]), int,
|
|
||||||
Setting().defaults["pwd_min_uppercase"])
|
|
||||||
pwd_min_digits = safe_cast(request.form.get('pwd_min_digits', Setting().defaults["pwd_min_digits"]), int,
|
|
||||||
Setting().defaults["pwd_min_digits"])
|
|
||||||
pwd_min_special = safe_cast(request.form.get('pwd_min_special', Setting().defaults["pwd_min_special"]), int,
|
|
||||||
Setting().defaults["pwd_min_special"])
|
|
||||||
|
|
||||||
pwd_enforce_complexity = True if request.form.get('pwd_enforce_complexity') else False
|
if request.form.get('commit') == '1':
|
||||||
pwd_min_complexity = safe_cast(request.form.get('pwd_min_complexity', Setting().defaults["pwd_min_complexity"]), int,
|
model = Setting()
|
||||||
Setting().defaults["pwd_min_complexity"])
|
data = json.loads(request.form.get('data'))
|
||||||
|
|
||||||
if not has_an_auth_method(local_db_enabled=local_db_enabled):
|
for key, value in data.items():
|
||||||
result = {
|
if key in model.groups['authentication']:
|
||||||
'status':
|
model.set(key, value)
|
||||||
False,
|
|
||||||
'msg':
|
|
||||||
'Must have at least one authentication method enabled.'
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
Setting().set('local_db_enabled', local_db_enabled)
|
|
||||||
Setting().set('signup_enabled', signup_enabled)
|
|
||||||
|
|
||||||
Setting().set('pwd_enforce_characters', pwd_enforce_characters)
|
result['data'] = Setting().get_group('authentication')
|
||||||
Setting().set('pwd_min_len', pwd_min_len)
|
|
||||||
Setting().set('pwd_min_lowercase', pwd_min_lowercase)
|
|
||||||
Setting().set('pwd_min_uppercase', pwd_min_uppercase)
|
|
||||||
Setting().set('pwd_min_digits', pwd_min_digits)
|
|
||||||
Setting().set('pwd_min_special', pwd_min_special)
|
|
||||||
|
|
||||||
Setting().set('pwd_enforce_complexity', pwd_enforce_complexity)
|
return result
|
||||||
Setting().set('pwd_min_complexity', pwd_min_complexity)
|
|
||||||
|
|
||||||
result = {'status': True, 'msg': 'Saved successfully'}
|
|
||||||
|
|
||||||
elif conf_type == 'ldap':
|
|
||||||
ldap_enabled = True if request.form.get('ldap_enabled') else False
|
|
||||||
|
|
||||||
if not has_an_auth_method(ldap_enabled=ldap_enabled):
|
|
||||||
result = {
|
|
||||||
'status':
|
|
||||||
False,
|
|
||||||
'msg':
|
|
||||||
'Must have at least one authentication method enabled.'
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
Setting().set('ldap_enabled', ldap_enabled)
|
|
||||||
Setting().set('ldap_type', request.form.get('ldap_type'))
|
|
||||||
Setting().set('ldap_uri', request.form.get('ldap_uri'))
|
|
||||||
Setting().set('ldap_base_dn', request.form.get('ldap_base_dn'))
|
|
||||||
Setting().set('ldap_admin_username',
|
|
||||||
request.form.get('ldap_admin_username'))
|
|
||||||
Setting().set('ldap_admin_password',
|
|
||||||
request.form.get('ldap_admin_password'))
|
|
||||||
Setting().set('ldap_filter_basic',
|
|
||||||
request.form.get('ldap_filter_basic'))
|
|
||||||
Setting().set('ldap_filter_group',
|
|
||||||
request.form.get('ldap_filter_group'))
|
|
||||||
Setting().set('ldap_filter_username',
|
|
||||||
request.form.get('ldap_filter_username'))
|
|
||||||
Setting().set('ldap_filter_groupname',
|
|
||||||
request.form.get('ldap_filter_groupname'))
|
|
||||||
Setting().set(
|
|
||||||
'ldap_sg_enabled', True
|
|
||||||
if request.form.get('ldap_sg_enabled') == 'ON' else False)
|
|
||||||
Setting().set('ldap_admin_group',
|
|
||||||
request.form.get('ldap_admin_group'))
|
|
||||||
Setting().set('ldap_operator_group',
|
|
||||||
request.form.get('ldap_operator_group'))
|
|
||||||
Setting().set('ldap_user_group',
|
|
||||||
request.form.get('ldap_user_group'))
|
|
||||||
Setting().set('ldap_domain', request.form.get('ldap_domain'))
|
|
||||||
Setting().set(
|
|
||||||
'autoprovisioning', True
|
|
||||||
if request.form.get('autoprovisioning') == 'ON' else False)
|
|
||||||
Setting().set('autoprovisioning_attribute',
|
|
||||||
request.form.get('autoprovisioning_attribute'))
|
|
||||||
|
|
||||||
if request.form.get('autoprovisioning') == 'ON':
|
|
||||||
if validateURN(request.form.get('urn_value')):
|
|
||||||
Setting().set('urn_value',
|
|
||||||
request.form.get('urn_value'))
|
|
||||||
else:
|
|
||||||
return render_template('admin_setting_authentication.html',
|
|
||||||
error="Invalid urn")
|
|
||||||
else:
|
|
||||||
Setting().set('urn_value',
|
|
||||||
request.form.get('urn_value'))
|
|
||||||
|
|
||||||
Setting().set('purge', True
|
|
||||||
if request.form.get('purge') == 'ON' else False)
|
|
||||||
|
|
||||||
result = {'status': True, 'msg': 'Saved successfully'}
|
|
||||||
elif conf_type == 'google':
|
|
||||||
google_oauth_enabled = True if request.form.get(
|
|
||||||
'google_oauth_enabled') else False
|
|
||||||
if not has_an_auth_method(google_oauth_enabled=google_oauth_enabled):
|
|
||||||
result = {
|
|
||||||
'status':
|
|
||||||
False,
|
|
||||||
'msg':
|
|
||||||
'Must have at least one authentication method enabled.'
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
Setting().set('google_oauth_enabled', google_oauth_enabled)
|
|
||||||
Setting().set('google_oauth_client_id',
|
|
||||||
request.form.get('google_oauth_client_id'))
|
|
||||||
Setting().set('google_oauth_client_secret',
|
|
||||||
request.form.get('google_oauth_client_secret'))
|
|
||||||
Setting().set('google_oauth_metadata_url',
|
|
||||||
request.form.get('google_oauth_metadata_url'))
|
|
||||||
Setting().set('google_token_url',
|
|
||||||
request.form.get('google_token_url'))
|
|
||||||
Setting().set('google_oauth_scope',
|
|
||||||
request.form.get('google_oauth_scope'))
|
|
||||||
Setting().set('google_authorize_url',
|
|
||||||
request.form.get('google_authorize_url'))
|
|
||||||
Setting().set('google_base_url',
|
|
||||||
request.form.get('google_base_url'))
|
|
||||||
result = {
|
|
||||||
'status': True,
|
|
||||||
'msg':
|
|
||||||
'Saved successfully. Please reload PDA to take effect.'
|
|
||||||
}
|
|
||||||
elif conf_type == 'github':
|
|
||||||
github_oauth_enabled = True if request.form.get(
|
|
||||||
'github_oauth_enabled') else False
|
|
||||||
if not has_an_auth_method(github_oauth_enabled=github_oauth_enabled):
|
|
||||||
result = {
|
|
||||||
'status':
|
|
||||||
False,
|
|
||||||
'msg':
|
|
||||||
'Must have at least one authentication method enabled.'
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
Setting().set('github_oauth_enabled', github_oauth_enabled)
|
|
||||||
Setting().set('github_oauth_key',
|
|
||||||
request.form.get('github_oauth_key'))
|
|
||||||
Setting().set('github_oauth_secret',
|
|
||||||
request.form.get('github_oauth_secret'))
|
|
||||||
Setting().set('github_oauth_scope',
|
|
||||||
request.form.get('github_oauth_scope'))
|
|
||||||
Setting().set('github_oauth_api_url',
|
|
||||||
request.form.get('github_oauth_api_url'))
|
|
||||||
Setting().set('github_oauth_metadata_url',
|
|
||||||
request.form.get('github_oauth_metadata_url'))
|
|
||||||
Setting().set('github_oauth_token_url',
|
|
||||||
request.form.get('github_oauth_token_url'))
|
|
||||||
Setting().set('github_oauth_authorize_url',
|
|
||||||
request.form.get('github_oauth_authorize_url'))
|
|
||||||
result = {
|
|
||||||
'status': True,
|
|
||||||
'msg':
|
|
||||||
'Saved successfully. Please reload PDA to take effect.'
|
|
||||||
}
|
|
||||||
elif conf_type == 'azure':
|
|
||||||
azure_oauth_enabled = True if request.form.get(
|
|
||||||
'azure_oauth_enabled') else False
|
|
||||||
if not has_an_auth_method(azure_oauth_enabled=azure_oauth_enabled):
|
|
||||||
result = {
|
|
||||||
'status':
|
|
||||||
False,
|
|
||||||
'msg':
|
|
||||||
'Must have at least one authentication method enabled.'
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
Setting().set('azure_oauth_enabled', azure_oauth_enabled)
|
|
||||||
Setting().set('azure_oauth_key',
|
|
||||||
request.form.get('azure_oauth_key'))
|
|
||||||
Setting().set('azure_oauth_secret',
|
|
||||||
request.form.get('azure_oauth_secret'))
|
|
||||||
Setting().set('azure_oauth_scope',
|
|
||||||
request.form.get('azure_oauth_scope'))
|
|
||||||
Setting().set('azure_oauth_api_url',
|
|
||||||
request.form.get('azure_oauth_api_url'))
|
|
||||||
Setting().set('azure_oauth_metadata_url',
|
|
||||||
request.form.get('azure_oauth_metadata_url'))
|
|
||||||
Setting().set('azure_oauth_token_url',
|
|
||||||
request.form.get('azure_oauth_token_url'))
|
|
||||||
Setting().set('azure_oauth_authorize_url',
|
|
||||||
request.form.get('azure_oauth_authorize_url'))
|
|
||||||
Setting().set(
|
|
||||||
'azure_sg_enabled', True
|
|
||||||
if request.form.get('azure_sg_enabled') == 'ON' else False)
|
|
||||||
Setting().set('azure_admin_group',
|
|
||||||
request.form.get('azure_admin_group'))
|
|
||||||
Setting().set('azure_operator_group',
|
|
||||||
request.form.get('azure_operator_group'))
|
|
||||||
Setting().set('azure_user_group',
|
|
||||||
request.form.get('azure_user_group'))
|
|
||||||
Setting().set(
|
|
||||||
'azure_group_accounts_enabled', True
|
|
||||||
if request.form.get('azure_group_accounts_enabled') == 'ON' else False)
|
|
||||||
Setting().set('azure_group_accounts_name',
|
|
||||||
request.form.get('azure_group_accounts_name'))
|
|
||||||
Setting().set('azure_group_accounts_name_re',
|
|
||||||
request.form.get('azure_group_accounts_name_re'))
|
|
||||||
Setting().set('azure_group_accounts_description',
|
|
||||||
request.form.get('azure_group_accounts_description'))
|
|
||||||
Setting().set('azure_group_accounts_description_re',
|
|
||||||
request.form.get('azure_group_accounts_description_re'))
|
|
||||||
result = {
|
|
||||||
'status': True,
|
|
||||||
'msg':
|
|
||||||
'Saved successfully. Please reload PDA to take effect.'
|
|
||||||
}
|
|
||||||
elif conf_type == 'oidc':
|
|
||||||
oidc_oauth_enabled = True if request.form.get(
|
|
||||||
'oidc_oauth_enabled') else False
|
|
||||||
if not has_an_auth_method(oidc_oauth_enabled=oidc_oauth_enabled):
|
|
||||||
result = {
|
|
||||||
'status':
|
|
||||||
False,
|
|
||||||
'msg':
|
|
||||||
'Must have at least one authentication method enabled.'
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
Setting().set(
|
|
||||||
'oidc_oauth_enabled',
|
|
||||||
True if request.form.get('oidc_oauth_enabled') else False)
|
|
||||||
Setting().set('oidc_oauth_key',
|
|
||||||
request.form.get('oidc_oauth_key'))
|
|
||||||
Setting().set('oidc_oauth_secret',
|
|
||||||
request.form.get('oidc_oauth_secret'))
|
|
||||||
Setting().set('oidc_oauth_scope',
|
|
||||||
request.form.get('oidc_oauth_scope'))
|
|
||||||
Setting().set('oidc_oauth_api_url',
|
|
||||||
request.form.get('oidc_oauth_api_url'))
|
|
||||||
Setting().set('oidc_oauth_metadata_url',
|
|
||||||
request.form.get('oidc_oauth_metadata_url'))
|
|
||||||
Setting().set('oidc_oauth_token_url',
|
|
||||||
request.form.get('oidc_oauth_token_url'))
|
|
||||||
Setting().set('oidc_oauth_authorize_url',
|
|
||||||
request.form.get('oidc_oauth_authorize_url'))
|
|
||||||
Setting().set('oidc_oauth_logout_url',
|
|
||||||
request.form.get('oidc_oauth_logout_url'))
|
|
||||||
Setting().set('oidc_oauth_username',
|
|
||||||
request.form.get('oidc_oauth_username'))
|
|
||||||
Setting().set('oidc_oauth_firstname',
|
|
||||||
request.form.get('oidc_oauth_firstname'))
|
|
||||||
Setting().set('oidc_oauth_last_name',
|
|
||||||
request.form.get('oidc_oauth_last_name'))
|
|
||||||
Setting().set('oidc_oauth_email',
|
|
||||||
request.form.get('oidc_oauth_email'))
|
|
||||||
Setting().set('oidc_oauth_account_name_property',
|
|
||||||
request.form.get('oidc_oauth_account_name_property'))
|
|
||||||
Setting().set('oidc_oauth_account_description_property',
|
|
||||||
request.form.get('oidc_oauth_account_description_property'))
|
|
||||||
result = {
|
|
||||||
'status': True,
|
|
||||||
'msg':
|
|
||||||
'Saved successfully. Please reload PDA to take effect.'
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
return abort(400)
|
|
||||||
|
|
||||||
return render_template('admin_setting_authentication.html',
|
|
||||||
result=result)
|
|
||||||
|
|
||||||
|
|
||||||
@admin_bp.route('/templates', methods=['GET', 'POST'])
|
@admin_bp.route('/templates', methods=['GET', 'POST'])
|
||||||
@ -2097,16 +1848,16 @@ def global_search():
|
|||||||
results = server.global_search(object_type='all', query=query)
|
results = server.global_search(object_type='all', query=query)
|
||||||
|
|
||||||
# Filter results to domains to which the user has access permission
|
# Filter results to domains to which the user has access permission
|
||||||
if current_user.role.name not in [ 'Administrator', 'Operator' ]:
|
if current_user.role.name not in ['Administrator', 'Operator']:
|
||||||
allowed_domains = db.session.query(Domain) \
|
allowed_domains = db.session.query(Domain) \
|
||||||
.outerjoin(DomainUser, Domain.id == DomainUser.domain_id) \
|
.outerjoin(DomainUser, Domain.id == DomainUser.domain_id) \
|
||||||
.outerjoin(Account, Domain.account_id == Account.id) \
|
.outerjoin(Account, Domain.account_id == Account.id) \
|
||||||
.outerjoin(AccountUser, Account.id == AccountUser.account_id) \
|
.outerjoin(AccountUser, Account.id == AccountUser.account_id) \
|
||||||
.filter(
|
.filter(
|
||||||
db.or_(
|
db.or_(
|
||||||
DomainUser.user_id == current_user.id,
|
DomainUser.user_id == current_user.id,
|
||||||
AccountUser.user_id == current_user.id
|
AccountUser.user_id == current_user.id
|
||||||
)) \
|
)) \
|
||||||
.with_entities(Domain.name) \
|
.with_entities(Domain.name) \
|
||||||
.all()
|
.all()
|
||||||
allowed_domains = [value for value, in allowed_domains]
|
allowed_domains = [value for value, in allowed_domains]
|
||||||
|
@ -45,6 +45,7 @@ index_bp = Blueprint('index',
|
|||||||
template_folder='templates',
|
template_folder='templates',
|
||||||
url_prefix='/')
|
url_prefix='/')
|
||||||
|
|
||||||
|
|
||||||
@index_bp.before_app_first_request
|
@index_bp.before_app_first_request
|
||||||
def register_modules():
|
def register_modules():
|
||||||
global google
|
global google
|
||||||
@ -68,7 +69,7 @@ def before_request():
|
|||||||
# Check site is in maintenance mode
|
# Check site is in maintenance mode
|
||||||
maintenance = Setting().get('maintenance')
|
maintenance = Setting().get('maintenance')
|
||||||
if maintenance and current_user.is_authenticated and current_user.role.name not in [
|
if maintenance and current_user.is_authenticated and current_user.role.name not in [
|
||||||
'Administrator', 'Operator'
|
'Administrator', 'Operator'
|
||||||
]:
|
]:
|
||||||
return render_template('maintenance.html')
|
return render_template('maintenance.html')
|
||||||
|
|
||||||
@ -98,7 +99,11 @@ def google_login():
|
|||||||
)
|
)
|
||||||
abort(400)
|
abort(400)
|
||||||
else:
|
else:
|
||||||
redirect_uri = url_for('google_authorized', _external=True)
|
use_ssl = current_app.config.get('SERVER_EXTERNAL_SSL')
|
||||||
|
params = {'_external': True}
|
||||||
|
if isinstance(use_ssl, bool):
|
||||||
|
params['_scheme'] = 'https' if use_ssl else 'http'
|
||||||
|
redirect_uri = url_for('google_authorized', **params)
|
||||||
return google.authorize_redirect(redirect_uri)
|
return google.authorize_redirect(redirect_uri)
|
||||||
|
|
||||||
|
|
||||||
@ -110,7 +115,11 @@ def github_login():
|
|||||||
)
|
)
|
||||||
abort(400)
|
abort(400)
|
||||||
else:
|
else:
|
||||||
redirect_uri = url_for('github_authorized', _external=True)
|
use_ssl = current_app.config.get('SERVER_EXTERNAL_SSL')
|
||||||
|
params = {'_external': True}
|
||||||
|
if isinstance(use_ssl, bool):
|
||||||
|
params['_scheme'] = 'https' if use_ssl else 'http'
|
||||||
|
redirect_uri = url_for('github_authorized', **params)
|
||||||
return github.authorize_redirect(redirect_uri)
|
return github.authorize_redirect(redirect_uri)
|
||||||
|
|
||||||
|
|
||||||
@ -122,9 +131,11 @@ def azure_login():
|
|||||||
)
|
)
|
||||||
abort(400)
|
abort(400)
|
||||||
else:
|
else:
|
||||||
redirect_uri = url_for('azure_authorized',
|
use_ssl = current_app.config.get('SERVER_EXTERNAL_SSL')
|
||||||
_external=True,
|
params = {'_external': True}
|
||||||
_scheme='https')
|
if isinstance(use_ssl, bool):
|
||||||
|
params['_scheme'] = 'https' if use_ssl else 'http'
|
||||||
|
redirect_uri = url_for('azure_authorized', **params)
|
||||||
return azure.authorize_redirect(redirect_uri)
|
return azure.authorize_redirect(redirect_uri)
|
||||||
|
|
||||||
|
|
||||||
@ -136,7 +147,11 @@ def oidc_login():
|
|||||||
)
|
)
|
||||||
abort(400)
|
abort(400)
|
||||||
else:
|
else:
|
||||||
redirect_uri = url_for('oidc_authorized', _external=True)
|
use_ssl = current_app.config.get('SERVER_EXTERNAL_SSL')
|
||||||
|
params = {'_external': True}
|
||||||
|
if isinstance(use_ssl, bool):
|
||||||
|
params['_scheme'] = 'https' if use_ssl else 'http'
|
||||||
|
redirect_uri = url_for('oidc_authorized', **params)
|
||||||
return oidc.authorize_redirect(redirect_uri)
|
return oidc.authorize_redirect(redirect_uri)
|
||||||
|
|
||||||
|
|
||||||
@ -149,18 +164,18 @@ def login():
|
|||||||
|
|
||||||
if 'google_token' in session:
|
if 'google_token' in session:
|
||||||
user_data = json.loads(google.get('userinfo').text)
|
user_data = json.loads(google.get('userinfo').text)
|
||||||
first_name = user_data['given_name']
|
google_first_name = user_data['given_name']
|
||||||
surname = user_data['family_name']
|
google_last_name = user_data['family_name']
|
||||||
email = user_data['email']
|
google_email = user_data['email']
|
||||||
user = User.query.filter_by(username=email).first()
|
user = User.query.filter_by(username=google_email).first()
|
||||||
if user is None:
|
if user is None:
|
||||||
user = User.query.filter_by(email=email).first()
|
user = User.query.filter_by(email=google_email).first()
|
||||||
if not user:
|
if not user:
|
||||||
user = User(username=email,
|
user = User(username=google_email,
|
||||||
firstname=first_name,
|
firstname=google_first_name,
|
||||||
lastname=surname,
|
lastname=google_last_name,
|
||||||
plain_text_password=None,
|
plain_text_password=None,
|
||||||
email=email)
|
email=google_email)
|
||||||
|
|
||||||
result = user.create_local_user()
|
result = user.create_local_user()
|
||||||
if not result['status']:
|
if not result['status']:
|
||||||
@ -172,10 +187,18 @@ def login():
|
|||||||
return authenticate_user(user, 'Google OAuth')
|
return authenticate_user(user, 'Google OAuth')
|
||||||
|
|
||||||
if 'github_token' in session:
|
if 'github_token' in session:
|
||||||
me = json.loads(github.get('user').text)
|
user_data = json.loads(github.get('user').text)
|
||||||
github_username = me['login']
|
github_username = user_data['login']
|
||||||
github_name = me['name']
|
github_first_name = user_data['name']
|
||||||
github_email = me['email']
|
github_last_name = ''
|
||||||
|
github_email = user_data['email']
|
||||||
|
|
||||||
|
# If the user's full name from GitHub contains at least two words, use the first word as the first name and
|
||||||
|
# the rest as the last name.
|
||||||
|
github_name_parts = github_first_name.split(' ')
|
||||||
|
if len(github_name_parts) > 1:
|
||||||
|
github_first_name = github_name_parts[0]
|
||||||
|
github_last_name = ' '.join(github_name_parts[1:])
|
||||||
|
|
||||||
user = User.query.filter_by(username=github_username).first()
|
user = User.query.filter_by(username=github_username).first()
|
||||||
if user is None:
|
if user is None:
|
||||||
@ -183,8 +206,8 @@ def login():
|
|||||||
if not user:
|
if not user:
|
||||||
user = User(username=github_username,
|
user = User(username=github_username,
|
||||||
plain_text_password=None,
|
plain_text_password=None,
|
||||||
firstname=github_name,
|
firstname=github_first_name,
|
||||||
lastname='',
|
lastname=github_last_name,
|
||||||
email=github_email)
|
email=github_email)
|
||||||
|
|
||||||
result = user.create_local_user()
|
result = user.create_local_user()
|
||||||
@ -198,8 +221,8 @@ def login():
|
|||||||
|
|
||||||
if 'azure_token' in session:
|
if 'azure_token' in session:
|
||||||
azure_info = azure.get('me?$select=displayName,givenName,id,mail,surname,userPrincipalName').text
|
azure_info = azure.get('me?$select=displayName,givenName,id,mail,surname,userPrincipalName').text
|
||||||
current_app.logger.info('Azure login returned: '+azure_info)
|
current_app.logger.info('Azure login returned: ' + azure_info)
|
||||||
me = json.loads(azure_info)
|
user_data = json.loads(azure_info)
|
||||||
|
|
||||||
azure_info = azure.post('me/getMemberGroups',
|
azure_info = azure.post('me/getMemberGroups',
|
||||||
json={'securityEnabledOnly': False}).text
|
json={'securityEnabledOnly': False}).text
|
||||||
@ -211,15 +234,15 @@ def login():
|
|||||||
else:
|
else:
|
||||||
mygroups = []
|
mygroups = []
|
||||||
|
|
||||||
azure_username = me["userPrincipalName"]
|
azure_username = user_data["userPrincipalName"]
|
||||||
azure_givenname = me["givenName"]
|
azure_first_name = user_data["givenName"]
|
||||||
azure_familyname = me["surname"]
|
azure_last_name = user_data["surname"]
|
||||||
if "mail" in me:
|
if "mail" in user_data:
|
||||||
azure_email = me["mail"]
|
azure_email = user_data["mail"]
|
||||||
else:
|
else:
|
||||||
azure_email = ""
|
azure_email = ""
|
||||||
if not azure_email:
|
if not azure_email:
|
||||||
azure_email = me["userPrincipalName"]
|
azure_email = user_data["userPrincipalName"]
|
||||||
|
|
||||||
# Handle foreign principals such as guest users
|
# Handle foreign principals such as guest users
|
||||||
azure_email = re.sub(r"#.*$", "", azure_email)
|
azure_email = re.sub(r"#.*$", "", azure_email)
|
||||||
@ -229,8 +252,8 @@ def login():
|
|||||||
if not user:
|
if not user:
|
||||||
user = User(username=azure_username,
|
user = User(username=azure_username,
|
||||||
plain_text_password=None,
|
plain_text_password=None,
|
||||||
firstname=azure_givenname,
|
firstname=azure_first_name,
|
||||||
lastname=azure_familyname,
|
lastname=azure_last_name,
|
||||||
email=azure_email)
|
email=azure_email)
|
||||||
|
|
||||||
result = user.create_local_user()
|
result = user.create_local_user()
|
||||||
@ -250,30 +273,30 @@ def login():
|
|||||||
if Setting().get('azure_sg_enabled'):
|
if Setting().get('azure_sg_enabled'):
|
||||||
if Setting().get('azure_admin_group') in mygroups:
|
if Setting().get('azure_admin_group') in mygroups:
|
||||||
current_app.logger.info('Setting role for user ' +
|
current_app.logger.info('Setting role for user ' +
|
||||||
azure_username +
|
azure_username +
|
||||||
' to Administrator due to group membership')
|
' to Administrator due to group membership')
|
||||||
user.set_role("Administrator")
|
user.set_role("Administrator")
|
||||||
else:
|
else:
|
||||||
if Setting().get('azure_operator_group') in mygroups:
|
if Setting().get('azure_operator_group') in mygroups:
|
||||||
current_app.logger.info('Setting role for user ' +
|
current_app.logger.info('Setting role for user ' +
|
||||||
azure_username +
|
azure_username +
|
||||||
' to Operator due to group membership')
|
' to Operator due to group membership')
|
||||||
user.set_role("Operator")
|
user.set_role("Operator")
|
||||||
else:
|
else:
|
||||||
if Setting().get('azure_user_group') in mygroups:
|
if Setting().get('azure_user_group') in mygroups:
|
||||||
current_app.logger.info('Setting role for user ' +
|
current_app.logger.info('Setting role for user ' +
|
||||||
azure_username +
|
azure_username +
|
||||||
' to User due to group membership')
|
' to User due to group membership')
|
||||||
user.set_role("User")
|
user.set_role("User")
|
||||||
else:
|
else:
|
||||||
current_app.logger.warning('User ' +
|
current_app.logger.warning('User ' +
|
||||||
azure_username +
|
azure_username +
|
||||||
' has no relevant group memberships')
|
' has no relevant group memberships')
|
||||||
session.pop('azure_token', None)
|
session.pop('azure_token', None)
|
||||||
return render_template('login.html',
|
return render_template('login.html',
|
||||||
saml_enabled=SAML_ENABLED,
|
saml_enabled=SAML_ENABLED,
|
||||||
error=('User ' + azure_username +
|
error=('User ' + azure_username +
|
||||||
' is not in any authorised groups.'))
|
' is not in any authorised groups.'))
|
||||||
|
|
||||||
# Handle account/group creation, if enabled
|
# Handle account/group creation, if enabled
|
||||||
if Setting().get('azure_group_accounts_enabled') and mygroups:
|
if Setting().get('azure_group_accounts_enabled') and mygroups:
|
||||||
@ -369,23 +392,23 @@ def login():
|
|||||||
return authenticate_user(user, 'Azure OAuth')
|
return authenticate_user(user, 'Azure OAuth')
|
||||||
|
|
||||||
if 'oidc_token' in session:
|
if 'oidc_token' in session:
|
||||||
me = json.loads(oidc.get('userinfo').text)
|
user_data = json.loads(oidc.get('userinfo').text)
|
||||||
oidc_username = me[Setting().get('oidc_oauth_username')]
|
oidc_username = user_data[Setting().get('oidc_oauth_username')]
|
||||||
oidc_givenname = me[Setting().get('oidc_oauth_firstname')]
|
oidc_first_name = user_data[Setting().get('oidc_oauth_firstname')]
|
||||||
oidc_familyname = me[Setting().get('oidc_oauth_last_name')]
|
oidc_last_name = user_data[Setting().get('oidc_oauth_last_name')]
|
||||||
oidc_email = me[Setting().get('oidc_oauth_email')]
|
oidc_email = user_data[Setting().get('oidc_oauth_email')]
|
||||||
|
|
||||||
user = User.query.filter_by(username=oidc_username).first()
|
user = User.query.filter_by(username=oidc_username).first()
|
||||||
if not user:
|
if not user:
|
||||||
user = User(username=oidc_username,
|
user = User(username=oidc_username,
|
||||||
plain_text_password=None,
|
plain_text_password=None,
|
||||||
firstname=oidc_givenname,
|
firstname=oidc_first_name,
|
||||||
lastname=oidc_familyname,
|
lastname=oidc_last_name,
|
||||||
email=oidc_email)
|
email=oidc_email)
|
||||||
result = user.create_local_user()
|
result = user.create_local_user()
|
||||||
else:
|
else:
|
||||||
user.firstname = oidc_givenname
|
user.firstname = oidc_first_name
|
||||||
user.lastname = oidc_familyname
|
user.lastname = oidc_last_name
|
||||||
user.email = oidc_email
|
user.email = oidc_email
|
||||||
user.plain_text_password = None
|
user.plain_text_password = None
|
||||||
result = user.update_local_user()
|
result = user.update_local_user()
|
||||||
@ -394,20 +417,22 @@ def login():
|
|||||||
session.pop('oidc_token', None)
|
session.pop('oidc_token', None)
|
||||||
return redirect(url_for('index.login'))
|
return redirect(url_for('index.login'))
|
||||||
|
|
||||||
#This checks if the account_name_property and account_description property were included in settings.
|
# This checks if the account_name_property and account_description property were included in settings.
|
||||||
if Setting().get('oidc_oauth_account_name_property') and Setting().get('oidc_oauth_account_description_property'):
|
if Setting().get('oidc_oauth_account_name_property') and Setting().get(
|
||||||
|
'oidc_oauth_account_description_property'):
|
||||||
|
|
||||||
#Gets the name_property and description_property.
|
# Gets the name_property and description_property.
|
||||||
name_prop = Setting().get('oidc_oauth_account_name_property')
|
name_prop = Setting().get('oidc_oauth_account_name_property')
|
||||||
desc_prop = Setting().get('oidc_oauth_account_description_property')
|
desc_prop = Setting().get('oidc_oauth_account_description_property')
|
||||||
|
|
||||||
account_to_add = []
|
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
|
||||||
if name_prop in me and desc_prop in me:
|
# IdP).
|
||||||
accounts_name_prop = [me[name_prop]] if type(me[name_prop]) is not list else me[name_prop]
|
if name_prop in user_data and desc_prop in user_data:
|
||||||
accounts_desc_prop = [me[desc_prop]] if type(me[desc_prop]) is not list else me[desc_prop]
|
accounts_name_prop = [user_data[name_prop]] if type(user_data[name_prop]) is not list else user_data[name_prop]
|
||||||
|
accounts_desc_prop = [user_data[desc_prop]] if type(user_data[desc_prop]) is not list else user_data[desc_prop]
|
||||||
|
|
||||||
#Run on all groups the user is in by the index num.
|
# Run on all groups the user is in by the index num.
|
||||||
for i in range(len(accounts_name_prop)):
|
for i in range(len(accounts_name_prop)):
|
||||||
description = ''
|
description = ''
|
||||||
if i < len(accounts_desc_prop):
|
if i < len(accounts_desc_prop):
|
||||||
@ -417,7 +442,7 @@ def login():
|
|||||||
account_to_add.append(account)
|
account_to_add.append(account)
|
||||||
user_accounts = user.get_accounts()
|
user_accounts = user.get_accounts()
|
||||||
|
|
||||||
# Add accounts
|
# Add accounts
|
||||||
for account in account_to_add:
|
for account in account_to_add:
|
||||||
if account not in user_accounts:
|
if account not in user_accounts:
|
||||||
account.add_user(user)
|
account.add_user(user)
|
||||||
@ -426,7 +451,7 @@ def login():
|
|||||||
if Setting().get('delete_sso_accounts'):
|
if Setting().get('delete_sso_accounts'):
|
||||||
for account in user_accounts:
|
for account in user_accounts:
|
||||||
if account not in account_to_add:
|
if account not in account_to_add:
|
||||||
account.remove_user(user)
|
account.remove_user(user)
|
||||||
|
|
||||||
session['user_id'] = user.id
|
session['user_id'] = user.id
|
||||||
session['authentication_type'] = 'OAuth'
|
session['authentication_type'] = 'OAuth'
|
||||||
@ -490,34 +515,36 @@ def login():
|
|||||||
saml_enabled=SAML_ENABLED,
|
saml_enabled=SAML_ENABLED,
|
||||||
error='Token required')
|
error='Token required')
|
||||||
|
|
||||||
if Setting().get('autoprovisioning') and auth_method!='LOCAL':
|
if Setting().get('autoprovisioning') and auth_method != 'LOCAL':
|
||||||
urn_value=Setting().get('urn_value')
|
urn_value = Setting().get('urn_value')
|
||||||
Entitlements=user.read_entitlements(Setting().get('autoprovisioning_attribute'))
|
Entitlements = user.read_entitlements(Setting().get('autoprovisioning_attribute'))
|
||||||
if len(Entitlements)==0 and Setting().get('purge'):
|
if len(Entitlements) == 0 and Setting().get('purge'):
|
||||||
user.set_role("User")
|
user.set_role("User")
|
||||||
user.revoke_privilege(True)
|
user.revoke_privilege(True)
|
||||||
|
|
||||||
elif len(Entitlements)!=0:
|
elif len(Entitlements) != 0:
|
||||||
if checkForPDAEntries(Entitlements, urn_value):
|
if checkForPDAEntries(Entitlements, urn_value):
|
||||||
user.updateUser(Entitlements)
|
user.updateUser(Entitlements)
|
||||||
else:
|
else:
|
||||||
current_app.logger.warning('Not a single powerdns-admin record was found, possibly a typo in the prefix')
|
current_app.logger.warning(
|
||||||
|
'Not a single powerdns-admin record was found, possibly a typo in the prefix')
|
||||||
if Setting().get('purge'):
|
if Setting().get('purge'):
|
||||||
user.set_role("User")
|
user.set_role("User")
|
||||||
user.revoke_privilege(True)
|
user.revoke_privilege(True)
|
||||||
current_app.logger.warning('Procceding to revoke every privilige from ' + user.username + '.' )
|
current_app.logger.warning('Procceding to revoke every privilige from ' + user.username + '.')
|
||||||
|
|
||||||
return authenticate_user(user, auth_method, remember_me)
|
return authenticate_user(user, auth_method, remember_me)
|
||||||
|
|
||||||
|
|
||||||
def checkForPDAEntries(Entitlements, urn_value):
|
def checkForPDAEntries(Entitlements, urn_value):
|
||||||
"""
|
"""
|
||||||
Run through every record located in the ldap attribute given and determine if there are any valid powerdns-admin records
|
Run through every record located in the ldap attribute given and determine if there are any valid powerdns-admin records
|
||||||
"""
|
"""
|
||||||
urnArguments=[x.lower() for x in urn_value.split(':')]
|
urnArguments = [x.lower() for x in urn_value.split(':')]
|
||||||
for Entitlement in Entitlements:
|
for Entitlement in Entitlements:
|
||||||
entArguments=Entitlement.split(':powerdns-admin')
|
entArguments = Entitlement.split(':powerdns-admin')
|
||||||
entArguments=[x.lower() for x in entArguments[0].split(':')]
|
entArguments = [x.lower() for x in entArguments[0].split(':')]
|
||||||
if (entArguments==urnArguments):
|
if (entArguments == urnArguments):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -553,14 +580,15 @@ def signin_history(username, authenticator, success):
|
|||||||
|
|
||||||
# Write history
|
# Write history
|
||||||
History(msg='User {} authentication {}'.format(username, str_success),
|
History(msg='User {} authentication {}'.format(username, str_success),
|
||||||
detail = json.dumps({
|
detail=json.dumps({
|
||||||
'username': username,
|
'username': username,
|
||||||
'authenticator': authenticator,
|
'authenticator': authenticator,
|
||||||
'ip_address': request_ip,
|
'ip_address': request_ip,
|
||||||
'success': 1 if success else 0
|
'success': 1 if success else 0
|
||||||
}),
|
}),
|
||||||
created_by='System').add()
|
created_by='System').add()
|
||||||
|
|
||||||
|
|
||||||
# Get a list of Azure security groups the user is a member of
|
# Get a list of Azure security groups the user is a member of
|
||||||
def get_azure_groups(uri):
|
def get_azure_groups(uri):
|
||||||
azure_info = azure.get(uri).text
|
azure_info = azure.get(uri).text
|
||||||
@ -576,30 +604,33 @@ def get_azure_groups(uri):
|
|||||||
mygroups = []
|
mygroups = []
|
||||||
return mygroups
|
return mygroups
|
||||||
|
|
||||||
|
|
||||||
# Handle user login, write history and, if set, handle showing the register_otp QR code.
|
# Handle user login, write history and, if set, handle showing the register_otp QR code.
|
||||||
# if Setting for OTP on first login is enabled, and OTP field is also enabled,
|
# if Setting for OTP on first login is enabled, and OTP field is also enabled,
|
||||||
# but user isn't using it yet, enable OTP, get QR code and display it, logging the user out.
|
# but user isn't using it yet, enable OTP, get QR code and display it, logging the user out.
|
||||||
def authenticate_user(user, authenticator, remember=False):
|
def authenticate_user(user, authenticator, remember=False):
|
||||||
login_user(user, remember=remember)
|
login_user(user, remember=remember)
|
||||||
signin_history(user.username, authenticator, True)
|
signin_history(user.username, authenticator, True)
|
||||||
if Setting().get('otp_force') and Setting().get('otp_field_enabled') and not user.otp_secret and session['authentication_type'] not in ['OAuth']:
|
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.update_profile(enable_otp=True)
|
||||||
user_id = current_user.id
|
user_id = current_user.id
|
||||||
prepare_welcome_user(user_id)
|
prepare_welcome_user(user_id)
|
||||||
return redirect(url_for('index.welcome'))
|
return redirect(url_for('index.welcome'))
|
||||||
return redirect(url_for('index.login'))
|
return redirect(url_for('index.login'))
|
||||||
|
|
||||||
|
|
||||||
# Prepare user to enter /welcome screen, otherwise they won't have permission to do so
|
# Prepare user to enter /welcome screen, otherwise they won't have permission to do so
|
||||||
def prepare_welcome_user(user_id):
|
def prepare_welcome_user(user_id):
|
||||||
logout_user()
|
logout_user()
|
||||||
session['welcome_user_id'] = user_id
|
session['welcome_user_id'] = user_id
|
||||||
|
|
||||||
|
|
||||||
@index_bp.route('/logout')
|
@index_bp.route('/logout')
|
||||||
def logout():
|
def logout():
|
||||||
if current_app.config.get(
|
if current_app.config.get(
|
||||||
'SAML_ENABLED'
|
'SAML_ENABLED'
|
||||||
) and 'samlSessionIndex' in session and current_app.config.get(
|
) and 'samlSessionIndex' in session and current_app.config.get('SAML_LOGOUT'):
|
||||||
'SAML_LOGOUT'):
|
|
||||||
req = saml.prepare_flask_request(request)
|
req = saml.prepare_flask_request(request)
|
||||||
auth = saml.init_saml_auth(req)
|
auth = saml.init_saml_auth(req)
|
||||||
if current_app.config.get('SAML_LOGOUT_URL'):
|
if current_app.config.get('SAML_LOGOUT_URL'):
|
||||||
@ -651,13 +682,12 @@ def logout():
|
|||||||
|
|
||||||
|
|
||||||
def password_policy_check(user, password):
|
def password_policy_check(user, password):
|
||||||
|
|
||||||
def check_policy(chars, user_password, setting):
|
def check_policy(chars, user_password, setting):
|
||||||
lenreq = int(Setting().get(setting))
|
setting_as_int = int(Setting().get(setting))
|
||||||
test_string = user_password
|
test_string = user_password
|
||||||
for c in chars:
|
for c in chars:
|
||||||
test_string = test_string.replace(c, '')
|
test_string = test_string.replace(c, '')
|
||||||
return (lenreq, len(user_password) - len(test_string))
|
return (setting_as_int, len(user_password) - len(test_string))
|
||||||
|
|
||||||
def matches_policy(item, policy_fails):
|
def matches_policy(item, policy_fails):
|
||||||
return "*" if item in policy_fails else ""
|
return "*" if item in policy_fails else ""
|
||||||
@ -704,12 +734,14 @@ def password_policy_check(user, password):
|
|||||||
(pwd_min_lowercase_setting, pwd_lowercase) = check_policy(string.digits, password, 'pwd_min_lowercase')
|
(pwd_min_lowercase_setting, pwd_lowercase) = check_policy(string.digits, password, 'pwd_min_lowercase')
|
||||||
if pwd_lowercase < pwd_min_lowercase_setting:
|
if pwd_lowercase < pwd_min_lowercase_setting:
|
||||||
policy_fails["lowercase"] = True
|
policy_fails["lowercase"] = True
|
||||||
policy.append(f"{matches_policy('lowercase', policy_fails)}lowercase={pwd_lowercase}/{pwd_min_lowercase_setting}")
|
policy.append(
|
||||||
|
f"{matches_policy('lowercase', policy_fails)}lowercase={pwd_lowercase}/{pwd_min_lowercase_setting}")
|
||||||
# Uppercase
|
# Uppercase
|
||||||
(pwd_min_uppercase_setting, pwd_uppercase) = check_policy(string.digits, password, 'pwd_min_uppercase')
|
(pwd_min_uppercase_setting, pwd_uppercase) = check_policy(string.digits, password, 'pwd_min_uppercase')
|
||||||
if pwd_uppercase < pwd_min_uppercase_setting:
|
if pwd_uppercase < pwd_min_uppercase_setting:
|
||||||
policy_fails["uppercase"] = True
|
policy_fails["uppercase"] = True
|
||||||
policy.append(f"{matches_policy('uppercase', policy_fails)}uppercase={pwd_uppercase}/{pwd_min_uppercase_setting}")
|
policy.append(
|
||||||
|
f"{matches_policy('uppercase', policy_fails)}uppercase={pwd_uppercase}/{pwd_min_uppercase_setting}")
|
||||||
# Special
|
# Special
|
||||||
(pwd_min_special_setting, pwd_special) = check_policy(string.digits, password, 'pwd_min_special')
|
(pwd_min_special_setting, pwd_special) = check_policy(string.digits, password, 'pwd_min_special')
|
||||||
if pwd_special < pwd_min_special_setting:
|
if pwd_special < pwd_min_special_setting:
|
||||||
@ -728,7 +760,8 @@ def password_policy_check(user, password):
|
|||||||
pwd_complexity = result['guesses_log10']
|
pwd_complexity = result['guesses_log10']
|
||||||
if pwd_complexity < pwd_min_complexity_setting:
|
if pwd_complexity < pwd_min_complexity_setting:
|
||||||
policy_fails["complexity"] = True
|
policy_fails["complexity"] = True
|
||||||
policy.append(f"{matches_policy('complexity', policy_fails)}complexity={pwd_complexity:.0f}/{pwd_min_complexity_setting}")
|
policy.append(
|
||||||
|
f"{matches_policy('complexity', policy_fails)}complexity={pwd_complexity:.0f}/{pwd_min_complexity_setting}")
|
||||||
|
|
||||||
policy_str = {"password": f"Fails policy: {', '.join(policy)}. Items prefixed with '*' failed."}
|
policy_str = {"password": f"Fails policy: {', '.join(policy)}. Items prefixed with '*' failed."}
|
||||||
|
|
||||||
@ -738,77 +771,78 @@ def password_policy_check(user, password):
|
|||||||
|
|
||||||
@index_bp.route('/register', methods=['GET', 'POST'])
|
@index_bp.route('/register', methods=['GET', 'POST'])
|
||||||
def register():
|
def register():
|
||||||
CAPTCHA_ENABLE = current_app.config.get('CAPTCHA_ENABLE')
|
CAPTCHA_ENABLE = current_app.config.get('CAPTCHA_ENABLE')
|
||||||
if Setting().get('signup_enabled'):
|
if Setting().get('signup_enabled'):
|
||||||
if current_user.is_authenticated:
|
if current_user.is_authenticated:
|
||||||
return redirect(url_for('index.index'))
|
return redirect(url_for('index.index'))
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
return render_template('register.html', captcha_enable=CAPTCHA_ENABLE)
|
return render_template('register.html', captcha_enable=CAPTCHA_ENABLE)
|
||||||
elif request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
username = request.form.get('username', '').strip()
|
username = request.form.get('username', '').strip()
|
||||||
password = request.form.get('password', '')
|
password = request.form.get('password', '')
|
||||||
firstname = request.form.get('firstname', '').strip()
|
firstname = request.form.get('firstname', '').strip()
|
||||||
lastname = request.form.get('lastname', '').strip()
|
lastname = request.form.get('lastname', '').strip()
|
||||||
email = request.form.get('email', '').strip()
|
email = request.form.get('email', '').strip()
|
||||||
rpassword = request.form.get('rpassword', '')
|
rpassword = request.form.get('rpassword', '')
|
||||||
|
|
||||||
is_valid_email = re.compile(r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$')
|
is_valid_email = re.compile(r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$')
|
||||||
|
|
||||||
error_messages = {}
|
error_messages = {}
|
||||||
if not firstname:
|
if not firstname:
|
||||||
error_messages['firstname'] = 'First Name is required'
|
error_messages['firstname'] = 'First Name is required'
|
||||||
if not lastname:
|
if not lastname:
|
||||||
error_messages['lastname'] = 'Last Name is required'
|
error_messages['lastname'] = 'Last Name is required'
|
||||||
if not username:
|
if not username:
|
||||||
error_messages['username'] = 'Username is required'
|
error_messages['username'] = 'Username is required'
|
||||||
if not password:
|
if not password:
|
||||||
error_messages['password'] = 'Password is required'
|
error_messages['password'] = 'Password is required'
|
||||||
if not rpassword:
|
if not rpassword:
|
||||||
error_messages['rpassword'] = 'Password confirmation is required'
|
error_messages['rpassword'] = 'Password confirmation is required'
|
||||||
if not email:
|
if not email:
|
||||||
error_messages['email'] = 'Email is required'
|
error_messages['email'] = 'Email is required'
|
||||||
if not is_valid_email.match(email):
|
if not is_valid_email.match(email):
|
||||||
error_messages['email'] = 'Invalid email address'
|
error_messages['email'] = 'Invalid email address'
|
||||||
if password != rpassword:
|
if password != rpassword:
|
||||||
error_messages['password'] = 'Password confirmation does not match'
|
error_messages['password'] = 'Password confirmation does not match'
|
||||||
error_messages['rpassword'] = 'Password confirmation does not match'
|
error_messages['rpassword'] = 'Password confirmation does not match'
|
||||||
|
|
||||||
if not captcha.validate():
|
if not captcha.validate():
|
||||||
return render_template(
|
return render_template(
|
||||||
'register.html', error='Invalid CAPTCHA answer', error_messages=error_messages, captcha_enable=CAPTCHA_ENABLE)
|
'register.html', error='Invalid CAPTCHA answer', error_messages=error_messages,
|
||||||
|
captcha_enable=CAPTCHA_ENABLE)
|
||||||
|
|
||||||
if error_messages:
|
if error_messages:
|
||||||
return render_template('register.html', error_messages=error_messages, captcha_enable=CAPTCHA_ENABLE)
|
return render_template('register.html', error_messages=error_messages, captcha_enable=CAPTCHA_ENABLE)
|
||||||
|
|
||||||
user = User(username=username,
|
user = User(username=username,
|
||||||
plain_text_password=password,
|
plain_text_password=password,
|
||||||
firstname=firstname,
|
firstname=firstname,
|
||||||
lastname=lastname,
|
lastname=lastname,
|
||||||
email=email
|
email=email
|
||||||
)
|
)
|
||||||
|
|
||||||
(password_policy_pass, password_policy) = password_policy_check(user, password)
|
(password_policy_pass, password_policy) = password_policy_check(user, password)
|
||||||
if not password_policy_pass:
|
if not password_policy_pass:
|
||||||
return render_template('register.html', error_messages=password_policy, captcha_enable=CAPTCHA_ENABLE)
|
return render_template('register.html', error_messages=password_policy, captcha_enable=CAPTCHA_ENABLE)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = user.create_local_user()
|
result = user.create_local_user()
|
||||||
if result and result['status']:
|
if result and result['status']:
|
||||||
if Setting().get('verify_user_email'):
|
if Setting().get('verify_user_email'):
|
||||||
send_account_verification(email)
|
send_account_verification(email)
|
||||||
if Setting().get('otp_force') and Setting().get('otp_field_enabled'):
|
if Setting().get('otp_force') and Setting().get('otp_field_enabled'):
|
||||||
user.update_profile(enable_otp=True)
|
user.update_profile(enable_otp=True)
|
||||||
prepare_welcome_user(user.id)
|
prepare_welcome_user(user.id)
|
||||||
return redirect(url_for('index.welcome'))
|
return redirect(url_for('index.welcome'))
|
||||||
else:
|
else:
|
||||||
return redirect(url_for('index.login'))
|
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:
|
else:
|
||||||
return render_template('register.html',
|
return render_template('errors/404.html'), 404
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
# Show welcome page on first login if otp_force is enabled
|
# Show welcome page on first login if otp_force is enabled
|
||||||
@ -827,12 +861,15 @@ def welcome():
|
|||||||
if otp_token and otp_token.isdigit():
|
if otp_token and otp_token.isdigit():
|
||||||
good_token = user.verify_totp(otp_token)
|
good_token = user.verify_totp(otp_token)
|
||||||
if not good_token:
|
if not good_token:
|
||||||
return render_template('register_otp.html', qrcode_image=encoded_img_data.decode(), user=user, error="Invalid token")
|
return render_template('register_otp.html', qrcode_image=encoded_img_data.decode(), user=user,
|
||||||
|
error="Invalid token")
|
||||||
else:
|
else:
|
||||||
return render_template('register_otp.html', qrcode_image=encoded_img_data.decode(), user=user, error="Token required")
|
return render_template('register_otp.html', qrcode_image=encoded_img_data.decode(), user=user,
|
||||||
|
error="Token required")
|
||||||
session.pop('welcome_user_id')
|
session.pop('welcome_user_id')
|
||||||
return redirect(url_for('index.index'))
|
return redirect(url_for('index.index'))
|
||||||
|
|
||||||
|
|
||||||
@index_bp.route('/confirm/<token>', methods=['GET'])
|
@index_bp.route('/confirm/<token>', methods=['GET'])
|
||||||
def confirm_email(token):
|
def confirm_email(token):
|
||||||
email = confirm_token(token)
|
email = confirm_token(token)
|
||||||
@ -919,10 +956,10 @@ def dyndns_update():
|
|||||||
.outerjoin(Account, Domain.account_id == Account.id) \
|
.outerjoin(Account, Domain.account_id == Account.id) \
|
||||||
.outerjoin(AccountUser, Account.id == AccountUser.account_id) \
|
.outerjoin(AccountUser, Account.id == AccountUser.account_id) \
|
||||||
.filter(
|
.filter(
|
||||||
db.or_(
|
db.or_(
|
||||||
DomainUser.user_id == current_user.id,
|
DomainUser.user_id == current_user.id,
|
||||||
AccountUser.user_id == current_user.id
|
AccountUser.user_id == current_user.id
|
||||||
)).all()
|
)).all()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
current_app.logger.error('DynDNS Error: {0}'.format(e))
|
current_app.logger.error('DynDNS Error: {0}'.format(e))
|
||||||
current_app.logger.debug(traceback.format_exc())
|
current_app.logger.debug(traceback.format_exc())
|
||||||
@ -982,13 +1019,13 @@ def dyndns_update():
|
|||||||
if result['status'] == 'ok':
|
if result['status'] == 'ok':
|
||||||
history = History(
|
history = History(
|
||||||
msg='DynDNS update: updated {} successfully'.format(hostname),
|
msg='DynDNS update: updated {} successfully'.format(hostname),
|
||||||
detail = json.dumps({
|
detail=json.dumps({
|
||||||
'domain': domain.name,
|
'domain': domain.name,
|
||||||
'record': hostname,
|
'record': hostname,
|
||||||
'type': rtype,
|
'type': rtype,
|
||||||
'old_value': oldip,
|
'old_value': oldip,
|
||||||
'new_value': str(ip)
|
'new_value': str(ip)
|
||||||
}),
|
}),
|
||||||
created_by=current_user.username,
|
created_by=current_user.username,
|
||||||
domain_id=domain.id)
|
domain_id=domain.id)
|
||||||
history.add()
|
history.add()
|
||||||
@ -999,7 +1036,7 @@ def dyndns_update():
|
|||||||
elif r.is_allowed_edit():
|
elif r.is_allowed_edit():
|
||||||
ondemand_creation = DomainSetting.query.filter(
|
ondemand_creation = DomainSetting.query.filter(
|
||||||
DomainSetting.domain == domain).filter(
|
DomainSetting.domain == domain).filter(
|
||||||
DomainSetting.setting == 'create_via_dyndns').first()
|
DomainSetting.setting == 'create_via_dyndns').first()
|
||||||
if (ondemand_creation is not None) and (strtobool(
|
if (ondemand_creation is not None) and (strtobool(
|
||||||
ondemand_creation.value) == True):
|
ondemand_creation.value) == True):
|
||||||
|
|
||||||
@ -1024,11 +1061,11 @@ def dyndns_update():
|
|||||||
msg=
|
msg=
|
||||||
'DynDNS update: created record {0} in zone {1} successfully'
|
'DynDNS update: created record {0} in zone {1} successfully'
|
||||||
.format(hostname, domain.name, str(ip)),
|
.format(hostname, domain.name, str(ip)),
|
||||||
detail = json.dumps({
|
detail=json.dumps({
|
||||||
'domain': domain.name,
|
'domain': domain.name,
|
||||||
'record': hostname,
|
'record': hostname,
|
||||||
'value': str(ip)
|
'value': str(ip)
|
||||||
}),
|
}),
|
||||||
created_by=current_user.username,
|
created_by=current_user.username,
|
||||||
domain_id=domain.id)
|
domain_id=domain.id)
|
||||||
history.add()
|
history.add()
|
||||||
@ -1088,7 +1125,7 @@ def saml_authorized():
|
|||||||
req = saml.prepare_flask_request(request)
|
req = saml.prepare_flask_request(request)
|
||||||
auth = saml.init_saml_auth(req)
|
auth = saml.init_saml_auth(req)
|
||||||
auth.process_response()
|
auth.process_response()
|
||||||
current_app.logger.debug( auth.get_attributes() )
|
current_app.logger.debug(auth.get_attributes())
|
||||||
errors = auth.get_errors()
|
errors = auth.get_errors()
|
||||||
if len(errors) == 0:
|
if len(errors) == 0:
|
||||||
session['samlUserdata'] = auth.get_attributes()
|
session['samlUserdata'] = auth.get_attributes()
|
||||||
@ -1097,7 +1134,7 @@ def saml_authorized():
|
|||||||
self_url = OneLogin_Saml2_Utils.get_self_url(req)
|
self_url = OneLogin_Saml2_Utils.get_self_url(req)
|
||||||
self_url = self_url + req['script_name']
|
self_url = self_url + req['script_name']
|
||||||
if 'RelayState' in request.form and self_url != request.form[
|
if 'RelayState' in request.form and self_url != request.form[
|
||||||
'RelayState']:
|
'RelayState']:
|
||||||
return redirect(auth.redirect_to(request.form['RelayState']))
|
return redirect(auth.redirect_to(request.form['RelayState']))
|
||||||
if current_app.config.get('SAML_ATTRIBUTE_USERNAME', False):
|
if current_app.config.get('SAML_ATTRIBUTE_USERNAME', False):
|
||||||
username = session['samlUserdata'][
|
username = session['samlUserdata'][
|
||||||
@ -1129,7 +1166,7 @@ def saml_authorized():
|
|||||||
admin_group_name = current_app.config.get('SAML_GROUP_ADMIN_NAME',
|
admin_group_name = current_app.config.get('SAML_GROUP_ADMIN_NAME',
|
||||||
None)
|
None)
|
||||||
operator_group_name = current_app.config.get('SAML_GROUP_OPERATOR_NAME',
|
operator_group_name = current_app.config.get('SAML_GROUP_OPERATOR_NAME',
|
||||||
None)
|
None)
|
||||||
group_to_account_mapping = create_group_to_account_mapping()
|
group_to_account_mapping = create_group_to_account_mapping()
|
||||||
|
|
||||||
if email_attribute_name in session['samlUserdata']:
|
if email_attribute_name in session['samlUserdata']:
|
||||||
@ -1170,13 +1207,13 @@ def saml_authorized():
|
|||||||
account.add_user(user)
|
account.add_user(user)
|
||||||
history = History(msg='Adding {0} to account {1}'.format(
|
history = History(msg='Adding {0} to account {1}'.format(
|
||||||
user.username, account.name),
|
user.username, account.name),
|
||||||
created_by='SAML Assertion')
|
created_by='SAML Assertion')
|
||||||
history.add()
|
history.add()
|
||||||
for account in user_accounts - saml_accounts:
|
for account in user_accounts - saml_accounts:
|
||||||
account.remove_user(user)
|
account.remove_user(user)
|
||||||
history = History(msg='Removing {0} from account {1}'.format(
|
history = History(msg='Removing {0} from account {1}'.format(
|
||||||
user.username, account.name),
|
user.username, account.name),
|
||||||
created_by='SAML Assertion')
|
created_by='SAML Assertion')
|
||||||
history.add()
|
history.add()
|
||||||
if admin_attribute_name and 'true' in session['samlUserdata'].get(
|
if admin_attribute_name and 'true' in session['samlUserdata'].get(
|
||||||
admin_attribute_name, []):
|
admin_attribute_name, []):
|
||||||
@ -1190,7 +1227,7 @@ def saml_authorized():
|
|||||||
user.role_id = Role.query.filter_by(name='User').first().id
|
user.role_id = Role.query.filter_by(name='User').first().id
|
||||||
history = History(msg='Demoting {0} to user'.format(
|
history = History(msg='Demoting {0} to user'.format(
|
||||||
user.username),
|
user.username),
|
||||||
created_by='SAML Assertion')
|
created_by='SAML Assertion')
|
||||||
history.add()
|
history.add()
|
||||||
user.plain_text_password = None
|
user.plain_text_password = None
|
||||||
user.update_profile()
|
user.update_profile()
|
||||||
@ -1234,15 +1271,16 @@ def uplift_to_admin(user):
|
|||||||
user.role_id = Role.query.filter_by(name='Administrator').first().id
|
user.role_id = Role.query.filter_by(name='Administrator').first().id
|
||||||
history = History(msg='Promoting {0} to administrator'.format(
|
history = History(msg='Promoting {0} to administrator'.format(
|
||||||
user.username),
|
user.username),
|
||||||
created_by='SAML Assertion')
|
created_by='SAML Assertion')
|
||||||
history.add()
|
history.add()
|
||||||
|
|
||||||
|
|
||||||
def uplift_to_operator(user):
|
def uplift_to_operator(user):
|
||||||
if user.role.name != 'Operator':
|
if user.role.name != 'Operator':
|
||||||
user.role_id = Role.query.filter_by(name='Operator').first().id
|
user.role_id = Role.query.filter_by(name='Operator').first().id
|
||||||
history = History(msg='Promoting {0} to operator'.format(
|
history = History(msg='Promoting {0} to operator'.format(
|
||||||
user.username),
|
user.username),
|
||||||
created_by='SAML Assertion')
|
created_by='SAML Assertion')
|
||||||
history.add()
|
history.add()
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,8 +20,6 @@ def azure_oauth():
|
|||||||
'client_secret': Setting().get('azure_oauth_secret'),
|
'client_secret': Setting().get('azure_oauth_secret'),
|
||||||
'api_base_url': Setting().get('azure_oauth_api_url'),
|
'api_base_url': Setting().get('azure_oauth_api_url'),
|
||||||
'request_token_url': None,
|
'request_token_url': None,
|
||||||
'access_token_url': Setting().get('azure_oauth_token_url'),
|
|
||||||
'authorize_url': Setting().get('azure_oauth_authorize_url'),
|
|
||||||
'client_kwargs': {'scope': Setting().get('azure_oauth_scope')},
|
'client_kwargs': {'scope': Setting().get('azure_oauth_scope')},
|
||||||
'fetch_token': fetch_azure_token,
|
'fetch_token': fetch_azure_token,
|
||||||
}
|
}
|
||||||
@ -30,6 +28,9 @@ def azure_oauth():
|
|||||||
|
|
||||||
if isinstance(server_metadata_url, str) and len(server_metadata_url.strip()) > 0:
|
if isinstance(server_metadata_url, str) and len(server_metadata_url.strip()) > 0:
|
||||||
authlib_params['server_metadata_url'] = server_metadata_url
|
authlib_params['server_metadata_url'] = server_metadata_url
|
||||||
|
else:
|
||||||
|
authlib_params['access_token_url'] = Setting().get('azure_oauth_token_url')
|
||||||
|
authlib_params['authorize_url'] = Setting().get('azure_oauth_authorize_url')
|
||||||
|
|
||||||
azure = authlib_oauth_client.register(
|
azure = authlib_oauth_client.register(
|
||||||
'azure',
|
'azure',
|
||||||
@ -38,14 +39,16 @@ def azure_oauth():
|
|||||||
|
|
||||||
@current_app.route('/azure/authorized')
|
@current_app.route('/azure/authorized')
|
||||||
def azure_authorized():
|
def azure_authorized():
|
||||||
session['azure_oauthredir'] = url_for('.azure_authorized',
|
use_ssl = current_app.config.get('SERVER_EXTERNAL_SSL')
|
||||||
_external=True,
|
params = {'_external': True}
|
||||||
_scheme='https')
|
if isinstance(use_ssl, bool):
|
||||||
|
params['_scheme'] = 'https' if use_ssl else 'http'
|
||||||
|
session['azure_oauthredir'] = url_for('.azure_authorized', **params)
|
||||||
token = azure.authorize_access_token()
|
token = azure.authorize_access_token()
|
||||||
if token is None:
|
if token is None:
|
||||||
return 'Access denied: reason=%s error=%s' % (
|
return 'Access denied: reason=%s error=%s' % (
|
||||||
request.args['error'], request.args['error_description'])
|
request.args['error'], request.args['error_description'])
|
||||||
session['azure_token'] = (token)
|
session['azure_token'] = (token)
|
||||||
return redirect(url_for('index.login', _external=True, _scheme='https'))
|
return redirect(url_for('index.login', **params))
|
||||||
|
|
||||||
return azure
|
return azure
|
||||||
|
@ -21,8 +21,6 @@ def github_oauth():
|
|||||||
'request_token_params': {'scope': Setting().get('github_oauth_scope')},
|
'request_token_params': {'scope': Setting().get('github_oauth_scope')},
|
||||||
'api_base_url': Setting().get('github_oauth_api_url'),
|
'api_base_url': Setting().get('github_oauth_api_url'),
|
||||||
'request_token_url': None,
|
'request_token_url': None,
|
||||||
'access_token_url': Setting().get('github_oauth_token_url'),
|
|
||||||
'authorize_url': Setting().get('github_oauth_authorize_url'),
|
|
||||||
'client_kwargs': {'scope': Setting().get('github_oauth_scope')},
|
'client_kwargs': {'scope': Setting().get('github_oauth_scope')},
|
||||||
'fetch_token': fetch_github_token,
|
'fetch_token': fetch_github_token,
|
||||||
'update_token': update_token
|
'update_token': update_token
|
||||||
@ -32,6 +30,9 @@ def github_oauth():
|
|||||||
|
|
||||||
if isinstance(server_metadata_url, str) and len(server_metadata_url.strip()) > 0:
|
if isinstance(server_metadata_url, str) and len(server_metadata_url.strip()) > 0:
|
||||||
authlib_params['server_metadata_url'] = server_metadata_url
|
authlib_params['server_metadata_url'] = server_metadata_url
|
||||||
|
else:
|
||||||
|
authlib_params['access_token_url'] = Setting().get('github_oauth_token_url')
|
||||||
|
authlib_params['authorize_url'] = Setting().get('github_oauth_authorize_url')
|
||||||
|
|
||||||
github = authlib_oauth_client.register(
|
github = authlib_oauth_client.register(
|
||||||
'github',
|
'github',
|
||||||
@ -40,13 +41,16 @@ def github_oauth():
|
|||||||
|
|
||||||
@current_app.route('/github/authorized')
|
@current_app.route('/github/authorized')
|
||||||
def github_authorized():
|
def github_authorized():
|
||||||
session['github_oauthredir'] = url_for('.github_authorized',
|
use_ssl = current_app.config.get('SERVER_EXTERNAL_SSL')
|
||||||
_external=True)
|
params = {'_external': True}
|
||||||
|
if isinstance(use_ssl, bool):
|
||||||
|
params['_scheme'] = 'https' if use_ssl else 'http'
|
||||||
|
session['github_oauthredir'] = url_for('.github_authorized', **params)
|
||||||
token = github.authorize_access_token()
|
token = github.authorize_access_token()
|
||||||
if token is None:
|
if token is None:
|
||||||
return 'Access denied: reason=%s error=%s' % (
|
return 'Access denied: reason=%s error=%s' % (
|
||||||
request.args['error'], request.args['error_description'])
|
request.args['error'], request.args['error_description'])
|
||||||
session['github_token'] = (token)
|
session['github_token'] = token
|
||||||
return redirect(url_for('index.login'))
|
return redirect(url_for('index.login', **params))
|
||||||
|
|
||||||
return github
|
return github
|
||||||
|
@ -20,8 +20,6 @@ def google_oauth():
|
|||||||
'client_secret': Setting().get('google_oauth_client_secret'),
|
'client_secret': Setting().get('google_oauth_client_secret'),
|
||||||
'api_base_url': Setting().get('google_base_url'),
|
'api_base_url': Setting().get('google_base_url'),
|
||||||
'request_token_url': None,
|
'request_token_url': None,
|
||||||
'access_token_url': Setting().get('google_token_url'),
|
|
||||||
'authorize_url': Setting().get('google_authorize_url'),
|
|
||||||
'client_kwargs': {'scope': Setting().get('google_oauth_scope')},
|
'client_kwargs': {'scope': Setting().get('google_oauth_scope')},
|
||||||
'fetch_token': fetch_google_token,
|
'fetch_token': fetch_google_token,
|
||||||
'update_token': update_token
|
'update_token': update_token
|
||||||
@ -31,6 +29,9 @@ def google_oauth():
|
|||||||
|
|
||||||
if isinstance(server_metadata_url, str) and len(server_metadata_url.strip()) > 0:
|
if isinstance(server_metadata_url, str) and len(server_metadata_url.strip()) > 0:
|
||||||
authlib_params['server_metadata_url'] = server_metadata_url
|
authlib_params['server_metadata_url'] = server_metadata_url
|
||||||
|
else:
|
||||||
|
authlib_params['access_token_url'] = Setting().get('google_token_url')
|
||||||
|
authlib_params['authorize_url'] = Setting().get('google_authorize_url')
|
||||||
|
|
||||||
google = authlib_oauth_client.register(
|
google = authlib_oauth_client.register(
|
||||||
'google',
|
'google',
|
||||||
@ -39,16 +40,18 @@ def google_oauth():
|
|||||||
|
|
||||||
@current_app.route('/google/authorized')
|
@current_app.route('/google/authorized')
|
||||||
def google_authorized():
|
def google_authorized():
|
||||||
session['google_oauthredir'] = url_for(
|
use_ssl = current_app.config.get('SERVER_EXTERNAL_SSL')
|
||||||
'.google_authorized', _external=True)
|
params = {'_external': True}
|
||||||
|
if isinstance(use_ssl, bool):
|
||||||
|
params['_scheme'] = 'https' if use_ssl else 'http'
|
||||||
|
session['google_oauthredir'] = url_for('.google_authorized', **params)
|
||||||
token = google.authorize_access_token()
|
token = google.authorize_access_token()
|
||||||
if token is None:
|
if token is None:
|
||||||
return 'Access denied: reason=%s error=%s' % (
|
return 'Access denied: reason=%s error=%s' % (
|
||||||
request.args['error_reason'],
|
request.args['error_reason'],
|
||||||
request.args['error_description']
|
request.args['error_description']
|
||||||
)
|
)
|
||||||
session['google_token'] = (token)
|
session['google_token'] = token
|
||||||
return redirect(url_for('index.login'))
|
return redirect(url_for('index.login', **params))
|
||||||
|
|
||||||
return google
|
return google
|
||||||
|
|
||||||
|
@ -20,8 +20,6 @@ def oidc_oauth():
|
|||||||
'client_secret': Setting().get('oidc_oauth_secret'),
|
'client_secret': Setting().get('oidc_oauth_secret'),
|
||||||
'api_base_url': Setting().get('oidc_oauth_api_url'),
|
'api_base_url': Setting().get('oidc_oauth_api_url'),
|
||||||
'request_token_url': None,
|
'request_token_url': None,
|
||||||
'access_token_url': Setting().get('oidc_oauth_token_url'),
|
|
||||||
'authorize_url': Setting().get('oidc_oauth_authorize_url'),
|
|
||||||
'client_kwargs': {'scope': Setting().get('oidc_oauth_scope')},
|
'client_kwargs': {'scope': Setting().get('oidc_oauth_scope')},
|
||||||
'fetch_token': fetch_oidc_token,
|
'fetch_token': fetch_oidc_token,
|
||||||
'update_token': update_token
|
'update_token': update_token
|
||||||
@ -31,6 +29,9 @@ def oidc_oauth():
|
|||||||
|
|
||||||
if isinstance(server_metadata_url, str) and len(server_metadata_url.strip()) > 0:
|
if isinstance(server_metadata_url, str) and len(server_metadata_url.strip()) > 0:
|
||||||
authlib_params['server_metadata_url'] = server_metadata_url
|
authlib_params['server_metadata_url'] = server_metadata_url
|
||||||
|
else:
|
||||||
|
authlib_params['access_token_url'] = Setting().get('oidc_oauth_token_url')
|
||||||
|
authlib_params['authorize_url'] = Setting().get('oidc_oauth_authorize_url')
|
||||||
|
|
||||||
oidc = authlib_oauth_client.register(
|
oidc = authlib_oauth_client.register(
|
||||||
'oidc',
|
'oidc',
|
||||||
@ -39,13 +40,16 @@ def oidc_oauth():
|
|||||||
|
|
||||||
@current_app.route('/oidc/authorized')
|
@current_app.route('/oidc/authorized')
|
||||||
def oidc_authorized():
|
def oidc_authorized():
|
||||||
session['oidc_oauthredir'] = url_for('.oidc_authorized',
|
use_ssl = current_app.config.get('SERVER_EXTERNAL_SSL')
|
||||||
_external=True)
|
params = {'_external': True}
|
||||||
|
if isinstance(use_ssl, bool):
|
||||||
|
params['_scheme'] = 'https' if use_ssl else 'http'
|
||||||
|
session['oidc_oauthredir'] = url_for('.oidc_authorized', **params)
|
||||||
token = oidc.authorize_access_token()
|
token = oidc.authorize_access_token()
|
||||||
if token is None:
|
if token is None:
|
||||||
return 'Access denied: reason=%s error=%s' % (
|
return 'Access denied: reason=%s error=%s' % (
|
||||||
request.args['error'], request.args['error_description'])
|
request.args['error'], request.args['error_description'])
|
||||||
session['oidc_token'] = (token)
|
session['oidc_token'] = token
|
||||||
return redirect(url_for('index.login'))
|
return redirect(url_for('index.login', **params))
|
||||||
|
|
||||||
return oidc
|
return oidc
|
@ -0,0 +1,801 @@
|
|||||||
|
let AuthenticationSettingsModel = function (user_data, api_url, csrf_token, selector) {
|
||||||
|
let self = this;
|
||||||
|
let target = null;
|
||||||
|
self.api_url = api_url;
|
||||||
|
self.csrf_token = csrf_token;
|
||||||
|
self.selector = selector;
|
||||||
|
self.loading = false;
|
||||||
|
self.saving = false;
|
||||||
|
self.saved = false;
|
||||||
|
self.save_failed = false;
|
||||||
|
self.messages = [];
|
||||||
|
self.messages_class = 'info';
|
||||||
|
self.tab_active = '';
|
||||||
|
self.tab_default = 'local';
|
||||||
|
|
||||||
|
let defaults = {
|
||||||
|
// Local Authentication Settings
|
||||||
|
local_db_enabled: 1,
|
||||||
|
signup_enabled: 1,
|
||||||
|
pwd_enforce_characters: 0,
|
||||||
|
pwd_min_len: 10,
|
||||||
|
pwd_min_lowercase: 3,
|
||||||
|
pwd_min_uppercase: 2,
|
||||||
|
pwd_min_digits: 2,
|
||||||
|
pwd_min_special: 1,
|
||||||
|
pwd_enforce_complexity: 0,
|
||||||
|
pwd_min_complexity: 11,
|
||||||
|
|
||||||
|
// LDAP Authentication Settings
|
||||||
|
ldap_enabled: 0,
|
||||||
|
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: 0,
|
||||||
|
ldap_admin_group: '',
|
||||||
|
ldap_operator_group: '',
|
||||||
|
ldap_user_group: '',
|
||||||
|
autoprovisioning: 0,
|
||||||
|
autoprovisioning_attribute: '',
|
||||||
|
urn_value: '',
|
||||||
|
purge: 0,
|
||||||
|
|
||||||
|
// Google OAuth2 Settings
|
||||||
|
google_oauth_enabled: 0,
|
||||||
|
google_oauth_client_id: '',
|
||||||
|
google_oauth_client_secret: '',
|
||||||
|
google_oauth_scope: '',
|
||||||
|
google_base_url: '',
|
||||||
|
google_oauth_auto_configure: 1,
|
||||||
|
google_oauth_metadata_url: '',
|
||||||
|
google_token_url: '',
|
||||||
|
google_authorize_url: '',
|
||||||
|
|
||||||
|
// GitHub OAuth2 Settings
|
||||||
|
github_oauth_enabled: 0,
|
||||||
|
github_oauth_key: '',
|
||||||
|
github_oauth_secret: '',
|
||||||
|
github_oauth_scope: '',
|
||||||
|
github_oauth_api_url: '',
|
||||||
|
github_oauth_auto_configure: 0,
|
||||||
|
github_oauth_metadata_url: '',
|
||||||
|
github_oauth_token_url: '',
|
||||||
|
github_oauth_authorize_url: '',
|
||||||
|
|
||||||
|
// Azure AD OAuth2 Settings
|
||||||
|
azure_oauth_enabled: 0,
|
||||||
|
azure_oauth_key: '',
|
||||||
|
azure_oauth_secret: '',
|
||||||
|
azure_oauth_scope: '',
|
||||||
|
azure_oauth_api_url: '',
|
||||||
|
azure_oauth_auto_configure: 1,
|
||||||
|
azure_oauth_metadata_url: '',
|
||||||
|
azure_oauth_token_url: '',
|
||||||
|
azure_oauth_authorize_url: '',
|
||||||
|
azure_sg_enabled: 0,
|
||||||
|
azure_admin_group: '',
|
||||||
|
azure_operator_group: '',
|
||||||
|
azure_user_group: '',
|
||||||
|
azure_group_accounts_enabled: 0,
|
||||||
|
azure_group_accounts_name: '',
|
||||||
|
azure_group_accounts_name_re: '',
|
||||||
|
azure_group_accounts_description: '',
|
||||||
|
azure_group_accounts_description_re: '',
|
||||||
|
|
||||||
|
// OIDC OAuth2 Settings
|
||||||
|
oidc_oauth_enabled: 0,
|
||||||
|
oidc_oauth_key: '',
|
||||||
|
oidc_oauth_secret: '',
|
||||||
|
oidc_oauth_scope: '',
|
||||||
|
oidc_oauth_api_url: '',
|
||||||
|
oidc_oauth_auto_configure: 1,
|
||||||
|
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: '',
|
||||||
|
}
|
||||||
|
|
||||||
|
self.init = function (autoload) {
|
||||||
|
self.loading = ko.observable(self.loading);
|
||||||
|
self.saving = ko.observable(self.saving);
|
||||||
|
self.saved = ko.observable(self.saved);
|
||||||
|
self.save_failed = ko.observable(self.save_failed);
|
||||||
|
self.messages = ko.observableArray(self.messages);
|
||||||
|
self.messages_class = ko.observable(self.messages_class);
|
||||||
|
self.tab_active = ko.observable(self.tab_active);
|
||||||
|
self.tab_default = ko.observable(self.tab_default);
|
||||||
|
self.update(user_data);
|
||||||
|
|
||||||
|
let el = null;
|
||||||
|
if (typeof selector !== 'undefined') {
|
||||||
|
el = $(selector)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (el !== null && el.length > 0) {
|
||||||
|
target = el;
|
||||||
|
ko.applyBindings(self, el[0]);
|
||||||
|
} else {
|
||||||
|
ko.applyBindings(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.hasHash()) {
|
||||||
|
self.activateTab(self.getHash());
|
||||||
|
} else {
|
||||||
|
self.activateDefaultTab();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setupListeners();
|
||||||
|
self.setupValidation();
|
||||||
|
|
||||||
|
if (autoload) {
|
||||||
|
self.load();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.load = function () {
|
||||||
|
self.loading(true);
|
||||||
|
$.ajax({
|
||||||
|
url: self.api_url,
|
||||||
|
type: 'POST',
|
||||||
|
data: {_csrf_token: csrf_token},
|
||||||
|
dataType: 'json',
|
||||||
|
success: self.onDataLoaded
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
self.save = function () {
|
||||||
|
if (!target.valid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
self.saving(true);
|
||||||
|
$.ajax({
|
||||||
|
url: self.api_url,
|
||||||
|
type: 'POST',
|
||||||
|
data: {_csrf_token: csrf_token, commit: 1, data: ko.toJSON(self)},
|
||||||
|
dataType: 'json',
|
||||||
|
success: self.onDataSaved
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
self.update = function (instance) {
|
||||||
|
for (const [key, value] of Object.entries($.extend(defaults, instance))) {
|
||||||
|
if (ko.isObservable(self[key])) {
|
||||||
|
self[key](value);
|
||||||
|
} else {
|
||||||
|
self[key] = ko.observable(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setupListeners = function () {
|
||||||
|
if ('onhashchange' in window) {
|
||||||
|
$(window).bind('hashchange', self.onHashChange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.destroyListeners = function () {
|
||||||
|
if ('onhashchange' in window) {
|
||||||
|
$(window).unbind('hashchange', self.onHashChange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setupValidation = function () {
|
||||||
|
let uuidRegExp = /^([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})|[0-9]+$/i;
|
||||||
|
|
||||||
|
let footerErrorElements = [
|
||||||
|
'input#local_db_enabled',
|
||||||
|
];
|
||||||
|
|
||||||
|
let errorCheckSelectors = [
|
||||||
|
'input.error:not([disabled])',
|
||||||
|
'select.error:not([disabled])',
|
||||||
|
'textarea.error:not([disabled])',
|
||||||
|
];
|
||||||
|
|
||||||
|
let errorCheckQuery = errorCheckSelectors.join(',');
|
||||||
|
let tabs = target.find('.tab-content > *[data-tab]')
|
||||||
|
|
||||||
|
let onElementChanged = function (event) {
|
||||||
|
target.valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
let auth_enabled = function (value, element, params) {
|
||||||
|
let enabled = 0;
|
||||||
|
if (self.local_db_enabled()) {
|
||||||
|
enabled++;
|
||||||
|
}
|
||||||
|
if (self.ldap_enabled()) {
|
||||||
|
enabled++;
|
||||||
|
}
|
||||||
|
if (self.google_oauth_enabled()) {
|
||||||
|
enabled++;
|
||||||
|
}
|
||||||
|
if (self.github_oauth_enabled()) {
|
||||||
|
enabled++;
|
||||||
|
}
|
||||||
|
if (self.azure_oauth_enabled()) {
|
||||||
|
enabled++;
|
||||||
|
}
|
||||||
|
if (self.oidc_oauth_enabled()) {
|
||||||
|
enabled++;
|
||||||
|
}
|
||||||
|
return enabled > 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
let ldap_exclusive = function (value, element, params) {
|
||||||
|
let enabled = 0;
|
||||||
|
if (self.ldap_sg_enabled() === 1) {
|
||||||
|
enabled++;
|
||||||
|
}
|
||||||
|
if (self.autoprovisioning() === 1) {
|
||||||
|
enabled++;
|
||||||
|
}
|
||||||
|
return enabled < 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
let uuid = function (value, element, params) {
|
||||||
|
return uuidRegExp.test(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
let local_enabled = function (element) {
|
||||||
|
return self.local_db_enabled();
|
||||||
|
};
|
||||||
|
|
||||||
|
let ldap_enabled = function (element) {
|
||||||
|
return self.ldap_enabled();
|
||||||
|
};
|
||||||
|
|
||||||
|
let google_oauth_enabled = function (element) {
|
||||||
|
return self.google_oauth_enabled();
|
||||||
|
};
|
||||||
|
|
||||||
|
let github_oauth_enabled = function (element) {
|
||||||
|
return self.github_oauth_enabled();
|
||||||
|
};
|
||||||
|
|
||||||
|
let azure_oauth_enabled = function (element) {
|
||||||
|
return self.azure_oauth_enabled();
|
||||||
|
};
|
||||||
|
|
||||||
|
let oidc_oauth_enabled = function (element) {
|
||||||
|
return self.oidc_oauth_enabled();
|
||||||
|
};
|
||||||
|
|
||||||
|
let enforce_characters = function (element) {
|
||||||
|
return self.local_db_enabled() === 1 && self.pwd_enforce_characters() === 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
let enforce_complexity = function (element) {
|
||||||
|
return self.local_db_enabled() === 1 && self.pwd_enforce_complexity() === 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
let ldap_type_openldap = function (element) {
|
||||||
|
return self.ldap_enabled() && self.ldap_type() === 'ldap';
|
||||||
|
};
|
||||||
|
|
||||||
|
let ldap_type_ad = function (element) {
|
||||||
|
return self.ldap_enabled() && self.ldap_type() === 'ad';
|
||||||
|
};
|
||||||
|
|
||||||
|
let ldap_sg_enabled = function (element) {
|
||||||
|
return self.ldap_enabled() === 1 && self.ldap_sg_enabled() === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ldap_ap_enabled = function (element) {
|
||||||
|
return self.ldap_enabled() === 1 && self.autoprovisioning() === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let azure_gs_enabled = function (element) {
|
||||||
|
return self.azure_oauth_enabled() === 1 && self.azure_sg_enabled() === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let azure_gas_enabled = function (element) {
|
||||||
|
return self.azure_oauth_enabled() && self.azure_group_accounts_enabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
let google_oauth_auto_configure_enabled = function (element) {
|
||||||
|
return self.google_oauth_enabled() && self.google_oauth_auto_configure();
|
||||||
|
}
|
||||||
|
|
||||||
|
let google_oauth_auto_configure_disabled = function (element) {
|
||||||
|
return self.google_oauth_enabled() && !self.google_oauth_auto_configure();
|
||||||
|
}
|
||||||
|
|
||||||
|
let github_oauth_auto_configure_enabled = function (element) {
|
||||||
|
return self.github_oauth_enabled() && self.github_oauth_auto_configure();
|
||||||
|
}
|
||||||
|
|
||||||
|
let github_oauth_auto_configure_disabled = function (element) {
|
||||||
|
return self.github_oauth_enabled() && !self.github_oauth_auto_configure();
|
||||||
|
}
|
||||||
|
|
||||||
|
let azure_oauth_auto_configure_enabled = function (element) {
|
||||||
|
return self.azure_oauth_enabled() && self.azure_oauth_auto_configure();
|
||||||
|
}
|
||||||
|
|
||||||
|
let azure_oauth_auto_configure_disabled = function (element) {
|
||||||
|
return self.azure_oauth_enabled() && !self.azure_oauth_auto_configure();
|
||||||
|
}
|
||||||
|
|
||||||
|
let oidc_oauth_auto_configure_enabled = function (element) {
|
||||||
|
return self.oidc_oauth_enabled() && self.oidc_oauth_auto_configure();
|
||||||
|
}
|
||||||
|
|
||||||
|
let oidc_oauth_auto_configure_disabled = function (element) {
|
||||||
|
return self.oidc_oauth_enabled() && !self.oidc_oauth_auto_configure();
|
||||||
|
}
|
||||||
|
|
||||||
|
jQuery.validator.addMethod('auth_enabled', auth_enabled, 'At least one authentication method must be enabled.');
|
||||||
|
jQuery.validator.addMethod('ldap_exclusive', ldap_exclusive, 'The LDAP group security and role auto-provisioning features are mutually exclusive.');
|
||||||
|
jQuery.validator.addMethod('uuid', uuid, 'A valid UUID is required.');
|
||||||
|
|
||||||
|
target.validate({
|
||||||
|
ignore: '',
|
||||||
|
errorPlacement: function (error, element) {
|
||||||
|
let useFooter = false;
|
||||||
|
for (let i = 0; i < footerErrorElements.length; i++) {
|
||||||
|
if (element.is(footerErrorElements[i])) {
|
||||||
|
useFooter = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (useFooter) {
|
||||||
|
target.find('.card-footer > .error').append(error);
|
||||||
|
} else if (element.is('input[type=radio]')) {
|
||||||
|
error.insertAfter(element.parents('div.radio'));
|
||||||
|
} else {
|
||||||
|
element.after(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showErrors: function (errorMap, errorList) {
|
||||||
|
this.defaultShowErrors();
|
||||||
|
tabs.each(function (index, tab) {
|
||||||
|
tab = $(tab);
|
||||||
|
let tabId = tab.data('tab');
|
||||||
|
let tabLink = target.find('.nav-tabs > li > a[data-tab="' + tabId + '"]');
|
||||||
|
if (tab.find(errorCheckQuery).length > 0) {
|
||||||
|
tabLink.addClass('error');
|
||||||
|
} else {
|
||||||
|
tabLink.removeClass('error');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
local_db_enabled: 'auth_enabled',
|
||||||
|
ldap_enabled: 'auth_enabled',
|
||||||
|
google_oauth_enabled: 'auth_enabled',
|
||||||
|
github_oauth_enabled: 'auth_enabled',
|
||||||
|
azure_oauth_enabled: 'auth_enabled',
|
||||||
|
oidc_oauth_enabled: 'auth_enabled',
|
||||||
|
pwd_min_len: {
|
||||||
|
required: enforce_characters,
|
||||||
|
digits: true,
|
||||||
|
min: 1,
|
||||||
|
max: 64,
|
||||||
|
},
|
||||||
|
pwd_min_lowercase: {
|
||||||
|
required: enforce_characters,
|
||||||
|
digits: true,
|
||||||
|
min: 0,
|
||||||
|
max: 64,
|
||||||
|
},
|
||||||
|
pwd_min_uppercase: {
|
||||||
|
required: enforce_characters,
|
||||||
|
digits: true,
|
||||||
|
min: 0,
|
||||||
|
max: 64,
|
||||||
|
},
|
||||||
|
pwd_min_digits: {
|
||||||
|
required: enforce_characters,
|
||||||
|
digits: true,
|
||||||
|
min: 0,
|
||||||
|
max: 64,
|
||||||
|
},
|
||||||
|
pwd_min_special: {
|
||||||
|
required: enforce_characters,
|
||||||
|
digits: true,
|
||||||
|
min: 0,
|
||||||
|
max: 64,
|
||||||
|
},
|
||||||
|
pwd_min_complexity: {
|
||||||
|
required: enforce_complexity,
|
||||||
|
digits: true,
|
||||||
|
min: 1,
|
||||||
|
max: 1000,
|
||||||
|
},
|
||||||
|
ldap_type: ldap_enabled,
|
||||||
|
ldap_uri: {
|
||||||
|
required: ldap_enabled,
|
||||||
|
minlength: 11,
|
||||||
|
maxlength: 255,
|
||||||
|
},
|
||||||
|
ldap_base_dn: {
|
||||||
|
required: ldap_enabled,
|
||||||
|
minlength: 4,
|
||||||
|
maxlength: 255,
|
||||||
|
},
|
||||||
|
ldap_admin_username: {
|
||||||
|
required: ldap_type_openldap,
|
||||||
|
minlength: 4,
|
||||||
|
maxlength: 255,
|
||||||
|
},
|
||||||
|
ldap_admin_password: {
|
||||||
|
required: ldap_type_openldap,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
},
|
||||||
|
ldap_domain: {
|
||||||
|
required: ldap_type_ad,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
},
|
||||||
|
ldap_filter_basic: {
|
||||||
|
required: ldap_enabled,
|
||||||
|
minlength: 3,
|
||||||
|
maxlength: 1000,
|
||||||
|
},
|
||||||
|
ldap_filter_username: {
|
||||||
|
required: ldap_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 100,
|
||||||
|
},
|
||||||
|
ldap_filter_group: {
|
||||||
|
required: ldap_type_openldap,
|
||||||
|
minlength: 3,
|
||||||
|
maxlength: 100,
|
||||||
|
},
|
||||||
|
ldap_filter_groupname: {
|
||||||
|
required: ldap_type_openldap,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 100,
|
||||||
|
},
|
||||||
|
ldap_sg_enabled: {
|
||||||
|
required: ldap_enabled,
|
||||||
|
ldap_exclusive: true,
|
||||||
|
},
|
||||||
|
ldap_admin_group: {
|
||||||
|
required: ldap_sg_enabled,
|
||||||
|
minlength: 3,
|
||||||
|
maxlength: 100,
|
||||||
|
},
|
||||||
|
ldap_operator_group: {
|
||||||
|
required: ldap_sg_enabled,
|
||||||
|
minlength: 3,
|
||||||
|
maxlength: 100,
|
||||||
|
},
|
||||||
|
ldap_user_group: {
|
||||||
|
required: ldap_sg_enabled,
|
||||||
|
minlength: 3,
|
||||||
|
maxlength: 100,
|
||||||
|
},
|
||||||
|
autoprovisioning: {
|
||||||
|
required: ldap_enabled,
|
||||||
|
ldap_exclusive: true,
|
||||||
|
},
|
||||||
|
autoprovisioning_attribute: {
|
||||||
|
required: ldap_ap_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 100,
|
||||||
|
},
|
||||||
|
urn_value: {
|
||||||
|
required: ldap_ap_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 100,
|
||||||
|
},
|
||||||
|
purge: ldap_enabled,
|
||||||
|
google_oauth_client_id: {
|
||||||
|
required: google_oauth_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
},
|
||||||
|
google_oauth_client_secret: {
|
||||||
|
required: google_oauth_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
},
|
||||||
|
google_oauth_scope: {
|
||||||
|
required: google_oauth_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
},
|
||||||
|
google_base_url: {
|
||||||
|
required: google_oauth_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
url: true,
|
||||||
|
},
|
||||||
|
google_oauth_metadata_url: {
|
||||||
|
required: google_oauth_auto_configure_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
url: true,
|
||||||
|
},
|
||||||
|
google_token_url: {
|
||||||
|
required: google_oauth_auto_configure_disabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
url: true,
|
||||||
|
},
|
||||||
|
google_authorize_url: {
|
||||||
|
required: google_oauth_auto_configure_disabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
url: true,
|
||||||
|
},
|
||||||
|
github_oauth_key: {
|
||||||
|
required: github_oauth_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
},
|
||||||
|
github_oauth_secret: {
|
||||||
|
required: github_oauth_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
},
|
||||||
|
github_oauth_scope: {
|
||||||
|
required: github_oauth_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
},
|
||||||
|
github_oauth_api_url: {
|
||||||
|
required: github_oauth_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
url: true,
|
||||||
|
},
|
||||||
|
github_oauth_metadata_url: {
|
||||||
|
required: github_oauth_auto_configure_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
url: true,
|
||||||
|
},
|
||||||
|
github_oauth_token_url: {
|
||||||
|
required: github_oauth_auto_configure_disabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
url: true,
|
||||||
|
},
|
||||||
|
github_oauth_authorize_url: {
|
||||||
|
required: github_oauth_auto_configure_disabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
url: true,
|
||||||
|
},
|
||||||
|
azure_oauth_key: {
|
||||||
|
required: azure_oauth_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
uuid: true,
|
||||||
|
},
|
||||||
|
azure_oauth_secret: {
|
||||||
|
required: azure_oauth_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
},
|
||||||
|
azure_oauth_scope: {
|
||||||
|
required: azure_oauth_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
},
|
||||||
|
azure_oauth_api_url: {
|
||||||
|
required: azure_oauth_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
url: true,
|
||||||
|
},
|
||||||
|
azure_oauth_metadata_url: {
|
||||||
|
required: azure_oauth_auto_configure_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
url: true,
|
||||||
|
},
|
||||||
|
azure_oauth_token_url: {
|
||||||
|
required: azure_oauth_auto_configure_disabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
url: true,
|
||||||
|
},
|
||||||
|
azure_oauth_authorize_url: {
|
||||||
|
required: azure_oauth_auto_configure_disabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
url: true,
|
||||||
|
},
|
||||||
|
azure_sg_enabled: azure_oauth_enabled,
|
||||||
|
azure_admin_group: {
|
||||||
|
uuid: azure_gs_enabled,
|
||||||
|
},
|
||||||
|
azure_operator_group: {
|
||||||
|
uuid: azure_gs_enabled,
|
||||||
|
},
|
||||||
|
azure_user_group: {
|
||||||
|
uuid: azure_gs_enabled,
|
||||||
|
},
|
||||||
|
azure_group_accounts_enabled: azure_oauth_enabled,
|
||||||
|
azure_group_accounts_name: {
|
||||||
|
required: azure_gas_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
},
|
||||||
|
azure_group_accounts_name_re: {
|
||||||
|
required: azure_gas_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
},
|
||||||
|
azure_group_accounts_description: {
|
||||||
|
required: azure_gas_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
},
|
||||||
|
azure_group_accounts_description_re: {
|
||||||
|
required: azure_gas_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
},
|
||||||
|
oidc_oauth_key: {
|
||||||
|
required: oidc_oauth_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
},
|
||||||
|
oidc_oauth_secret: {
|
||||||
|
required: oidc_oauth_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
},
|
||||||
|
oidc_oauth_scope: {
|
||||||
|
required: oidc_oauth_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
},
|
||||||
|
oidc_oauth_api_url: {
|
||||||
|
required: oidc_oauth_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
url: true,
|
||||||
|
},
|
||||||
|
oidc_oauth_metadata_url: {
|
||||||
|
required: oidc_oauth_auto_configure_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
url: true,
|
||||||
|
},
|
||||||
|
oidc_oauth_token_url: {
|
||||||
|
required: oidc_oauth_auto_configure_disabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
url: true,
|
||||||
|
},
|
||||||
|
oidc_oauth_authorize_url: {
|
||||||
|
required: oidc_oauth_auto_configure_disabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
url: true,
|
||||||
|
},
|
||||||
|
oidc_oauth_logout_url: {
|
||||||
|
required: oidc_oauth_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
url: true,
|
||||||
|
},
|
||||||
|
oidc_oauth_username: {
|
||||||
|
required: oidc_oauth_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
},
|
||||||
|
oidc_oauth_email: {
|
||||||
|
required: oidc_oauth_enabled,
|
||||||
|
minlength: 1,
|
||||||
|
maxlength: 255,
|
||||||
|
},
|
||||||
|
oidc_oauth_firstname: {
|
||||||
|
minlength: 0,
|
||||||
|
maxlength: 255,
|
||||||
|
},
|
||||||
|
oidc_oauth_last_name: {
|
||||||
|
minlength: 0,
|
||||||
|
maxlength: 255,
|
||||||
|
},
|
||||||
|
oidc_oauth_account_name_property: {
|
||||||
|
minlength: 0,
|
||||||
|
maxlength: 255,
|
||||||
|
},
|
||||||
|
oidc_oauth_account_description_property: {
|
||||||
|
minlength: 0,
|
||||||
|
maxlength: 255,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
ldap_sg_enabled: {
|
||||||
|
ldap_exclusive: 'The LDAP group security feature is mutually exclusive with the LDAP role auto-provisioning feature.',
|
||||||
|
},
|
||||||
|
autoprovisioning: {
|
||||||
|
ldap_exclusive: 'The LDAP role auto-provisioning feature is mutually exclusive with the LDAP group security feature.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
target.find('input, select, textarea, label').on('change,keyup,blur,click', onElementChanged);
|
||||||
|
target.valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.activateTab = function (tab) {
|
||||||
|
$('[role="tablist"] a.nav-link').blur();
|
||||||
|
self.tab_active(tab);
|
||||||
|
window.location.hash = tab;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.activateDefaultTab = function () {
|
||||||
|
self.activateTab(self.tab_default());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.getHash = function () {
|
||||||
|
return window.location.hash.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.hasHash = function () {
|
||||||
|
return window.location.hash.length > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.onDataLoaded = function (result) {
|
||||||
|
if (result.status == 0) {
|
||||||
|
self.messages_class('danger');
|
||||||
|
self.messages(result.messages);
|
||||||
|
self.loading(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.update(result.data);
|
||||||
|
self.messages_class('info');
|
||||||
|
self.messages(result.messages);
|
||||||
|
self.loading(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.onDataSaved = function (result) {
|
||||||
|
if (result.status == 0) {
|
||||||
|
self.saved(false);
|
||||||
|
self.save_failed(true);
|
||||||
|
self.messages_class('danger');
|
||||||
|
self.messages(result.messages);
|
||||||
|
self.saving(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.update(result.data);
|
||||||
|
self.saved(true);
|
||||||
|
self.save_failed(false);
|
||||||
|
self.messages_class('info');
|
||||||
|
self.messages(result.messages);
|
||||||
|
self.saving(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.onHashChange = function (event) {
|
||||||
|
let hash = window.location.hash.trim();
|
||||||
|
if (hash.length > 1) {
|
||||||
|
self.activateTab(hash.substring(1));
|
||||||
|
} else {
|
||||||
|
self.activateDefaultTab();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.onSaveClick = function (model, event) {
|
||||||
|
self.save();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.onTabClick = function (model, event) {
|
||||||
|
self.activateTab($(event.target).data('tab'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -228,7 +228,7 @@
|
|||||||
<footer class="main-footer">
|
<footer class="main-footer">
|
||||||
<strong><a href="https://github.com/PowerDNS-Admin/PowerDNS-Admin">PowerDNS-Admin</a></strong> - A PowerDNS web
|
<strong><a href="https://github.com/PowerDNS-Admin/PowerDNS-Admin">PowerDNS-Admin</a></strong> - A PowerDNS web
|
||||||
interface with advanced features.
|
interface with advanced features.
|
||||||
<span class="float-right">Version 0.4.0</span>
|
<span class="float-right">Version 0.4.1</span>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
<!-- ./wrapper -->
|
<!-- ./wrapper -->
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@foliojs-fork/restructure/-/restructure-2.0.2.tgz#73759aba2aff1da87b7c4554e6839c70d43c92b4"
|
resolved "https://registry.yarnpkg.com/@foliojs-fork/restructure/-/restructure-2.0.2.tgz#73759aba2aff1da87b7c4554e6839c70d43c92b4"
|
||||||
integrity sha512-59SgoZ3EXbkfSX7b63tsou/SDGzwUEK6MuB5sKqgVK1/XE0fxmpsOb9DQI8LXW3KfGnAjImCGhhEb7uPPAUVNA==
|
integrity sha512-59SgoZ3EXbkfSX7b63tsou/SDGzwUEK6MuB5sKqgVK1/XE0fxmpsOb9DQI8LXW3KfGnAjImCGhhEb7uPPAUVNA==
|
||||||
|
|
||||||
"@fortawesome/fontawesome-free@6.3.0":
|
"@fortawesome/fontawesome-free@6.3.0", "@fortawesome/fontawesome-free@^5.15.4":
|
||||||
version "6.3.0"
|
version "6.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-6.3.0.tgz#b5877182692a6f7a39d1108837bec24247ba4bd7"
|
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-6.3.0.tgz#b5877182692a6f7a39d1108837bec24247ba4bd7"
|
||||||
integrity sha512-qVtd5i1Cc7cdrqnTWqTObKQHjPWAiRwjUPaXObaeNPcy7+WKxJumGBx66rfSFgK6LNpIasVKkEgW8oyf0tmPLA==
|
integrity sha512-qVtd5i1Cc7cdrqnTWqTObKQHjPWAiRwjUPaXObaeNPcy7+WKxJumGBx66rfSFgK6LNpIasVKkEgW8oyf0tmPLA==
|
||||||
@ -1026,7 +1026,7 @@ jquery-ui-dist@^1.13.0, jquery-ui-dist@^1.13.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
jquery ">=1.8.0 <4.0.0"
|
jquery ">=1.8.0 <4.0.0"
|
||||||
|
|
||||||
jquery-validation@^1.19.3:
|
jquery-validation@^1.19.3, jquery-validation@^1.19.5:
|
||||||
version "1.19.5"
|
version "1.19.5"
|
||||||
resolved "https://registry.yarnpkg.com/jquery-validation/-/jquery-validation-1.19.5.tgz#557495b7cad79716897057c4447ad3cd76fda811"
|
resolved "https://registry.yarnpkg.com/jquery-validation/-/jquery-validation-1.19.5.tgz#557495b7cad79716897057c4447ad3cd76fda811"
|
||||||
integrity sha512-X2SmnPq1mRiDecVYL8edWx+yTBZDyC8ohWXFhXdtqFHgU9Wd4KHkvcbCoIZ0JaSaumzS8s2gXSkP8F7ivg/8ZQ==
|
integrity sha512-X2SmnPq1mRiDecVYL8edWx+yTBZDyC8ohWXFhXdtqFHgU9Wd4KHkvcbCoIZ0JaSaumzS8s2gXSkP8F7ivg/8ZQ==
|
||||||
@ -1081,6 +1081,11 @@ jtimeout@^3.2.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
jquery ">=1.7.1 <4.0.0"
|
jquery ">=1.7.1 <4.0.0"
|
||||||
|
|
||||||
|
knockout@^3.5.1:
|
||||||
|
version "3.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/knockout/-/knockout-3.5.1.tgz#62c81e81843bea2008fd23c575edd9ca978e75cf"
|
||||||
|
integrity sha512-wRJ9I4az0QcsH7A4v4l0enUpkS++MBx0BnL/68KaLzJg7x1qmbjSlwEoCNol7KTYZ+pmtI7Eh2J0Nu6/2Z5J/Q==
|
||||||
|
|
||||||
levn@~0.3.0:
|
levn@~0.3.0:
|
||||||
version "0.3.0"
|
version "0.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
|
resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
|
||||||
|
Loading…
Reference in New Issue
Block a user