Skip to content

Documentation

authenticate_user(username, password)

Authenticate a user by verifying the provided password against the stored hash.

Parameters:

Name Type Description Default
username str

The username to authenticate.

required
password str

The plaintext password to verify.

required

Returns:

Name Type Description
bool

True if authentication is successful, False otherwise.

Source code in secure_file_storage/src/auth.py
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
def authenticate_user(username, password):
    """
    Authenticate a user by verifying the provided password against the stored hash.

    Args:
        username (str): The username to authenticate.
        password (str): The plaintext password to verify.

    Returns:
        bool: True if authentication is successful, False otherwise.
    """
    with sqlite3.connect('metadata.db') as conn:
        c = conn.cursor()
        c.execute('SELECT password_hash FROM users WHERE username=?', (username,))
        row = c.fetchone()
        if row:
            return bcrypt.checkpw(password.encode(), row[0])
        return False

create_files_table()

Create the 'files' table in the 'metadata.db' SQLite database if it does not exist.

Returns:

Type Description

None

Source code in secure_file_storage/src/auth.py
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
def create_files_table():
    """
    Create the 'files' table in the 'metadata.db' SQLite database if it does not exist.

    The table stores metadata about uploaded files with columns:
        - id (INTEGER): Auto-incremented primary key.
        - username (TEXT): Owner of the file.
        - filename (TEXT): Original file name.
        - stored_name (TEXT): Internal stored file name.
        - hash (TEXT): Hash of the encrypted file.
        - uploaded_at (TIMESTAMP): Timestamp of upload, defaults to current time.

    Returns:
        None
    """
    with sqlite3.connect('metadata.db') as conn:
        c = conn.cursor()
        c.execute('''
            CREATE TABLE IF NOT EXISTS files (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                username TEXT,
                filename TEXT,
                stored_name TEXT,
                hash TEXT,
                uploaded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        ''')
        conn.commit()

create_user_table()

Create the 'users' table in the 'metadata.db' SQLite database if it does not exist.

The table has two columns
  • username (TEXT): Primary key for user identification.
  • password_hash (TEXT): Stores the bcrypt hashed password.

Returns:

Type Description

None

Source code in secure_file_storage/src/auth.py
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
def create_user_table():
    """
    Create the 'users' table in the 'metadata.db' SQLite database if it does not exist.

    The table has two columns:
        - username (TEXT): Primary key for user identification.
        - password_hash (TEXT): Stores the bcrypt hashed password.

    Returns:
        None
    """
    with sqlite3.connect('metadata.db') as conn:
        c = conn.cursor()
        c.execute('''CREATE TABLE IF NOT EXISTS users (
                        username TEXT PRIMARY KEY,
                        password_hash TEXT)''')
        conn.commit()

register_user(username, password)

Register a new user with a hashed password in the database.

Parameters:

Name Type Description Default
username str

The username to register.

required
password str

The plaintext password for the user.

required

Returns:

Name Type Description
bool

True if registration succeeded, False if username already exists.

Source code in secure_file_storage/src/auth.py
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
def register_user(username, password):
    """
    Register a new user with a hashed password in the database.

    Args:
        username (str): The username to register.
        password (str): The plaintext password for the user.

    Returns:
        bool: True if registration succeeded, False if username already exists.
    """
    pw_hash = bcrypt.hashpw(password.encode(), bcrypt.gensalt())
    with sqlite3.connect('metadata.db') as conn:
        c = conn.cursor()
        c.execute('SELECT * FROM users WHERE username=?', (username,))
        if c.fetchone():
            return False
        c.execute(
            'INSERT INTO users (username, password_hash) VALUES (?, ?)', (username, pw_hash))
        conn.commit()
        return True

decrypt_file(encrypted_path, key)

Decrypt a previously encrypted file using the provided key.

The decrypted file is saved by removing the '.enc' suffix from the encrypted file's name.

Parameters:

Name Type Description Default
encrypted_path str

Path to the encrypted '.enc' file.

required
key bytes

Raw decryption key provided by the user.

required
Source code in secure_file_storage/src/encryption.py
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
def decrypt_file(encrypted_path, key):
    """
    Decrypt a previously encrypted file using the provided key.

    The decrypted file is saved by removing the '.enc' suffix from the encrypted file's name.

    Args:
        encrypted_path (str): Path to the encrypted '.enc' file.
        key (bytes): Raw decryption key provided by the user.
    """
    formatted_key = _format_key(key)
    fernet = Fernet(formatted_key)
    with open(encrypted_path, 'rb') as enc_file:
        encrypted = enc_file.read()
    decrypted = fernet.decrypt(encrypted)
    with open(encrypted_path.replace('.enc', ''), 'wb') as dec_file:
        dec_file.write(decrypted)

encrypt_file(file_path, key)

Encrypt the contents of a file using the provided key.

The encrypted output is saved to a new file with '.enc' appended to the original filename.

Parameters:

Name Type Description Default
file_path str

Path to the plaintext file to encrypt.

required
key bytes

Raw encryption key provided by the user.

required
Source code in secure_file_storage/src/encryption.py
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
def encrypt_file(file_path, key):
    """
    Encrypt the contents of a file using the provided key.

    The encrypted output is saved to a new file with '.enc' appended to the original filename.

    Args:
        file_path (str): Path to the plaintext file to encrypt.
        key (bytes): Raw encryption key provided by the user.
    """
    formatted_key = _format_key(key)
    fernet = Fernet(formatted_key)
    with open(file_path, 'rb') as file:
        original = file.read()
    encrypted = fernet.encrypt(original)
    with open(file_path + '.enc', 'wb') as encrypted_file:
        encrypted_file.write(encrypted)

