Commit 8e71aac4 authored by David Seaward's avatar David Seaward

authenticate against ldap, fallback on jwt

* if jwt exists, create user in ldap
* minor code tidying
parent 49fa8c3f
import logging
from django.conf import settings
from django.contrib.auth.password_validation import MinimumLengthValidator as BaseValidator
from django.core import validators
from django.utils.deconstruct import deconstructible
from django.utils.translation import ugettext_lazy as _
from django_auth_ldap.backend import LDAPBackend as BaseBackend
from woocommerce import API as WOOCOMMERCE_API
from .models import UserManager
log = logging.getLogger(__name__)
......@@ -14,20 +18,70 @@ class AuthenticationBackend(BaseBackend):
def __init__(self, *args, **kwargs):
super(AuthenticationBackend, self).__init__(*args, **kwargs)
def is_valid_jwt(self, username=None, password=None):
is_valid = False
try:
jwt_wcapi = WOOCOMMERCE_API(
url=settings.WOO_URL,
# consumer_key=settings.WOO_CONSUMER_KEY,
# consumer_secret=settings.WOO_CONSUMER_SECRET,
wp_api=True,
version="jwt-auth/v1",
)
jwt_response = jwt_wcapi.post("token", {"username": username, "password": password})
jwt_status = jwt_response.status_code
jwt_token = jwt_response.json().get("token", None)
jwt_code = jwt_response.json().get("code", None)
known_codes = ["[jwt_auth] incorrect_password", "[jwt_auth] invalid_username"]
if jwt_status == 200 and jwt_token is not None:
is_valid = True
elif jwt_code == 403 and jwt_code in known_codes:
# recognised authentication failure
is_valid = False
else:
# raise exception for an unrecognised failure
raise Exception("Unrecognised JWT response: %s" % (jwt_response,))
except Exception as e:
logging.exception("JWT authentication failed with an unrecognised error: %s" % (e,))
finally:
return is_valid
def authenticate(self, request=None, username=None, password=None, **kwargs):
model = self.get_user_model()
username = model.normalize_username(username)
user_model = self.get_user_model()
normalized_username = user_model.normalize_username(username)
# TODO: also validate, so that existing but invalid usernames are not permitted?
# first attempt LDAP authentication (with early exit)
user = super(AuthenticationBackend, self).authenticate(request, normalized_username, password, **kwargs)
if user is not None:
return user
return super(AuthenticationBackend, self).authenticate(request, username, password, **kwargs)
# secondly attempt WooCommerce/JWT authentication
# (if successful, create and return LDAP user, otherwise return None)
if self.is_valid_jwt(normalized_username, password):
UserManager.create_user(username=normalized_username, email=None, password=password)
return super(AuthenticationBackend, self).authenticate(request, normalized_username, password, **kwargs)
else:
return None
# TODO: also validate, so that existing but invalid usernames are not permitted?
@deconstructible
class UsernameValidator(validators.RegexValidator):
regex = r'^[A-Za-z][A-Za-z0-9]*$'
message = _(
'Enter a valid username. Must start with a letter, followed by letters and numbers. No punctuation or special characters.'
'Enter a valid username. Must start with a letter, followed by letters and numbers.'
' No punctuation or special characters.'
)
......
......@@ -21,12 +21,12 @@ class UserManager(BaseUserManager):
def create_user(self, username, email=None, password=None, **extra_fields):
"""Create regular users in LDAP, with no Django password."""
user = super(UserManager, self).create_user(username, email, password, **extra_fields)
return super(UserManager, self).create_user(username, email, password, **extra_fields)
def create_superuser(self, username, email=None, password=None, **extra_fields):
"""Create superusers with a Django password."""
super(UserManager, self).create_superuser(username, email, password, **extra_fields)
return super(UserManager, self).create_superuser(username, email, password, **extra_fields)
class User(AbstractUser):
......@@ -53,10 +53,11 @@ class User(AbstractUser):
username = super(User, cls).normalize_username(username)
username = username.lower()
suffix = "@" + settings.SITE_DOMAIN.lower()
offset = 0 - len(suffix)
suffix = "@" + settings.SITE_DOMAIN
if username.endswith(suffix):
username = username[:-len(suffix)]
username = username[:offset]
return username
......@@ -81,7 +82,7 @@ class User(AbstractUser):
def get_identity(self):
# TODO: associated with https://code.puri.sm/purist/account_web/issues/25
return self.get_username() + "@" + settings.SITE_DOMAIN
return self.get_username() + "@" + settings.SITE_DOMAIN.lower()
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment