Added local storage for users

This commit is contained in:
simon 2018-02-13 20:42:13 -05:00
parent fec23d40d9
commit de0a835ecd
7 changed files with 141 additions and 32 deletions

View File

@ -10,7 +10,7 @@ class FileParser:
pass
class CheckSumCalculator:
class FileCheckSumCalculator:
def checksum(self, path: str) -> str:
"""
@ -21,7 +21,7 @@ class CheckSumCalculator:
raise NotImplementedError()
class Md5CheckSumCalculator(CheckSumCalculator):
class Md5CheckSumCalculator(FileCheckSumCalculator):
def __init__(self):
self.name = "md5"
@ -41,7 +41,7 @@ class Md5CheckSumCalculator(CheckSumCalculator):
return result.hexdigest().upper()
class Sha1CheckSumCalculator(CheckSumCalculator):
class Sha1CheckSumCalculator(FileCheckSumCalculator):
def __init__(self):
self.name = "sha1"
@ -61,7 +61,7 @@ class Sha1CheckSumCalculator(CheckSumCalculator):
return result.hexdigest().upper()
class Sha256CheckSumCalculator(CheckSumCalculator):
class Sha256CheckSumCalculator(FileCheckSumCalculator):
def __init__(self):
self.name = "sha256"

View File

@ -35,6 +35,8 @@ CREATE TABLE User (
CREATE TABLE User_canRead_Directory (
username TEXT,
directory_id INTEGER,
PRIMARY KEY (username, directory_id)
PRIMARY KEY (username, directory_id),
FOREIGN KEY (username) REFERENCES User(username),
FOREIGN KEY (directory_id) REFERENCES Directory(id)
)

View File

@ -1,2 +1,4 @@
PIL
simplejson
flask
flask_bcrypt

View File

@ -1,4 +0,0 @@
#!/bin/bash
rm test.db
sqlite3 local_storage.db -init "database.sql"

View File

@ -1,18 +1,17 @@
from unittest import TestCase
from storage import LocalStorage, Directory, DuplicateDirectoryException
from storage import LocalStorage, Directory, DuplicateDirectoryException, User
class LocalStorageTest(TestCase):
def setUp(self):
s = LocalStorage()
s = LocalStorage("test_database.db")
s.init_db("../database.sql")
def test_save_and_retrieve_dir(self):
storage = LocalStorage()
storage = LocalStorage("test_database.db")
d = Directory("/some/directory", True, ["opt1", "opt2", "opt3"])
@ -23,19 +22,19 @@ class LocalStorageTest(TestCase):
def test_save_and_retrieve_dir_persistent(self):
s1 = LocalStorage()
s1 = LocalStorage("test_database.db")
d = Directory("/some/directory", True, ["opt1", "opt2", "opt3"])
s1.save_directory(d)
s2 = LocalStorage()
s2 = LocalStorage("test_database.db")
self.assertEqual(s2.dirs()["/some/directory"].enabled, True)
self.assertEqual(s2.dirs()["/some/directory"].options[0], "opt1")
def test_reject_duplicate_path(self):
s = LocalStorage()
s = LocalStorage("test_database.db")
d1 = Directory("/some/directory", True, ["opt1", "opt2"])
d2 = Directory("/some/directory", True, ["opt1", "opt2"])
@ -45,3 +44,34 @@ class LocalStorageTest(TestCase):
with self.assertRaises(DuplicateDirectoryException) as e:
s.save_directory(d2)
def test_save_and_retrieve_user(self):
s = LocalStorage("test_database.db")
u = User("bob", "anHashedPassword", True)
s.save_user(u)
self.assertEqual(s.users()["bob"].username, "bob")
self.assertEqual(s.users()["bob"].admin, True)
def test_return_none_with_unknown_user(self):
s = LocalStorage("test_database.db")
with self.assertRaises(KeyError) as e:
_ = s.users()["unknown_user"]
def test_auth_user(self):
s = LocalStorage("test_database.db")
u = User("bob", b'$2b$14$VZEMbwAdy/HvLL/zh0.Iv.8XYnoZMz/LU9V4VKXLiuS.pthcUly2O', True)
s.save_user(u)
self.assertTrue(s.auth_user("bob", "test"))
self.assertFalse(s.auth_user("bob", "wrong"))
self.assertFalse(s.auth_user("wrong", "test"))
pass

View File

@ -1,11 +1,34 @@
import sqlite3
import os
import flask_bcrypt
class CheckSumCalculator:
def checksum(self, string: str):
return flask_bcrypt.generate_password_hash(string, 14) # todo load from config
class DuplicateDirectoryException(Exception):
pass
class DuplicateUsernameException(Exception):
pass
class User:
"""
Data structure to hold user information
"""
def __init__(self, username: str, hashed_password: bytes, admin: bool):
self.username = username
self.hashed_password = hashed_password
self.admin = admin
class Directory:
"""
Data structure to hold directory information
@ -25,22 +48,20 @@ class LocalStorage:
Could be refactored into a abstract class to switch from SQLite3 to something else
"""
cache_outdated = True
"""Static variable that indicates that the database was changed since the last time it was cached in memory"""
db_path = "../local_storage.db"
def __init__(self):
def __init__(self, db_path):
self.cached_dirs = {}
self.cached_users = {}
self.db_path = db_path
self.dir_cache_outdated = True # Indicates that the database was changed since it was cached in memory
self.user_cache_outdated = True
pass
@staticmethod
def init_db(script_path):
def init_db(self, script_path):
"""Creates a blank database. Overwrites the old one"""
if os.path.isfile(LocalStorage.db_path):
os.remove(LocalStorage.db_path)
if os.path.isfile(self.db_path):
os.remove(self.db_path)
conn = sqlite3.connect(LocalStorage.db_path)
conn = sqlite3.connect(self.db_path)
c = conn.cursor()
with open(script_path, "r") as f:
c.executescript(f.read())
@ -56,9 +77,9 @@ class LocalStorage:
:return: None
"""
LocalStorage.cache_outdated = True
self.dir_cache_outdated = True
conn = sqlite3.connect(LocalStorage.db_path)
conn = sqlite3.connect(self.db_path)
c = conn.cursor()
c.execute("PRAGMA FOREIGN_KEYS = ON;")
try:
@ -79,11 +100,11 @@ class LocalStorage:
def dirs(self):
if LocalStorage.cache_outdated:
if self.dir_cache_outdated:
self.cached_dirs = {}
conn = sqlite3.connect(LocalStorage.db_path)
conn = sqlite3.connect(self.db_path)
c = conn.cursor()
c.execute("SELECT id, path, enabled FROM Directory")
db_directories = c.fetchall()
@ -100,8 +121,63 @@ class LocalStorage:
options.append(db_opt[0])
self.cached_dirs[directory.path] = directory
LocalStorage.cache_outdated = False
self.dir_cache_outdated = False
return self.cached_dirs
else:
return self.cached_dirs
def save_user(self, user: User):
"""Save user to storage"""
self.user_cache_outdated = True
try:
conn = sqlite3.connect(self.db_path)
c = conn.cursor()
c.execute("PRAGMA FOREIGN_KEYS = ON;")
c.execute("INSERT INTO User (username, password, is_admin) VALUES (?,?,?)",
(user.username, user.hashed_password, user.admin))
conn.commit()
conn.close()
except sqlite3.IntegrityError:
raise DuplicateDirectoryException("Duplicate username: " + user.username)
def users(self):
"""Get user list"""
if self.user_cache_outdated:
self.cached_users = {}
conn = sqlite3.connect(self.db_path)
c = conn.cursor()
c.execute("SELECT username, is_admin FROM User")
db_users = c.fetchall()
for db_user in db_users:
self.cached_users[db_user[0]] = User(db_user[0], "", db_user[1])
conn.close()
return self.cached_users
else:
return self.cached_users
def auth_user(self, username: str, password: str) -> bool:
"""Authenticates an user"""
conn = sqlite3.connect(self.db_path)
c = conn.cursor()
c.execute("SELECT username, password FROM User WHERE username=?", (username,))
db_user = c.fetchone()
if db_user is not None:
return flask_bcrypt.check_password_hash(db_user[1], password)
return False

View File

@ -6,5 +6,8 @@
</head>
<body>
{% block body %}
{% endblock body %}
</body>
</html>