Exemplo n.º 1
0
 def __init__(self, db, env, permissions, name, root_id):
     self.db = db
     self.cache = CacheDict(capacity=self.db.settings("cache", "path_capacity"))
     self.env = env
     self.root_id = root_id
     self.permissions = permissions
     self.name = name
Exemplo n.º 2
0
class Env(object):
    def __init__(self, db, env, permissions, name, root_id):
        self.db = db
        self.cache = CacheDict(capacity=self.db.settings("cache", "path_capacity"))
        self.env = env
        self.root_id = root_id
        self.permissions = permissions
        self.name = name

    def get_auth(self):
        from .auth import Auth

        users_root = self.db.get_env_root("users")
        self_root = self.db.get_child(users_root, self.name)
        if self_root:
            return Auth(self.db, self.name, self_root["id"])

    @staticmethod
    def _get_parent_path(sanitized_path):
        prev_slash = sanitized_path[:-1].rindex("/")
        if prev_slash:
            return sanitized_path[:prev_slash]
        return "/"

    def _get_parent_id(self, sanitized_path):
        parent_path = Env._get_parent_path(sanitized_path)
        if parent_path == "/":
            return self.root_id
        return self._get_index_of(parent_path)

    def _index_from_cache(self, cache_key):
        """
        Attempts to retrieve the index of this path from cache

        :param cache_key: The string representation of the cahce key in
         the form of /fully/sanatized/path/:override
        :return: the int() index_id or None
        """
        return self.cache.get(cache_key, None)

    def _get_index_of(self, path, override=None, parent_id=None, parent_path=None):
        """
        Returns the index of the given path.

        It searches out from /, recursively calling itself with the next item
        in the path.  As it searches, it greedily caches all paths it finds.

        :param path: This should be a string.  When _get_index_of() calls
         itself it is an array of strings (broken up by '/'). which must be
         walked. When len(path) == 1, we're at the end of the search
        :param override: This should be a string. Only override == '' may have
         children, so this override only applies to the last item in the path
        :param parent_id: the current parent id to search from.  Starts out
         from self.root_id
        :param parent_path: the current parent path we are searching from.
         Starts out from /
        :return: the index of the path, or -1 if the path doesn't exist
        """
        override = "" if override is None else override

        if isinstance(path, basestring):
            if path == "/":
                return self.root_id

            path = sanitize_path(path)
            cache_key = "{}:{}".format(path, override)
            index = self._index_from_cache(cache_key)

            if index is None:
                return self._get_index_of(path_split(path), override, self.root_id, "/")

            return index

        children = self.db.get_children_of(parent_id)

        # only global values may have children, so we only invoke the override
        # when we get to the end of the search
        seek_override = ""

        if len(path) == 1:
            # we are at the end of the search, must invoke the override
            seek_override = override

        first_item_id = -1

        for child in children:
            cache_key = "{}{}/:{}".format(parent_path, child["name"], child["override"])
            self.cache[cache_key] = child["id"]

            if child["name"] == path[0] and child["override"] == seek_override:
                if len(path) == 1:
                    return child["id"]

                first_item_id = child["id"]

        if first_item_id == -1:
            return -1

        return self._get_index_of(path[1:], override, first_item_id, "{}{}/".format(parent_path, path[0]))

    def _honor_permissions(self, val_pair):
        if self.permissions >= Rights.ReadProtected:
            return val_pair
        if self.permissions >= Rights.Read and not val_pair[1]:
            return val_pair
        return None, None

    def set(self, path, value, override=None):
        override = "" if override is None else override
        value = "" if value is None else value

        if self.permissions < Rights.Write:
            return False

        if (path == "/") and (override == ""):
            self.db.update(self.name, self.root_id, value)
            return True

        if path == "/":
            return False  # Cannot create overrides for root

        path = sanitize_path(path)
        cache_key = "{}:{}".format(path, override)
        cached = self._index_from_cache(cache_key)

        if cached is not None:
            # An item might have been deleted, but still lingers in cache
            current = self.db.get_value(cached)
            if current == (None, None):
                self.cache.delete(cache_key)
            else:
                self.db.update(self.name, cached, value)
                return True

        parent_index = self._get_parent_id(path)

        if parent_index < 0:
            return False

        children = self.db.get_children_of(parent_index)
        search = get_name_of_value(path)
        global_entry_must_be_created = override != ""

        for child in children:
            if (child["name"] == search) and (child["override"] == override):
                self.cache[cache_key] = child["id"]
                self.db.update(self.name, child["id"], value)
                return True
            if (child["name"] == search) and (child["override"] == ""):
                global_entry_must_be_created = False

        if global_entry_must_be_created:
            self.db.create(self.name, parent_index, search, "", "")

        self.db.create(self.name, parent_index, search, value, override)

        return True

    def delete(self, path, override=None):
        sanitized_path = sanitize_path(path)
        if sanitized_path == "/":
            return

        if not override:
            parent_path = Env._get_parent_path(sanitized_path)
            children = self.get_children(parent_path)
            name = get_name_of_value(sanitized_path)

            for child in children:
                if child["name"] == name:
                    self.db.delete(self.name, child["id"])
        else:
            index = self._get_index_of(path, override)
            if index >= 0:
                self.db.delete(self.name, index)

    def get_explicit(self, path, override=None):
        index = self._get_index_of(path, override)
        if index >= 0:
            return self._honor_permissions(self.db.get_value(index))
        return None, None

    def get(self, path):
        if path == "/":
            return self._honor_permissions(self.db.get_value(self.root_id))

        path = sanitize_path(path)
        parent_id = self._get_parent_id(path)
        value_name = get_name_of_value(path)

        if parent_id >= 0:
            return self._honor_permissions(self.db.get_value_for(parent_id, value_name, self.name))
        return None, None

    def get_children(self, path):
        index = self._get_index_of(path)
        path = sanitize_path(path)
        can_read = lambda i: True

        if self.permissions < Rights.Read:
            return []
        elif self.permissions == Rights.Read:
            can_read = lambda i: not i["protect"]

        if index >= 0:
            return [
                {
                    "name": child["name"],
                    "id": child["id"],
                    "override": child["override"],
                    "path": path + child["name"] + "/",
                    "value": child["value"],
                    "protect": child["protect"],
                }
                for child in self.db.get_children_of(index)
                if can_read(child)
            ]
        return []

    def get_history(self, path, override=None):
        if self.permissions < Rights.ReadProtected:
            return []

        index = self._get_index_of(path, override)

        return [
            {
                "id": item["id"],
                "name": item["name"],
                "value": item["value"],
                "author": item["author"],
                "datetime": item["datetime"],
                "active": item["active"],
                "protect": item["protect"],
                "override": item["override"],
            }
            for item in self.db.get_history(index)
        ]

    def set_protect(self, status, path, override):
        if self.permissions < Rights.Write:
            return False

        override = "" if override is None else override

        if (path == "/") and (override == ""):
            self.db.set_protect_status(self.name, self.root_id, status)
            return True

        if path == "/":
            return False  # Cannot create overrides for root

        index = self._get_index_of(path, override)
        if index > -1:
            self.db.set_protect_status(self.name, index, status)
            return True

        return False