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 .lib.errors import RequestIsNotJSON, NotEnoughPrivileges
|
||||
from .lib.errors import DomainAccessForbidden
|
||||
from .lib.errors import DomainAccessForbidden, DomainOverrideForbidden
|
||||
|
||||
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"
|
||||
current_app.logger.error(msg.format(current_user.username))
|
||||
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 decorated_function
|
||||
@ -269,6 +276,9 @@ def apikey_can_create_domain(f):
|
||||
Grant access if:
|
||||
- user is in Operator role or higher, or
|
||||
- allow_user_create_domain is on
|
||||
and
|
||||
- deny_domain_override is off or
|
||||
- override_domain is true (from request)
|
||||
"""
|
||||
@wraps(f)
|
||||
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"
|
||||
current_app.logger.error(msg.format(g.apikey.id))
|
||||
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 decorated_function
|
||||
|
@ -44,6 +44,13 @@ class DomainAccessForbidden(StructuredException):
|
||||
self.message = message
|
||||
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):
|
||||
status_code = 500
|
||||
|
@ -881,3 +881,18 @@ class Domain(db.Model):
|
||||
DomainUser.user_id == user_id,
|
||||
AccountUser.user_id == user_id
|
||||
)).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,
|
||||
'custom_css': '',
|
||||
'otp_force': False,
|
||||
'max_history_records': 1000
|
||||
'max_history_records': 1000,
|
||||
'deny_domain_override': False
|
||||
}
|
||||
|
||||
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',
|
||||
'session_timeout', 'warn_session_timeout', 'ttl_options',
|
||||
'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)
|
||||
|
@ -32,7 +32,6 @@ domain_bp = Blueprint('domain',
|
||||
template_folder='templates',
|
||||
url_prefix='/domain')
|
||||
|
||||
|
||||
@domain_bp.before_request
|
||||
def before_request():
|
||||
# Check if user is anonymous
|
||||
@ -401,6 +400,38 @@ def add():
|
||||
account_name = Account().get_name_by_id(account_id)
|
||||
|
||||
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,
|
||||
domain_type=domain_type,
|
||||
soa_edit_api=soa_edit_api,
|
||||
@ -478,14 +509,17 @@ def add():
|
||||
|
||||
# Get
|
||||
else:
|
||||
domain_override_toggle = False
|
||||
# Admins and Operators can set to any account
|
||||
if current_user.role.name in ['Administrator', 'Operator']:
|
||||
accounts = Account.query.order_by(Account.name).all()
|
||||
domain_override_toggle = True
|
||||
else:
|
||||
accounts = current_user.get_accounts()
|
||||
return render_template('domain_add.html',
|
||||
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"
|
||||
placeholder="Enter a valid domain name (required)">
|
||||
</div>
|
||||
{% if domain_override_toggle == True %}
|
||||
<div class="form-group">
|
||||
<label>Domain Override Record</label>
|
||||
<input type="checkbox" id="domain_override" name="domain_override"
|
||||
class="checkbox">
|
||||
</div>
|
||||
{% endif %}
|
||||
<select name="accountid" class="form-control" style="width:15em;">
|
||||
<option value="0">- No Account -</option>
|
||||
{% for account in accounts %}
|
||||
@ -178,3 +185,37 @@
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% block modals %}
|
||||
<script>
|
||||
{% if domain_override_message %}
|
||||
$(document.body).ready(function () {
|
||||
var modal = $("#modal_warning");
|
||||
var info = "{{ domain_override_message }}";
|
||||
modal.find('.modal-body p').text(info);
|
||||
modal.modal('show');
|
||||
});
|
||||
{% endif %}
|
||||
</script>
|
||||
</div>
|
||||
<div class="modal fade" id="modal_warning">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content modal-sm">
|
||||
<div class="modal-header alert-danger">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close" id="button_close_warn_modal">
|
||||
<span aria-hidden="true">×</span>
|
||||
</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