Esempio n. 1
0
def _check_excessive_login_attempts(user):
    """
    See if account has been locked out due to excessive login failures
    """
    if user and LoginFailures.is_feature_enabled():
        if LoginFailures.is_user_locked_out(user):
            _generate_locked_out_error_message()
Esempio n. 2
0
def _check_excessive_login_attempts(user):
    """
    See if account has been locked out due to excessive login failures
    """
    if user and LoginFailures.is_feature_enabled():
        if LoginFailures.is_user_locked_out(user):
            raise AuthFailedError(_('This account has been temporarily locked due '
                                    'to excessive login failures. Try again later.'))
Esempio n. 3
0
    def _handle_failed_authentication(self, user):
        """
        Handles updating the failed login count, inactive user notifications, and logging failed authentications.
        """
        if user and LoginFailures.is_feature_enabled():
            LoginFailures.increment_lockout_counter(user)

        raise AuthFailedError(_('Email or password is incorrect.'))
Esempio n. 4
0
    def _handle_failed_authentication(self, user):
        """
        Handles updating the failed login count, inactive user notifications, and logging failed authentications.
        """
        if user and LoginFailures.is_feature_enabled():
            LoginFailures.increment_lockout_counter(user)

        raise AuthFailedError(_('Email or password is incorrect.'))
Esempio n. 5
0
 def _check_excessive_login_attempts(self, user):
     """
     See if account has been locked out due to excessive login failures
     """
     if user and LoginFailures.is_feature_enabled():
         if LoginFailures.is_user_locked_out(user):
             raise AuthFailedError(_('Due to multiple login failures, the account is temporarily locked.'
                                     ' Please try again after 15 minutes.'))
Esempio n. 6
0
def _check_excessive_login_attempts(user):
    """
    See if account has been locked out due to excessive login failures
    """
    if user and LoginFailures.is_feature_enabled():
        if LoginFailures.is_user_locked_out(user):
            raise AuthFailedError(_('This account has been temporarily locked due '
                                    'to excessive login failures. Try again later.'))
Esempio n. 7
0
def _handle_successful_authentication_and_login(user, request):
    """
    Handles clearing the failed login counter, login tracking, and setting session timeout.
    """
    if LoginFailures.is_feature_enabled():
        LoginFailures.clear_lockout_counter(user)

    _track_user_login(user, request)

    try:
        django_login(request, user)
        request.session.set_expiry(604800 * 4)
        log.debug("Setting user session expiry to 4 weeks")
    except Exception as exc:
        AUDIT_LOG.critical("Login failed - Could not create session. Is memcached running?")
        log.critical("Login failed - Could not create session. Is memcached running?")
        log.exception(exc)
        raise
Esempio n. 8
0
def _handle_successful_authentication_and_login(user, request):
    """
    Handles clearing the failed login counter, login tracking, and setting session timeout.
    """
    if LoginFailures.is_feature_enabled():
        LoginFailures.clear_lockout_counter(user)

    _track_user_login(user, request)

    try:
        django_login(request, user)
        request.session.set_expiry(604800 * 4)
        log.debug("Setting user session expiry to 4 weeks")
    except Exception as exc:
        AUDIT_LOG.critical("Login failed - Could not create session. Is memcached running?")
        log.critical("Login failed - Could not create session. Is memcached running?")
        log.exception(exc)
        raise
Esempio n. 9
0
def _handle_failed_authentication(user):
    """
    Handles updating the failed login count, inactive user notifications, and logging failed authentications.
    """
    if user:
        if LoginFailures.is_feature_enabled():
            LoginFailures.increment_lockout_counter(user)

        if not user.is_active:
            _log_and_raise_inactive_user_auth_error(user)

        # if we didn't find this username earlier, the account for this email
        # doesn't exist, and doesn't have a corresponding password
        if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
            loggable_id = user.id if user else "<unknown>"
            AUDIT_LOG.warning(u"Login failed - password for user.id: {0} is invalid".format(loggable_id))
        else:
            AUDIT_LOG.warning(u"Login failed - password for {0} is invalid".format(user.email))

    raise AuthFailedError(_('Email or password is incorrect.'))
Esempio n. 10
0
def _handle_failed_authentication(user):
    """
    Handles updating the failed login count, inactive user notifications, and logging failed authentications.
    """
    if user:
        if LoginFailures.is_feature_enabled():
            LoginFailures.increment_lockout_counter(user)

        if not user.is_active:
            _log_and_raise_inactive_user_auth_error(user)

        # if we didn't find this username earlier, the account for this email
        # doesn't exist, and doesn't have a corresponding password
        if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
            loggable_id = user.id if user else "<unknown>"
            AUDIT_LOG.warning(u"Login failed - password for user.id: {0} is invalid".format(loggable_id))
        else:
            AUDIT_LOG.warning(u"Login failed - password for {0} is invalid".format(user.email))

    raise AuthFailedError(_('Email or password is incorrect.'))
