mirror of
https://github.com/cwinfo/powerdns-admin.git
synced 2025-01-07 10:55:40 +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_list_account_users)
|
||||||
csrf.exempt(routes.api.api_add_account_user)
|
csrf.exempt(routes.api.api_add_account_user)
|
||||||
csrf.exempt(routes.api.api_remove_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
|
# Load config from env variables if using docker
|
||||||
if os.path.exists(os.path.join(app.root_path, 'docker_config.py')):
|
if os.path.exists(os.path.join(app.root_path, 'docker_config.py')):
|
||||||
|
@ -192,6 +192,24 @@ def is_json(f):
|
|||||||
return decorated_function
|
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):
|
def api_role_can(action, roles=None, allow_self=False):
|
||||||
"""
|
"""
|
||||||
Grant access if:
|
Grant access if:
|
||||||
@ -304,15 +322,18 @@ def apikey_is_admin(f):
|
|||||||
|
|
||||||
|
|
||||||
def apikey_can_access_domain(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)
|
@wraps(f)
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
apikey = g.apikey
|
|
||||||
if g.apikey.role.name not in ['Administrator', 'Operator']:
|
if g.apikey.role.name not in ['Administrator', 'Operator']:
|
||||||
domains = apikey.domains
|
|
||||||
zone_id = kwargs.get('zone_id').rstrip(".")
|
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]
|
accounts_domains = [domain.name for a in accounts for domain in a.domains]
|
||||||
|
|
||||||
allowed_domains = set(domain_names + accounts_domains)
|
allowed_domains = set(domain_names + accounts_domains)
|
||||||
@ -324,6 +345,29 @@ def apikey_can_access_domain(f):
|
|||||||
return decorated_function
|
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):
|
def apikey_auth(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
|
@ -28,8 +28,9 @@ from ..lib.errors import (
|
|||||||
from ..decorators import (
|
from ..decorators import (
|
||||||
api_basic_auth, api_can_create_domain, is_json, apikey_auth,
|
api_basic_auth, api_can_create_domain, is_json, apikey_auth,
|
||||||
apikey_can_create_domain, apikey_can_remove_domain,
|
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,
|
api_role_can, apikey_or_basic_auth,
|
||||||
|
callback_if_request_body_contains_key,
|
||||||
)
|
)
|
||||||
import secrets
|
import secrets
|
||||||
import string
|
import string
|
||||||
@ -1024,6 +1025,28 @@ def api_remove_account_user(account_id, user_id):
|
|||||||
return '', 204
|
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(
|
@api_bp.route(
|
||||||
'/servers/<string:server_id>/zones/<string:zone_id>/<path:subpath>',
|
'/servers/<string:server_id>/zones/<string:zone_id>/<path:subpath>',
|
||||||
methods=['GET', 'POST', 'PUT', 'PATCH', 'DELETE'])
|
methods=['GET', 'POST', 'PUT', 'PATCH', 'DELETE'])
|
||||||
@ -1039,6 +1062,9 @@ def api_zone_subpath_forward(server_id, zone_id, subpath):
|
|||||||
@apikey_auth
|
@apikey_auth
|
||||||
@apikey_can_access_domain
|
@apikey_can_access_domain
|
||||||
@apikey_can_remove_domain(http_methods=['DELETE'])
|
@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):
|
def api_zone_forward(server_id, zone_id):
|
||||||
resp = helper.forward_request()
|
resp = helper.forward_request()
|
||||||
if not Setting().get('bg_domain_updates'):
|
if not Setting().get('bg_domain_updates'):
|
||||||
@ -1072,6 +1098,7 @@ def api_zone_forward(server_id, zone_id):
|
|||||||
history.add()
|
history.add()
|
||||||
return resp.content, resp.status_code, resp.headers.items()
|
return resp.content, resp.status_code, resp.headers.items()
|
||||||
|
|
||||||
|
|
||||||
@api_bp.route('/servers/<path:subpath>', methods=['GET', 'PUT'])
|
@api_bp.route('/servers/<path:subpath>', methods=['GET', 'PUT'])
|
||||||
@apikey_auth
|
@apikey_auth
|
||||||
@apikey_is_admin
|
@apikey_is_admin
|
||||||
|
Loading…
Reference in New Issue
Block a user