generate_key()

Generate a secure random key for symmetric encryption.

Returns:

Name Type Description
bytes

A base64-encoded 32-byte key suitable for Fernet encryption.

Source code in secure_file_storage/src/encryption.py
 5
 6
 7
 8
 9
10
11
12
def generate_key():
    """
    Generate a secure random key for symmetric encryption.

    Returns:
        bytes: A base64-encoded 32-byte key suitable for Fernet encryption.
    """
    return Fernet.generate_key()

ensure_env()

Ensures that a .env file exists in the current directory.

If the .env file does not exist, this function creates it and writes a randomly generated SECRET_KEY and a COMPOSE_BAKE variable.

Returns:

Type Description

None

Source code in secure_file_storage/src/setup_env.py
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
def ensure_env():
    """
    Ensures that a .env file exists in the current directory.

    If the .env file does not exist, this function creates it and writes a randomly
    generated SECRET_KEY and a COMPOSE_BAKE variable.

    Returns:
        None
    """
    if not os.path.exists('.env'):
        with open('.env', 'w') as f:
            f.write(f'SECRET_KEY={token_urlsafe(32)}\n')
            f.write(f'COMPOSE_BAKE=true\n')

hash_file(path)

Calculate the SHA-256 hash of a file's contents.

Parameters:

Name Type Description Default
path str

Path to the file to hash.

required

Returns:

Name Type Description
str

Hexadecimal SHA-256 digest of the file.

Source code in secure_file_storage/src/utils.py
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
def hash_file(path):
    """
    Calculate the SHA-256 hash of a file's contents.

    Args:
        path (str): Path to the file to hash.

    Returns:
        str: Hexadecimal SHA-256 digest of the file.
    """
    sha256 = hashlib.sha256()
    with open(path, 'rb') as f:
        while chunk := f.read(8192):
            sha256.update(chunk)
    return sha256.hexdigest()

Main module for Secure File Storage application

main()

Entry point to start the Flask web application.

Prints a warning if not running inside a virtual environment, then runs the Flask server on host 0.0.0.0 and port 5000.

Source code in secure_file_storage/main.py
29
30
31
32
33
34
35
36
37
38
39
40
def main():
    """
    Entry point to start the Flask web application.

    Prints a warning if not running inside a virtual environment,
    then runs the Flask server on host 0.0.0.0 and port 5000.
    """
    if sys.prefix == sys.base_prefix:
        print("Warning: It looks like you're not running inside a virtual environment.")

    if "gunicorn" not in os.environ.get("SERVER_SOFTWARE", ""):
        app.run(debug=False, host="0.0.0.0", port=5000)

Contains the version of the package

get_version()

Retrieve the current project version from 'pyproject.toml'.

Returns:

Name Type Description
str

Version string from the 'project.version' field.

Source code in secure_file_storage/version.py
 6
 7
 8
 9
10
11
12
13
14
def get_version():
    """
    Retrieve the current project version from 'pyproject.toml'.

    Returns:
        str: Version string from the 'project.version' field.
    """
    pyproject = toml.load("pyproject.toml")
    return pyproject['project']['version']

remove_testuser()

Remove the user with username 'testuser' from the database.

This function is useful for cleaning up test data before running tests.

Source code in tests/test_auth.py
 5
 6
 7
 8
 9
10
11
12
13
14
def remove_testuser():
    """
    Remove the user with username 'testuser' from the database.

    This function is useful for cleaning up test data before running tests.
    """
    with sqlite3.connect('metadata.db') as conn:
        c = conn.cursor()
        c.execute('DELETE FROM users WHERE username = ?', ('testuser',))
        conn.commit()

test_register_and_authenticate()

Test the user registration and authentication workflow.

  • Creates the users table if it doesn't exist.
  • Removes any existing 'testuser' entry.
  • Registers a new user 'testuser' with password 'testpass'.
  • Asserts that the user can be authenticated successfully.
Source code in tests/test_auth.py
17
18
19
20
21
22
23
24
25
26
27
28
29
def test_register_and_authenticate():
    """
    Test the user registration and authentication workflow.

    - Creates the users table if it doesn't exist.
    - Removes any existing 'testuser' entry.
    - Registers a new user 'testuser' with password 'testpass'.
    - Asserts that the user can be authenticated successfully.
    """
    auth.create_user_table()
    remove_testuser()
    auth.register_user('testuser', 'testpass')
    assert auth.authenticate_user('testuser', 'testpass') is True

test_encrypt_decrypt(tmp_path)

Test the file encryption and decryption process.

  • Creates a temporary file with known content.
  • Encrypts the file with a generated key.
  • Deletes the original file.
  • Decrypts the encrypted file.
  • Checks that the decrypted content matches the original.

Parameters:

Name Type Description Default
tmp_path Path

Pytest fixture providing a temporary directory.

required
Source code in tests/test_encryption.py
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def test_encrypt_decrypt(tmp_path):
    """
    Test the file encryption and decryption process.

    - Creates a temporary file with known content.
    - Encrypts the file with a generated key.
    - Deletes the original file.
    - Decrypts the encrypted file.
    - Checks that the decrypted content matches the original.

    Args:
        tmp_path (pathlib.Path): Pytest fixture providing a temporary directory.
    """
    key = encryption.generate_key()
    test_file = tmp_path / "test.txt"
    test_file.write_text("Secret content")

    encryption.encrypt_file(str(test_file), key)
    os.remove(test_file)
    encryption.decrypt_file(str(test_file) + '.enc', key)

    with open(test_file, 'r') as f:
        assert f.read() == "Secret content"