You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

168 lines
5.3 KiB

# Copyright 2016 Splunk, Inc.
# SPDX-FileCopyrightText: 2020 2020
#
# SPDX-License-Identifier: Apache-2.0
"""
This module contains interfaces that support CRUD operations on ACL.
"""
import json
from . import splunk_rest_client as rest_client
from splunklib import binding
from .utils import retry
__all__ = ["ACLException", "ACLManager"]
class ACLException(Exception):
pass
class ACLManager:
"""ACL manager.
:param session_key: Splunk access token.
:type session_key: ``string``
:param app: App name of namespace.
:type app: ``string``
:param owner: (optional) Owner of namespace, default is `nobody`.
:type owner: ``string``
:param scheme: (optional) The access scheme, default is None.
:type scheme: ``string``
:param host: (optional) The host name, default is None.
:type host: ``string``
:param port: (optional) The port number, default is None.
:type port: ``integer``
:param context: Other configurations for Splunk rest client.
:type context: ``dict``
Usage::
>>> import solnlib.acl as sacl
>>> saclm = sacl.ACLManager(session_key, 'Splunk_TA_test')
>>> saclm.get('data/transforms/extractions')
>>> saclm.update('data/transforms/extractions/_acl',
perms_read=['*'], perms_write=['*'])
"""
def __init__(
self,
session_key,
app,
owner="nobody",
scheme=None,
host=None,
port=None,
**context
):
self._rest_client = rest_client.SplunkRestClient(
session_key,
app,
owner=owner,
scheme=scheme,
host=host,
port=port,
**context
)
@retry(exceptions=[binding.HTTPError])
def get(self, path):
"""Get ACL of /servicesNS/{`owner`}/{`app`}/{`path`}.
:param path: Path of ACL relative to /servicesNS/{`owner`}/{`app`}
:type path: ``string``
:returns: A dict contains ACL.
:rtype: ``dict``
:raises ACLException: If `path` is invalid.
Usage::
>>> aclm = acl.ACLManager(session_key, 'Splunk_TA_test')
>>> perms = aclm.get('data/transforms/extractions/_acl')
"""
try:
content = self._rest_client.get(path, output_mode="json").body.read()
except binding.HTTPError as e:
if e.status != 404:
raise
raise ACLException("Invalid endpoint: %s.", path)
return json.loads(content)["entry"][0]["acl"]
@retry(exceptions=[binding.HTTPError])
def update(self, path, owner=None, perms_read=None, perms_write=None):
"""Update ACL of /servicesNS/{`owner`}/{`app`}/{`path`}.
If the ACL is per-entity (ends in /acl), owner can be reassigned. If
the acl is endpoint-level (ends in _acl), owner will be ignored. The
'sharing' setting is always retrieved from the current.
:param path: Path of ACL relative to /servicesNS/{owner}/{app}. MUST
end with /acl or /_acl indicating whether the permission is applied
at the per-entity level or endpoint level respectively.
:type path: ``string``
:param owner: (optional) New owner of ACL, default is `nobody`.
:type owner: ``string``
:param perms_read: (optional) List of roles (['*'] for all roles). If
unspecified we will POST with current (if available) perms.read,
default is None.
:type perms_read: ``list``
:param perms_write: (optional) List of roles (['*'] for all roles). If
unspecified we will POST with current (if available) perms.write,
default is None.
:type perms_write: ``list``
:returns: A dict contains ACL after update.
:rtype: ``dict``
:raises ACLException: If `path` is invalid.
Usage::
>>> aclm = acl.ACLManager(session_key, 'Splunk_TA_test')
>>> perms = aclm.update('data/transforms/extractions/_acl',
perms_read=['admin'], perms_write=['admin'])
"""
if not path.endswith("/acl") and not path.endswith("/_acl"):
raise ACLException(
"Invalid endpoint: %s, must end with /acl or /_acl." % path
)
curr_acl = self.get(path)
postargs = {}
if perms_read:
postargs["perms.read"] = ",".join(perms_read)
else:
curr_read = curr_acl["perms"].get("read", [])
if curr_read:
postargs["perms.read"] = ",".join(curr_read)
if perms_write:
postargs["perms.write"] = ",".join(perms_write)
else:
curr_write = curr_acl["perms"].get("write", [])
if curr_write:
postargs["perms.write"] = ",".join(curr_write)
if path.endswith("/acl"):
# Allow ownership to be reset only at entity level.
postargs["owner"] = owner or curr_acl["owner"]
postargs["sharing"] = curr_acl["sharing"]
try:
content = self._rest_client.post(
path, body=binding._encode(**postargs), output_mode="json"
).body.read()
except binding.HTTPError as e:
if e.status != 404:
raise
raise ACLException("Invalid endpoint: %s.", path)
return json.loads(content)["entry"][0]["acl"]