allow users to remove domain (#952)

This commit is contained in:
steschuser 2021-10-30 21:21:45 +02:00 committed by GitHub
parent 1f34dbf810
commit bf83662108
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 211 additions and 2 deletions

View File

@ -93,6 +93,23 @@ def can_configure_dnssec(f):
return decorated_function return decorated_function
def can_remove_domain(f):
"""
Grant access if:
- user is in Operator role or higher, or
- allow_user_remove_domain is on
"""
@wraps(f)
def decorated_function(*args, **kwargs):
if current_user.role.name not in [
'Administrator', 'Operator'
] and not Setting().get('allow_user_remove_domain'):
abort(403)
return f(*args, **kwargs)
return decorated_function
def can_create_domain(f): def can_create_domain(f):
""" """

View File

@ -26,6 +26,7 @@ class Setting(db.Model):
'pretty_ipv6_ptr': False, 'pretty_ipv6_ptr': False,
'dnssec_admins_only': False, 'dnssec_admins_only': False,
'allow_user_create_domain': False, 'allow_user_create_domain': False,
'allow_user_remove_domain': False,
'allow_user_view_history': False, 'allow_user_view_history': False,
'bg_domain_updates': False, 'bg_domain_updates': False,
'site_name': 'PowerDNS-Admin', 'site_name': 'PowerDNS-Admin',

View File

@ -642,7 +642,7 @@ def setting_basic():
'login_ldap_first', 'default_record_table_size', 'login_ldap_first', 'default_record_table_size',
'default_domain_table_size', 'auto_ptr', 'record_quick_edit', 'default_domain_table_size', 'auto_ptr', 'record_quick_edit',
'pretty_ipv6_ptr', 'dnssec_admins_only', 'pretty_ipv6_ptr', 'dnssec_admins_only',
'allow_user_create_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', 'otp_field_enabled', 'custom_css' 'pdns_api_timeout', 'verify_ssl_connections', 'verify_user_email', 'otp_field_enabled', 'custom_css'
] ]

View File

@ -10,7 +10,7 @@ from flask_login import login_required, current_user, login_manager
from ..lib.utils import pretty_domain_name from ..lib.utils import pretty_domain_name
from ..lib.utils import pretty_json from ..lib.utils import pretty_json
from ..decorators import can_create_domain, operator_role_required, can_access_domain, can_configure_dnssec from ..decorators import can_create_domain, operator_role_required, can_access_domain, can_configure_dnssec, can_remove_domain
from ..models.user import User, Anonymous from ..models.user import User, Anonymous
from ..models.account import Account from ..models.account import Account
from ..models.setting import Setting from ..models.setting import Setting
@ -21,6 +21,9 @@ from ..models.record_entry import RecordEntry
from ..models.domain_template import DomainTemplate from ..models.domain_template import DomainTemplate
from ..models.domain_template_record import DomainTemplateRecord from ..models.domain_template_record import DomainTemplateRecord
from ..models.domain_setting import DomainSetting from ..models.domain_setting import DomainSetting
from ..models.base import db
from ..models.domain_user import DomainUser
from ..models.account_user import AccountUser
domain_bp = Blueprint('domain', domain_bp = Blueprint('domain',
__name__, __name__,
@ -131,6 +134,60 @@ def domain(domain_name):
ttl_options=ttl_options) ttl_options=ttl_options)
@domain_bp.route('/remove', methods=['GET', 'POST'])
@login_required
@can_remove_domain
def remove():
# domains is a list of all the domains a User may access
# Admins may access all
# Regular users only if they are associated with the domain
if current_user.role.name in ['Administrator', 'Operator']:
domains = Domain.query.order_by(Domain.name).all()
else:
# Get query for domain to which the user has access permission.
# This includes direct domain permission AND permission through
# account membership
domains = db.session.query(Domain) \
.outerjoin(DomainUser, Domain.id == DomainUser.domain_id) \
.outerjoin(Account, Domain.account_id == Account.id) \
.outerjoin(AccountUser, Account.id == AccountUser.account_id) \
.filter(
db.or_(
DomainUser.user_id == current_user.id,
AccountUser.user_id == current_user.id
)).order_by(Domain.name)
if request.method == 'POST':
# TODO Change name from 'domainid' to something else, its confusing
domain_name = request.form['domainid']
# Get domain from Database, might be None
domain = Domain.query.filter(Domain.name == domain_name).first()
# Check if the domain is in domains before removal
if domain not in domains:
abort(403)
# Delete
d = Domain()
result = d.delete(domain_name)
if result['status'] == 'error':
abort(500)
history = History(msg='Delete domain {0}'.format(
pretty_domain_name(domain_name)),
created_by=current_user.username)
history.add()
return redirect(url_for('dashboard.dashboard'))
else:
# On GET return the domains we got earlier
return render_template('domain_remove.html',
domainss=domains)
@domain_bp.route('/add', methods=['GET', 'POST']) @domain_bp.route('/add', methods=['GET', 'POST'])
@login_required @login_required
@can_create_domain @can_create_domain

