Merge SyncApp's SqliteUserManager and the newly introduced UserManager and group all user managers in user_managers.py.
This commit is contained in:
@@ -38,6 +38,8 @@ from anki.sync import Syncer, MediaSyncer
|
||||
from anki.utils import intTime, checksum, isMac
|
||||
from anki.consts import SYNC_ZIP_SIZE, SYNC_ZIP_COUNT
|
||||
|
||||
from ankisyncd.users import SimpleUserManager, SqliteUserManager
|
||||
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
except ImportError:
|
||||
@@ -316,25 +318,6 @@ class SimpleSessionManager(object):
|
||||
def delete(self, hkey):
|
||||
del self.sessions[hkey]
|
||||
|
||||
class SimpleUserManager(object):
|
||||
"""A simple user manager that always allows any user."""
|
||||
|
||||
def authenticate(self, username, password):
|
||||
"""
|
||||
Returns True if this username is allowed to connect with this password. False otherwise.
|
||||
Override this to change how users are authenticated.
|
||||
"""
|
||||
|
||||
return True
|
||||
|
||||
def username2dirname(self, username):
|
||||
"""
|
||||
Returns the directory name for the given user. By default, this is just the username.
|
||||
Override this to adjust the mapping between users and their directory.
|
||||
"""
|
||||
|
||||
return username
|
||||
|
||||
class SyncApp(object):
|
||||
valid_urls = SyncCollectionHandler.operations + SyncMediaHandler.operations + ['hostKey', 'upload', 'download']
|
||||
|
||||
@@ -694,34 +677,6 @@ class SqliteSessionManager(SimpleSessionManager):
|
||||
cursor.execute("DELETE FROM session WHERE hkey=?", (hkey,))
|
||||
conn.commit()
|
||||
|
||||
class SqliteUserManager(SimpleUserManager):
|
||||
"""Authenticates users against a SQLite database."""
|
||||
|
||||
def __init__(self, auth_db_path):
|
||||
self.auth_db_path = os.path.abspath(auth_db_path)
|
||||
|
||||
def authenticate(self, username, password):
|
||||
"""Returns True if this username is allowed to connect with this password. False otherwise."""
|
||||
|
||||
conn = sqlite.connect(self.auth_db_path)
|
||||
cursor = conn.cursor()
|
||||
param = (username,)
|
||||
|
||||
cursor.execute("SELECT hash FROM auth WHERE user=?", param)
|
||||
|
||||
db_ret = cursor.fetchone()
|
||||
|
||||
if db_ret != None:
|
||||
db_hash = str(db_ret[0])
|
||||
salt = db_hash[-16:]
|
||||
hashobj = hashlib.sha256()
|
||||
|
||||
hashobj.update(username+password+salt)
|
||||
|
||||
conn.close()
|
||||
|
||||
return (db_ret != None and hashobj.hexdigest()+salt == db_hash)
|
||||
|
||||
def make_app(global_conf, **local_conf):
|
||||
return SyncApp(**local_conf)
|
||||
|
||||
|
||||
@@ -2,25 +2,57 @@
|
||||
|
||||
|
||||
import binascii
|
||||
from contextlib import closing
|
||||
import hashlib
|
||||
import logging
|
||||
import os
|
||||
import sqlite3 as sqlite
|
||||
|
||||
|
||||
class UserManager:
|
||||
def __init__(self, auth_db_path, collection_path):
|
||||
self.auth_db_path = auth_db_path
|
||||
class SimpleUserManager(object):
|
||||
"""A simple user manager that always allows any user."""
|
||||
|
||||
def __init__(self, collection_path=''):
|
||||
self.collection_path = collection_path
|
||||
|
||||
def authenticate(self, username, password):
|
||||
"""
|
||||
Returns True if this username is allowed to connect with this password.
|
||||
False otherwise. Override this to change how users are authenticated.
|
||||
"""
|
||||
|
||||
return True
|
||||
|
||||
def username2dirname(self, username):
|
||||
"""
|
||||
Returns the directory name for the given user. By default, this is just
|
||||
the username. Override this to adjust the mapping between users and
|
||||
their directory.
|
||||
"""
|
||||
|
||||
return username
|
||||
|
||||
def _create_user_dir(self, username):
|
||||
user_dir_path = os.path.join(self.collection_path, username)
|
||||
if not os.path.isdir(user_dir_path):
|
||||
logging.info("Creating collection directory for user '{}' at {}"
|
||||
.format(username, user_dir_path))
|
||||
os.makedirs(user_dir_path)
|
||||
|
||||
|
||||
class SqliteUserManager(SimpleUserManager):
|
||||
"""Authenticates users against a SQLite database."""
|
||||
|
||||
def __init__(self, auth_db_path, collection_path=None):
|
||||
SimpleUserManager.__init__(self, collection_path)
|
||||
self.auth_db_path = auth_db_path
|
||||
|
||||
def auth_db_exists(self):
|
||||
return os.path.isfile(self.auth_db_path)
|
||||
|
||||
def user_list(self):
|
||||
if not self.auth_db_exists():
|
||||
self.create_auth_db()
|
||||
return []
|
||||
raise ValueError("Cannot list users for nonexistent auth db {}."
|
||||
.format(self.auth_db_path))
|
||||
else:
|
||||
conn = sqlite.connect(self.auth_db_path)
|
||||
cursor = conn.cursor()
|
||||
@@ -37,15 +69,16 @@ class UserManager:
|
||||
|
||||
def del_user(self, username):
|
||||
if not self.auth_db_exists():
|
||||
self.create_auth_db()
|
||||
|
||||
conn = sqlite.connect(self.auth_db_path)
|
||||
cursor = conn.cursor()
|
||||
logging.info("Removing user '{}' from auth db."
|
||||
.format(username))
|
||||
cursor.execute("DELETE FROM auth WHERE user=?", (username,))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
raise ValueError("Cannot remove user from nonexistent auth db {}."
|
||||
.format(self.auth_db_path))
|
||||
else:
|
||||
conn = sqlite.connect(self.auth_db_path)
|
||||
cursor = conn.cursor()
|
||||
logging.info("Removing user '{}' from auth db."
|
||||
.format(username))
|
||||
cursor.execute("DELETE FROM auth WHERE user=?", (username,))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def add_user(self, username, password):
|
||||
self._add_user_to_auth_db(username, password)
|
||||
@@ -69,6 +102,59 @@ class UserManager:
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def set_password_for_user(self, username, new_password):
|
||||
if not self.auth_db_exists():
|
||||
raise ValueError("Cannot remove user from nonexistent auth db {}."
|
||||
.format(self.auth_db_path))
|
||||
elif not self.user_exists(username):
|
||||
raise ValueError("Cannot remove nonexistent user {}."
|
||||
.format(username))
|
||||
else:
|
||||
hash = self._create_pass_hash(username, new_password)
|
||||
|
||||
conn = sqlite.connect(self.auth_db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("UPDATE auth SET hash=? WHERE user=?", (hash, username))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
logging.info("Changed password for user {}.".format(username))
|
||||
|
||||
def authenticate_user(self, username, password):
|
||||
"""Returns True if this username is allowed to connect with this password. False otherwise."""
|
||||
|
||||
conn = sqlite.connect(self.auth_db_path)
|
||||
cursor = conn.cursor()
|
||||
param = (username,)
|
||||
cursor.execute("SELECT hash FROM auth WHERE user=?", param)
|
||||
db_hash = cursor.fetchone()
|
||||
conn.close()
|
||||
|
||||
if db_hash is None:
|
||||
logging.info("Authentication failed for nonexistent user {}."
|
||||
.format(username))
|
||||
return False
|
||||
else:
|
||||
expected_value = str(db_hash[0])
|
||||
salt = self._extract_salt(expected_value)
|
||||
|
||||
hashobj = hashlib.sha256()
|
||||
hashobj.update(username + password + salt)
|
||||
actual_value = hashobj.hexdigest() + salt
|
||||
|
||||
if actual_value == expected_value:
|
||||
logging.info("Authentication succeeded for user {}."
|
||||
.format(username))
|
||||
return True
|
||||
else:
|
||||
logging.info("Authentication failed for user {}."
|
||||
.format(username))
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def _extract_salt(hash):
|
||||
return hash[-16:]
|
||||
|
||||
@staticmethod
|
||||
def _create_pass_hash(username, password):
|
||||
salt = binascii.b2a_hex(os.urandom(8))
|
||||
@@ -85,10 +171,3 @@ class UserManager:
|
||||
(user VARCHAR PRIMARY KEY, hash VARCHAR)""")
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def _create_user_dir(self, username):
|
||||
user_dir_path = os.path.join(self.collection_path, username)
|
||||
if not os.path.isdir(user_dir_path):
|
||||
logging.info("Creating collection directory for user '{}' at {}"
|
||||
.format(username, user_dir_path))
|
||||
os.makedirs(user_dir_path)
|
||||
|
||||
Reference in New Issue
Block a user