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
29 changes: 28 additions & 1 deletion docs/backends.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Using Twilio
"sms_twilio": {
VERBOSE_NAME: _("sms_twilio"),
VALIDITY_PERIOD: 30,
HANDLER: "trench.backends.twilio.TwilioMessageDispatcher",
HANDLER: "trench.backends.twilio.TwilioSMSMessageDispatcher",
SOURCE_FIELD: "phone_number",
TWILIO_VERIFIED_FROM_NUMBER: "+48 123 456 789",
},
Expand Down Expand Up @@ -86,6 +86,33 @@ Using SMS API
:SMSAPI_ACCESS_TOKEN: Access token obtained from `SMS API`_
:SMSAPI_FROM_NUMBER: This will be used as the sender's phone number.

Phone call
**********

| Phone call backend make call with `Twilio`_ . Credentials can be set in method's specific settings.

Using Twilio
------------

| If you are using Twilio service for calling then you need to set ``TWILIO_ACCOUNT_SID`` and ``TWILIO_AUTH_TOKEN`` environment variables for Twilio API client to be used as credentials.

.. code-block:: python

TRENCH_AUTH = {
"MFA_METHODS": {
"call_twilio": {
VERBOSE_NAME: _("call_twilio"),
VALIDITY_PERIOD: 30,
HANDLER: "trench.backends.twilio.TwilioCallMessageDispatcher",
SOURCE_FIELD: "phone_number",
TWILIO_VERIFIED_FROM_NUMBER: "+48 123 456 789",
},
},
}

:SOURCE_FIELD: Defines the field name in your ``AUTH_USER_MODEL`` to be looked up and used as field containing the phone number of the recipient of the OTP code.
:TWILIO_VERIFIED_FROM_NUMBER: This will be used as the sender's phone number. Note: this number must be verified in the Twilio's client panel.

Authentication apps
*******************
| This backend returns OTP based QR link to be scanned by apps like Gooogle Authenticator and Authy.
Expand Down
24 changes: 18 additions & 6 deletions testproject/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import environ
import os

from trench.settings import MfaMethods


