add authentication to roject resource
authorymh <ymh.work@gmail.com>
Wed, 16 Jan 2013 05:09:22 +0100
changeset 1065 1bee39cbd1ea
parent 1064 d8b2af693c69
child 1066 2a37cb682d58
add authentication to roject resource
src/ldt/ldt/api/ldt/authentication.py
src/ldt/ldt/api/ldt/resources/project.py
src/ldt/ldt/user/admin.py
--- a/src/ldt/ldt/api/ldt/authentication.py	Mon Jan 14 15:48:45 2013 +0100
+++ b/src/ldt/ldt/api/ldt/authentication.py	Wed Jan 16 05:09:22 2013 +0100
@@ -2,6 +2,12 @@
 from django.middleware.csrf import _sanitize_token, constant_time_compare
 from django.utils.http import same_origin
 from tastypie.authentication import Authentication
+from tastypie.http import HttpUnauthorized
+from django.contrib.auth.models import User
+from django.contrib.auth import login
+from ldt.security import set_current_user
+
+
 
 # imported from tastypie's next version 0.9.12
 class SessionAuthentication(Authentication):
@@ -58,4 +64,138 @@
         if request.user:
             return request.user.username
         else:
-            return "anon."
\ No newline at end of file
+            return "anon."
+        
+# imported from tastypie's next version 1.0.0
+class MultiAuthentication(object):
+    """
+    An authentication backend that tries a number of backends in order.
+    """
+    def __init__(self, *backends, **kwargs):
+        super(MultiAuthentication, self).__init__(**kwargs)
+        self.backends = backends
+
+    def is_authenticated(self, request, **kwargs):
+        """
+        Identifies if the user is authenticated to continue or not.
+
+        Should return either ``True`` if allowed, ``False`` if not or an
+        ``HttpResponse`` if you need something custom.
+        """
+        unauthorized = False
+
+        for backend in self.backends:
+            check = backend.is_authenticated(request, **kwargs)
+
+            if check:
+                if isinstance(check, HttpUnauthorized):
+                    unauthorized = unauthorized or check
+                else:
+                    request._authentication_backend = backend
+                    return check
+
+        return unauthorized
+
+    def get_identifier(self, request):
+        """
+        Provides a unique string identifier for the requestor.
+
+        This implementation returns a combination of IP address and hostname.
+        """
+        try:
+            return request._authentication_backend.get_identifier(request)
+        except AttributeError:
+            return 'anon.'
+
+class ApiKeyAuthentication(Authentication):
+    """
+    Handles API key auth, in which a user provides a username & API key.
+
+    Uses the ``ApiKey`` model that ships with tastypie. If you wish to use
+    a different model, override the ``get_key`` method to perform the key check
+    as suits your needs.
+    """
+    def __init__(self, require_active=True):
+        self.require_active = require_active
+        
+    def _unauthorized(self):
+        return HttpUnauthorized()
+
+    def extract_credentials(self, request):
+        if request.META.get('HTTP_AUTHORIZATION') and request.META['HTTP_AUTHORIZATION'].lower().startswith('apikey '):
+            (auth_type, data) = request.META['HTTP_AUTHORIZATION'].split()
+
+            if auth_type.lower() != 'apikey':
+                raise ValueError("Incorrect authorization header.")
+
+            username, api_key = data.split(':', 1)
+        else:
+            username = request.GET.get('username') or request.POST.get('username')
+            api_key = request.GET.get('api_key') or request.POST.get('api_key')
+
+        return username, api_key
+
+    def is_authenticated(self, request, **kwargs):
+        """
+        Finds the user and checks their API key.
+
+        Should return either ``True`` if allowed, ``False`` if not or an
+        ``HttpResponse`` if you need something custom.
+        """
+
+        try:
+            username, api_key = self.extract_credentials(request)
+        except ValueError:
+            return self._unauthorized()
+
+        if not username or not api_key:
+            return self._unauthorized()
+
+        try:
+            user = User.objects.get(username=username)
+        except (User.DoesNotExist, User.MultipleObjectsReturned):
+            return self._unauthorized()
+
+        if not self.check_active(user):
+            return False
+        user.backend = "django.contrib.auth.backends.ModelBackend"
+        request.user = user
+        login(request,user)
+        set_current_user(user)
+        return self.get_key(user, api_key)
+
+    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 get_key(self, user, api_key):
+        """
+        Attempts to find the API key for the user. Uses ``ApiKey`` by default
+        but can be overridden.
+        """
+        from tastypie.models import ApiKey
+
+        try:
+            ApiKey.objects.get(user=user, key=api_key)
+        except ApiKey.DoesNotExist:
+            return self._unauthorized()
+
+        return True
+
+    def get_identifier(self, request):
+        """
+        Provides a unique string identifier for the requestor.
+
+        This implementation returns the user's username.
+        """
+        username, api_key = self.extract_credentials(request)
+        return username or 'nouser'        
\ No newline at end of file
--- a/src/ldt/ldt/api/ldt/resources/project.py	Mon Jan 14 15:48:45 2013 +0100
+++ b/src/ldt/ldt/api/ldt/resources/project.py	Wed Jan 16 05:09:22 2013 +0100
@@ -3,7 +3,7 @@
 from django.contrib.auth.models import Group
 from guardian.shortcuts import assign
 from ldt.ldt_utils.models import Project
-from ldt.api.ldt.authentication import SessionAuthentication
+from ldt.api.ldt.authentication import SessionAuthentication, MultiAuthentication, ApiKeyAuthentication
 from ldt.api.ldt.serializers.cinelabserializer import CinelabSerializer
 from ldt.api.ldt.resources import ContentResource
 from ldt.api.ldt.resources.user import UserResource
@@ -17,9 +17,9 @@
     contents = fields.ManyToManyField(ContentResource, 'contents')
     owner = fields.ForeignKey(UserResource, 'owner')
     class Meta:
-        allowed_methods = ['get', 'post']
+        allowed_methods = ['get', 'post', 'put']
         authorization = Authorization() # BE CAREFUL WITH THAT, it's unsecure
-        authentication = SessionAuthentication()
+        authentication = MultiAuthentication(ApiKeyAuthentication(), SessionAuthentication())
         resource_name = 'projects'
         queryset = Project.objects.all()
         serializer = CinelabSerializer()
--- a/src/ldt/ldt/user/admin.py	Mon Jan 14 15:48:45 2013 +0100
+++ b/src/ldt/ldt/user/admin.py	Wed Jan 16 05:09:22 2013 +0100
@@ -6,6 +6,9 @@
 from forms import LdtForm
 from guardian.admin import GuardedModelAdmin
 from models import Ldt, UserProfile, GroupProfile
+from tastypie.admin import ApiKeyInline
+from tastypie.models import ApiAccess, ApiKey
+
 
 class GroupProfileInline(admin.StackedInline):
     model = GroupProfile
@@ -38,7 +41,7 @@
     form = LdtForm
     model = Ldt
     filter_horizontal = ('user_permissions',)
-    inlines = [UserProfileInline, ]
+    inlines = [UserProfileInline, ApiKeyInline]
     
     def get_fieldsets(self, request, obj=None): 
         fieldsets = deepcopy(self.fieldsets)
@@ -58,5 +61,8 @@
 admin.site.unregister(Ldt) 
 admin.site.register(Ldt, LdtAdmin)
 
+admin.site.register(ApiKey)
+admin.site.register(ApiAccess)
+
 admin.site.unregister(User)
 admin.site.register(User, UserProfileAdmin)