Esempio n. 11
0
def _handle_successful_authentication_and_login(user, request):
    """
    Handles clearing the failed login counter, login tracking, and setting session timeout.
    """
    if LoginFailures.is_feature_enabled():
        LoginFailures.clear_lockout_counter(user)

    _track_user_login(user, request)

    try:
        django_login(request, user)
        if request.POST.get('remember') == 'true':
            request.session.set_expiry(604800)
            log.debug("Setting user session to never expire")
        else:
            request.session.set_expiry(0)
    except Exception as exc:  # pylint: disable=broad-except
        AUDIT_LOG.critical("Login failed - Could not create session. Is memcached running?")
        log.critical("Login failed - Could not create session. Is memcached running?")
        log.exception(exc)
        raise
Esempio n. 12
0
    def _verify_user_password(self, request):
        """
        If the user is logged in and we want to verify that they have submitted the correct password
        for a major account change (for example, retiring this user's account).

        Args:
            request (HttpRequest): A request object where the password should be included in the POST fields.
        """
        try:
            self._check_excessive_login_attempts(request.user)
            user = authenticate(username=request.user.username, password=request.POST['password'], request=request)
            if user:
                if LoginFailures.is_feature_enabled():
                    LoginFailures.clear_lockout_counter(user)
                return Response(status=status.HTTP_204_NO_CONTENT)
            else:
                self._handle_failed_authentication(request.user)
        except AuthFailedError as err:
            return Response(text_type(err), status=status.HTTP_403_FORBIDDEN)
        except Exception as err:  # pylint: disable=broad-except
            return Response(u"Could not verify user password: {}".format(err), status=status.HTTP_400_BAD_REQUEST)
Esempio n. 13
0
    def _verify_user_password(self, request):
        """
        If the user is logged in and we want to verify that they have submitted the correct password
        for a major account change (for example, retiring this user's account).

        Args:
            request (HttpRequest): A request object where the password should be included in the POST fields.
        """
        try:
            self._check_excessive_login_attempts(request.user)
            user = authenticate(username=request.user.username, password=request.POST['password'], request=request)
            if user:
                if LoginFailures.is_feature_enabled():
                    LoginFailures.clear_lockout_counter(user)
                return Response(status=status.HTTP_204_NO_CONTENT)
            else:
                self._handle_failed_authentication(request.user)
        except AuthFailedError as err:
            return Response(text_type(err), status=status.HTTP_403_FORBIDDEN)
        except Exception as err:  # pylint: disable=broad-except
            return Response(u"Could not verify user password: {}".format(err), status=status.HTTP_400_BAD_REQUEST)
Esempio n. 14
0
def _handle_successful_authentication_and_login(user, request):
    """
    Handles clearing the failed login counter, login tracking, and setting session timeout.
    """
    if LoginFailures.is_feature_enabled():
        LoginFailures.clear_lockout_counter(user)

    _track_user_login(user, request)

    try:
        django_login(request, user)
        if request.POST.get('remember') == 'true':
            request.session.set_expiry(604800)
            log.debug("Setting user session to never expire")
        else:
            request.session.set_expiry(0)
    except Exception as exc:  # pylint: disable=broad-except
        AUDIT_LOG.critical("Login failed - Could not create session. Is memcached running?")
        log.critical("Login failed - Could not create session. Is memcached running?")
        log.exception(exc)
        raise
Esempio n. 15
0
def verify_user_password(request):
    """
    If the user is logged in and we want to verify that they have submitted the correct password
    for a major account change (for example, retiring this user's account).

    Args:
        request (HttpRequest): A request object where the password should be included in the POST fields.
    """
    try:
        _check_excessive_login_attempts(request.user)
        user = authenticate(username=request.user.username, password=request.POST['password'], request=request)
        if user:
            if LoginFailures.is_feature_enabled():
                LoginFailures.clear_lockout_counter(user)
            return JsonResponse({'success': True})
        else:
            _handle_failed_authentication(request.user)
    except AuthFailedError as err:
        return HttpResponse(err.value, content_type="text/plain", status=403)
    except Exception:  # pylint: disable=broad-except
        log.exception("Could not verify user password")
        return HttpResponseBadRequest()
Esempio n. 16
0
def verify_user_password(request):
    """
    If the user is logged in and we want to verify that they have submitted the correct password
    for a major account change (for example, retiring this user's account).

    Args:
        request (HttpRequest): A request object where the password should be included in the POST fields.
    """
    try:
        _check_excessive_login_attempts(request.user)
        user = authenticate(username=request.user.username, password=request.POST['password'], request=request)
        if user:
            if LoginFailures.is_feature_enabled():
                LoginFailures.clear_lockout_counter(user)
            return JsonResponse({'success': True})
        else:
            _handle_failed_authentication(request.user)
    except AuthFailedError as err:
        return HttpResponse(err.value, content_type="text/plain", status=403)
    except Exception:  # pylint: disable=broad-except
        log.exception("Could not verify user password")
        return HttpResponseBadRequest()
