mirror of
				https://github.com/simon987/Simple-Incremental-Search-Tool.git
				synced 2025-11-03 08:46:54 +00:00 
			
		
		
		
	Added local storage for users
This commit is contained in:
		
							parent
							
								
									fec23d40d9
								
							
						
					
					
						commit
						de0a835ecd
					
				@ -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"
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -1,2 +1,4 @@
 | 
			
		||||
PIL
 | 
			
		||||
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 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
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										108
									
								
								storage.py
									
									
									
									
									
								
							
							
						
						
									
										108
									
								
								storage.py
									
									
									
									
									
								
							@ -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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -6,5 +6,8 @@
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
 | 
			
		||||
{% block body %}
 | 
			
		||||
{% endblock body %}
 | 
			
		||||
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user