# TODO OAuth1/2

import logging

log = logging.getLogger(__name__)

class Authn(object):
    '''
    Basic interface for any authenticator.  Always returns the request as
    not authenticated.
    '''
    def __init__(self, realm=None):
        self.use_realm = realm

    def authenticate(self, request):
        return None

    def authorization(self, request):
        return ''

    def can_handle(self, request):
        return bool(self.parse_expect(request))

    def challenge(self, request, response):
        return response

    @property
    def expect(self):
        return None

    def find_realm(self):
        return ''

    def is_valid(self, authned):
        return True

    def parse(self, request):
        credentials = self.parse_expect(request)
        if not credentials:
            return None

        try:
            identifer, credential = credentials.split(':', 1)
        except ValueError:
            return None

        if not (identifer and credential):
            return None

        return (identifer, credential)

    def parse_expect(self, request):
        header = self.authorization(request)
        if not header:
            return None

        try:
            method, credentials = header.split(None, 1)
        except ValueError:
            return None

        expect = self.expect
        if expect and method.lower() != expect:
            return None

        return credentials

    @property
    def realm(self):
        return self.use_realm or self.find_realm()

    @property
    def secret(self):
        return ''

class AuthnDjango(Authn):
    '''
    Meant to be subclassed by Django Authenticators.  Will not work by itself.
    '''
    def authenticate(self, **credentials):
        from django.contrib.auth import authenticate as auth_authenticate
        return auth_authenticate(**credentials)

    def authorization(self, request):
        return request.META.get('HTTP_AUTHORIZATION', '')

    def find_realm(self):
        from django.contrib.sites.models import Site
        return Site.objects.get_current().name

    def is_valid(self, authned):
        return authned.is_active

    @property
    def secret(self):
        from django.conf import settings
        return settings.SECRET_KEY

class AuthnDjangoAPIToken(AuthnDjango):
    '''AuthnDjangoAPIToken(APIToken)

    An authenticator that uses a model that sub-classes the
    krankshaft.models.APITokenBase class as a form of authentication. Intended
    to make it possible to have credentials for a user separate from the
    password that are easily invalidated in one way or another.
    '''
    def __init__(self, model, **kwargs):
        super(AuthnDjangoAPIToken, self).__init__(**kwargs)
        self.model = self.model

    def authenticate(self, request):
        try:
            owner, token = self.parse(request)
        except TypeError:
            return None

        try:
            return self.model.get(owner, token)
        except (self.model.DoesNotExist, self.model.MultipleObjectsReturned):
            return None

    def challenge(self, request, response):
        response['WWW-Authenticate'] = 'APIToken Realm="%s"' % self.realm
        return response

    @property
    def expect(self):
        return 'apitoken'

    def is_valid(self, authned):
        return authned.is_valid()

class AuthnDjangoBasic(AuthnDjango):
    '''
    Basic authentication using Django's Auth framework.
    '''
    def authenticate(self, request):
        try:
            username, password = self.parse(request)
        except TypeError:
            return None

        return super(AuthnDjangoBasic, self).authenticate(
            username=username,
            password=password,
        )

    def challenge(self, request, response):
        response['WWW-Authenticate'] = 'Basic Realm="%s"' % self.realm
        return response

    @property
    def expect(self):
        return 'basic'

class AuthnDjangoMiddleware(AuthnDjango):
    '''
    An authenticator that passes through any authentication already provided
    by middleware.  Typically usage could be with Session middleware.
    Specifically, it uses whatever user is attached to the request object.

    note: CSRF validation is done to ensure a certain level of safety that the
    request came from a page served by the application.
    See: https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax
    '''
    def authenticate(self, request):
        from django.middleware.csrf import CsrfViewMiddleware

        # return reason not response
        class CSRF(CsrfViewMiddleware):
            def _reject(self, request, reason):
                return reason

        user = getattr(request, 'user', None)
        if not user:
            return None

        if not user.is_authenticated():
            return None

        reason = CSRF().process_view(request, None, (), {})
        if reason:
            log.warn('Authn failed due to CSRF verification failure')
            return None

        return user

@@ -187,32 +187,51 @@
 icated.%0A    '''%0A
+    expect = None%0A%0A
     def __init__
@@ -554,65 +554,8 @@
 se%0A%0A
-    @property%0A    def expect(self):%0A        return None%0A%0A
     
@@ -2584,32 +2584,57 @@
 nother.%0A    '''%0A
+    expect = 'apitoken'%0A%0A
     def __init__
@@ -2654,24 +2654,24 @@
  **kwargs):%0A
-
         supe
@@ -3210,71 +3210,8 @@
 se%0A%0A
-    @property%0A    def expect(self):%0A        return 'apitoken'%0A%0A
     
@@ -3375,32 +3375,54 @@
 mework.%0A    '''%0A
+    expect = 'basic'%0A%0A
     def authenti
@@ -3798,32 +3798,32 @@
 %22' %25 self.realm%0A
+
         return r
@@ -3835,68 +3835,8 @@
 se%0A%0A
-    @property%0A    def expect(self):%0A        return 'basic'%0A%0A
 clas

make expect an attribute not a property method