Esempio n. 17
0
def _handle_failed_authentication(user, authenticated_user):
    """
    Handles updating the failed login count, inactive user notifications, and logging failed authentications.
    """
    if user:
        if LoginFailures.is_feature_enabled():
            LoginFailures.increment_lockout_counter(user)

        if authenticated_user and not user.is_active:
            _log_and_raise_inactive_user_auth_error(user)

        # if we didn't find this username earlier, the account for this email
        # doesn't exist, and doesn't have a corresponding password
        if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
            loggable_id = user.id if user else "<unknown>"
            AUDIT_LOG.warning(
                u"Login failed - password for user.id: {0} is invalid".format(
                    loggable_id))
        else:
            AUDIT_LOG.warning(
                u"Login failed - password for {0} is invalid".format(
                    user.email))

    if user and LoginFailures.is_feature_enabled():
        blocked_threshold, failure_count = LoginFailures.check_user_reset_password_threshold(
            user)
        if blocked_threshold:
            if not LoginFailures.is_user_locked_out(user):
                max_failures_allowed = settings.MAX_FAILED_LOGIN_ATTEMPTS_ALLOWED
                remaining_attempts = max_failures_allowed - failure_count
                raise AuthFailedError(
                    Text(
                        _('Email or password is incorrect.'
                          '{li_start}You have {remaining_attempts} more sign-in '
                          'attempts before your account is temporarily locked.{li_end}'
                          '{li_start}If you\'ve forgotten your password, click '
                          '{link_start}here{link_end} to reset.{li_end}')).
                    format(link_start=HTML(
                        '<a http="#login" class="form-toggle" data-type="password-reset">'
                    ),
                           link_end=HTML('</a>'),
                           li_start=HTML('<li>'),
                           li_end=HTML('</li>'),
                           remaining_attempts=remaining_attempts))
            else:
                _generate_locked_out_error_message()

    raise AuthFailedError(_('Email or password is incorrect.'))
Esempio n. 18
0
 def decorator(*args, **kwargs):
     """Decorator class to return"""
     if not LoginFailures.is_feature_enabled():
         return False
     return func(*args, **kwargs)
Esempio n. 19
0
    def post(self, request, **kwargs):
        #from user_authn.views.login import _check_excessive_login_attempts
        from student.models import LoginFailures
        #from openedx.core.djangoapps.user_authn.views.login import _check_excessive_login_attempts
        self.data = request.POST.dict()
        if not ('uservalue' and 'sendotptype' and 'password' in self.data):
            return JsonResponse({
                "status": 400,
                "message": "Please enter Valid Mobile Number or Email Address or password",
            })
        if self.data.get('sendotptype') == "mobile":
            mobile = self.data.get('uservalue')
            user = User.objects.get(extrafields__phone=mobile)
            email = user.email
        else:
            email = self.data.get('uservalue')

        password = self.data.get('password')
        if not password:
            return JsonResponse({
                "status": 400,
                "message": "Password fiels can not be blank",
            })
        try:
            user = User.objects.get(email=email)
            if not user.is_active:
                registration = Registration.objects.get(user=user)
                profile = UserProfile.objects.get(user=user)
                compose_and_send_activation_email(user, profile, registration)
                return JsonResponse({
                    "status": 400,
                    "message": "Please check mail and active it.",
               })
            try:
                if user and LoginFailures.is_feature_enabled():
                    if LoginFailures.is_user_locked_out(user):
                        raise AuthFailedError(_('This account has been temporarily locked due '
                                    'to excessive login failures. Try again later.'))
                #_check_excessive_login_attempts(user)
            except Exception as e:
                logs.info(e.message)
                return JsonResponse(status=200, data={
                    "status": 400,
                    "message": "Login attempt failed",
            })

            new_user = authenticate_new_user(request, user.username, password)
            #compose_and_send_activation_email(user, profile)
            #compose_and_send_activation_email(user, profile, registration)
            django_login(request, new_user)
            request.session.set_expiry(604800 * 4)
            if not new_user.extrafields.rpincode:
                response =  JsonResponse(status=200, data={
                    "status":200,
                    "message": "profile",
                    "userid":new_user.id,
                    "number":new_user.extrafields.rcountry_code
                 })
            else:
                response = JsonResponse(status=200, data={
                    "status": 200,
                    "message": "success",
            })
            devlog = set_logged_in_cookies(request, response, new_user)
            return devlog

        except Exception as e:
            logs.info(e.message)
            return JsonResponse(status=200, data={
                    "status": 400,
                    "message": "Enter Valid password",
            })
