Limit the rate of logins.
This commit is contained in:
@@ -3,7 +3,7 @@ django admin pages for courseware model
|
||||
'''
|
||||
|
||||
from external_auth.models import *
|
||||
from django.contrib import admin
|
||||
from ratelimitbackend import admin
|
||||
|
||||
|
||||
class ExternalAuthMapAdmin(admin.ModelAdmin):
|
||||
|
||||
@@ -9,12 +9,15 @@ from urlparse import parse_qs
|
||||
|
||||
from django.conf import settings
|
||||
from django.test import TestCase, LiveServerTestCase
|
||||
from django.core.cache import cache
|
||||
from django.test.utils import override_settings
|
||||
# from django.contrib.auth.models import User
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test.client import RequestFactory
|
||||
from unittest import skipUnless
|
||||
|
||||
from student.tests.factories import UserFactory
|
||||
from external_auth.views import provider_login
|
||||
|
||||
|
||||
class MyFetcher(HTTPFetcher):
|
||||
"""A fetcher that uses server-internal calls for performing HTTP
|
||||
@@ -199,6 +202,49 @@ class OpenIdProviderTest(TestCase):
|
||||
""" Test for 403 error code when the url"""
|
||||
self.attempt_login(403, return_to="http://apps.cs50.edx.or")
|
||||
|
||||
def _send_bad_redirection_login(self):
|
||||
"""
|
||||
Attempt to log in to the provider with setup parameters
|
||||
|
||||
Intentionally fail the login to force a redirect
|
||||
"""
|
||||
user = UserFactory()
|
||||
|
||||
factory = RequestFactory()
|
||||
post_params = {'email': user.email, 'password': 'password'}
|
||||
fake_url = 'fake url'
|
||||
request = factory.post(reverse('openid-provider-login'), post_params)
|
||||
openid_setup = {
|
||||
'request': factory.request(),
|
||||
'url': fake_url
|
||||
}
|
||||
request.session = {
|
||||
'openid_setup': openid_setup
|
||||
}
|
||||
response = provider_login(request)
|
||||
return response
|
||||
|
||||
@skipUnless(settings.MITX_FEATURES.get('AUTH_USE_OPENID') or
|
||||
settings.MITX_FEATURES.get('AUTH_USE_OPENID_PROVIDER'), True)
|
||||
def test_login_openid_handle_redirection(self):
|
||||
""" Test to see that we can handle login redirection properly"""
|
||||
response = self._send_bad_redirection_login()
|
||||
self.assertEquals(response.status_code, 302)
|
||||
|
||||
@skipUnless(settings.MITX_FEATURES.get('AUTH_USE_OPENID') or
|
||||
settings.MITX_FEATURES.get('AUTH_USE_OPENID_PROVIDER'), True)
|
||||
def test_login_openid_handle_redirection_ratelimited(self):
|
||||
# try logging in 30 times, the default limit in the number of failed
|
||||
# log in attempts before the rate gets limited
|
||||
for _ in xrange(30):
|
||||
self._send_bad_redirection_login()
|
||||
|
||||
response = self._send_bad_redirection_login()
|
||||
# verify that we are not returning the default 403
|
||||
self.assertEquals(response.status_code, 302)
|
||||
# clear the ratelimit cache so that we don't fail other logins
|
||||
cache.clear()
|
||||
|
||||
|
||||
class OpenIdProviderLiveServerTest(LiveServerTestCase):
|
||||
"""
|
||||
|
||||
@@ -39,6 +39,7 @@ from openid.consumer.consumer import SUCCESS
|
||||
from openid.server.server import Server, ProtocolError, UntrustedReturnURL
|
||||
from openid.server.trustroot import TrustRoot
|
||||
from openid.extensions import ax, sreg
|
||||
from ratelimitbackend.exceptions import RateLimitException
|
||||
|
||||
import student.views as student_views
|
||||
# Required for Pearson
|
||||
@@ -191,7 +192,7 @@ def _external_login_or_signup(request,
|
||||
user.backend = auth_backend
|
||||
AUDIT_LOG.info('Linked user "%s" logged in via Shibboleth', user.email)
|
||||
else:
|
||||
user = authenticate(username=uname, password=eamap.internal_password)
|
||||
user = authenticate(username=uname, password=eamap.internal_password, request=request)
|
||||
if user is None:
|
||||
# we want to log the failure, but don't want to log the password attempted:
|
||||
AUDIT_LOG.warning('External Auth Login failed for "%s"', uname)
|
||||
@@ -718,7 +719,12 @@ def provider_login(request):
|
||||
# Failure is again redirected to the login dialog.
|
||||
username = user.username
|
||||
password = request.POST.get('password', None)
|
||||
user = authenticate(username=username, password=password)
|
||||
try:
|
||||
user = authenticate(username=username, password=password, request=request)
|
||||
except RateLimitException:
|
||||
AUDIT_LOG.warning('OpenID - Too many failed login attempts.')
|
||||
return HttpResponseRedirect(openid_request_url)
|
||||
|
||||
if user is None:
|
||||
request.session['openid_error'] = True
|
||||
msg = "OpenID login failed - password for %s is invalid"
|
||||
|
||||
Reference in New Issue
Block a user