Merge SyncApp's SqliteUserManager and the newly introduced UserManager and group all user managers in user_managers.py.

This commit is contained in:
Christoph Mack
2016-05-27 21:33:22 +02:00
committed by flan
parent 573aeece81
commit c7d7ff3e85
5 changed files with 158 additions and 159 deletions

View File

@@ -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)

View File

@@ -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)