From 1aed9c1604be3db8f3f2d2de748eb6f7c574637a Mon Sep 17 00:00:00 2001 From: swistakm Date: Mon, 25 Feb 2013 16:58:16 +0100 Subject: add OAuthAuthentication class --- rest_framework/authentication.py | 119 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 118 insertions(+), 1 deletion(-) (limited to 'rest_framework/authentication.py') diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py index 14b2136b..2bd0767e 100644 --- a/rest_framework/authentication.py +++ b/rest_framework/authentication.py @@ -9,6 +9,18 @@ from rest_framework.compat import CsrfViewMiddleware from rest_framework.authtoken.models import Token import base64 +from django.core.exceptions import ImproperlyConfigured +try: + import oauth2 +except ImportError: + oauth2 = None + +try: + import oauth_provider + from oauth_provider.store import store +except ImportError: + oauth_provider = None + class BaseAuthentication(object): """ @@ -155,4 +167,109 @@ class TokenAuthentication(BaseAuthentication): return 'Token' -# TODO: OAuthAuthentication +class OAuthAuthentication(BaseAuthentication): + """rest_framework OAuth authentication backend using + django-oath-plus""" + www_authenticate_realm = 'api' + require_active = True + + def __init__(self, **kwargs): + super(OAuthAuthentication, self).__init__(**kwargs) + + if oauth2 is None: + raise ImproperlyConfigured("The 'python-oauth2' package could not be imported. It is required for use with the 'OAuthAuthentication' class.") + + if oauth_provider is None: + raise ImproperlyConfigured("The 'django-oauth-plus' package could not be imported. It is required for use with the 'OAuthAuthentication' class.") + + + def authenticate(self, request): + """ + :returns: two-tuple of (user, auth) if authentication succeeds, or None otherwise. + """ + from oauth_provider.store import store + if self.is_valid_request(request): + oauth_request = oauth_provider.utils.get_oauth_request(request) + + if not self.check_nonce(request, oauth_request): + raise exceptions.AuthenticationFailed("Nonce check failed") + + try: + consumer = store.get_consumer(request, oauth_request, + oauth_request.get_parameter('oauth_consumer_key')) + except oauth_provider.store.InvalidConsumerError, e: + raise exceptions.AuthenticationFailed(e) + + if consumer.status != oauth_provider.consts.ACCEPTED: + raise exceptions.AuthenticationFailed('Invalid consumer key status: %s' % consumer.get_status_display()) + + try: + token = store.get_access_token(request, oauth_request, + consumer, oauth_request.get_parameter('oauth_token')) + + except oauth_provider.store.InvalidTokenError: + raise exceptions.AuthenticationFailed( + 'Invalid access token: %s' % oauth_request.get_parameter('oauth_token')) + + try: + self.validate_token(request, consumer, token) + except oauth2.Error, e: + print "got e" + raise exceptions.AuthenticationFailed(e.message) + + if not self.check_active(token.user): + raise exceptions.AuthenticationFailed('User not active: %s' % token.user.username) + + if consumer and token: + request.user = token.user + return (request.user, None) + + raise exceptions.AuthenticationFailed( + 'You are not allowed to access this resource.') + + return None + + def authenticate_header(self, request): + return 'OAuth realm="%s"' % self.www_authenticate_realm + + def is_in(self, params): + """ + Checks to ensure that all the OAuth parameter names are in the + provided ``params``. + """ + from oauth_provider.consts import OAUTH_PARAMETERS_NAMES + + for param_name in OAUTH_PARAMETERS_NAMES: + if param_name not in params: + return False + + return True + + def is_valid_request(self, request): + """ + Checks whether the required parameters are either in the HTTP + ``Authorization`` header sent by some clients (the preferred method + according to OAuth spec) or fall back to ``GET/POST``. + """ + auth_params = request.META.get("HTTP_AUTHORIZATION", []) + return self.is_in(auth_params) or self.is_in(request.REQUEST) + + def validate_token(self, request, consumer, token): + oauth_server, oauth_request = oauth_provider.utils.initialize_server_request(request) + return oauth_server.verify_request(oauth_request, consumer, token) + + def check_active(self, user): + """ + Ensures the user has an active account. + + Optimized for the ``django.contrib.auth.models.User`` case. + """ + if not self.require_active: + # Ignore & move on. + return True + + return user.is_active + + def check_nonce(self, request, oauth_request): + """Checks nonce of request""" + return store.check_nonce(request, oauth_request, oauth_request['oauth_nonce']) -- cgit v1.2.3 From 59a6f5f463472656518cb0680b9da5f22a724882 Mon Sep 17 00:00:00 2001 From: swistakm Date: Tue, 26 Feb 2013 11:22:21 +0100 Subject: Move oauth2 and django-oauth-plus imports to compat and fix some minor issues - alias oauth2 as oauth - remove rouge print - remove docstring markups - OAuthAuthentication.authenticate() now returns (user, token) two-tuple on success - don't set request.user because it's already set --- rest_framework/authentication.py | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) (limited to 'rest_framework/authentication.py') diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py index 2bd0767e..b507c5e1 100644 --- a/rest_framework/authentication.py +++ b/rest_framework/authentication.py @@ -4,23 +4,14 @@ Provides a set of pluggable authentication policies. from __future__ import unicode_literals from django.contrib.auth import authenticate from django.utils.encoding import DjangoUnicodeDecodeError +from django.core.exceptions import ImproperlyConfigured from rest_framework import exceptions, HTTP_HEADER_ENCODING from rest_framework.compat import CsrfViewMiddleware +from rest_framework.compat import oauth +from rest_framework.compat import oauth_provider from rest_framework.authtoken.models import Token import base64 -from django.core.exceptions import ImproperlyConfigured -try: - import oauth2 -except ImportError: - oauth2 = None - -try: - import oauth_provider - from oauth_provider.store import store -except ImportError: - oauth_provider = None - class BaseAuthentication(object): """ @@ -169,15 +160,15 @@ class TokenAuthentication(BaseAuthentication): class OAuthAuthentication(BaseAuthentication): """rest_framework OAuth authentication backend using - django-oath-plus""" + django-oath-plus and oauth2""" www_authenticate_realm = 'api' require_active = True def __init__(self, **kwargs): super(OAuthAuthentication, self).__init__(**kwargs) - if oauth2 is None: - raise ImproperlyConfigured("The 'python-oauth2' package could not be imported. It is required for use with the 'OAuthAuthentication' class.") + if oauth is None: + raise ImproperlyConfigured("The 'oauth2' package could not be imported. It is required for use with the 'OAuthAuthentication' class.") if oauth_provider is None: raise ImproperlyConfigured("The 'django-oauth-plus' package could not be imported. It is required for use with the 'OAuthAuthentication' class.") @@ -185,7 +176,7 @@ class OAuthAuthentication(BaseAuthentication): def authenticate(self, request): """ - :returns: two-tuple of (user, auth) if authentication succeeds, or None otherwise. + Returns two-tuple of (user, auth token) if authentication succeeds, or None otherwise. """ from oauth_provider.store import store if self.is_valid_request(request): @@ -213,16 +204,14 @@ class OAuthAuthentication(BaseAuthentication): try: self.validate_token(request, consumer, token) - except oauth2.Error, e: - print "got e" + except oauth.Error, e: raise exceptions.AuthenticationFailed(e.message) if not self.check_active(token.user): raise exceptions.AuthenticationFailed('User not active: %s' % token.user.username) if consumer and token: - request.user = token.user - return (request.user, None) + return (token.user, token) raise exceptions.AuthenticationFailed( 'You are not allowed to access this resource.') @@ -272,4 +261,4 @@ class OAuthAuthentication(BaseAuthentication): def check_nonce(self, request, oauth_request): """Checks nonce of request""" - return store.check_nonce(request, oauth_request, oauth_request['oauth_nonce']) + return oauth_provider.store.store.check_nonce(request, oauth_request, oauth_request['oauth_nonce']) -- cgit v1.2.3