View File

@ -117,6 +117,11 @@
<a href="{{ url_for('domain.add') }}"><i class="fa fa-plus"></i> <span>New Domain</span></a> <a href="{{ url_for('domain.add') }}"><i class="fa fa-plus"></i> <span>New Domain</span></a>
</li> </li>
{% endif %} {% endif %}
{% if SETTING.get('allow_user_remove_domain') or current_user.role.name in ['Administrator', 'Operator'] %}
<li class="{{ 'active' if active_page == 'remove_domain' else '' }}">
<a href="{{ url_for('domain.remove') }}"><i class="fa fa-trash-o"></i> <span>Remove Domain</span></a>
</li>
{% endif %}
{% if current_user.role.name in ['Administrator', 'Operator'] %} {% if current_user.role.name in ['Administrator', 'Operator'] %}
<li class="header">ADMINISTRATION</li> <li class="header">ADMINISTRATION</li>
<li class="{{ 'active' if active_page == 'admin_console' else '' }}"> <li class="{{ 'active' if active_page == 'admin_console' else '' }}">

View File

@ -0,0 +1,129 @@
{% extends "base.html" %}
{% set active_page = "remove_domain" %}
{% block title %}<title>Remove Domain - {{ SITE_NAME }}</title>{% endblock %}
{% block dashboard_stat %}
<!-- Content Header (Page header) -->
<section class="content-header">
<h1>
Domain
<small>Remove existing</small>
</h1>
<ol class="breadcrumb">
<li><a href="{{ url_for('dashboard.dashboard') }}"><i class="fa fa-dashboard"></i>Home</a></li>
<li><a href="{{ url_for('dashboard.dashboard') }}">Domain</a></li>
<li class="active">Remove Domain</li>
</ol>
</section>
{% endblock %}
{% block content %}
<section class="content">
<div class="row">
<div class="col-md-4">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Remove domain</h3>
</div>
<!-- /.box-header -->
<!-- form start -->
<form role="form" method="post" action="{{ url_for('domain.remove') }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="box-body">
<select id=domainid class="form-control" style="width:15em;">
<option value="0">- Select Domain -</option>
{% for domain in domainss %}
<option value="{{ domain.id }}">{{ domain.name }}</option>
{% endfor %}
</select><br />
</div>
<!-- /.box-body -->
<div class="box-footer">
<button type="button" class="btn btn-flat btn-danger button_delete">Remove</button>
<button type="button" class="btn btn-flat btn-default"
onclick="window.location.href='{{ url_for('dashboard.dashboard') }}'">Cancel</button>
</div>
</form>
</div>
<!-- /.box -->
</div>
<div class="col-md-8">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Help with removing a new domain</h3>
</div>
<div class="box-body">
<dl class="dl-horizontal">
<dt>Domain name</dt>
<dd>Select domain you wish to remove from DNS.</dd>
</dl>
<p>Find more details at <a href="https://docs.powerdns.com/md/">https://docs.powerdns.com/md/</a>
</p>
</div>
</div>
</div>
</div>
</section>
{% endblock %}
{% block extrascripts %}
<script>
// handle delete button
$(document.body).on("click", ".button_delete", function(e) {
e.stopPropagation();
if ( $("#domainid").val() == 0 ){
var modal = $("#modal_error");
modal.find('.modal-body p').text("Please select domain to remove.");
modal.modal('show');
return;
}
var modal = $("#modal_delete");
var domain = $("#domainid option:selected").text();
var info = "Are you sure you want to delete " + domain + "?";
modal.find('.modal-body p').text(info);
modal.find('#button_delete_confirm').click(function () {
$.post($SCRIPT_ROOT + '/domain/remove' , {
'_csrf_token': '{{ csrf_token() }}',
'domainid': domain,
}, function () {
window.location.href = '{{ url_for('dashboard.dashboard') }}';
});
modal.modal('hide');
})
modal.modal('show');
$("#button_delete_cancel").unbind().one('click', function(e) {
modal.modal('hide');
});
});
</script>
{% endblock %}
{% block modals %}
<div class="modal fade modal-warning" id="modal_delete">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">Confirmation</h4>
</div>
<div class="modal-body">
<p></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-flat btn-default pull-left" id="button_delete_cancel"
data-dismiss="modal">Close</button>
<button type="button" class="btn btn-flat btn-danger" id="button_delete_confirm">Delete</button>
</div>
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
{% endblock %}