Ejemplo n.º 1
0
 def auth_request(self, jwt, service, methods, resources):
     """
     Return:
         bool: authorization response
     """
     if isinstance(resources, string_types):
         resources = [resources]
     if isinstance(methods, string_types):
         methods = [methods]
     data = {
         "user": {"token": jwt},
         "requests": [
             {"resource": resource, "action": {"service": service, "method": method}}
             for resource in resources
             for method in methods
         ],
     }
     response = self.post(self._auth_url.rstrip("/") + "/request", json=data)
     if not response.successful:
         msg = "request to arborist failed: {}".format(response.error_msg)
         raise ArboristError(msg, response.code)
     elif response.code == 200:
         return bool(response.json["auth"])
     else:
         # arborist could send back a 400 for things like, the user has some policy
         # that it doesn't recognize, or the request is structured incorrectly; for
         # these cases we will default to unauthorized
         msg = "arborist could not process auth request: {}".format(
             response.error_msg
         )
         self.logger.info(msg)
         raise ArboristError(msg, response.code)
Ejemplo n.º 2
0
 def update_policy(self, policy_id, policy_json, create_if_not_exist=False):
     """
     Arborist will create policy if not exist and overwrite if exist.
     """
     if "id" in policy_json and policy_json.pop("id") != policy_id:
         self.logger.warn(
             "id in policy_json provided but not equal to policy_id, ignoring."
         )
     try:
         # Arborist 3.x.x
         url = self._policy_url + urllib.quote(policy_id)
         response = self.put(url, json=policy_json)
     except ArboristError as e:
         if e.code == 405:
             # For compatibility with Arborist 2.x.x
             self.logger.info(
                 "This Arborist version has no PUT /policy/{policyID} endpt yet. Falling back on PUT /policy"
             )
             policy_json["id"] = policy_id
             response = self.put(self._policy_url, json=policy_json)
         else:
             raise
     if response.code == 404 and create_if_not_exist:
         self.logger.info("Policy `{}` does not exist: Creating".format(policy_id))
         policy_json["id"] = policy_id
         return self.create_policy(policy_json, skip_if_exists=False)
     if not response.successful:
         msg = "could not put policy `{}` in arborist: {}".format(
             policy_id, response.error_msg
         )
         self.logger.error(msg)
         raise ArboristError(msg, response.code)
     self.logger.info("put policy {}".format(policy_id))
     return response
Ejemplo n.º 3
0
 def delete_resource(self, path):
     url = self._resource_url + urllib.quote(path)
     response = self.delete(url)
     if response.code not in [204, 404]:
         msg = "could not delete resource `{}` in arborist: {}".format(
             path, response.error_msg
         )
         raise ArboristError(msg, response.code)
     return True
Ejemplo n.º 4
0
 def delete_role(self, role_id):
     response = self.delete(self._role_url + urllib.quote(role_id))
     if response.code == 404:
         # already doesn't exist, this is fine
         return
     elif response.code >= 400:
         msg = "could not delete role in arborist: {}".format(response.error_msg)
         self.logger.error(msg)
         raise ArboristError(msg, response.code)
Ejemplo n.º 5
0
 def list_groups(self):
     response = self.get(self._group_url)
     if response.code != 200:
         self.logger.error("could not list groups: {}".format(response.error_msg))
         raise ArboristError(response.error_msg, response.code)
     groups = response.json
     self.logger.info(
         "got arborist groups: `{}`".format(json.dumps(groups, indent=2))
     )
     return groups
Ejemplo n.º 6
0
    def update_client(self, client_id, policies):
        # retrieve existing client, create one if not found
        response = self.get("/".join((self._client_url, urllib.quote(client_id))))
        if response.code == 404:
            self.create_client(client_id, policies)
            return

        # unpack the result
        if "error" in response.json:
            msg = "could not fetch client `{}` in arborist: {}".format(
                client_id, response.error_msg
            )
            self.logger.error(msg)
            raise ArboristError(msg, response.code)
        current_policies = set(response.json["policies"])
        policies = set(policies)

        # find newly granted policies, revoke all if needed
        url = "/".join((self._client_url, urllib.quote(client_id), "policy"))
        if current_policies.difference(policies):
            # if some policies must be removed, revoke all and re-grant later
            response = self.delete(url)
            if response.code != 204:
                msg = "could not revoke policies from client `{}` in arborist: {}".format(
                    client_id, response.error_msg
                )
                self.logger.error(msg)
                raise ArboristError(msg, response.code)
        else:
            # do not add policies that already exist
            policies.difference_update(current_policies)

        # grant missing policies
        for policy in policies:
            response = self.post(url, json=dict(policy=policy), expect_json=False)
            if response.code != 204:
                msg = "could not grant policy `{}` to client `{}` in arborist: {}".format(
                    policy, client_id, response.error_msg
                )
                self.logger.error(msg)
                raise ArboristError(msg, response.code)
        self.logger.info("updated policies for client {}".format(client_id))
