@@ -43,6 +43,7 @@ except ImportError: | |||||
from earwigbot.config.formatter import BotFormatter | from earwigbot.config.formatter import BotFormatter | ||||
from earwigbot.config.node import ConfigNode | from earwigbot.config.node import ConfigNode | ||||
from earwigbot.config.permissions import PermissionsDB | |||||
from earwigbot.exceptions import NoConfigError | from earwigbot.exceptions import NoConfigError | ||||
__all__ = ["BotConfig"] | __all__ = ["BotConfig"] | ||||
@@ -77,8 +78,10 @@ class BotConfig(object): | |||||
def __init__(self, root_dir, level): | def __init__(self, root_dir, level): | ||||
self._root_dir = root_dir | self._root_dir = root_dir | ||||
self._logging_level = level | self._logging_level = level | ||||
self._config_path = path.join(self._root_dir, "config.yml") | |||||
self._log_dir = path.join(self._root_dir, "logs") | |||||
self._config_path = path.join(self.root_dir, "config.yml") | |||||
self._log_dir = path.join(self.root_dir, "logs") | |||||
perms_file = path.join(self.root_dir, "permissions.db") | |||||
self._permissions = PermissionsDB(perms_file) | |||||
self._decryption_cipher = None | self._decryption_cipher = None | ||||
self._data = None | self._data = None | ||||
@@ -291,6 +294,10 @@ class BotConfig(object): | |||||
for node, nodes in self._decryptable_nodes: | for node, nodes in self._decryptable_nodes: | ||||
self._decrypt(node, nodes) | self._decrypt(node, nodes) | ||||
if self.irc: | |||||
self.irc["permissions"] = self._permissions | |||||
self._permissions.load() | |||||
def decrypt(self, node, *nodes): | def decrypt(self, node, *nodes): | ||||
"""Decrypt an object in our config tree. | """Decrypt an object in our config tree. | ||||
@@ -26,9 +26,14 @@ class ConfigNode(object): | |||||
def __init__(self): | def __init__(self): | ||||
self._data = {} | self._data = {} | ||||
def __iter__(self): | |||||
for key in self._data: | |||||
yield key | |||||
def __repr__(self): | |||||
return self._data | |||||
def __nonzero__(self): | |||||
return bool(self._data) | |||||
def __len__(self): | |||||
retrun len(self._data) | |||||
def __getitem__(self, key): | def __getitem__(self, key): | ||||
return self._data[key] | return self._data[key] | ||||
@@ -42,6 +47,13 @@ class ConfigNode(object): | |||||
def __setattr__(self, key, item): | def __setattr__(self, key, item): | ||||
self._data[key] = item | self._data[key] = item | ||||
def __iter__(self): | |||||
for key in self._data: | |||||
yield key | |||||
def __contains__(self, item): | |||||
return item in self._data | |||||
def _dump(self): | def _dump(self): | ||||
data = self._data.copy() | data = self._data.copy() | ||||
for key, val in data.iteritems(): | for key, val in data.iteritems(): | ||||
@@ -0,0 +1,68 @@ | |||||
# -*- coding: utf-8 -*- | |||||
# | |||||
# Copyright (C) 2009-2012 Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
# of this software and associated documentation files (the "Software"), to deal | |||||
# in the Software without restriction, including without limitation the rights | |||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
# copies of the Software, and to permit persons to whom the Software is | |||||
# furnished to do so, subject to the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included in | |||||
# all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
# SOFTWARE. | |||||
import sqlite3 as sqlite | |||||
from threading import Lock | |||||
__all__ = ["PermissionsDB"] | |||||
class PermissionsDB(object): | |||||
def __init__(self, dbfile): | |||||
self._dbfile = dbfile | |||||
self._db_access_lock = Lock() | |||||
self._data = {} | |||||
def __repr__(self): | |||||
"""Return the canonical string representation of the PermissionsDB.""" | |||||
res = "PermissionsDB(dbfile={0!r})" | |||||
return res.format(self._dbfile) | |||||
def __str__(self): | |||||
"""Return a nice string representation of the PermissionsDB.""" | |||||
return "<PermissionsDB at {0}>".format(self._dbfile) | |||||
def _create(self, conn): | |||||
"""Initialize the permissions database with its necessary tables.""" | |||||
query = """CREATE TABLE users (user_nick, user_ident, user_host, | |||||
user_rank)""" | |||||
conn.execute(query) | |||||
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 = {} | |||||
with sqlite.connect(self._dbfile) as conn, self._db_access_lock: | |||||
try: | |||||
for nick, ident, host, rank in conn.execute(query): | |||||
try: | |||||
self._data[rank].append(_User(nick, ident, host)) | |||||
except KeyError: | |||||
self._data[rank] = [_User(nick, ident, host)] | |||||
except sqlite.OperationalError: | |||||
self._create(conn) | |||||
class _User(object): | |||||
def __init__(self, nick, ident, host): | |||||
self.nick = nick | |||||
self.ident = ident | |||||
self.host = host |