| @@ -39,7 +39,8 @@ class PermissionsDB(object): | |||
| def __init__(self, dbfile): | |||
| self._dbfile = dbfile | |||
| self._db_access_lock = Lock() | |||
| self._data = {} | |||
| self._users = {} | |||
| self._attributes = {} | |||
| def __repr__(self): | |||
| """Return the canonical string representation of the PermissionsDB.""" | |||
| @@ -53,13 +54,14 @@ class PermissionsDB(object): | |||
| def _create(self, conn): | |||
| """Initialize the permissions database with its necessary tables.""" | |||
| query = """CREATE TABLE users (user_nick, user_ident, user_host, | |||
| user_rank)""" | |||
| user_rank); | |||
| CREATE TABLE attributes (attr_uid, attr_key, attr_value);""" | |||
| conn.execute(query) | |||
| def _is_rank(self, user, rank): | |||
| """Return True if the given user has the given rank, else False.""" | |||
| try: | |||
| for rule in self._data[rank]: | |||
| for rule in self._users[rank]: | |||
| if user in rule: | |||
| return rule | |||
| except KeyError: | |||
| @@ -73,9 +75,9 @@ class PermissionsDB(object): | |||
| with sqlite.connect(self._dbfile) as conn: | |||
| conn.execute(query, (user.nick, user.ident, user.host, rank)) | |||
| try: | |||
| self._data[rank].append(user) | |||
| self._users[rank].append(user) | |||
| except KeyError: | |||
| self._data[rank] = [user] | |||
| self._users[rank] = [user] | |||
| return user | |||
| def _del_rank(self, user, rank): | |||
| @@ -84,40 +86,51 @@ class PermissionsDB(object): | |||
| user_host = ? AND user_rank = ?""" | |||
| with self._db_access_lock: | |||
| try: | |||
| for rule in self._data[rank]: | |||
| for rule in self._users[rank]: | |||
| if user in rule: | |||
| with sqlite.connect(self._dbfile) as conn: | |||
| args = (user.nick, user.ident, user.host, rank) | |||
| conn.execute(query, args) | |||
| self._data[rank].remove(rule) | |||
| self._users[rank].remove(rule) | |||
| return rule | |||
| except KeyError: | |||
| pass | |||
| return None | |||
| @property | |||
| def data(self): | |||
| """A dict of all entries in the permissions database.""" | |||
| return self._data | |||
| def users(self): | |||
| """A dict of all users in the permissions database.""" | |||
| return self._users | |||
| @property | |||
| def attributes(self): | |||
| """A dict of all attributes in the permissions database.""" | |||
| return self._attributes | |||
| def load(self): | |||
| """Load permissions from an existing database, or create a new one.""" | |||
| query = "SELECT user_nick, user_ident, user_host, user_rank FROM users" | |||
| self._data = {} | |||
| qry1 = "SELECT user_nick, user_ident, user_host, user_rank FROM users" | |||
| qry2 = "SELECT attr_uid, attr_key, attr_value FROM attributes" | |||
| self._users = {} | |||
| with sqlite.connect(self._dbfile) as conn, self._db_access_lock: | |||
| try: | |||
| for nick, ident, host, rank in conn.execute(query): | |||
| for nick, ident, host, rank in conn.execute(qry1): | |||
| try: | |||
| self._users[rank].append(_User(nick, ident, host)) | |||
| except KeyError: | |||
| self._users[rank] = [_User(nick, ident, host)] | |||
| for user, key, value in conn.execute(qry2): | |||
| try: | |||
| self._data[rank].append(_User(nick, ident, host)) | |||
| self._attributes[user][key] = value | |||
| except KeyError: | |||
| self._data[rank] = [_User(nick, ident, host)] | |||
| self._attributes[user] = {key: value} | |||
| except sqlite.OperationalError: | |||
| self._create(conn) | |||
| def has_exact(self, rank, nick="*", ident="*", host="*"): | |||
| """Return ``True`` if there is an exact match for this rule.""" | |||
| try: | |||
| for usr in self._data[rank]: | |||
| for usr in self._users[rank]: | |||
| if nick != usr.nick or ident != usr.ident or host != usr.host: | |||
| continue | |||
| return usr | |||
| @@ -151,6 +164,39 @@ class PermissionsDB(object): | |||
| """Remove a nick/ident/host combo to the bot owners list.""" | |||
| return self._del_rank(_User(nick, ident, host), rank=self.OWNER) | |||
| def has_attr(self, user, key): | |||
| """Return ``True`` if a given user has a certain attribute, *key*.""" | |||
| return user in self._attributes and key in self._attributes[user] | |||
| def get_attr(self, user, key): | |||
| """Get the value of the attribute *key* of a given *user*. | |||
| Raises :py:exc:`KeyError` if the *key* or *user* is not found. | |||
| """ | |||
| return self._attributes[user][key] | |||
| def set_attr(self, user, key, value): | |||
| """Set the *value* of the attribute *key* of a given *user*.""" | |||
| query1 = """SELECT attr_value FROM attributes WHERE attr_uid = ? | |||
| AND attr_key = ?""" | |||
| query2 = "INSERT INTO attributes VALUES (?, ?, ?)" | |||
| query3 = """UPDATE attributes SET attr_value = ? WHERE attr_uid = ? | |||
| AND attr_key = ?""" | |||
| with self._db_access_lock, sqlite.connect(self._dbfile) as conn: | |||
| if conn.execute(query1, (user, key)): | |||
| conn.execute(query2, (user, key, value)) | |||
| else: | |||
| conn.execute(query3, (value, user, key)) | |||
| try: | |||
| self._attributes[user][key] = value | |||
| except KeyError: | |||
| self.attributes[user] = {key: value} | |||
| def remove_attr(self, user, key): | |||
| """Remove the attribute *key* of a given *user*.""" | |||
| query = "DELETE FROM attributes WHERE attr_uid = ? AND attr_key = ?" | |||
| with self._db_access_lock, sqlite.connect(self._dbfile) as conn: | |||
| conn.execute(query, (user, key)) | |||
| class _User(object): | |||
| """A class that represents an IRC user for the purpose of testing rules.""" | |||