def auth_request(self, data): """ Return: bool: authorization response """ authed = False response = requests.post(self._auth_url.rstrip("/") + "/request", json=data) if response.status_code == 200: authed = bool(response.json()["auth"]) elif response.status_code == 500: msg = "request to arborist failed: {}".format(response.json()) raise ArboristError(message=msg, code=500) 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 denied auth request" try: detail = response.json()["error"] raise Forbidden("{}: {}".format(msg, detail)) except (KeyError, ValueError): raise Forbidden(msg) return authed
def delete_role(self, role_id): response = requests.delete(self._role_url + role_id) if response.status_code == 404: # already doesn't exist, this is fine return elif response.status_code >= 400: raise ArboristError("could not delete role in arborist: {}".format( response.json()["error"]))
def update_resource(self, path, resource_json): response = _request_get_json(requests.put(path, json=resource_json)) if "error" in response: msg = response["error"].get("message", str(response["error"])) self.logger.error( "could not update resource `{}` in arborist: {}".format( path, msg)) raise ArboristError(response["error"]) self.logger.info("updated resource {}".format(resource_json["name"])) return response
def update_client(self, client_id, policies): # retrieve existing client, create one if not found response = requests.get("/".join((self._client_url, client_id))) if response.status_code == 404: self.create_client(client_id, policies) return # unpack the result data = _request_get_json(response) if "error" in data: self.logger.error( "could not fetch client `{}` in arborist: {}".format( client_id, data["error"])) raise ArboristError(data["error"]) current_policies = set(data["policies"]) policies = set(policies) # find newly granted policies, revoke all if needed url = "/".join((self._client_url, client_id, "policy")) if current_policies.difference(policies): # if some policies must be removed, revoke all and re-grant later response = requests.delete(url) if response.status_code != 204: data = _request_get_json(response) self.logger.error( "could not revoke policies from client `{}` in arborist: {}" .format(client_id, data.get("error"))) raise ArboristError(data.get("error")) else: # do not add policies that already exist policies.difference_update(current_policies) # grant missing policies for policy in policies: response = requests.post(url, json=dict(policy=policy)) if response.status_code != 204: data = _request_get_json(response) self.logger.error( "could not grant policy `{}` to client `{}` in arborist: {}" .format(policy, client_id, data["error"])) raise ArboristError(data["error"]) self.logger.info("updated policies for client {}".format(client_id))
def create_client(self, client_id, policies): response = requests.post(self._client_url, json=dict(clientID=client_id, policies=policies or [])) data = _request_get_json(response) if "error" in data: self.logger.error( "could not create client `{}` in arborist: {}".format( client_id, data["error"])) raise ArboristError(data["error"]) self.logger.info("created client {}".format(client_id)) return data
def update_resource(self, path, resource_json, create_parents=False): path = self._resource_url + path if create_parents: path = path + "?p" response = _request_get_json(requests.put(path, json=resource_json)) if "error" in response: self.logger.error( "could not update resource `{}` in arborist: {}".format( path, response["error"])) raise ArboristError(response["error"]) self.logger.info("updated resource {}".format(resource_json["name"])) return response
def create_policy(self, policy_json, skip_if_exists=True): response = requests.post(self._policy_url, json=policy_json) data = _request_get_json(response) if response.status_code == 409: return None if "error" in data: self.logger.error( "could not create policy `{}` in arborist: {}".format( policy_json["id"], data["error"])) raise ArboristError(data["error"]) self.logger.info("created policy {}".format(policy_json["id"])) return data
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 = requests.post(self._role_url, json=role_json) if response.status_code == 409: # already exists; this is ok return None data = _request_get_json(response) if "error" in data: self.logger.error( "could not create role `{}` in arborist: {}".format( role_json["id"], data["error"])) raise ArboristError(data["error"]) self.logger.info("created role {}".format(role_json["id"])) return data
def create_user_if_not_exist(self, username): self.logger.info("making sure user exists: `{}`".format(username)) user_json = {"name": username} response = requests.post(self._user_url, json=user_json) data = _request_get_json(response) if response.status_code == 409: return None if "error" in data: self.logger.error( "could not create user `{}` in arborist: {}".format( username, data["error"])) raise ArboristError(data["error"]) self.logger.info("created user {}".format(username)) return data
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, username) response = requests.get(url) data = _request_get_json(response) if response.status_code != 200: raise ArboristError( data.get("error", "unhelpful response from arborist")) return data["resources"]
def create_policy(self, policy_json, overwrite=False): if overwrite: response = requests.put(self._policy_url, json=policy_json) else: response = requests.post(self._policy_url, json=policy_json) data = _request_get_json(response) self.logger.info("arborist data: {}".format(data)) if response.status_code == 409: # already exists; this is ok, but leave warning self.logger.warn("policy `{}` already exists in arborist".format( policy_json["id"])) return None if isinstance(data, dict) and "error" in data: self.logger.error( "could not create policy `{}` in arborist: {}".format( policy_json["id"], data["error"])) raise ArboristError(data["error"]) self.logger.info("created policy {}".format(policy_json["id"])) return data
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) 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 + parent_path if create_parents: path = path + "?p" response = requests.post(path, json=resource_json) data = _request_get_json(response) if isinstance(data, dict) and "error" in data: msg = data["error"] if isinstance(data["error"], dict): msg = data["error"].get("message", msg) resource = resource_json.get("path", "/" + resource_json.get("name")) self.logger.error( "could not create resource `{}` in arborist: {}".format( resource, msg)) raise ArboristError(data["error"]) self.logger.info("created resource {}".format(resource_json["name"])) return data