Esempio n. 20
0
    def tma_users_registered(self):
        context = {}
        enrolled_to_current_course = False
        user_email = self.request.POST['user_email']

        if User.objects.filter(email=user_email).exists():
            user = User.objects.get(email=user_email)
            #Get preprofile info
            userprofile = UserProfile.objects.get(user=user)
            try:
                custom_field = json.loads(user.profile.custom_field)
                first_name = custom_field['first_name']
                last_name = custom_field['last_name']
            except:
                custom_field = {}
                last_name = 'Undefined'
                first_name = 'Undefined'

            #Get courses enrollments
            microsite_courses = get_courses(
                user=user,
                org=configuration_helpers.get_value('course_org_filter'))

            user_ms_course_list = {}
            for course in microsite_courses:
                if CourseEnrollment.objects.filter(
                        user=user, course_id=course.id).exists():
                    user_ms_course_list[str(course.id)] = {
                        'course_name':
                        course.display_name_with_default,
                        'course_grades':
                        '/courses/' + str(course.id) + '/progress/' +
                        str(user.id),
                        'opened_enrollments':
                        is_enrollment_opened(course),
                        'opened_course':
                        is_course_opened(course),
                        'on_invitation':
                        course.invitation_only,
                    }

            #Get user grade for this course
            course_key = SlashSeparatedCourseKey.from_deprecated_string(
                self.course_id)
            current_course = get_course_by_id(course_key)
            grade = CourseGradeFactory().create(user, current_course)

            #User dates
            if user.last_login is not None:
                last_login = user.date_joined.strftime("%d-%b-%Y %H:%M:%S")
            else:
                last_login = _('User has not logged in yet')

            context = {
                'email': str(user_email),
                'id': str(user.id),
                'inscription':
                str(user.date_joined.strftime("%d-%b-%Y %H:%M:%S")),
                'last_login': last_login,
                'first_name': first_name,
                'last_name': last_name,
                'user_ms_course_list': user_ms_course_list,
                'custom_field': custom_field,
                'grade': grade.grade_value['percent'],
                'passed': grade.grade_value['grade'],
                'active': user.is_active,
                'login_failure': LoginFailures.is_user_locked_out(user),
            }

            #Check if user is registered to this course
            course_key = SlashSeparatedCourseKey.from_deprecated_string(
                self.course_id)
            if CourseEnrollment.objects.filter(user=user,
                                               course_id=course_key,
                                               is_active=1).exists():
                current_course_grades = '/courses/' + str(
                    self.course_id) + '/progress/' + str(user.id)
                context['enrolled_to_current_course'] = True
                context['current_course_grades'] = current_course_grades
            else:
                context['enrolled_to_current_course'] = False

        else:
            context = {
                'error':
                'Le participant n\'a pas de compte sur nos plateformes.'
            }

        return context
Esempio n. 21
0
 def decorator(*args, **kwargs):
     """Decorator class to return"""
     if not LoginFailures.is_feature_enabled():
         return False
     return func(*args, **kwargs)
