mirror of
https://github.com/cwinfo/powerdns-admin.git
synced 2024-11-08 22:50:26 +00:00
feat: Option to forbid the creation of domain if it exists as a record (#1127)
When enabled, forbids the creation of a domain if it exists as a record in one of its parent domains (administrators and operators are not limited though).
This commit is contained in:
parent
1112105683
commit
1926b862b8
@ -6,7 +6,7 @@ from flask_login import current_user
|
|||||||
|
|
||||||
from .models import User, ApiKey, Setting, Domain, Setting
|
from .models import User, ApiKey, Setting, Domain, Setting
|
||||||
from .lib.errors import RequestIsNotJSON, NotEnoughPrivileges
|
from .lib.errors import RequestIsNotJSON, NotEnoughPrivileges
|
||||||
from .lib.errors import DomainAccessForbidden
|
from .lib.errors import DomainAccessForbidden, DomainOverrideForbidden
|
||||||
|
|
||||||
def admin_role_required(f):
|
def admin_role_required(f):
|
||||||
"""
|
"""
|
||||||
@ -259,6 +259,13 @@ def api_can_create_domain(f):
|
|||||||
msg = "User {0} does not have enough privileges to create domain"
|
msg = "User {0} does not have enough privileges to create domain"
|
||||||
current_app.logger.error(msg.format(current_user.username))
|
current_app.logger.error(msg.format(current_user.username))
|
||||||
raise NotEnoughPrivileges()
|
raise NotEnoughPrivileges()
|
||||||
|
|
||||||
|
if Setting().get('deny_domain_override'):
|
||||||
|
req = request.get_json(force=True)
|
||||||
|
domain = Domain()
|
||||||
|
if req['name'] and domain.is_overriding(req['name']):
|
||||||
|
raise DomainOverrideForbidden()
|
||||||
|
|
||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
|
|
||||||
return decorated_function
|
return decorated_function
|
||||||
@ -269,6 +276,9 @@ def apikey_can_create_domain(f):
|
|||||||
Grant access if:
|
Grant access if:
|
||||||
- user is in Operator role or higher, or
|
- user is in Operator role or higher, or
|
||||||
- allow_user_create_domain is on
|
- allow_user_create_domain is on
|
||||||
|
and
|
||||||
|
- deny_domain_override is off or
|
||||||
|
- override_domain is true (from request)
|
||||||
"""
|
"""
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
@ -278,6 +288,13 @@ def apikey_can_create_domain(f):
|
|||||||
msg = "ApiKey #{0} does not have enough privileges to create domain"
|
msg = "ApiKey #{0} does not have enough privileges to create domain"
|
||||||
current_app.logger.error(msg.format(g.apikey.id))
|
current_app.logger.error(msg.format(g.apikey.id))
|
||||||
raise NotEnoughPrivileges()
|
raise NotEnoughPrivileges()
|
||||||
|
|
||||||
|
if Setting().get('deny_domain_override'):
|
||||||
|
req = request.get_json(force=True)
|
||||||
|
domain = Domain()
|
||||||
|
if req['name'] and domain.is_overriding(req['name']):
|
||||||
|
raise DomainOverrideForbidden()
|
||||||
|
|
||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
|
|
||||||
return decorated_function
|
return decorated_function
|
||||||
|
@ -44,6 +44,13 @@ class DomainAccessForbidden(StructuredException):
|
|||||||
self.message = message
|
self.message = message
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
|
class DomainOverrideForbidden(StructuredException):
|
||||||
|
status_code = 409
|
||||||
|
|
||||||
|
def __init__(self, name=None, message="Domain override of record not allowed"):
|
||||||
|
StructuredException.__init__(self)
|
||||||
|
self.message = message
|
||||||
|
self.name = name
|
||||||
|
|
||||||
class ApiKeyCreateFail(StructuredException):
|
class ApiKeyCreateFail(StructuredException):
|
||||||
status_code = 500
|
status_code = 500
|
||||||
|
@ -881,3 +881,18 @@ class Domain(db.Model):
|
|||||||
DomainUser.user_id == user_id,
|
DomainUser.user_id == user_id,
|
||||||
AccountUser.user_id == user_id
|
AccountUser.user_id == user_id
|
||||||
)).filter(Domain.id == self.id).first()
|
)).filter(Domain.id == self.id).first()
|
||||||
|
|
||||||
|
# Return None if this domain does not exist as record,
|
||||||
|
# Return the parent domain that hold the record if exist
|
||||||
|
def is_overriding(self, domain_name):
|
||||||
|
upper_domain_name = '.'.join(domain_name.split('.')[1:])
|
||||||
|
while upper_domain_name != '':
|
||||||
|
if self.get_id_by_name(upper_domain_name.rstrip('.')) != None:
|
||||||
|
upper_domain = self.get_domain_info(upper_domain_name)
|
||||||
|
if 'rrsets' in upper_domain:
|
||||||
|
for r in upper_domain['rrsets']:
|
||||||
|
if domain_name.rstrip('.') in r['name'].rstrip('.'):
|
||||||
|
current_app.logger.error('Domain already exists as a record: {} under domain: {}'.format(r['name'].rstrip('.'), upper_domain_name))
|
||||||
|
return upper_domain_name
|
||||||
|
upper_domain_name = '.'.join(upper_domain_name.split('.')[1:])
|
||||||
|
return None
|
@ -190,7 +190,8 @@ class Setting(db.Model):
|
|||||||
'otp_field_enabled': True,
|
'otp_field_enabled': True,
|
||||||
'custom_css': '',
|
'custom_css': '',
|
||||||
'otp_force': False,
|
'otp_force': False,
|
||||||
'max_history_records': 1000
|
'max_history_records': 1000,
|
||||||
|
'deny_domain_override': False
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, id=None, name=None, value=None):
|
def __init__(self, id=None, name=None, value=None):
|
||||||
|
@ -1268,7 +1268,7 @@ def setting_basic():
|
|||||||
'allow_user_create_domain', 'allow_user_remove_domain', 'allow_user_view_history', 'bg_domain_updates', 'site_name',
|
'allow_user_create_domain', 'allow_user_remove_domain', 'allow_user_view_history', 'bg_domain_updates', 'site_name',
|
||||||
'session_timeout', 'warn_session_timeout', 'ttl_options',
|
'session_timeout', 'warn_session_timeout', 'ttl_options',
|
||||||
'pdns_api_timeout', 'verify_ssl_connections', 'verify_user_email',
|
'pdns_api_timeout', 'verify_ssl_connections', 'verify_user_email',
|
||||||
'delete_sso_accounts', 'otp_field_enabled', 'custom_css', 'enable_api_rr_history', 'max_history_records', 'otp_force'
|
'delete_sso_accounts', 'otp_field_enabled', 'custom_css', 'enable_api_rr_history', 'max_history_records', 'otp_force', 'deny_domain_override'
|
||||||
]
|
]
|
||||||
|
|
||||||
return render_template('admin_setting_basic.html', settings=settings)
|
return render_template('admin_setting_basic.html', settings=settings)
|
||||||
|
@ -32,7 +32,6 @@ domain_bp = Blueprint('domain',
|
|||||||
template_folder='templates',
|
template_folder='templates',
|
||||||
url_prefix='/domain')
|
url_prefix='/domain')
|
||||||
|
|
||||||
|
|
||||||
@domain_bp.before_request
|
@domain_bp.before_request
|
||||||
def before_request():
|
def before_request():
|
||||||
# Check if user is anonymous
|
# Check if user is anonymous
|
||||||
@ -401,6 +400,38 @@ def add():
|
|||||||
account_name = Account().get_name_by_id(account_id)
|
account_name = Account().get_name_by_id(account_id)
|
||||||
|
|
||||||
d = Domain()
|
d = Domain()
|
||||||
|
|
||||||
|
### Test if a record same as the domain already exists in an upper level domain
|
||||||
|
if Setting().get('deny_domain_override'):
|
||||||
|
|
||||||
|
upper_domain = None
|
||||||
|
domain_override = False
|
||||||
|
domain_override_toggle = False
|
||||||
|
|
||||||
|
if current_user.role.name in ['Administrator', 'Operator']:
|
||||||
|
domain_override = request.form.get('domain_override')
|
||||||
|
domain_override_toggle = True
|
||||||
|
|
||||||
|
|
||||||
|
# If overriding box is not selected.
|
||||||
|
# False = Do not allow ovrriding, perform checks
|
||||||
|
# True = Allow overriding, do not perform checks
|
||||||
|
if not domain_override:
|
||||||
|
upper_domain = d.is_overriding(domain_name)
|
||||||
|
|
||||||
|
if upper_domain:
|
||||||
|
if current_user.role.name in ['Administrator', 'Operator']:
|
||||||
|
accounts = Account.query.order_by(Account.name).all()
|
||||||
|
else:
|
||||||
|
accounts = current_user.get_accounts()
|
||||||
|
|
||||||
|
msg = 'Domain already exists as a record under domain: {}'.format(upper_domain)
|
||||||
|
|
||||||
|
return render_template('domain_add.html',
|
||||||
|
domain_override_message=msg,
|
||||||
|
accounts=accounts,
|
||||||
|
domain_override_toggle=domain_override_toggle)
|
||||||
|
|
||||||
result = d.add(domain_name=domain_name,
|
result = d.add(domain_name=domain_name,
|
||||||
domain_type=domain_type,
|
domain_type=domain_type,
|
||||||
soa_edit_api=soa_edit_api,
|
soa_edit_api=soa_edit_api,
|
||||||
@ -478,14 +509,17 @@ def add():
|
|||||||
|
|
||||||
# Get
|
# Get
|
||||||
else:
|
else:
|
||||||
|
domain_override_toggle = False
|
||||||
# Admins and Operators can set to any account
|
# Admins and Operators can set to any account
|
||||||
if current_user.role.name in ['Administrator', 'Operator']:
|
if current_user.role.name in ['Administrator', 'Operator']:
|
||||||
accounts = Account.query.order_by(Account.name).all()
|
accounts = Account.query.order_by(Account.name).all()
|
||||||
|
domain_override_toggle = True
|
||||||
else:
|
else:
|
||||||
accounts = current_user.get_accounts()
|
accounts = current_user.get_accounts()
|
||||||
return render_template('domain_add.html',
|
return render_template('domain_add.html',
|
||||||
templates=templates,
|
templates=templates,
|
||||||
accounts=accounts)
|
accounts=accounts,
|
||||||
|
domain_override_toggle=domain_override_toggle)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,6 +34,13 @@
|
|||||||
<input type="text" class="form-control" name="domain_name" id="domain_name"
|
<input type="text" class="form-control" name="domain_name" id="domain_name"
|
||||||
placeholder="Enter a valid domain name (required)">
|
placeholder="Enter a valid domain name (required)">
|
||||||
</div>
|
</div>
|
||||||
|
{% if domain_override_toggle == True %}
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Domain Override Record</label>
|
||||||
|
<input type="checkbox" id="domain_override" name="domain_override"
|
||||||
|
class="checkbox">
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
<select name="accountid" class="form-control" style="width:15em;">
|
<select name="accountid" class="form-control" style="width:15em;">
|
||||||
<option value="0">- No Account -</option>
|
<option value="0">- No Account -</option>
|
||||||
{% for account in accounts %}
|
{% for account in accounts %}
|
||||||
@ -178,3 +185,37 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
{% block modals %}
|
||||||
|
<script>
|
||||||
|
{% if domain_override_message %}
|
||||||
|
$(document.body).ready(function () {
|
||||||
|
var modal = $("#modal_warning");
|
||||||
|
var info = "{{ domain_override_message }}";
|
||||||
|
modal.find('.modal-body p').text(info);
|
||||||
|
modal.modal('show');
|
||||||
|
});
|
||||||
|
{% endif %}
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
<div class="modal fade" id="modal_warning">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content modal-sm">
|
||||||
|
<div class="modal-header alert-danger">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close" id="button_close_warn_modal">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
<h4 class="modal-title">WARNING</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p></p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-flat btn-primary center-block" data-dismiss="modal" id="button_confirm_warn_modal">
|
||||||
|
CLOSE</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- /.modal-content -->
|
||||||
|
</div>
|
||||||
|
<!-- /.modal-dialog -->
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
Loading…
Reference in New Issue
Block a user