root = environ.Path(__file__) - 1
env = environ.Env()
Expand Down Expand Up @@ -123,25 +125,35 @@
"BACKUP_CODES_QUANTITY": 8,
"DEFAULT_VALIDITY_PERIOD": 60,
"MFA_METHODS": {
"sms_twilio": {
MfaMethods.CALL_TWILIO.value: {
"VERBOSE_NAME": "call",
"VALIDITY_PERIOD": 60,
"HANDLER": "trench.backends.twilio.TwilioCallMessageDispatcher",
"SOURCE_FIELD": "phone_number",
"TWILIO_VERIFIED_FROM_NUMBER": env(
"TWILIO_VERIFIED_FROM_NUMBER",
default="",
),
},
MfaMethods.SMS_TWILIO.value: {
"VERBOSE_NAME": "sms",
"VALIDITY_PERIOD": 60,
"HANDLER": "trench.backends.twilio.TwilioMessageDispatcher",
"HANDLER": "trench.backends.twilio.TwilioSMSMessageDispatcher",
"SOURCE_FIELD": "phone_number",
"TWILIO_VERIFIED_FROM_NUMBER": env(
"TWILIO_VERIFIED_FROM_NUMBER",
default="",
),
},
"sms_api": {
MfaMethods.SMS_API.value: {
"VERBOSE_NAME": "sms",
"VALIDITY_PERIOD": 60,
"HANDLER": "trench.backends.sms_api.SMSAPIMessageDispatcher",
"SOURCE_FIELD": "phone_number",
"SMSAPI_ACCESS_TOKEN": "token",
"SMSAPI_FROM_NUMBER": "123 456 789",
},
"email": {
MfaMethods.EMAIL.value: {
"VERBOSE_NAME": "email",
"VALIDITY_PERIOD": 60,
"HANDLER": "trench.backends.basic_mail.SendMailMessageDispatcher",
Expand All @@ -150,13 +162,13 @@
"EMAIL_PLAIN_TEMPLATE": "trench/backends/email/code.txt",
"EMAIL_HTML_TEMPLATE": "trench/backends/email/code.html",
},
"app": {
MfaMethods.APP.value: {
"VERBOSE_NAME": "app",
"VALIDITY_PERIOD": 60,
"USES_THIRD_PARTY_CLIENT": True,
"HANDLER": "trench.backends.application.ApplicationMessageDispatcher",
},
"yubi": {
MfaMethods.YUBI.value: {
"VERBOSE_NAME": "yubi",
"HANDLER": "trench.backends.yubikey.YubiKeyMessageDispatcher",
"YUBICLOUD_CLIENT_ID": env("YUBICLOUD_CLIENT_ID", default=""),
Expand Down
75 changes: 44 additions & 31 deletions testproject/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from trench.command.create_secret import create_secret_command
from trench.command.generate_backup_codes import generate_backup_codes_command
from trench.models import MFAMethod as MFAMethodModel
from trench.settings import MfaMethods


User = get_user_model()
Expand Down Expand Up @@ -49,19 +50,17 @@ def mfa_method_creator(

@pytest.fixture()
def active_user_with_application_otp() -> UserModel:
user, created = User.objects.get_or_create(
username="imhotep", email="imhotep@pyramids.eg"
)
if created:
user.set_password("secretkey")
user.is_active = True
user.save()
mfa_method_creator(user=user, method_name="app")
user = active_user_with_email(MfaMethods.APP.value)
return user


@pytest.fixture()
def active_user_with_email_otp() -> UserModel:
user = active_user_with_email(MfaMethods.EMAIL.value)
return user


def active_user_with_email(method_name: str) -> UserModel:
user, created = User.objects.get_or_create(
username="imhotep",
email="imhotep@pyramids.eg",
Expand All @@ -70,7 +69,7 @@ def active_user_with_email_otp() -> UserModel:
user.set_password("secretkey"),
user.is_active = True
user.save()
mfa_method_creator(user=user, method_name="email")
mfa_method_creator(user=user, method_name=method_name)
return user


Expand All @@ -83,27 +82,31 @@ def deactivated_user_with_email_otp(active_user_with_email_otp) -> UserModel:

@pytest.fixture()
def active_user_with_sms_otp() -> UserModel:
user, created = User.objects.get_or_create(
username="imhotep", email="imhotep@pyramids.eg", phone_number="555-555-555"
)
if created:
user.set_password("secretkey"),
user.is_active = True
user.save()
mfa_method_creator(user=user, method_name="sms_api")
user = active_user_with_phone_number(MfaMethods.SMS_API.value)
return user


@pytest.fixture()
def active_user_with_twilio_otp() -> UserModel:
def active_user_with_twilio_sms_otp() -> UserModel:
user = active_user_with_phone_number(MfaMethods.SMS_TWILIO.value)
return user


@pytest.fixture()
def active_user_with_twilio_call_otp() -> UserModel:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code inside this function and here: active_user_with_twilio_sms_otp are the same instead of method_name. maybe extracting it to another function will avoid DRY?

user = active_user_with_phone_number(MfaMethods.CALL_TWILIO.value)
return user


def active_user_with_phone_number(method_name: str) -> UserModel:
user, created = User.objects.get_or_create(
username="imhotep", email="imhotep@pyramids.eg", phone_number="555-555-555"
)
if created:
user.set_password("secretkey"),
user.is_active = True
user.save()
mfa_method_creator(user=user, method_name="sms_twilio")
mfa_method_creator(user=user, method_name=method_name)
return user


Expand All @@ -117,12 +120,18 @@ def active_user_with_email_and_inactive_other_methods_otp() -> UserModel:
user.set_password("secretkey"),
user.is_active = True
user.save()
mfa_method_creator(user=user, method_name="email")
mfa_method_creator(user=user, method_name=MfaMethods.EMAIL.value)
mfa_method_creator(
user=user, method_name="sms_twilio", is_primary=False, is_active=False
user=user,
method_name=MfaMethods.SMS_TWILIO.value,
is_primary=False,
is_active=False,
)
mfa_method_creator(
user=user, method_name="app", is_primary=False, is_active=False
user=user,
method_name=MfaMethods.APP.value,
is_primary=False,
is_active=False,
)
return user

Expand All @@ -137,12 +146,12 @@ def active_user_with_email_and_active_other_methods_otp() -> UserModel:
user.set_password("secretkey"),
user.is_active = True
user.save()
mfa_method_creator(user=user, method_name="email")
mfa_method_creator(user=user, method_name=MfaMethods.EMAIL.value)
mfa_method_creator(
user=user, method_name="sms_twilio", is_primary=False
user=user, method_name=MfaMethods.SMS_TWILIO.value, is_primary=False
)
mfa_method_creator(
user=user, method_name="app", is_primary=False
user=user, method_name=MfaMethods.APP.value, is_primary=False
)
return user

Expand Down Expand Up @@ -171,7 +180,9 @@ def active_user_with_backup_codes(encrypt_codes: bool) -> Tuple[UserModel, Set[s
user.is_active = True
user.save()
mfa_method_creator(
user=user, method_name="email", _backup_codes=serialized_backup_codes
user=user,
method_name=MfaMethods.EMAIL.value,
_backup_codes=serialized_backup_codes,
)
return user, backup_codes

Expand All @@ -192,23 +203,25 @@ def active_user_with_many_otp_methods() -> Tuple[UserModel, str]:
user.is_active = True
user.save()
mfa_method_creator(
user=user, method_name="email", _backup_codes=encrypted_backup_codes
user=user,
method_name=MfaMethods.EMAIL.value,
_backup_codes=encrypted_backup_codes,
)
mfa_method_creator(
user=user,
method_name="sms_twilio",
method_name=MfaMethods.SMS_TWILIO.value,
is_primary=False,
_backup_codes=encrypted_backup_codes,
)
mfa_method_creator(
user=user,
method_name="app",
method_name=MfaMethods.APP.value,
is_primary=False,
_backup_codes=encrypted_backup_codes,
)
mfa_method_creator(
user=user,
method_name="yubi",
method_name=MfaMethods.YUBI.value,
is_primary=False,
_backup_codes=encrypted_backup_codes,
)
Expand Down Expand Up @@ -272,7 +285,7 @@ def active_user_with_yubi() -> UserModel:
user.save()
mfa_method_creator(
user=user,
method_name="yubi",
method_name=MfaMethods.YUBI.value,
secret=FAKE_YUBI_SECRET,
_backup_codes=encrypted_backup_codes,
)
Expand Down
3 changes: 2 additions & 1 deletion testproject/tests/test_add_mfa.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,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 MfaMethods


User = get_user_model()
Expand All @@ -18,7 +19,7 @@ def test_add_user_mfa(active_user):
client.authenticate(user=active_user)
secret = create_secret_command()
response = client.post(
path="/auth/email/activate/",
path=f"/auth/{MfaMethods.EMAIL}/activate/",
data={
"secret": secret,
"code": create_otp_command(secret=secret, interval=60).now(),
Expand Down
Loading