Rafał Pitoń 10 years ago
parent
commit
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.
 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
 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``).
 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
 MISAGO_MAILER_BATCH_SIZE = 20
 
 
 # Auth paths
 # Auth paths
+MISAGO_AUTH_API_URL = 'misago:api:authenticate'
+
 LOGIN_REDIRECT_URL = 'misago:index'
 LOGIN_REDIRECT_URL = 'misago:index'
 LOGIN_URL = 'misago:login'
 LOGIN_URL = 'misago:login'
 LOGOUT_URL = 'misago:logout'
 LOGOUT_URL = 'misago:logout'

+ 7 - 3
misago/conf/middleware.py

@@ -10,8 +10,12 @@ class PreloadConfigMiddleware(object):
             'staticUrl': settings.STATIC_URL,
             'staticUrl': settings.STATIC_URL,
             'mediaUrl': settings.MEDIA_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):
 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
 @admin_error_page
@@ -24,14 +21,14 @@ def _error_page(request, code, message=None):
 
 
 def permission_denied(request, message=None):
 def permission_denied(request, message=None):
     if request.is_ajax():
     if request.is_ajax():
-        return _ajax_error(403, message)
+        return _ajax_error(403, message or _("Permission denied."))
     else:
     else:
         return _error_page(request, 403, message)
         return _error_page(request, 403, message)
 
 
 
 
 def page_not_found(request):
 def page_not_found(request):
     if request.is_ajax():
     if request.is_ajax():
-        return _ajax_error(404, _("Invalid API link."))
+        return _ajax_error(404, "Not found.")
     else:
     else:
         return _error_page(request, 404)
         return _error_page(request, 404)
 
 
@@ -39,6 +36,8 @@ def page_not_found(request):
 @admin_csrf_failure
 @admin_csrf_failure
 def csrf_failure(request, reason=""):
 def csrf_failure(request, reason=""):
     if request.is_ajax():
     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."))
         return _ajax_error(403, _("Request authentication is invalid."))
     else:
     else:
         response = render(request, 'misago/errorpages/csrf_failure.html')
         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):
 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):
 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">
     <meta name="viewport" content="width=device-width, initial-scale=1">
     <title>{% block title %}{{ misago_settings.forum_name }}{% endblock %}</title>
     <title>{% block title %}{{ misago_settings.forum_name }}{% endblock %}</title>
     <meta name="description" content="{% block meta-description %}{% endblock %}">
     <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 -->
     <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
     <!--[if lt IE 9]>
     <!--[if lt IE 9]>
       <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
       <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
 # Register API
 apipatterns = patterns('',
 apipatterns = patterns('',
     url(r'^legal-pages/', include('misago.legal.urls.api')),
     url(r'^legal-pages/', include('misago.legal.urls.api')),
+    url(r'^', include('misago.users.urls.api')),
 )
 )
 
 
 urlpatterns += patterns('',
 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):
 class MisagoAuthMixin(object):
     error_messages = {
     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 "
         'inactive_user': _("You have to activate your account before "
                            "you will be able to sign in."),
                            "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):
     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()
 @sensitive_post_parameters()
+@never_cache
 @deny_authenticated
 @deny_authenticated
 @csrf_protect
 @csrf_protect
-@never_cache
 @deny_banned_ips
 @deny_banned_ips
 def login(request):
 def login(request):
     form = AuthenticationForm(request)
     form = AuthenticationForm(request)
@@ -35,10 +35,10 @@ def login(request):
     return render(request, 'misago/login.html', {'form': form})
     return render(request, 'misago/login.html', {'form': form})
 
 
 
 
+@never_cache
 @deny_guests
 @deny_guests
 @require_POST
 @require_POST
 @csrf_protect
 @csrf_protect
-@never_cache
 def logout(request):
 def logout(request):
     message = _("%(user)s, you have been signed out.")
     message = _("%(user)s, you have been signed out.")
     messages.info(
     messages.info(