Rafał Pitoń 10 лет назад
Родитель
Сommit
f67f44bae2

+ 5 - 0
docs/developers/settings.rst

@@ -185,6 +185,11 @@ MISAGO_ATTACHMENTS_ROOT
 Path to directory that Misago should use to store post attachments. This directory shouldn't be accessible from outside world.
 
 
+MISAGO_AUTH_API_URL
+-------------------
+Link name to API view used to validate sign-in credentials.
+
+
 MISAGO_AVATAR_SERVER_PATH
 -------------------------
 Url path that that all avatar server urls starts with. If you are running Misago subdirectory, make sure to update it (i.e. valid path for  "http://somesite.com/forums/" is ``/forums/user-avatar``).

+ 2 - 0
misago/conf/defaults.py

@@ -249,6 +249,8 @@ MISAGO_STOP_FORUM_SPAM_MIN_CONFIDENCE = 80
 MISAGO_MAILER_BATCH_SIZE = 20
 
 # Auth paths
+MISAGO_AUTH_API_URL = 'misago:api:authenticate'
+
 LOGIN_REDIRECT_URL = 'misago:index'
 LOGIN_URL = 'misago:login'
 LOGOUT_URL = 'misago:logout'

+ 7 - 3
misago/conf/middleware.py

@@ -10,8 +10,12 @@ class PreloadConfigMiddleware(object):
             'staticUrl': settings.STATIC_URL,
             'mediaUrl': settings.MEDIA_URL,
 
-            'loginRedirectUrl': reverse('misago:index'),
-            'loginUrl': reverse('misago:login'),
+            'csrfCookieName': settings.CSRF_COOKIE_NAME,
 
-            'logoutUrl': reverse('misago:logout'),
+            'authApiUrl': reverse(settings.MISAGO_AUTH_API_URL),
+
+            'loginRedirectUrl': reverse(settings.LOGIN_REDIRECT_URL),
+            'loginUrl': reverse(settings.LOGIN_URL),
+
+            'logoutUrl': reverse(settings.LOGOUT_URL),
         })

+ 5 - 6
misago/core/errorpages.py

@@ -7,10 +7,7 @@ from misago.admin.views.errorpages import admin_error_page, admin_csrf_failure
 
 
 def _ajax_error(code=406, message=None):
-    response_dict = {'is_error': True}
-    if message:
-        response_dict['message'] = unicode(message)
-    return JsonResponse(response_dict, status=code)
+    return JsonResponse({'detail': message}, status=code)
 
 
 @admin_error_page
@@ -24,14 +21,14 @@ def _error_page(request, code, message=None):
 
 def permission_denied(request, message=None):
     if request.is_ajax():
-        return _ajax_error(403, message)
+        return _ajax_error(403, message or _("Permission denied."))
     else:
         return _error_page(request, 403, message)
 
 
 def page_not_found(request):
     if request.is_ajax():
-        return _ajax_error(404, _("Invalid API link."))
+        return _ajax_error(404, "Not found.")
     else:
         return _error_page(request, 404)
 
@@ -39,6 +36,8 @@ def page_not_found(request):
 @admin_csrf_failure
 def csrf_failure(request, reason=""):
     if request.is_ajax():
+        print 'C> %s' % request.META.get("CSRF_COOKIE")
+        print 'E> %s' % request.META.get('HTTP_X_CSRFTOKEN', '')
         return _ajax_error(403, _("Request authentication is invalid."))
     else:
         response = render(request, 'misago/errorpages/csrf_failure.html')

+ 4 - 1
misago/core/exceptionhandler.py

@@ -35,7 +35,10 @@ def handle_explicit_first_page_exception(request, exception):
 
 
 def handle_http404_exception(request, exception):
-    return errorpages.page_not_found(request)
+    if request.is_ajax():
+        return JsonResponse({'detail': 'Not found'}, status=404)
+    else:
+        return errorpages.page_not_found(request)
 
 
 def handle_outdated_slug_exception(request, exception):

+ 11 - 0
misago/emberapp/tests/unit/controllers/guest-nav-test.js

@@ -0,0 +1,11 @@
+import {
+  moduleFor,
+  test
+} from 'ember-qunit';
+
+moduleFor('controller:guest-nav', 'GuestNavController');
+
+test('it exists', function(assert) {
+  var controller = this.subject();
+  assert.ok(controller);
+});

+ 3 - 2
misago/templates/misago/base.html

@@ -7,8 +7,9 @@
     <meta name="viewport" content="width=device-width, initial-scale=1">
     <title>{% block title %}{{ misago_settings.forum_name }}{% endblock %}</title>
     <meta name="description" content="{% block meta-description %}{% endblock %}">
