Commit 3732bd16 by Zia Fazal

Merge pull request #22 from edx-solutions/security_enhacements_785

Security enhacements 785
parents 3d65edb4 9292e947
......@@ -8,6 +8,7 @@ from django.contrib.auth import SESSION_KEY, BACKEND_SESSION_KEY, load_backend
from django.contrib.auth.models import AnonymousUser, User
from django.core.exceptions import ObjectDoesNotExist
from django.utils.importlib import import_module
from django.utils.translation import ugettext as _
from rest_framework import status
from rest_framework.decorators import api_view, permission_classes
......@@ -15,6 +16,7 @@ from rest_framework.response import Response
from api_manager.permissions import ApiKeyHeaderPermission
from api_manager.serializers import UserSerializer
from student.models import LoginFailures
def _generate_base_uri(request):
......@@ -45,9 +47,23 @@ def session_list(request):
existing_user = User.objects.get(username=request.DATA['username'])
except ObjectDoesNotExist:
existing_user = None
# see if account has been locked out due to excessive login failures
if existing_user and LoginFailures.is_feature_enabled():
if LoginFailures.is_user_locked_out(existing_user):
response_status = status.HTTP_403_FORBIDDEN
response_data['message'] = _('This account has been temporarily locked due to excessive login failures. '
'Try again later.')
return Response(response_data, status=response_status)
if existing_user:
user = authenticate(username=existing_user.username, password=request.DATA['password'])
if user is not None:
# successful login, clear failed login attempts counters, if applicable
if LoginFailures.is_feature_enabled():
LoginFailures.clear_lockout_counter(user)
if user.is_active:
login(request, user)
response_data['token'] = request.session.session_key
......@@ -59,6 +75,10 @@ def session_list(request):
else:
response_status = status.HTTP_403_FORBIDDEN
else:
# tick the failed login counters if the user exists in the database
if LoginFailures.is_feature_enabled():
LoginFailures.increment_lockout_counter(existing_user)
response_status = status.HTTP_401_UNAUTHORIZED
else:
response_status = status.HTTP_404_NOT_FOUND
......
......@@ -5,6 +5,9 @@ import logging
from django.contrib.auth.models import User, Group
from django.core.exceptions import ObjectDoesNotExist
from django.db import IntegrityError
from django.core.validators import validate_email, validate_slug, ValidationError
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from rest_framework import status
from rest_framework.decorators import api_view, permission_classes
......@@ -14,8 +17,12 @@ from api_manager.permissions import ApiKeyHeaderPermission
from courseware import module_render
from courseware.model_data import FieldDataCache
from courseware.views import get_module_for_descriptor, save_child_position, get_current_child
from student.models import CourseEnrollment
from student.models import CourseEnrollment, PasswordHistory
from xmodule.modulestore.django import modulestore
from util.password_policy_validators import (
validate_password_length, validate_password_complexity,
validate_password_dictionary
)
log = logging.getLogger(__name__)
......@@ -95,6 +102,32 @@ def user_list(request):
password = request.DATA['password']
first_name = request.DATA.get('first_name', '')
last_name = request.DATA.get('last_name', '')
# enforce password complexity as an optional feature
if settings.FEATURES.get('ENFORCE_PASSWORD_POLICY', False):
try:
validate_password_length(password)
validate_password_complexity(password)
validate_password_dictionary(password)
except ValidationError, err:
status_code = status.HTTP_400_BAD_REQUEST
response_data['message'] = _('Password: ') + '; '.join(err.messages)
return Response(response_data, status=status_code)
try:
validate_email(email)
except ValidationError:
status_code = status.HTTP_400_BAD_REQUEST
response_data['message'] = _('Valid e-mail is required.')
return Response(response_data, status=status_code)
try:
validate_slug(username)
except ValidationError:
status_code = status.HTTP_400_BAD_REQUEST
response_data['message'] = _('Username should only consist of A-Z and 0-9, with no spaces.')
return Response(response_data, status=status_code)
try:
user = User.objects.create(email=email, username=username)
except IntegrityError:
......@@ -105,6 +138,11 @@ def user_list(request):
user.last_name = last_name
user.save()
# add this account creation to password history
# NOTE, this will be a NOP unless the feature has been turned on in configuration
password_history_entry = PasswordHistory()
password_history_entry.create(user)
# CDODGE: @TODO: We will have to extend this to look in the CourseEnrollmentAllowed table and
# auto-enroll students when they create a new account. Also be sure to remove from
# the CourseEnrollmentAllow table after the auto-registration has taken place
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment