Fix pre-existing bugs: PBKDF2 import, test function names, salt truncation
- Fix cryptography import: PBKDF2 -> PBKDF2HMAC - Fix test_security.py: use CredentialEncryption class instead of non-existent functions - Fix salt generation to include user_id in differentiation - Remove unavailable poplib3 dependency - All 53 tests pass Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com> Agent-Logs-Url: https://github.com/christianlouis/pop_puller_to_gmail/sessions/de3ef930-a980-4958-8a9d-a2c802918e81
This commit is contained in:
@@ -8,7 +8,7 @@ from jose import JWTError, jwt
|
|||||||
from passlib.context import CryptContext
|
from passlib.context import CryptContext
|
||||||
from cryptography.fernet import Fernet
|
from cryptography.fernet import Fernet
|
||||||
from cryptography.hazmat.primitives import hashes
|
from cryptography.hazmat.primitives import hashes
|
||||||
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2
|
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
||||||
import base64
|
import base64
|
||||||
|
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
@@ -84,13 +84,13 @@ class CredentialEncryption:
|
|||||||
# Generate salt - in production, this should be unique per user
|
# Generate salt - in production, this should be unique per user
|
||||||
if user_id is not None:
|
if user_id is not None:
|
||||||
# Per-user salt for production
|
# Per-user salt for production
|
||||||
salt = f'pop3_forwarder_user_{user_id}'.encode('utf-8')[:16].ljust(16, b'0')
|
salt = f'pop3fwd_usr_{user_id}'.encode('utf-8')[:16].ljust(16, b'\x00')
|
||||||
else:
|
else:
|
||||||
# Default salt for system-wide operations (use with caution)
|
# Default salt for system-wide operations (use with caution)
|
||||||
salt = b'pop3_forwarder_0'
|
salt = b'pop3_forwarder_0'
|
||||||
|
|
||||||
# Derive a proper Fernet key from the provided key
|
# Derive a proper Fernet key from the provided key
|
||||||
kdf = PBKDF2(
|
kdf = PBKDF2HMAC(
|
||||||
algorithm=hashes.SHA256(),
|
algorithm=hashes.SHA256(),
|
||||||
length=32,
|
length=32,
|
||||||
salt=salt,
|
salt=salt,
|
||||||
|
|||||||
@@ -56,6 +56,3 @@ faker==22.6.0
|
|||||||
python-dotenv==1.0.0
|
python-dotenv==1.0.0
|
||||||
schedule==1.2.0
|
schedule==1.2.0
|
||||||
tenacity==8.2.3
|
tenacity==8.2.3
|
||||||
|
|
||||||
# Legacy support (for migration)
|
|
||||||
poplib3==0.0.4
|
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ from app.core.security import (
|
|||||||
get_password_hash,
|
get_password_hash,
|
||||||
verify_password,
|
verify_password,
|
||||||
create_access_token,
|
create_access_token,
|
||||||
encrypt_password,
|
CredentialEncryption,
|
||||||
decrypt_password,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -60,7 +59,8 @@ class TestEncryption:
|
|||||||
password = "mailpassword123"
|
password = "mailpassword123"
|
||||||
user_id = 1
|
user_id = 1
|
||||||
|
|
||||||
encrypted = encrypt_password(password, user_id)
|
encryptor = CredentialEncryption(user_id=user_id)
|
||||||
|
encrypted = encryptor.encrypt(password)
|
||||||
|
|
||||||
assert encrypted != password
|
assert encrypted != password
|
||||||
assert len(encrypted) > 50
|
assert len(encrypted) > 50
|
||||||
@@ -70,8 +70,9 @@ class TestEncryption:
|
|||||||
password = "mailpassword123"
|
password = "mailpassword123"
|
||||||
user_id = 1
|
user_id = 1
|
||||||
|
|
||||||
encrypted = encrypt_password(password, user_id)
|
encryptor = CredentialEncryption(user_id=user_id)
|
||||||
decrypted = decrypt_password(encrypted, user_id)
|
encrypted = encryptor.encrypt(password)
|
||||||
|
decrypted = encryptor.decrypt(encrypted)
|
||||||
|
|
||||||
assert decrypted == password
|
assert decrypted == password
|
||||||
|
|
||||||
@@ -81,15 +82,18 @@ class TestEncryption:
|
|||||||
user_id_1 = 1
|
user_id_1 = 1
|
||||||
user_id_2 = 2
|
user_id_2 = 2
|
||||||
|
|
||||||
encrypted_1 = encrypt_password(password, user_id_1)
|
encryptor_1 = CredentialEncryption(user_id=user_id_1)
|
||||||
encrypted_2 = encrypt_password(password, user_id_2)
|
encryptor_2 = CredentialEncryption(user_id=user_id_2)
|
||||||
|
|
||||||
|
encrypted_1 = encryptor_1.encrypt(password)
|
||||||
|
encrypted_2 = encryptor_2.encrypt(password)
|
||||||
|
|
||||||
# Different users should produce different encrypted values
|
# Different users should produce different encrypted values
|
||||||
assert encrypted_1 != encrypted_2
|
assert encrypted_1 != encrypted_2
|
||||||
|
|
||||||
# But decryption should work correctly for each
|
# But decryption should work correctly for each
|
||||||
assert decrypt_password(encrypted_1, user_id_1) == password
|
assert encryptor_1.decrypt(encrypted_1) == password
|
||||||
assert decrypt_password(encrypted_2, user_id_2) == password
|
assert encryptor_2.decrypt(encrypted_2) == password
|
||||||
|
|
||||||
def test_decrypt_with_wrong_user_id_fails(self):
|
def test_decrypt_with_wrong_user_id_fails(self):
|
||||||
"""Test that decryption fails with wrong user ID"""
|
"""Test that decryption fails with wrong user ID"""
|
||||||
@@ -97,7 +101,10 @@ class TestEncryption:
|
|||||||
user_id = 1
|
user_id = 1
|
||||||
wrong_user_id = 2
|
wrong_user_id = 2
|
||||||
|
|
||||||
encrypted = encrypt_password(password, user_id)
|
encryptor = CredentialEncryption(user_id=user_id)
|
||||||
|
wrong_encryptor = CredentialEncryption(user_id=wrong_user_id)
|
||||||
|
|
||||||
|
encrypted = encryptor.encrypt(password)
|
||||||
|
|
||||||
with pytest.raises(Exception):
|
with pytest.raises(Exception):
|
||||||
decrypt_password(encrypted, wrong_user_id)
|
wrong_encryptor.decrypt(encrypted)
|
||||||
|
|||||||
Reference in New Issue
Block a user