Esempio n. 22
0
def post(request, error=""):  # pylint: disable-msg=too-many-statements,unused-argument
    """AJAX request to log in the user."""

    backend_name = None
    email = None
    password = None
    redirect_url = None
    response = None
    running_pipeline = None
    third_party_auth_requested = settings.FEATURES.get('ENABLE_THIRD_PARTY_AUTH') and pipeline.running(request)
    third_party_auth_successful = False
    trumped_by_first_party_auth = bool(request.POST.get('email')) or bool(request.POST.get('password'))
    user = None

    

    

    if 'email' not in request.POST or 'password' not in request.POST:
        return JsonResponse({
            "success": False,
            "value": _('There was an error receiving your login information. Please email us.'),  # TODO: User error message
        })  # TODO: this should be status code 400  # pylint: disable=fixme

    email = request.POST['email']
    password = request.POST['password']
    try:
        user = User.objects.get(email=email)
    except User.DoesNotExist:
        if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
            AUDIT_LOG.warning(u"Login failed - Unknown user email")
        else:
            AUDIT_LOG.warning(u"Login failed - Unknown user email: {0}".format(email))

    # see if account has been locked out due to excessive login failures
    user_found_by_email_lookup = user
    if user_found_by_email_lookup and LoginFailures.is_feature_enabled():
        if LoginFailures.is_user_locked_out(user_found_by_email_lookup):
            return JsonResponse({
                "success": False,
                "value": _('This account has been temporarily locked due to excessive login failures. Try again later.'),
            })  # TODO: this should be status code 429  # pylint: disable=fixme

    # see if the user must reset his/her password due to any policy settings
    if PasswordHistory.should_user_reset_password_now(user_found_by_email_lookup):
        return JsonResponse({
            "success": False,
            "value": _('Your password has expired due to password policy on this account. You must '
                       'reset your password before you can log in again. Please click the '
                       '"Forgot Password" link on this page to reset your password before logging in again.'),
        })  # TODO: this should be status code 403  # pylint: disable=fixme

    # if the user doesn't exist, we want to set the username to an invalid
    # username so that authentication is guaranteed to fail and we can take
    # advantage of the ratelimited backend
    username = user.username if user else ""

    if not third_party_auth_successful:
        try:
            user = authenticate(username=username, password=password, request=request)
        # this occurs when there are too many attempts from the same IP address
        except RateLimitException:
            return JsonResponse({
                "success": False,
                "value": _('Too many failed login attempts. Try again later.'),
            })  # TODO: this should be status code 429  # pylint: disable=fixme

    if user is None:
        # tick the failed login counters if the user exists in the database
        if user_found_by_email_lookup and LoginFailures.is_feature_enabled():
            LoginFailures.increment_lockout_counter(user_found_by_email_lookup)

        # if we didn't find this username earlier, the account for this email
        # doesn't exist, and doesn't have a corresponding password
        if username != "":
            if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
                loggable_id = user_found_by_email_lookup.id if user_found_by_email_lookup else "<unknown>"
                AUDIT_LOG.warning(u"Login failed - password for user.id: {0} is invalid".format(loggable_id))
            else:
                AUDIT_LOG.warning(u"Login failed - password for {0} is invalid".format(email))
        return JsonResponse({
            "success": False,
            "value": _('Email or password is incorrect.'),
        })  # TODO: this should be status code 400  # pylint: disable=fixme

    # successful login, clear failed login attempts counters, if applicable
    if LoginFailures.is_feature_enabled():
        LoginFailures.clear_lockout_counter(user)

    if user is not None and user.is_active:
        try:
            # We do not log here, because we have a handler registered
            # to perform logging on successful logins.
            login(request, user)
            if request.POST.get('remember') == 'true':
                request.session.set_expiry(604800)
                log.debug("Setting user session to never expire")
            else:
                request.session.set_expiry(0)
        except Exception as e:
            AUDIT_LOG.critical("Login failed - Could not create session. Is memcached running?")
            log.critical("Login failed - Could not create session. Is memcached running?")
            log.exception(e)
            raise

        
        response = JsonResponse({
            "success": True,
            "username": user.username,
            "email": user.email
        })

        # set the login cookie for the edx marketing site 
        # we want this cookie to be accessed via javascript
        # so httponly is set to None

        if request.session.get_expire_at_browser_close():
            max_age = None
            expires = None
        else:
            max_age = request.session.get_expiry_age()
            expires_time = time.time() + max_age
            expires = cookie_date(expires_time)

        response.set_cookie(
            settings.EDXMKTG_COOKIE_NAME, 'true', max_age=max_age,
            expires=expires, domain=settings.SESSION_COOKIE_DOMAIN,
            path='/', secure=None, httponly=None,
        )

        return response

    if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
        AUDIT_LOG.warning(u"Login failed - Account not active for user.id: {0}, resending activation".format(user.id))
    else:
        AUDIT_LOG.warning(u"Login failed - Account not active for user {0}, resending activation".format(username))

    reactivation_email_for_user(user)
    not_activated_msg = _("This account has not been activated. We have sent another activation message. Please check your e-mail for the activation instructions.")
    return JsonResponse({
        "success": False,
        "value": not_activated_msg,
    })  # TODO: this should be status code 400  # pylint: disable=fixme
Esempio n. 23
0
    def post(self, request):
        response_data = {}
        # Add some rate limiting here by re-using the RateLimitMixin as a helper class
        limiter = BadRequestRateLimiter()
        if limiter.is_rate_limit_exceeded(request):
            response_data['message'] = _('Rate limit exceeded in api login.')
            return Response(response_data, status=status.HTTP_403_FORBIDDEN)

        base_uri = generate_base_uri(request)
        try:
            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)

        # see if the user must reset his/her password due to any policy settings
        if existing_user and PasswordHistory.should_user_reset_password_now(existing_user):
            response_status = status.HTTP_403_FORBIDDEN
            response_data['message'] = _(
                'Your password has expired due to password policy on this account. '
                'You must reset your password before you can log in again.'
            )
            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:
                    #
                    # Create a new session directly with the SESSION_ENGINE
                    # We don't call the django.contrib.auth login() method
                    # because it is bound with the HTTP request.
                    #
                    # Since we are a server-to-server API, we shouldn't
                    # be stateful with respect to the HTTP request
                    # and anything that might come with it, as it could
                    # violate our RESTfulness
                    #
                    engine = import_module(settings.SESSION_ENGINE)
                    new_session = engine.SessionStore()
                    new_session.create()

                    # These values are expected to be set in any new session
                    new_session[SESSION_KEY] = user.id
                    new_session[BACKEND_SESSION_KEY] = user.backend

                    new_session.save()

                    response_data['token'] = new_session.session_key
                    response_data['expires'] = new_session.get_expiry_age()
                    user_dto = UserSerializer(user)
                    response_data['user'] = user_dto.data
                    response_data['uri'] = '{}/{}'.format(base_uri, new_session.session_key)
                    response_status = status.HTTP_201_CREATED

                    # generate a CSRF tokens for any web clients that may need to
                    # call into the LMS via Ajax (for example Notifications)
                    response_data['csrftoken'] = RequestContext(request, {}).get('csrf_token')

                    # update the last_login fields in the auth_user table for this user
                    user.last_login = timezone.now()
                    user.save()

                    # add to audit log
                    AUDIT_LOG.info(u"API::User logged in successfully with user-id - {0}".format(user.id))
                else:
                    response_status = status.HTTP_403_FORBIDDEN
            else:
                limiter.tick_bad_request_counter(request)
                # 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
                AUDIT_LOG.warn(u"API::User authentication failed with user-id - {0}".format(existing_user.id))
        else:
            AUDIT_LOG.warn(u"API::Failed login attempt with unknown email/username")
            response_status = status.HTTP_404_NOT_FOUND
        return Response(response_data, status=response_status)
