mirror of
https://github.com/cwinfo/powerdns-admin.git
synced 2024-11-09 23:20:27 +00:00
PDNS-API: factor in 'dnssec_admins_only' basic setting (#1055)
`GET cryptokeys/{cryptokey_id}` returns the private key, which justifies that the setting is honored in this case.
This commit is contained in:
parent
fc8367535b
commit
07f0d215a7
@ -55,6 +55,8 @@ def create_app(config=None):
|
||||
csrf.exempt(routes.api.api_list_account_users)
|
||||
csrf.exempt(routes.api.api_add_account_user)
|
||||
csrf.exempt(routes.api.api_remove_account_user)
|
||||
csrf.exempt(routes.api.api_zone_cryptokeys)
|
||||
csrf.exempt(routes.api.api_zone_cryptokey)
|
||||
|
||||
# Load config from env variables if using docker
|
||||
if os.path.exists(os.path.join(app.root_path, 'docker_config.py')):
|
||||
|
@ -192,6 +192,24 @@ def is_json(f):
|
||||
return decorated_function
|
||||
|
||||
|
||||
def callback_if_request_body_contains_key(callback, http_methods=[], keys=[]):
|
||||
"""
|
||||
If request body contains one or more of specified keys, call
|
||||
:param callback
|
||||
"""
|
||||
def decorator(f):
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
check_current_http_method = not http_methods or request.method in http_methods
|
||||
if (check_current_http_method and
|
||||
set(request.get_json(force=True).keys()).intersection(set(keys))
|
||||
):
|
||||
callback(*args, **kwargs)
|
||||
return f(*args, **kwargs)
|
||||
return decorated_function
|
||||
return decorator
|
||||
|
||||
|
||||
def api_role_can(action, roles=None, allow_self=False):
|
||||
"""
|
||||
Grant access if:
|
||||
@ -304,15 +322,18 @@ def apikey_is_admin(f):
|
||||
|
||||
|
||||
def apikey_can_access_domain(f):
|
||||
"""
|
||||
Grant access if:
|
||||
- user has Operator role or higher, or
|
||||
- user has explicitly been granted access to domain
|
||||
"""
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
apikey = g.apikey
|
||||
if g.apikey.role.name not in ['Administrator', 'Operator']:
|
||||
domains = apikey.domains
|
||||
zone_id = kwargs.get('zone_id').rstrip(".")
|
||||
domain_names = [item.name for item in domains]
|
||||
domain_names = [item.name for item in g.apikey.domains]
|
||||
|
||||
accounts = apikey.accounts
|
||||
accounts = g.apikey.accounts
|
||||
accounts_domains = [domain.name for a in accounts for domain in a.domains]
|
||||
|
||||
allowed_domains = set(domain_names + accounts_domains)
|
||||
@ -324,6 +345,29 @@ def apikey_can_access_domain(f):
|
||||
return decorated_function
|
||||
|
||||
|
||||
def apikey_can_configure_dnssec(http_methods=[]):
|
||||
"""
|
||||
Grant access if:
|
||||
- user is in Operator role or higher, or
|
||||
- dnssec_admins_only is off
|
||||
"""
|
||||
def decorator(f=None):
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
check_current_http_method = not http_methods or request.method in http_methods
|
||||
|
||||
if (check_current_http_method and
|
||||
g.apikey.role.name not in ['Administrator', 'Operator'] and
|
||||
Setting().get('dnssec_admins_only')
|
||||
):
|
||||
msg = "ApiKey #{0} does not have enough privileges to configure dnssec"
|
||||
current_app.logger.error(msg.format(g.apikey.id))
|
||||
raise DomainAccessForbidden(message=msg)
|
||||
return f(*args, **kwargs) if f else None
|
||||
return decorated_function
|
||||
return decorator
|
||||
|
||||
|
||||
def apikey_auth(f):
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
|
@ -28,8 +28,9 @@ from ..lib.errors import (
|
||||
from ..decorators import (
|
||||
api_basic_auth, api_can_create_domain, is_json, apikey_auth,
|
||||
apikey_can_create_domain, apikey_can_remove_domain,
|
||||
apikey_is_admin, apikey_can_access_domain,
|
||||
apikey_is_admin, apikey_can_access_domain, apikey_can_configure_dnssec,
|
||||
api_role_can, apikey_or_basic_auth,
|
||||
callback_if_request_body_contains_key,
|
||||
)
|
||||
import secrets
|
||||
import string
|
||||
@ -1024,6 +1025,28 @@ def api_remove_account_user(account_id, user_id):
|
||||
return '', 204
|
||||
|
||||
|
||||
@api_bp.route(
|
||||
'/servers/<string:server_id>/zones/<string:zone_id>/cryptokeys',
|
||||
methods=['GET', 'POST'])
|
||||
@apikey_auth
|
||||
@apikey_can_access_domain
|
||||
@apikey_can_configure_dnssec(http_methods=['POST'])
|
||||
def api_zone_cryptokeys(server_id, zone_id):
|
||||
resp = helper.forward_request()
|
||||
return resp.content, resp.status_code, resp.headers.items()
|
||||
|
||||
|
||||
@api_bp.route(
|
||||
'/servers/<string:server_id>/zones/<string:zone_id>/cryptokeys/<string:cryptokey_id>',
|
||||
methods=['GET', 'PUT', 'DELETE'])
|
||||
@apikey_auth
|
||||
@apikey_can_access_domain
|
||||
@apikey_can_configure_dnssec()
|
||||
def api_zone_cryptokey(server_id, zone_id, cryptokey_id):
|
||||
resp = helper.forward_request()
|
||||
return resp.content, resp.status_code, resp.headers.items()
|
||||
|
||||
|
||||
@api_bp.route(
|
||||
'/servers/<string:server_id>/zones/<string:zone_id>/<path:subpath>',
|
||||
methods=['GET', 'POST', 'PUT', 'PATCH', 'DELETE'])
|
||||
@ -1039,6 +1062,9 @@ def api_zone_subpath_forward(server_id, zone_id, subpath):
|
||||
@apikey_auth
|
||||
@apikey_can_access_domain
|
||||
@apikey_can_remove_domain(http_methods=['DELETE'])
|
||||
@callback_if_request_body_contains_key(apikey_can_configure_dnssec()(),
|
||||
http_methods=['PUT'],
|
||||
keys=['dnssec', 'nsec3param'])
|
||||
def api_zone_forward(server_id, zone_id):
|
||||
resp = helper.forward_request()
|
||||
if not Setting().get('bg_domain_updates'):
|
||||
@ -1072,6 +1098,7 @@ def api_zone_forward(server_id, zone_id):
|
||||
history.add()
|
||||
return resp.content, resp.status_code, resp.headers.items()
|
||||
|
||||
|
||||
@api_bp.route('/servers/<path:subpath>', methods=['GET', 'PUT'])
|
||||
@apikey_auth
|
||||
@apikey_is_admin
|
||||
|
Loading…
Reference in New Issue
Block a user