mirror of
https://github.com/simon987/Simple-Incremental-Search-Tool.git
synced 2025-04-19 02:06:45 +00:00
Added local storage for users
This commit is contained in:
parent
fec23d40d9
commit
de0a835ecd
@ -10,7 +10,7 @@ class FileParser:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CheckSumCalculator:
|
class FileCheckSumCalculator:
|
||||||
|
|
||||||
def checksum(self, path: str) -> str:
|
def checksum(self, path: str) -> str:
|
||||||
"""
|
"""
|
||||||
@ -21,7 +21,7 @@ class CheckSumCalculator:
|
|||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
class Md5CheckSumCalculator(CheckSumCalculator):
|
class Md5CheckSumCalculator(FileCheckSumCalculator):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.name = "md5"
|
self.name = "md5"
|
||||||
@ -41,7 +41,7 @@ class Md5CheckSumCalculator(CheckSumCalculator):
|
|||||||
return result.hexdigest().upper()
|
return result.hexdigest().upper()
|
||||||
|
|
||||||
|
|
||||||
class Sha1CheckSumCalculator(CheckSumCalculator):
|
class Sha1CheckSumCalculator(FileCheckSumCalculator):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.name = "sha1"
|
self.name = "sha1"
|
||||||
@ -61,7 +61,7 @@ class Sha1CheckSumCalculator(CheckSumCalculator):
|
|||||||
return result.hexdigest().upper()
|
return result.hexdigest().upper()
|
||||||
|
|
||||||
|
|
||||||
class Sha256CheckSumCalculator(CheckSumCalculator):
|
class Sha256CheckSumCalculator(FileCheckSumCalculator):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.name = "sha256"
|
self.name = "sha256"
|
||||||
|
@ -35,6 +35,8 @@ CREATE TABLE User (
|
|||||||
CREATE TABLE User_canRead_Directory (
|
CREATE TABLE User_canRead_Directory (
|
||||||
username TEXT,
|
username TEXT,
|
||||||
directory_id INTEGER,
|
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)
|
||||||
|
|
||||||
)
|
)
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
PIL
|
PIL
|
||||||
simplejson
|
simplejson
|
||||||
|
flask
|
||||||
|
flask_bcrypt
|
@ -1,4 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
rm test.db
|
|
||||||
sqlite3 local_storage.db -init "database.sql"
|
|
@ -1,18 +1,17 @@
|
|||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
from storage import LocalStorage, Directory, DuplicateDirectoryException, User
|
||||||
from storage import LocalStorage, Directory, DuplicateDirectoryException
|
|
||||||
|
|
||||||
|
|
||||||
class LocalStorageTest(TestCase):
|
class LocalStorageTest(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
||||||
s = LocalStorage()
|
s = LocalStorage("test_database.db")
|
||||||
s.init_db("../database.sql")
|
s.init_db("../database.sql")
|
||||||
|
|
||||||
def test_save_and_retrieve_dir(self):
|
def test_save_and_retrieve_dir(self):
|
||||||
|
|
||||||
storage = LocalStorage()
|
storage = LocalStorage("test_database.db")
|
||||||
|
|
||||||
d = Directory("/some/directory", True, ["opt1", "opt2", "opt3"])
|
d = Directory("/some/directory", True, ["opt1", "opt2", "opt3"])
|
||||||
|
|
||||||
@ -23,19 +22,19 @@ class LocalStorageTest(TestCase):
|
|||||||
|
|
||||||
def test_save_and_retrieve_dir_persistent(self):
|
def test_save_and_retrieve_dir_persistent(self):
|
||||||
|
|
||||||
s1 = LocalStorage()
|
s1 = LocalStorage("test_database.db")
|
||||||
|
|
||||||
d = Directory("/some/directory", True, ["opt1", "opt2", "opt3"])
|
d = Directory("/some/directory", True, ["opt1", "opt2", "opt3"])
|
||||||
|
|
||||||
s1.save_directory(d)
|
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"].enabled, True)
|
||||||
self.assertEqual(s2.dirs()["/some/directory"].options[0], "opt1")
|
self.assertEqual(s2.dirs()["/some/directory"].options[0], "opt1")
|
||||||
|
|
||||||
def test_reject_duplicate_path(self):
|
def test_reject_duplicate_path(self):
|
||||||
|
|
||||||
s = LocalStorage()
|
s = LocalStorage("test_database.db")
|
||||||
|
|
||||||
d1 = Directory("/some/directory", True, ["opt1", "opt2"])
|
d1 = Directory("/some/directory", True, ["opt1", "opt2"])
|
||||||
d2 = 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:
|
with self.assertRaises(DuplicateDirectoryException) as e:
|
||||||
s.save_directory(d2)
|
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
|
||||||
|
108
storage.py
108
storage.py
@ -1,11 +1,34 @@
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
import os
|
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):
|
class DuplicateDirectoryException(Exception):
|
||||||
pass
|
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:
|
class Directory:
|
||||||
"""
|
"""
|
||||||
Data structure to hold directory information
|
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
|
Could be refactored into a abstract class to switch from SQLite3 to something else
|
||||||
"""
|
"""
|
||||||
|
|
||||||
cache_outdated = True
|
def __init__(self, db_path):
|
||||||
"""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):
|
|
||||||
self.cached_dirs = {}
|
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
|
pass
|
||||||
|
|
||||||
@staticmethod
|
def init_db(self, script_path):
|
||||||
def init_db(script_path):
|
|
||||||
"""Creates a blank database. Overwrites the old one"""
|
"""Creates a blank database. Overwrites the old one"""
|
||||||
if os.path.isfile(LocalStorage.db_path):
|
if os.path.isfile(self.db_path):
|
||||||
os.remove(LocalStorage.db_path)
|
os.remove(self.db_path)
|
||||||
|
|
||||||
conn = sqlite3.connect(LocalStorage.db_path)
|
conn = sqlite3.connect(self.db_path)
|
||||||
c = conn.cursor()
|
c = conn.cursor()
|
||||||
with open(script_path, "r") as f:
|
with open(script_path, "r") as f:
|
||||||
c.executescript(f.read())
|
c.executescript(f.read())
|
||||||
@ -56,9 +77,9 @@ class LocalStorage:
|
|||||||
:return: None
|
: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 = conn.cursor()
|
||||||
c.execute("PRAGMA FOREIGN_KEYS = ON;")
|
c.execute("PRAGMA FOREIGN_KEYS = ON;")
|
||||||
try:
|
try:
|
||||||
@ -79,11 +100,11 @@ class LocalStorage:
|
|||||||
|
|
||||||
def dirs(self):
|
def dirs(self):
|
||||||
|
|
||||||
if LocalStorage.cache_outdated:
|
if self.dir_cache_outdated:
|
||||||
|
|
||||||
self.cached_dirs = {}
|
self.cached_dirs = {}
|
||||||
|
|
||||||
conn = sqlite3.connect(LocalStorage.db_path)
|
conn = sqlite3.connect(self.db_path)
|
||||||
c = conn.cursor()
|
c = conn.cursor()
|
||||||
c.execute("SELECT id, path, enabled FROM Directory")
|
c.execute("SELECT id, path, enabled FROM Directory")
|
||||||
db_directories = c.fetchall()
|
db_directories = c.fetchall()
|
||||||
@ -100,8 +121,63 @@ class LocalStorage:
|
|||||||
options.append(db_opt[0])
|
options.append(db_opt[0])
|
||||||
|
|
||||||
self.cached_dirs[directory.path] = directory
|
self.cached_dirs[directory.path] = directory
|
||||||
LocalStorage.cache_outdated = False
|
self.dir_cache_outdated = False
|
||||||
return self.cached_dirs
|
return self.cached_dirs
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return self.cached_dirs
|
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
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,5 +6,8 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
{% endblock body %}
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
Loading…
x
Reference in New Issue
Block a user