Esempio n. 24
0
    def post(self, request):
        response_data = {}
        # Add some rate limiting here by re-using the RateLimitMixin as a helper class
        limiter = BadRequestRateLimiter()
        if limiter.is_rate_limit_exceeded(request):
            response_data['message'] = _('Rate limit exceeded in api login.')
            return Response(response_data, status=status.HTTP_403_FORBIDDEN)

        base_uri = generate_base_uri(request)
        try:
            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)

         # see if the user must reset his/her password due to any policy settings
        if existing_user and PasswordHistory.should_user_reset_password_now(existing_user):
            response_status = status.HTTP_403_FORBIDDEN
            response_data['message'] = _(
                'Your password has expired due to password policy on this account. '
                'You must reset your password before you can log in again.'
            )
            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
                    response_data['expires'] = request.session.get_expiry_age()
                    user_dto = UserSerializer(user)
                    response_data['user'] = user_dto.data
                    response_data['uri'] = '{}/{}'.format(base_uri, request.session.session_key)
                    response_status = status.HTTP_201_CREATED

                    # add to audit log
                    AUDIT_LOG.info(u"API::User logged in successfully with user-id - {0}".format(user.id))
                else:
                    response_status = status.HTTP_403_FORBIDDEN
            else:
                limiter.tick_bad_request_counter(request)
                # 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
                AUDIT_LOG.warn(u"API::User authentication failed with user-id - {0}".format(existing_user.id))
        else:
            AUDIT_LOG.warn(u"API::Failed login attempt with unknown email/username")
            response_status = status.HTTP_404_NOT_FOUND
        return Response(response_data, status=response_status)
