from django.core.exceptions import ObjectDoesNotExist
from django.contrib.auth.backends import BaseBackend
from django.contrib.auth.models import User

from apps.user.models import ItsiUser, ConcordUser


class SSOAuthenticationBackend(BaseBackend):
    """
    A custom authentication back-end for Single Sign On providers.

    Before we can call django.contrib.auth.login on an SSO user, we must first
    authenticate them. This must be done using a custom authentication back-
    end, which sets the backend attribute on the user model.

    This class should be instantiated with an SSO provider user model, such
    as ItsiUser or ConcordUser, before it can be used.
    """
    def __init__(self, model, field):
        self.SSOUserModel = model
        self.SSOField = field

    def authenticate(self, sso_id=None):
        if sso_id is not None:
            try:
                query = {self.SSOField: sso_id}
                user = self.SSOUserModel.objects.get(**query).user
                return user
            except ObjectDoesNotExist:
                return None
        return None

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None


class ItsiAuthenticationBackend(SSOAuthenticationBackend):
    def __init__(self):
        super(ItsiAuthenticationBackend, self).__init__(
            ItsiUser, 'itsi_id')


class ConcordAuthenticationBackend(SSOAuthenticationBackend):
    def __init__(self):
        super(ConcordAuthenticationBackend, self).__init__(
            ConcordUser, 'concord_id')

from django.core.exceptions import ObjectDoesNotExist
from django.contrib.auth.backends import BaseBackend
from django.contrib.auth.models import User

from apps.user.models import ItsiUser, ConcordUser


class SSOAuthenticationBackend(BaseBackend):
    """
    A custom authentication back-end for Single Sign On providers.

    Before we can call django.contrib.auth.login on an SSO user, we must first
    authenticate them. This must be done using a custom authentication back-
    end, which sets the backend attribute on the user model.

    This class should be instantiated with an SSO provider user model, such
    as ItsiUser or ConcordUser, before it can be used.
    """
    def __init__(self, model, field):
        self.SSOUserModel = model
        self.SSOField = field

    def authenticate(self, request=None, sso_id=None):
        if sso_id is not None:
            try:
                query = {self.SSOField: sso_id}
                user = self.SSOUserModel.objects.get(**query).user
                return user
            except ObjectDoesNotExist:
                return None
        return None

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None


class ItsiAuthenticationBackend(SSOAuthenticationBackend):
    def __init__(self):
        super(ItsiAuthenticationBackend, self).__init__(
            ItsiUser, 'itsi_id')


class ConcordAuthenticationBackend(SSOAuthenticationBackend):
    def __init__(self):
        super(ConcordAuthenticationBackend, self).__init__(
            ConcordUser, 'concord_id')

Add request parameter to backend.authenticate
Add request parameter to backend.authenticate

Without this, the signature of our custom backend does not
match that of the function call. This signature is tested
in django.contrib.auth.authenticate here: https://github.com/django/django/blob/fdf209eab8949ddc345aa0212b349c79fc6fdebb/django/contrib/auth/__init__.py#L69
and `request` was added to that signature in Django 1.11
in https://github.com/django/django/commit/4b9330ccc04575f9e5126529ec355a450d12e77c.

With this, the Concord users are authenticated correctly.

  from django.core.exceptions import ObjectDoesNotExist
  from django.contrib.auth.backends import BaseBackend
  from django.contrib.auth.models import User
  
  from apps.user.models import ItsiUser, ConcordUser
  
  
  class SSOAuthenticationBackend(BaseBackend):
      """
      A custom authentication back-end for Single Sign On providers.
  
      Before we can call django.contrib.auth.login on an SSO user, we must first
      authenticate them. This must be done using a custom authentication back-
      end, which sets the backend attribute on the user model.
  
      This class should be instantiated with an SSO provider user model, such
      as ItsiUser or ConcordUser, before it can be used.
      """
      def __init__(self, model, field):
          self.SSOUserModel = model
          self.SSOField = field
  
-     def authenticate(self, sso_id=None):
+     def authenticate(self, request=None, sso_id=None):
          if sso_id is not None:
              try:
                  query = {self.SSOField: sso_id}
                  user = self.SSOUserModel.objects.get(**query).user
                  return user
              except ObjectDoesNotExist:
                  return None
          return None
  
      def get_user(self, user_id):
          try:
              return User.objects.get(pk=user_id)
          except User.DoesNotExist:
              return None
  
  
  class ItsiAuthenticationBackend(SSOAuthenticationBackend):
      def __init__(self):
          super(ItsiAuthenticationBackend, self).__init__(
              ItsiUser, 'itsi_id')
  
  
  class ConcordAuthenticationBackend(SSOAuthenticationBackend):
      def __init__(self):
          super(ConcordAuthenticationBackend, self).__init__(
              ConcordUser, 'concord_id')
  
Add request parameter to backend.authenticate
## Code Before:
from django.core.exceptions import ObjectDoesNotExist
from django.contrib.auth.backends import BaseBackend
from django.contrib.auth.models import User

from apps.user.models import ItsiUser, ConcordUser


class SSOAuthenticationBackend(BaseBackend):
    """
    A custom authentication back-end for Single Sign On providers.

    Before we can call django.contrib.auth.login on an SSO user, we must first
    authenticate them. This must be done using a custom authentication back-
    end, which sets the backend attribute on the user model.

    This class should be instantiated with an SSO provider user model, such
    as ItsiUser or ConcordUser, before it can be used.
    """
    def __init__(self, model, field):
        self.SSOUserModel = model
        self.SSOField = field

    def authenticate(self, sso_id=None):
        if sso_id is not None:
            try:
                query = {self.SSOField: sso_id}
                user = self.SSOUserModel.objects.get(**query).user
                return user
            except ObjectDoesNotExist:
                return None
        return None

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None


class ItsiAuthenticationBackend(SSOAuthenticationBackend):
    def __init__(self):
        super(ItsiAuthenticationBackend, self).__init__(
            ItsiUser, 'itsi_id')


class ConcordAuthenticationBackend(SSOAuthenticationBackend):
    def __init__(self):
        super(ConcordAuthenticationBackend, self).__init__(
            ConcordUser, 'concord_id')

## Instruction:
Add request parameter to backend.authenticate
## Code After:
from django.core.exceptions import ObjectDoesNotExist
from django.contrib.auth.backends import BaseBackend
from django.contrib.auth.models import User

from apps.user.models import ItsiUser, ConcordUser


class SSOAuthenticationBackend(BaseBackend):
    """
    A custom authentication back-end for Single Sign On providers.

    Before we can call django.contrib.auth.login on an SSO user, we must first
    authenticate them. This must be done using a custom authentication back-
    end, which sets the backend attribute on the user model.

    This class should be instantiated with an SSO provider user model, such
    as ItsiUser or ConcordUser, before it can be used.
    """
    def __init__(self, model, field):
        self.SSOUserModel = model
        self.SSOField = field

    def authenticate(self, request=None, sso_id=None):
        if sso_id is not None:
            try:
                query = {self.SSOField: sso_id}
                user = self.SSOUserModel.objects.get(**query).user
                return user
            except ObjectDoesNotExist:
                return None
        return None

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None


class ItsiAuthenticationBackend(SSOAuthenticationBackend):
    def __init__(self):
        super(ItsiAuthenticationBackend, self).__init__(
            ItsiUser, 'itsi_id')


class ConcordAuthenticationBackend(SSOAuthenticationBackend):
    def __init__(self):
        super(ConcordAuthenticationBackend, self).__init__(
            ConcordUser, 'concord_id')