Ejemplo n.º 7
0
 def update_role(self, role_id, role_json):
     url = self._role_url + urllib.quote(role_id)
     response = self.put(url, json=role_json)
     if not response.successful:
         msg = "could not update role `{}` in arborist: {}".format(
             role_id, response.error_msg
         )
         self.logger.error(msg)
         raise ArboristError(msg, response.code)
     self.logger.info("updated role {}".format(role_json["name"]))
     return response
Ejemplo n.º 8
0
            def response(*args, **kwargs):
                mocked_response = MagicMock(requests.Response)

                if function_name == "auth_mapping":
                    if not known_user:
                        raise ArboristError("User does not exist in Arborist")
                    mocked_response.items = auth_mapping.items

                if function_name == "create_resource":
                    mocked_response.get = lambda *args, **kwargs: None

                return mocked_response
Ejemplo n.º 9
0
 def create_client(self, client_id, policies):
     response = self.post(
         self._client_url, json=dict(clientID=client_id, policies=policies or [])
     )
     if "error" in response.json:
         msg = "could not create client `{}` in arborist: {}".format(
             client_id, response.error_msg
         )
         self.logger.error(msg)
         raise ArboristError(msg, response.code)
     self.logger.info("created client {}".format(client_id))
     return response.json
Ejemplo n.º 10
0
    def auth_mapping(self, username):
        """
        For given user, get mapping from the resources that this user can access
        to the actions on those resources for which they are authorized.

        Return:
            dict: response JSON from arborist
        """
        data = {"username": username}
        response = self.post(self._auth_url.rstrip("/") + "/mapping", json=data)
        if not response.successful:
            raise ArboristError(response.error_msg, response.code)
        return response.json
Ejemplo n.º 11
0
    def list_resources_for_user(self, username):
        """
        Args:
            username (str)

        Return:
            List[str]: list of resource paths which the user has any access to
        """
        url = "{}/{}/resources".format(self._user_url, urllib.quote(username))
        response = self.get(url)
        if response.code != 200:
            raise ArboristError(response.error_msg, response.code)
        return response.json["resources"]
Ejemplo n.º 12
0
 def update_resource(self, path, resource_json, create_parents=False):
     url = self._resource_url + urllib.quote(path)
     if create_parents:
         url = url + "?p"
     response = self.put(url, json=resource_json)
     if not response.successful:
         msg = "could not update resource `{}` in arborist: {}".format(
             path, response.error_msg
         )
         self.logger.error(msg)
         raise ArboristError(msg, response.code)
     self.logger.info("updated resource {}".format(resource_json["name"]))
     return response.json
Ejemplo n.º 13
0
    def create_role(self, role_json):
        """
        Create a new role in arborist (does not affect fence database or
        otherwise have any interaction with userdatamodel).

        Used for syncing project permissions from dbgap into arborist roles.

        Example schema for the role JSON:

            {
                "id": "role",
                "description": "...",
                "permissions": [
                    {
                        "id": "permission",
                        "description": "...",
                        "action": {
                            "service": "...",
                            "method": "..."
                        },
                        "constraints": {
                            "key": "value",
                        }
                    }
                ]
            }

        ("description" fields are optional, as is the "constraints" field in
        the permission.)

        Args:
            role_json (dict): dictionary of information about the role

        Return:
            dict: response JSON from arborist

        Raises:
            - ArboristError: if the operation failed (couldn't create role)
        """
        response = self.post(self._role_url, json=role_json)
        if response.code == 409:
            # already exists; this is ok
            return None
        if not response.successful:
            msg = "could not create role `{}` in arborist: {}".format(
                role_json["id"], response.error_msg
            )
            self.logger.error(msg)
            raise ArboristError(msg, response.code)
        self.logger.info("created role {}".format(role_json["id"]))
        return response.json
Ejemplo n.º 14
0
 def create_user_if_not_exist(self, username):
     self.logger.info("making sure user exists: `{}`".format(username))
     user_json = {"name": username}
     response = self.post(self._user_url, json=user_json)
     if response.code == 409:
         return None
     if "error" in response.json:
         msg = "could not create user `{}` in arborist: {}".format(
             username, response.error_msg
         )
         self.logger.error(msg)
         raise ArboristError(msg, response.code)
     self.logger.info("created user {}".format(username))
     return response.json
Ejemplo n.º 15
0
 def put_group(self, name, description="", users=[], policies=[]):
     """
     Arborist will create group if not exist and overwrite if exist.
     """
     data = {"name": name, "users": users, "policies": policies}
     if description:
         data["description"] = description
     response = self.put(self._group_url, json=data)
     if not response.successful:
         msg = "could not put group `{}` in arborist: {}".format(
             name, response.error_msg
         )
         self.logger.error(msg)
         raise ArboristError(msg, response.code)
     self.logger.info("put group {}".format(name))
     return response.json
Ejemplo n.º 16
0
 def create_policy(self, policy_json, skip_if_exists=True):
     response = self.post(self._policy_url, json=policy_json)
     if response.code == 409 and skip_if_exists:
         # already exists; this is ok, but leave warning
         self.logger.warning(
             "policy `{}` already exists in arborist".format(policy_json["id"])
         )
         return None
     if not response.successful:
         msg = "could not create policy `{}` in arborist: {}".format(
             policy_json["id"], response.error_msg
         )
         self.logger.error(msg)
         raise ArboristError(msg, response.code)
     self.logger.info("created policy {}".format(policy_json["id"]))
     return response.json
Ejemplo n.º 17
0
    def get_resource(self, path):
        """
        Return the information for a resource in arborist.

        Args:
            resource_path (str): path for the resource

        Return:
            dict: JSON representation of the resource
        """
        url = self._resource_url + urllib.quote(path)
        response = self.get(url)
        if response.code == 404:
            return None
        if not response.successful:
            self.logger.error(response.error_msg)
            raise ArboristError(response.error_msg, response.code)
        return response.json
Ejemplo n.º 18
0
    def list_resources(self):
        """
        Return the information for a resource in arborist.

        Args:
            resource_path (str): path for the resource

        Return:
            dict: JSON representation of the resource
        """
        response = self.get(self._resource_url)
        if response.code != 200:
            self.logger.error("could not list resources: {}".format(response.error_msg))
            raise ArboristError(response.error_msg, response.code)
        resources = response.json
        self.logger.info(
            "got arborist resources: `{}`".format(json.dumps(resources, indent=2))
        )
        return resources
Ejemplo n.º 19
0
    def __init__(self, response, expect_json=True):
        self._response = response
        self.code = response.status_code

        if not expect_json:
            return

        try:
            self.json = response.json()
        except ValueError as e:
            if self.code != 500:
                raise ArboristError(
                    "got a confusing response from arborist, couldn't parse JSON from"
                    " response but got code {} for this response: {}".format(
                        self.code, _escape_newlines(response.text)
                    ),
                    self.code,
                )
            self.json = {"error": {"message": str(e), "code": 500}}
Ejemplo n.º 20
0
    def create_resource(self, parent_path, resource_json, create_parents=False):
        """
        Create a new resource in arborist (does not affect fence database or
        otherwise have any interaction with userdatamodel).

        Used for syncing projects from dbgap into arborist resources.

        Example schema for resource JSON:

            {
                "name": "some_resource",
                "description": "..."
                "subresources": [
                    {
                        "name": "subresource",
                        "description": "..."
                    }
                ]
            }

        Supposing we have some ``"parent_path"``, then the new resource will be
        created as ``/parent_path/some_resource`` in arborist.

        ("description" fields are optional, as are subresources, which default
        to empty.)

        Args:
            parent_path (str):
                the path (like a filepath) to the parent resource above this
                one; if this one is in the root level, then use "/"
            resource_json (dict):
                dictionary of resource information (see the example above)
            create_parents (bool):
                if True, then arborist will create parent resources if they do
                not exist yet.

        Return:
            dict: response JSON from arborist

        Raises:
            - ArboristError: if the operation failed (couldn't create resource)
        """
        # To add a subresource, all we actually have to do is POST the resource
        # JSON to its parent in arborist:
        #
        #     POST /resource/parent
        #
        # and now the new resource will exist here:
        #
        #     /resource/parent/new_resource
        #

        path = self._resource_url + urllib.quote(parent_path)
        if create_parents:
            path = path + "?p"

        response = self.post(path, json=resource_json)
        if response.code == 409:
            # already exists; this is ok, but leave warning
            self.logger.warning(
                "resource `{}` already exists in arborist".format(resource_json["name"])
            )
            return None
        if not response.successful:
            msg = "could not create resource `{}` in arborist: {}".format(
                path, response.error_msg
            )
            self.logger.error(msg)
            raise ArboristError(msg, response.code)
        self.logger.info("created resource {}".format(resource_json["name"]))
        return response.json