Esempio n. 25
0
    def login_user(request, session_id=None):
        """ Create a new session and login the user, or upgrade an existing session """
        response_data = {}
        # Add some rate limiting here by re-using the RateLimitMixin as a helper class
        limiter = BadRequestRateLimiter()
        if limiter.is_rate_limit_exceeded(request):
            response_data['message'] = _('Rate limit exceeded in api login.')
            return Response(response_data, status=status.HTTP_403_FORBIDDEN)

        base_uri = generate_base_uri(request)

        username = request.data.get('username', None)
        if username is None:
            return Response({'message': _('username is missing')},
                            status=status.HTTP_400_BAD_REQUEST)

        password = request.data.get('password', None)
        if password is None:
            return Response({'message': _('password is missing')},
                            status=status.HTTP_400_BAD_REQUEST)

        try:
            existing_user = User.objects.get(username=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. '  # pylint: disable=C0301
                    'Try again later.')
                return Response(response_data, status=response_status)

        # see if the user must reset his/her password due to any policy settings
        if existing_user and PasswordHistory.should_user_reset_password_now(
                existing_user):
            response_status = status.HTTP_403_FORBIDDEN
            response_data['message'] = _(
                'Your password has expired due to password policy on this account. '
                'You must reset your password before you can log in again.')
            return Response(response_data, status=response_status)

        if existing_user:
            user = authenticate(username=existing_user.username,
                                password=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:
                    #
                    # Create a new session directly with the SESSION_ENGINE
                    # We don't call the django.contrib.auth login() method
                    # because it is bound with the HTTP request.
                    #
                    # Since we are a server-to-server API, we shouldn't
                    # be stateful with respect to the HTTP request
                    # and anything that might come with it, as it could
                    # violate our RESTfulness
                    #
                    engine = import_module(settings.SESSION_ENGINE)
                    if session_id is None:
                        session = engine.SessionStore()
                        session.create()
                        success_status = status.HTTP_201_CREATED
                    else:
                        session = engine.SessionStore(session_id)
                        success_status = status.HTTP_200_OK
                        if SESSION_KEY in session:
                            # Someone is already logged in. The user ID of whoever is logged in
                            # now might be different than the user ID we've been asked to login,
                            # which would be bad. But even if it is the same user, we should not
                            # be asked to login a user who is already logged in. This likely
                            # indicates some sort of programming/validation error and possibly
                            # even a potential security issue - so return 403.
                            return Response({},
                                            status=status.HTTP_403_FORBIDDEN)

                    # These values are expected to be set in any new session
                    session[SESSION_KEY] = user.id
                    session[BACKEND_SESSION_KEY] = user.backend
                    if hasattr(user, 'get_session_auth_hash'):
                        session_auth_hash = user.get_session_auth_hash()
                    else:
                        session_auth_hash = ''
                    session[HASH_SESSION_KEY] = session_auth_hash

                    session.save()

                    response_data['token'] = session.session_key
                    response_data['expires'] = session.get_expiry_age()
                    user_dto = SimpleUserSerializer(user)
                    response_data['user'] = user_dto.data
                    response_data['uri'] = '{}/{}'.format(
                        base_uri, session.session_key)
                    response_status = success_status

                    # generate a CSRF tokens for any web clients that may need to
                    # call into the LMS via Ajax (for example Notifications)
                    response_data['csrftoken'] = str(
                        csrf(request)['csrf_token'])

                    # update the last_login fields in the auth_user table for this user
                    user.last_login = timezone.now()
                    user.save()

                    # add to audit log
                    AUDIT_LOG.info(
                        "API::User logged in successfully with user-id - {}".
                        format(user.id))  # pylint: disable=W1202
                else:
                    response_status = status.HTTP_403_FORBIDDEN
            else:
                limiter.tick_request_counter(request)

                # 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
                AUDIT_LOG.warn(
                    "API::User authentication failed with user-id - {}".format(
                        existing_user.id))  # pylint: disable=W1202
        else:
            AUDIT_LOG.warn(
                "API::Failed login attempt with unknown email/username")
            response_status = status.HTTP_404_NOT_FOUND
        return Response(response_data, status=response_status)
Esempio n. 26
0
def login_user_custom(request, error=""):  # pylint: disable=too-many-statements,unused-argument
    """AJAX request to log in the user."""

    backend_name = None
    email = None
    password = None
    redirect_url = None
    response = None
    running_pipeline = None
    third_party_auth_requested = third_party_auth.is_enabled(
    ) and pipeline.running(request)
    third_party_auth_successful = False
    trumped_by_first_party_auth = bool(request.POST.get('email')) or bool(
        request.POST.get('password'))
    user = None
    platform_name = configuration_helpers.get_value("platform_name",
                                                    settings.PLATFORM_NAME)

    if third_party_auth_requested and not trumped_by_first_party_auth:
        # The user has already authenticated via third-party auth and has not
        # asked to do first party auth by supplying a username or password. We
        # now want to put them through the same logging and cookie calculation
        # logic as with first-party auth.
        running_pipeline = pipeline.get(request)
        username = running_pipeline['kwargs'].get('username')
        backend_name = running_pipeline['backend']
        third_party_uid = running_pipeline['kwargs']['uid']
        requested_provider = provider.Registry.get_from_pipeline(
            running_pipeline)

        try:
            user = pipeline.get_authenticated_user(requested_provider,
                                                   username, third_party_uid)
            third_party_auth_successful = True
        except User.DoesNotExist:
            AUDIT_LOG.warning(
                u"Login failed - user with username {username} has no social auth "
                "with backend_name {backend_name}".format(
                    username=username, backend_name=backend_name))
            message = _(
                "You've successfully logged into your {provider_name} account, "
                "but this account isn't linked with an {platform_name} account yet."
            ).format(
                platform_name=platform_name,
                provider_name=requested_provider.name,
            )
            message += "<br/><br/>"
            message += _(
                "Use your {platform_name} username and password to log into {platform_name} below, "
                "and then link your {platform_name} account with {provider_name} from your dashboard."
            ).format(
                platform_name=platform_name,
                provider_name=requested_provider.name,
            )
            message += "<br/><br/>"
            message += _(
                "If you don't have an {platform_name} account yet, "
                "click <strong>Register</strong> at the top of the page."
            ).format(platform_name=platform_name)

            return HttpResponse(message, content_type="text/plain", status=403)

    else:

        if 'email' not in request.POST or 'password' not in request.POST:
            return JsonResponse({
                "success":
                False,
                # TODO: User error message
                "value":
                _('There was an error receiving your login information. Please email us.'
                  ),
            })  # TODO: this should be status code 400

        email = request.POST['email']
        password = request.POST['password']
        try:
            user = User.objects.get(email=email)
        except User.DoesNotExist:
            if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
                AUDIT_LOG.warning(u"Login failed - Unknown user email")
            else:
                AUDIT_LOG.warning(
                    u"Login failed - Unknown user email: {0}".format(email))

    # check if the user has a linked shibboleth account, if so, redirect the user to shib-login
    # This behavior is pretty much like what gmail does for shibboleth.  Try entering some @stanford.edu
    # address into the Gmail login.
    if settings.FEATURES.get('AUTH_USE_SHIB') and user:
        try:
            eamap = ExternalAuthMap.objects.get(user=user)
            if eamap.external_domain.startswith(
                    openedx.core.djangoapps.external_auth.views.
                    SHIBBOLETH_DOMAIN_PREFIX):
                return JsonResponse({
                    "success": False,
                    "redirect": reverse('shib-login'),
                })  # TODO: this should be status code 301  # pylint: disable=fixme
        except ExternalAuthMap.DoesNotExist:
            # This is actually the common case, logging in user without external linked login
            AUDIT_LOG.info(u"User %s w/o external auth attempting login", user)

    # see if account has been locked out due to excessive login failures
    user_found_by_email_lookup = user
    if user_found_by_email_lookup and LoginFailures.is_feature_enabled():
        if LoginFailures.is_user_locked_out(user_found_by_email_lookup):
            lockout_message = _(
                'This account has been temporarily locked due '
                'to excessive login failures. Try again later.')
            return JsonResponse({
                "success": False,
                "value": lockout_message,
            })  # TODO: this should be status code 429  # pylint: disable=fixme

    # see if the user must reset his/her password due to any policy settings
    if user_found_by_email_lookup and PasswordHistory.should_user_reset_password_now(
            user_found_by_email_lookup):
        return JsonResponse({
            "success":
            False,
            "value":
            _('Your password has expired due to password policy on this account. You must '
              'reset your password before you can log in again. Please click the '
              '"Forgot Password" link on this page to reset your password before logging in again.'
              ),
        })  # TODO: this should be status code 403  # pylint: disable=fixme

    # if the user doesn't exist, we want to set the username to an invalid
    # username so that authentication is guaranteed to fail and we can take
    # advantage of the ratelimited backend
    username = user.username if user else ""

    if not third_party_auth_successful:
        try:
            user = authenticate(username=username,
                                password=password,
                                request=request)
        # this occurs when there are too many attempts from the same IP address
        except RateLimitException:
            return JsonResponse({
                "success":
                False,
                "value":
                _('Too many failed login attempts. Try again later.'),
            })  # TODO: this should be status code 429  # pylint: disable=fixme

    if user is None:
        # tick the failed login counters if the user exists in the database
        if user_found_by_email_lookup and LoginFailures.is_feature_enabled():
            LoginFailures.increment_lockout_counter(user_found_by_email_lookup)

        # if we didn't find this username earlier, the account for this email
        # doesn't exist, and doesn't have a corresponding password
        if username != "":
            if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
                loggable_id = user_found_by_email_lookup.id if user_found_by_email_lookup else "<unknown>"
                AUDIT_LOG.warning(
                    u"Login failed - password for user.id: {0} is invalid".
                    format(loggable_id))
            else:
                AUDIT_LOG.warning(
                    u"Login failed - password for {0} is invalid".format(
                        email))
        return JsonResponse({
            "success": False,
            "value": _('Email or password is incorrect.'),
        })  # TODO: this should be status code 400  # pylint: disable=fixme

    # successful login, clear failed login attempts counters, if applicable
    if LoginFailures.is_feature_enabled():
        LoginFailures.clear_lockout_counter(user)

    # Track the user's sign in
    if hasattr(settings, 'LMS_SEGMENT_KEY') and settings.LMS_SEGMENT_KEY:
        tracking_context = tracker.get_tracker().resolve_context()
        analytics.identify(
            user.id,
            {
                'email': email,
                'username': username
            },
            {
                # Disable MailChimp because we don't want to update the user's email
                # and username in MailChimp on every page load. We only need to capture
                # this data on registration/activation.
                'MailChimp': False
            })

        analytics.track(user.id,
                        "edx.bi.user.account.authenticated", {
                            'category': "conversion",
                            'label': request.POST.get('course_id'),
                            'provider': None
                        },
                        context={
                            'ip': tracking_context.get('ip'),
                            'Google Analytics': {
                                'clientId': tracking_context.get('client_id')
                            }
                        })

    if user is not None and user.is_active:
        try:
            # We do not log here, because we have a handler registered
            # to perform logging on successful logins.
            login(request, user)
            if request.POST.get('remember') == 'true':
                request.session.set_expiry(604800)
                log.debug("Setting user session to never expire")
            else:
                request.session.set_expiry(0)
        except Exception as exc:  # pylint: disable=broad-except
            AUDIT_LOG.critical(
                "Login failed - Could not create session. Is memcached running?"
            )
            log.critical(
                "Login failed - Could not create session. Is memcached running?"
            )
            log.exception(exc)
            raise

        redirect_url = None  # The AJAX method calling should know the default destination upon success
        if third_party_auth_successful:
            redirect_url = pipeline.get_complete_url(backend_name)

        response = JsonResponse({
            "success": True,
            "redirect_url": redirect_url,
        })

        # Ensure that the external marketing site can
        # detect that the user is logged in.
        return set_logged_in_cookies(request, response, user)

    if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
        AUDIT_LOG.warning(
            u"Login failed - Account not active for user.id: {0}, resending activation"
            .format(user.id))
    else:
        AUDIT_LOG.warning(
            u"Login failed - Account not active for user {0}, resending activation"
            .format(username))

    reactivation_email_for_user_custom(request, user)
    not_activated_msg = _(
        "Before you sign in, you need to activate your account. We have sent you an "
        "email message with instructions for activating your account.")
    return JsonResponse({
        "success": False,
        "value": not_activated_msg,
    })  # TODO: this should be status code 400  # pylint: disable=fixme