Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion testproject/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
ALLOWED_HOSTS = env.list("ALLOWED_HOSTS", default=["*"])
CORS_ORIGIN_ALLOW_ALL = env.bool("CORS_ORIGIN_ALLOW_ALL", default=False)
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
STATIC_ROOT= os.path.join(BASE_DIR, 'static/')
STATIC_ROOT = os.path.join(BASE_DIR, "static/")

INSTALLED_APPS = [
"django.contrib.admin",
Expand Down
6 changes: 3 additions & 3 deletions testproject/tests/test_add_mfa.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import pytest

from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AbstractUser

Expand All @@ -10,6 +9,7 @@
from tests.utils import TrenchAPIClient
from trench.command.create_otp import create_otp_command
from trench.command.create_secret import create_secret_command
from trench.settings import trench_settings


User: AbstractUser = get_user_model()
Expand Down Expand Up @@ -37,7 +37,7 @@ def test_should_fail_on_add_user_mfa_with_invalid_source_field(active_user: User
client = TrenchAPIClient()
client.authenticate(user=active_user)
secret = create_secret_command()
settings.TRENCH_AUTH["MFA_METHODS"]["email"]["SOURCE_FIELD"] = "email_test"
trench_settings.mfa_methods["email"]["source_field"] = "email_test"

response = client.post(
path="/auth/email/activate/",
Expand All @@ -53,7 +53,7 @@ def test_should_fail_on_add_user_mfa_with_invalid_source_field(active_user: User
response.data.get("error")
== "Field name `email_test` is not valid for model `User`."
)
settings.TRENCH_AUTH["MFA_METHODS"]["email"]["SOURCE_FIELD"] = "email"
trench_settings.mfa_methods["email"]["source_field"] = "email"


@flaky
Expand Down
26 changes: 13 additions & 13 deletions testproject/tests/test_backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from trench.backends.twilio import TwilioMessageDispatcher
from trench.backends.yubikey import YubiKeyMessageDispatcher
from trench.exceptions import MissingConfigurationError
from trench.settings import trench_settings


User = get_user_model()
Expand All @@ -16,7 +17,7 @@
@pytest.mark.django_db
def test_twilio_backend_without_credentials(active_user_with_twilio_otp, settings):
auth_method = active_user_with_twilio_otp.mfa_methods.get(name="sms_twilio")
conf = settings.TRENCH_AUTH["MFA_METHODS"]["sms_twilio"]
conf = trench_settings.mfa_methods["sms_twilio"]
response = TwilioMessageDispatcher(
mfa_method=auth_method, config=conf
).dispatch_message()
Expand All @@ -26,20 +27,19 @@ def test_twilio_backend_without_credentials(active_user_with_twilio_otp, setting
@pytest.mark.django_db
def test_sms_api_backend_without_credentials(active_user_with_sms_otp, settings):
auth_method = active_user_with_sms_otp.mfa_methods.get(name="sms_api")
conf = settings.TRENCH_AUTH["MFA_METHODS"]["sms_api"]
conf = trench_settings.mfa_methods["sms_api"]
response = SMSAPIMessageDispatcher(
mfa_method=auth_method, config=conf
).dispatch_message()
print(response.data.get("details"))
assert response.data.get("details") == "Authorization failed"


@pytest.mark.django_db
def test_sms_api_backend_with_wrong_credentials(active_user_with_sms_otp, settings):
auth_method = active_user_with_sms_otp.mfa_methods.get(name="sms_api")
conf = settings.TRENCH_AUTH["MFA_METHODS"]["sms_api"]
settings.TRENCH_AUTH["MFA_METHODS"]["sms_api"][
"SMSAPI_ACCESS_TOKEN"
] = "wrong-token"
conf = trench_settings.mfa_methods["sms_api"]
trench_settings.mfa_methods["sms_api"]["smsapi_access_token"] = "wrong-token"
response = SMSAPIMessageDispatcher(
mfa_method=auth_method, config=conf
).dispatch_message()
Expand All @@ -49,20 +49,20 @@ def test_sms_api_backend_with_wrong_credentials(active_user_with_sms_otp, settin
@pytest.mark.django_db
def test_sms_backend_misconfiguration_error(active_user_with_twilio_otp, settings):
auth_method = active_user_with_twilio_otp.mfa_methods.get(name="sms_twilio")
conf = settings.TRENCH_AUTH["MFA_METHODS"]["sms_twilio"]
current_source = settings.TRENCH_AUTH["MFA_METHODS"]["sms_twilio"]["SOURCE_FIELD"]
settings.TRENCH_AUTH["MFA_METHODS"]["sms_twilio"]["SOURCE_FIELD"] = "invalid.source"
conf = trench_settings.mfa_methods["sms_twilio"]
current_source = trench_settings.mfa_methods["sms_twilio"]["source_field"]
trench_settings.mfa_methods["sms_twilio"]["source_field"] = "invalid.source"
with pytest.raises(MissingConfigurationError):
SMSAPIMessageDispatcher(mfa_method=auth_method, config=conf).dispatch_message()
settings.TRENCH_AUTH["MFA_METHODS"]["sms_twilio"]["SOURCE_FIELD"] = current_source
trench_settings.mfa_methods["sms_twilio"]["source_field"] = current_source


@pytest.mark.django_db
def test_application_backend_generating_url_successfully(
active_user_with_application_otp, settings
):
auth_method = active_user_with_application_otp.mfa_methods.get(name="app")
conf = settings.TRENCH_AUTH["MFA_METHODS"]["app"]
conf = trench_settings.mfa_methods["app"]
response = ApplicationMessageDispatcher(
mfa_method=auth_method, config=conf
).dispatch_message()
Expand All @@ -75,7 +75,7 @@ def test_application_backend_generating_url_successfully(
@pytest.mark.django_db
def test_yubikey_backend(active_user_with_many_otp_methods, settings):
user, code = active_user_with_many_otp_methods
config = settings.TRENCH_AUTH["MFA_METHODS"]["yubi"]
config = trench_settings.mfa_methods["yubi"]
auth_method = user.mfa_methods.get(name="yubi")
dispatcher = YubiKeyMessageDispatcher(mfa_method=auth_method, config=config)
dispatcher.confirm_activation(code)
Expand All @@ -84,7 +84,7 @@ def test_yubikey_backend(active_user_with_many_otp_methods, settings):
@pytest.mark.django_db
def test_sms_aws_backend_without_credentials(active_user_with_sms_aws_otp, settings):
auth_method = active_user_with_sms_aws_otp.mfa_methods.get(name="sms_aws")
conf = settings.TRENCH_AUTH["MFA_METHODS"]["sms_aws"]
conf = trench_settings.mfa_methods["sms_aws"]
response = AWSMessageDispatcher(
mfa_method=auth_method, config=conf
).dispatch_message()
Expand Down
13 changes: 7 additions & 6 deletions testproject/tests/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
remove_backup_code_command,
)
from trench.exceptions import MFAMethodDoesNotExistError, MFANotEnabledError
from trench.settings import DEFAULTS, TrenchAPISettings
from trench.settings import TrenchSettingsParser
from trench.utils import get_mfa_model


Expand All @@ -23,13 +23,14 @@ def test_remove_backup_code_from_non_existing_method(


@pytest.mark.django_db
def test_remove_not_encrypted_code(active_user_with_non_encrypted_backup_codes):
def test_remove_not_encrypted_code(
active_user_with_non_encrypted_backup_codes, settings
):
user, codes = active_user_with_non_encrypted_backup_codes
settings = TrenchAPISettings(
user_settings={"ENCRYPT_BACKUP_CODES": False}, defaults=DEFAULTS
)
settings.TRENCH_AUTH["ENCRYPT_BACKUP_CODES"] = False
trench_settings = TrenchSettingsParser(user_settings=settings).get_settings
remove_backup_code_command = RemoveBackupCodeCommand(
mfa_model=get_mfa_model(), settings=settings
mfa_model=get_mfa_model(), settings=trench_settings
).execute
code = next(iter(codes))
remove_backup_code_command(
Expand Down
12 changes: 6 additions & 6 deletions testproject/tests/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@
ProtectedActionValidator,
RequestBodyValidator,
)
from trench.settings import DEFAULTS, TrenchAPISettings
from trench.settings import TrenchSettingsParser


def test_method_handler_missing_error():
settings = TrenchAPISettings(
user_settings={"MFA_METHODS": {"method_without_handler": {}}}, defaults=DEFAULTS
)
def test_method_handler_missing_error(settings):
settings.TRENCH_AUTH["MFA_METHODS"]["method_without_handler"] = {}

with pytest.raises(MethodHandlerMissingError):
assert settings.MFA_METHODS["method_without_handler"] is None
trench_settings = TrenchSettingsParser(user_settings=settings).get_settings
assert trench_settings.mfa_methods["method_without_handler"] == {}


def test_code_missing_error():
Expand Down
12 changes: 5 additions & 7 deletions testproject/tests/test_second_step_authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
)
from trench.exceptions import MFAMethodDoesNotExistError
from trench.models import MFAMethod
from trench.settings import trench_settings


User = get_user_model()
Expand All @@ -43,10 +44,8 @@ def test_mfa_model(active_user_with_email_otp):

@pytest.mark.django_db
def test_custom_validity_period(active_user_with_email_otp, settings):
ORIGINAL_VALIDITY_PERIOD = settings.TRENCH_AUTH["MFA_METHODS"]["email"][
"VALIDITY_PERIOD"
]
settings.TRENCH_AUTH["MFA_METHODS"]["email"]["VALIDITY_PERIOD"] = 3
ORIGINAL_VALIDITY_PERIOD = trench_settings.mfa_methods["email"]["validity_period"]
trench_settings.mfa_methods["email"]["validity_period"] = 3

mfa_method = active_user_with_email_otp.mfa_methods.first()
client = TrenchAPIClient()
Expand All @@ -69,9 +68,7 @@ def test_custom_validity_period(active_user_with_email_otp, settings):
)
assert response_second_step.status_code == HTTP_200_OK

settings.TRENCH_AUTH["MFA_METHODS"]["email"][
"VALIDITY_PERIOD"
] = ORIGINAL_VALIDITY_PERIOD
trench_settings.mfa_methods["email"]["VALIDITY_PERIOD"] = ORIGINAL_VALIDITY_PERIOD


@flaky
Expand Down Expand Up @@ -601,6 +598,7 @@ def test_yubikey(active_user_with_yubi, offline_yubikey):
response = client.authenticate_multi_factor(
mfa_method=yubikey_method, user=active_user_with_yubi
)
print(response)
assert response.status_code == HTTP_200_OK


Expand Down
2 changes: 1 addition & 1 deletion trench/backends/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ def dispatch_message(self) -> DispatchResponse:
def _create_qr_link(self, user: User) -> str:
return self._get_otp().provisioning_uri(
getattr(user, User.USERNAME_FIELD),
trench_settings.APPLICATION_ISSUER_NAME,
trench_settings.application_issuer_name,
)
8 changes: 4 additions & 4 deletions trench/backends/aws.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
from django.utils.translation import gettext_lazy as _

import logging
import boto3
import botocore.exceptions
import logging
from botocore.exceptions import ClientError, EndpointConnectionError

from trench.backends.base import AbstractMessageDispatcher
from trench.responses import (
DispatchResponse,
FailedDispatchResponse,
SuccessfulDispatchResponse,
)
from trench.settings import AWS_ACCESS_KEY, AWS_SECRET_KEY, AWS_REGION
from botocore.exceptions import ClientError, EndpointConnectionError
from trench.settings import AWS_ACCESS_KEY, AWS_REGION, AWS_SECRET_KEY


class AWSMessageDispatcher(AbstractMessageDispatcher):
_SMS_BODY = _("Your verification code is: ")
Expand Down
2 changes: 1 addition & 1 deletion trench/backends/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,5 @@ def _get_otp(self) -> TOTP:

def _get_valid_window(self) -> int:
return self._config.get(
VALIDITY_PERIOD, trench_settings.DEFAULT_VALIDITY_PERIOD
VALIDITY_PERIOD, trench_settings.default_validity_period
)
2 changes: 1 addition & 1 deletion trench/backends/basic_mail.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def dispatch_message(self) -> DispatchResponse:
email_html_template = self._config[EMAIL_HTML_TEMPLATE]
try:
send_mail(
subject=self._config.get(EMAIL_SUBJECT),
subject=self._config[EMAIL_SUBJECT],
message=get_template(email_plain_template).render(context),
html_message=get_template(email_html_template).render(context),
from_email=settings.DEFAULT_FROM_EMAIL,
Expand Down
4 changes: 3 additions & 1 deletion trench/backends/provider.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from rest_framework.settings import perform_import

from trench.backends.base import AbstractMessageDispatcher
from trench.models import MFAMethod
from trench.query.get_mfa_config_by_name import get_mfa_config_by_name_query
Expand All @@ -6,5 +8,5 @@

def get_mfa_handler(mfa_method: MFAMethod) -> AbstractMessageDispatcher:
conf = get_mfa_config_by_name_query(name=mfa_method.name)
dispatcher = conf[HANDLER]
dispatcher = perform_import(conf[HANDLER], HANDLER)
return dispatcher(mfa_method=mfa_method, config=conf)
7 changes: 4 additions & 3 deletions trench/command/create_secret.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
from pyotp import random_base32
from typing import Callable

from trench.settings import TrenchAPISettings, trench_settings
from trench.domain.models import TrenchConfig
from trench.settings import trench_settings


class CreateSecretCommand:
def __init__(self, generator: Callable, settings: TrenchAPISettings) -> None:
def __init__(self, generator: Callable, settings: TrenchConfig) -> None:
self._generator = generator
self._settings = settings

def execute(self) -> str:
return self._generator(length=self._settings.SECRET_KEY_LENGTH)
return self._generator(length=self._settings.secret_key_length)


create_secret_command = CreateSecretCommand(
Expand Down
17 changes: 5 additions & 12 deletions trench/command/generate_backup_codes.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,19 @@ def __init__(self, random_string_generator: Callable) -> None:

def execute(
self,
quantity: int = trench_settings.BACKUP_CODES_QUANTITY,
length: int = trench_settings.BACKUP_CODES_LENGTH,
allowed_chars: str = trench_settings.BACKUP_CODES_CHARACTERS,
) -> Set[str]:
"""
Generates random encrypted backup codes.

:param quantity: How many codes should be generated
:type quantity: int
:param length: How long codes should be
:type length: int
:param allowed_chars: Characters to create backup codes from
:type allowed_chars: str

:returns: Encrypted backup codes
:rtype: set[str]
"""
return {
self._random_string_generator(length, allowed_chars)
for _ in range(quantity)
self._random_string_generator(
trench_settings.backup_codes_length,
trench_settings.backup_codes_characters,
)
for _ in range(trench_settings.backup_codes_quantity)
}


Expand Down
7 changes: 4 additions & 3 deletions trench/command/remove_backup_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@

from typing import Any, Set, Type

from trench.domain.models import TrenchConfig
from trench.exceptions import InvalidCodeError, MFAMethodDoesNotExistError
from trench.models import MFAMethod
from trench.settings import TrenchAPISettings, trench_settings
from trench.settings import trench_settings
from trench.utils import get_mfa_model


class RemoveBackupCodeCommand:
def __init__(self, mfa_model: Type[MFAMethod], settings: TrenchAPISettings) -> None:
def __init__(self, mfa_model: Type[MFAMethod], settings: TrenchConfig) -> None:
self._mfa_model = mfa_model
self._settings = settings

Expand All @@ -34,7 +35,7 @@ def execute(self, user_id: Any, method_name: str, code: str) -> None:
)

def _remove_code_from_set(self, backup_codes: Set[str], code: str) -> Set[str]:
if not self._settings.ENCRYPT_BACKUP_CODES:
if not self._settings.encrypt_backup_codes:
backup_codes.remove(code)
return backup_codes
for backup_code in backup_codes:
Expand Down
2 changes: 1 addition & 1 deletion trench/command/replace_mfa_method_backup_codes.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def execute(self, user_id: int, name: str) -> Set[str]:

regenerate_backup_codes_for_mfa_method_command = (
RegenerateBackupCodesForMFAMethodCommand(
requires_encryption=trench_settings.ENCRYPT_BACKUP_CODES,
requires_encryption=trench_settings.encrypt_backup_codes,
mfa_model=get_mfa_model(),
code_hasher=make_password,
codes_generator=generate_backup_codes_command,
Expand Down
7 changes: 4 additions & 3 deletions trench/command/validate_backup_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@

from typing import Iterable, Optional

from trench.settings import TrenchAPISettings, trench_settings
from trench.domain.models import TrenchConfig
from trench.settings import trench_settings


class ValidateBackupCodeCommand:
def __init__(self, settings: TrenchAPISettings) -> None:
def __init__(self, settings: TrenchConfig) -> None:
self._settings = settings

def execute(self, value: str, backup_codes: Iterable) -> Optional[str]:
if not self._settings.ENCRYPT_BACKUP_CODES:
if not self._settings.encrypt_backup_codes:
return value if value in backup_codes else None
for backup_code in backup_codes:
if check_password(value, backup_code):
Expand Down
Empty file added trench/domain/__init__.py
Empty file.
Loading