Permalink
Browse files

Iterating toward full session integration.

  • Loading branch information...
1 parent 7b689da commit 1cfe081ab903ca514601997f0b7727dc9735ea20 @onyxfish onyxfish committed Aug 7, 2012
@@ -27,6 +27,9 @@ PANDA.views.Root = Backbone.View.extend({
// Track Ajax events
this.track_ajax_events();
+ // Handle CSRF cookies for POST data
+ this.configure_csrf_handling();
+
// Override Backbone's sync handler with the authenticated version
Backbone.noAuthSync = Backbone.sync;
Backbone.sync = _.bind(this.sync, this);
@@ -43,7 +46,22 @@ PANDA.views.Root = Backbone.View.extend({
// Setup occasional updates of notifications
this.notifications_refresh_timer_id = window.setInterval(this.refresh_notifications, PANDA.settings.NOTIFICATIONS_INTERVAL);
- // TODO - abstract into a method
+ return this;
+ },
+
+ track_ajax_events: function() {
+ $(document).ajaxStart(function() {
+ $("#loading-indicator img").show();
+ });
+
+ $(document).ajaxStop(function() {
+ $("#loading-indicator img").hide();
+ });
+ },
+
+ configure_csrf_handling: function() {
+ // Always attach the CSRF token to requests
+ // Cribbed from http://stackoverflow.com/a/7093862/24608
$.ajaxSetup({
beforeSend: function(xhr, settings) {
function getCookie(name) {
@@ -67,18 +85,6 @@ PANDA.views.Root = Backbone.View.extend({
}
}
});
-
- return this;
- },
-
- track_ajax_events: function() {
- $(document).ajaxStart(function() {
- $("#loading-indicator img").show();
- });
-
- $(document).ajaxStop(function() {
- $("#loading-indicator img").hide();
- });
},
start_routing: function() {
View
@@ -116,6 +116,8 @@
'client'
)
+SESSION_COOKIE_AGE = 2592000 # 30 days
+
AUTH_PROFILE_MODULE = 'panda.UserProfile'
# Django-compressor
@@ -5,7 +5,7 @@
from tastypie.exceptions import ImmediateHttpResponse
from tastypie.http import HttpConflict
-from panda.api.utils import PandaApiKeyAuthentication, PandaModelResource, PandaSerializer
+from panda.api.utils import PandaAuthentication, PandaModelResource, PandaSerializer
from django.db import IntegrityError
from panda.models import ActivityLog
@@ -22,7 +22,7 @@ class Meta:
resource_name = 'activity_log'
allowed_methods = ['get', 'post']
- authentication = PandaApiKeyAuthentication()
+ authentication = PandaAuthentication()
authorization = DjangoAuthorization()
serializer = PandaSerializer()
View
@@ -4,7 +4,7 @@
from django.db.models import Count
from tastypie.authorization import DjangoAuthorization
-from panda.api.utils import PandaApiKeyAuthentication, SluggedModelResource, PandaSerializer
+from panda.api.utils import PandaAuthentication, SluggedModelResource, PandaSerializer
from panda.models import Category, Dataset
class CategoryResource(SluggedModelResource):
@@ -16,7 +16,7 @@ class Meta:
resource_name = 'category'
allowed_methods = ['get']
- authentication = PandaApiKeyAuthentication()
+ authentication = PandaAuthentication()
authorization = DjangoAuthorization()
serializer = PandaSerializer()
View
@@ -16,7 +16,7 @@
from panda import solr
from panda.api.datasets import DatasetResource
from panda.exceptions import DatasetLockedError
-from panda.api.utils import PandaApiKeyAuthentication, PandaPaginator, PandaResource, PandaSerializer
+from panda.api.utils import PandaAuthentication, PandaPaginator, PandaResource, PandaSerializer
from panda.models import Category, Dataset, SearchLog, TaskStatus
from panda.tasks import ExportSearchTask, PurgeDataTask
@@ -82,7 +82,7 @@ class Meta:
allowed_methods = ['get', 'post', 'put', 'delete']
always_return_data = True
- authentication = PandaApiKeyAuthentication()
+ authentication = PandaAuthentication()
authorization = DjangoAuthorization()
serializer = PandaSerializer()
validation = DataValidation()
@@ -12,7 +12,7 @@
from tastypie.exceptions import ImmediateHttpResponse, NotFound
from tastypie.utils.urls import trailing_slash
-from panda.api.utils import JSONApiField, PandaApiKeyAuthentication, PandaModelResource, PandaSerializer
+from panda.api.utils import JSONApiField, PandaAuthentication, PandaModelResource, PandaSerializer
from panda.exceptions import DataUploadNotDeletable
from panda.models import DataUpload
@@ -43,7 +43,7 @@ class Meta:
allowed_methods = ['get', 'put', 'delete']
always_return_data = True
- authentication = PandaApiKeyAuthentication()
+ authentication = PandaAuthentication()
authorization = DjangoAuthorization()
serializer = PandaSerializer()
View
@@ -10,7 +10,7 @@
from tastypie.validation import Validation
from panda import solr
-from panda.api.utils import PandaApiKeyAuthentication, PandaPaginator, JSONApiField, SluggedModelResource, PandaSerializer
+from panda.api.utils import PandaAuthentication, PandaPaginator, JSONApiField, SluggedModelResource, PandaSerializer
from panda.exceptions import DataImportError, DatasetLockedError
from panda.models import Category, Dataset, DataUpload
from panda.utils.column_schema import make_column_schema
@@ -58,7 +58,7 @@ class Meta:
allowed_methods = ['get', 'post', 'put', 'delete']
always_return_data = True
- authentication = PandaApiKeyAuthentication()
+ authentication = PandaAuthentication()
authorization = DjangoAuthorization()
validation = DatasetValidation()
serializer = PandaSerializer()
View
@@ -9,7 +9,7 @@
from tastypie.authorization import DjangoAuthorization
from tastypie.utils.urls import trailing_slash
-from panda.api.utils import PandaApiKeyAuthentication, PandaModelResource, PandaSerializer
+from panda.api.utils import PandaAuthentication, PandaModelResource, PandaSerializer
from panda.models import Export
class ExportResource(PandaModelResource):
@@ -26,7 +26,7 @@ class Meta:
resource_name = 'export'
allowed_methods = ['get']
- authentication = PandaApiKeyAuthentication()
+ authentication = PandaAuthentication()
authorization = DjangoAuthorization()
serializer = PandaSerializer()
@@ -2,7 +2,7 @@
from tastypie.authorization import DjangoAuthorization
-from panda.api.utils import PandaApiKeyAuthentication, PandaSerializer, PandaModelResource
+from panda.api.utils import PandaAuthentication, PandaSerializer, PandaModelResource
from panda.models import Notification
class NotificationResource(PandaModelResource):
@@ -13,7 +13,7 @@ class Meta:
queryset = Notification.objects.all()
resource_name = 'notification'
- authentication = PandaApiKeyAuthentication()
+ authentication = PandaAuthentication()
authorization = DjangoAuthorization()
serializer = PandaSerializer()
@@ -11,7 +11,7 @@
from tastypie.exceptions import NotFound
from tastypie.utils.urls import trailing_slash
-from panda.api.utils import PandaApiKeyAuthentication, PandaModelResource, PandaSerializer
+from panda.api.utils import PandaAuthentication, PandaModelResource, PandaSerializer
from panda.models import RelatedUpload
class RelatedUploadResource(PandaModelResource):
@@ -33,7 +33,7 @@ class Meta:
resource_name = 'related_upload'
allowed_methods = ['get', 'put', 'delete']
- authentication = PandaApiKeyAuthentication()
+ authentication = PandaAuthentication()
authorization = DjangoAuthorization()
serializer = PandaSerializer()
@@ -3,7 +3,7 @@
from tastypie import fields
from tastypie.authorization import DjangoAuthorization
-from panda.api.utils import PandaApiKeyAuthentication, PandaSerializer, PandaModelResource
+from panda.api.utils import PandaAuthentication, PandaSerializer, PandaModelResource
from panda.models import SearchSubscription
class SearchSubscriptionResource(PandaModelResource):
@@ -21,7 +21,7 @@ class Meta:
resource_name = 'search_subscription'
allowed_methods = ['get', 'post', 'delete']
- authentication = PandaApiKeyAuthentication()
+ authentication = PandaAuthentication()
authorization = DjangoAuthorization()
serializer = PandaSerializer()
View
@@ -3,7 +3,7 @@
from tastypie import fields
from tastypie.authorization import DjangoAuthorization
-from panda.api.utils import PandaApiKeyAuthentication, PandaSerializer, PandaModelResource
+from panda.api.utils import PandaAuthentication, PandaSerializer, PandaModelResource
from panda.models import TaskStatus
class TaskResource(PandaModelResource):
@@ -24,7 +24,7 @@ class Meta:
'end': ('year', 'month', 'day')
}
- authentication = PandaApiKeyAuthentication()
+ authentication = PandaAuthentication()
authorization = DjangoAuthorization()
serializer = PandaSerializer()
View
@@ -11,7 +11,7 @@
from tastypie.utils.urls import trailing_slash
from tastypie.validation import Validation
-from panda.api.utils import PandaApiKeyAuthentication, PandaSerializer, PandaModelResource
+from panda.api.utils import PandaAuthentication, PandaSerializer, PandaModelResource
from panda.models import Export, UserProxy
class UserValidation(Validation):
@@ -50,7 +50,7 @@ class Meta:
excludes = ['username', 'is_staff', 'is_superuser']
always_return_data = True
- authentication = PandaApiKeyAuthentication()
+ authentication = PandaAuthentication()
authorization = UserAuthorization()
validation = UserValidation()
serializer = PandaSerializer()
View
@@ -109,12 +109,16 @@ def base_urls(self):
url(r"^(?P<resource_name>%s)/(?P<slug>[\w\d_-]+)%s$" % (self._meta.resource_name, trailing_slash()), self.wrap_view('dispatch_detail'), name="api_dispatch_detail"),
]
-class PandaApiKeyAuthentication(ApiKeyAuthentication):
+class PandaAuthentication(ApiKeyAuthentication):
"""
- Custom API Auth that accepts parameters as cookies or headers as well as GET params.
+ Custom API Auth that authenticates via sessions, headers or querystring parameters.
"""
- def is_authenticated(self, request, **kwargs):
- # Session handling shamelessly cribbed from a newer version of Tastypie
+ def try_sessions(self, request, **kwargs):
+ """
+ Attempt to authenticate with sessions.
+
+ Cribbed from a newer version of Tastypie than we're using.
+ """
csrf_token = _sanitize_token(request.COOKIES.get(settings.CSRF_COOKIE_NAME, ''))
if request.is_secure():
@@ -133,28 +137,43 @@ def is_authenticated(self, request, **kwargs):
if not constant_time_compare(request_csrf_token, csrf_token):
return False
- if request.user.is_authenticated():
- return request.user.is_authenticated()
+ return request.user.is_authenticated()
- # Now check for API credentials in the request
- email = request.COOKIES.get('email') or request.META.get('HTTP_PANDA_EMAIL') or request.GET.get('email')
- api_key = request.COOKIES.get('api_key') or request.META.get('HTTP_PANDA_API_KEY') or request.GET.get('api_key')
+ def try_api_keys(self, request, **kwargs):
+ """
+ Attempt to authenticate with API keys in headers or parameters.
+ """
+ email = request.META.get('HTTP_PANDA_EMAIL') or request.GET.get('email')
+ api_key = request.META.get('HTTP_PANDA_API_KEY') or request.GET.get('api_key')
if email:
email = unquote(email)
if not email or not api_key:
- return self._unauthorized()
+ return False
try:
user = UserProxy.objects.get(username=email.lower())
except (UserProxy.DoesNotExist, UserProxy.MultipleObjectsReturned):
- return self._unauthorized()
+ return False
+
+ if not user.is_active:
+ return False
+
+ request.user = user
+
+ return self.get_key(user, api_key)
+
+ def is_authenticated(self, request, **kwargs):
+ authenticated = self.try_sessions(request, **kwargs)
+
+ if authenticated:
+ return True
- if user.is_active:
- request.user = user
+ authenticated = self.try_api_keys(request, **kwargs)
- return self.get_key(user, api_key)
+ if authenticated:
+ return True
return self._unauthorized()
View
@@ -15,7 +15,7 @@
from client.utils import get_free_disk_space
from panda.api.notifications import NotificationResource
from panda.api.users import UserValidation
-from panda.api.utils import PandaApiKeyAuthentication
+from panda.api.utils import PandaAuthentication
from panda.models import UserProfile, UserProxy
from panda.storage import PANDADataUploadBackend, PANDARelatedUploadBackend
from panda.utils.mail import send_mail
@@ -36,7 +36,7 @@ class SecureAjaxFileUploader(AjaxFileUploader):
A custom version of AjaxFileUploader that checks for authorization.
"""
def __call__(self, request):
- auth = PandaApiKeyAuthentication()
+ auth = PandaAuthentication()
if auth.is_authenticated(request) != True:
# Valum's FileUploader only parses the response if the status code is 200.

0 comments on commit 1cfe081

Please sign in to comment.