-    <base href="/" />
-    <meta name="misago/config/environment" content="%7B%22modulePrefix%22%3A%22misago%22%2C%22environment%22%3A%22production%22%2C%22baseURL%22%3A%22/%22%2C%22locationType%22%3A%22trailing-slash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%7D%2C%22APP%22%3A%7B%22rootElement%22%3A%22%23main%22%2C%22API_HOST%22%3A%22%22%2C%22API_NAMESPACE%22%3A%22api%22%2C%22API_ADD_TRAILING_SLASHES%22%3Atrue%7D%2C%22contentSecurityPolicyHeader%22%3A%22Content-Security-Policy-Report-Only%22%2C%22contentSecurityPolicy%22%3A%7B%22default-src%22%3A%22%27none%27%22%2C%22script-src%22%3A%22%27self%27%22%2C%22font-src%22%3A%22%27self%27%22%2C%22connect-src%22%3A%22%27self%27%22%2C%22img-src%22%3A%22%27self%27%22%2C%22style-src%22%3A%22%27self%27%22%2C%22media-src%22%3A%22%27self%27%22%7D%2C%22exportApplicationGlobal%22%3Afalse%7D" />
+    <base href="/">
+    <meta name="misago/config/environment" content="%7B%22modulePrefix%22%3A%22misago%22%2C%22environment%22%3A%22production%22%2C%22baseURL%22%3A%22/%22%2C%22locationType%22%3A%22trailing-slash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%7D%2C%22APP%22%3A%7B%22rootElement%22%3A%22%23main%22%2C%22API_HOST%22%3A%22%22%2C%22API_NAMESPACE%22%3A%22api%22%2C%22API_ADD_TRAILING_SLASHES%22%3Atrue%7D%2C%22contentSecurityPolicyHeader%22%3A%22Content-Security-Policy-Report-Only%22%2C%22contentSecurityPolicy%22%3A%7B%22default-src%22%3A%22%27none%27%22%2C%22script-src%22%3A%22%27self%27%22%2C%22font-src%22%3A%22%27self%27%22%2C%22connect-src%22%3A%22%27self%27%22%2C%22img-src%22%3A%22%27self%27%22%2C%22style-src%22%3A%22%27self%27%22%2C%22media-src%22%3A%22%27self%27%22%7D%2C%22exportApplicationGlobal%22%3Afalse%7D">
+    <meta name="misago/csrf-token" content="{{ csrf_token }}">
     <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
     <!--[if lt IE 9]>
       <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>

+ 1 - 0
misago/urls.py

@@ -25,6 +25,7 @@ urlpatterns += patterns('',
 # Register API
 apipatterns = patterns('',
     url(r'^legal-pages/', include('misago.legal.urls.api')),
+    url(r'^', include('misago.users.urls.api')),
 )
 
 urlpatterns += patterns('',

+ 0 - 0
misago/users/api/__init__.py


+ 31 - 0
misago/users/api/auth.py

@@ -0,0 +1,31 @@
+from django.conf import settings
+from django.utils.translation import ugettext as _
+from django.views.decorators.cache import never_cache
+from django.views.decorators.csrf import csrf_protect
+from django.views.decorators.debug import sensitive_post_parameters
+
+from rest_framework import status
+from rest_framework.decorators import api_view
+from rest_framework.response import Response
+
+from misago.users.decorators import (deny_authenticated, deny_guests,
+                                     deny_banned_ips)
+from misago.users.forms.auth import AuthenticationForm
+
+
+@sensitive_post_parameters()
+@api_view(['POST'])
+@never_cache
+@deny_authenticated
+@csrf_protect
+@deny_banned_ips
+def authenticate(request):
+    form = AuthenticationForm(request, data=request.data)
+    if form.is_valid():
+        return Response()
+    else:
+        error = form.errors.as_data()['__all__'][0]
+        return Response({
+            'detail': error.messages[0],
+            'code': error.code
+        }, status=status.HTTP_400_BAD_REQUEST)

+ 5 - 4
misago/users/forms/auth.py

@@ -11,12 +11,13 @@ from misago.users.validators import validate_password
 
 class MisagoAuthMixin(object):
     error_messages = {
-        'empty_data': _("You have to fill out both fields."),
-        'invalid_login': _("Your login or password is incorrect."),
+        'empty_data': _("Fill out both fields."),
+        'invalid_login': _("Login or password is incorrect."),
         'inactive_user': _("You have to activate your account before "
                            "you will be able to sign in."),
-        'inactive_admin': _("Administrator has to activate your account "
-                            "before you will be able to sign in."),
+        'inactive_admin': _("Your account has to be activated by "
+                            "Administrator before you will be able "
+                            "to sign in."),
     }
 
     def confirm_user_active(self, user):

+ 0 - 0
misago/users/urls.py → misago/users/urls/__init__.py


+ 6 - 0
misago/users/urls/api.py

@@ -0,0 +1,6 @@
+from django.conf.urls import patterns, url
+
+
+urlpatterns = patterns('misago.users.api.auth',
+    url(r'^auth/$', 'authenticate', name='authenticate'),
+)

+ 2 - 2
misago/users/views/auth.py

@@ -14,9 +14,9 @@ from misago.users.forms.auth import AuthenticationForm
 
 
 @sensitive_post_parameters()
+@never_cache
 @deny_authenticated
 @csrf_protect
-@never_cache
 @deny_banned_ips
 def login(request):
     form = AuthenticationForm(request)
@@ -35,10 +35,10 @@ def login(request):
     return render(request, 'misago/login.html', {'form': form})
 
 
+@never_cache
 @deny_guests
 @require_POST
 @csrf_protect
-@never_cache
 def logout(request):
     message = _("%(user)s, you have been signed out.")
     messages.info(