Esempio n. 1
0
 def _change_password(self, user, password):
     """
     Helper method to change password on user and record in the PasswordHistory
     """
     user.set_password(password)
     user.save()
     history = PasswordHistory()
     history.create(user)
Esempio n. 2
0
    def test_pbkdf2_sha256_password_reuse(self):
        """
        Assert against the password reuse policy but using the normal Django PBKDF2
        """
        user = self._user_factory_with_history()
        staff = self._user_factory_with_history(is_staff=True)

        # students need to user at least one different passwords before reuse
        self.assertFalse(
            PasswordHistory.is_allowable_password_reuse(user, "test"))
        self.assertTrue(
            PasswordHistory.is_allowable_password_reuse(user, "different"))
        self._change_password(user, "different")

        self.assertTrue(
            PasswordHistory.is_allowable_password_reuse(user, "test"))

        # staff needs to use at least two different passwords before reuse
        self.assertFalse(
            PasswordHistory.is_allowable_password_reuse(staff, "test"))
        self.assertTrue(
            PasswordHistory.is_allowable_password_reuse(staff, "different"))
        self._change_password(staff, "different")

        self.assertFalse(
            PasswordHistory.is_allowable_password_reuse(staff, "test"))
        self.assertFalse(
            PasswordHistory.is_allowable_password_reuse(staff, "different"))
        self.assertTrue(
            PasswordHistory.is_allowable_password_reuse(staff, "third"))
        self._change_password(staff, "third")

        self.assertTrue(
            PasswordHistory.is_allowable_password_reuse(staff, "test"))
Esempio n. 3
0
    def test_disabled_too_frequent_password_resets(self):
        """
        Verify properly default behavior when feature is disabled
        """
        student = self._user_factory_with_history()

        self.assertFalse(PasswordHistory.is_password_reset_too_soon(student))
Esempio n. 4
0
    def test_too_frequent_password_resets(self):
        """
        Assert that a user should not be able to password reset too frequently
        """
        student = self._user_factory_with_history()
        grandfathered_student = self._user_factory_with_history(
            set_initial_history=False)

        self.assertTrue(PasswordHistory.is_password_reset_too_soon(student))
        self.assertFalse(
            PasswordHistory.is_password_reset_too_soon(grandfathered_student))

        staff_reset_time = timezone.now() + timedelta(days=100)
        with freeze_time(staff_reset_time):
            self.assertFalse(
                PasswordHistory.is_password_reset_too_soon(student))
Esempio n. 5
0
    def _user_factory_with_history(self,
                                   is_staff=False,
                                   set_initial_history=True):
        """
        Helper method to generate either an Admin or a User
        """
        if is_staff:
            user = AdminFactory()
        else:
            user = UserFactory()

        user.date_joined = timezone.now()

        if set_initial_history:
            history = PasswordHistory()
            history.create(user)

        return user
Esempio n. 6
0
    def _create_user(self, data, errors, response):
        """Register user and add him to a company."""
        user = data.get('user')
        email = user.get('email')
        company = data.get('company')

        # Create the user and their profile.
        try:
            # User
            user = User.objects.create(**user)
            user.set_password(user.password)
            user.save()
            data['user_object'] = user
            # Profile
            UserProfile.objects.create(user=user,
                                       name='{} {}'.format(
                                           user.first_name, user.last_name))
            # Notifications
            if settings.FEATURES.get('ENABLE_DISCUSSION_EMAIL_DIGEST'):
                enable_notifications(user)
            # Password History
            password_history_entry = PasswordHistory()
            password_history_entry.create(user)
        except Exception as exc:
            self._add_error(errors, str(exc.message),
                            _('Registering Participant'), email)
        else:
            response['user_id'] = user.id
            AUDIT_LOG.info("API::New account created with user-id - {}".format(
                user.id))

        # Associate with company.
        try:
            company.users.add(user)
        except Exception as exc:
            self._add_error(errors, str(exc.message),
                            _('Enrolling Participant in Company'), email)

        user_organization_updated.send(sender=__name__,
                                       user_id=user.id,
                                       organization_id=company.id)
Esempio n. 7
0
    def test_no_forced_password_change(self):
        """
        Assert that if we skip configuration, then user will never have to force reset password
        """
        student = self._user_factory_with_history()
        staff = self._user_factory_with_history(is_staff=True)

        # also create a user who doesn't have any history
        grandfathered_student = UserFactory()
        grandfathered_student.date_joined = timezone.now()

        self.assertFalse(
            PasswordHistory.should_user_reset_password_now(student))
        self.assertFalse(PasswordHistory.should_user_reset_password_now(staff))
        self.assertFalse(
            PasswordHistory.should_user_reset_password_now(
                grandfathered_student))

        staff_reset_time = timezone.now() + timedelta(days=100)
        with freeze_time(staff_reset_time):
            self.assertFalse(
                PasswordHistory.should_user_reset_password_now(student))
            self.assertFalse(
                PasswordHistory.should_user_reset_password_now(
                    grandfathered_student))
            self.assertFalse(
                PasswordHistory.should_user_reset_password_now(staff))
Esempio n. 8
0
    def test_retirement(self):
        """
        Verify that the user's password history contains no actual
        passwords after retirement is called.
        """
        user = self._user_factory_with_history()

        # create multiple rows in the password history table
        self._change_password(user, "different")
        self._change_password(user, "differentagain")
        # ensure the rows were actually created and stored the passwords
        self.assertTrue(
            PasswordHistory.objects.filter(user_id=user.id).exists())
        for row in PasswordHistory.objects.filter(user_id=user.id):
            self.assertNotEqual(row.password, "")

        # retire the user and ensure that the rows are still present, but with no passwords
        PasswordHistory.retire_user(user.id)
        self.assertTrue(
            PasswordHistory.objects.filter(user_id=user.id).exists())
        for row in PasswordHistory.objects.filter(user_id=user.id):
            self.assertEqual(row.password, "")
Esempio n. 9
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. 10
0
    def test_forced_password_change(self):
        """
        Assert when passwords must be reset
        """
        student = self._user_factory_with_history()
        staff = self._user_factory_with_history(is_staff=True)
        grandfathered_student = self._user_factory_with_history(
            set_initial_history=False)

        self.assertFalse(
            PasswordHistory.should_user_reset_password_now(student))
        self.assertFalse(PasswordHistory.should_user_reset_password_now(staff))
        self.assertFalse(
            PasswordHistory.should_user_reset_password_now(
                grandfathered_student))

        staff_reset_time = timezone.now() + timedelta(days=1)
        with freeze_time(staff_reset_time):
            self.assertFalse(
                PasswordHistory.should_user_reset_password_now(student))
            self.assertFalse(
                PasswordHistory.should_user_reset_password_now(
                    grandfathered_student))
            self.assertTrue(
                PasswordHistory.should_user_reset_password_now(staff))

            self._change_password(staff, 'Different')
            self.assertFalse(
                PasswordHistory.should_user_reset_password_now(staff))

        student_reset_time = timezone.now() + timedelta(days=5)

        with freeze_time(student_reset_time):
            self.assertTrue(
                PasswordHistory.should_user_reset_password_now(student))
            self.assertTrue(
                PasswordHistory.should_user_reset_password_now(
                    grandfathered_student))
            self.assertTrue(
                PasswordHistory.should_user_reset_password_now(staff))

            self._change_password(student, 'Different')
            self.assertFalse(
                PasswordHistory.should_user_reset_password_now(student))

            self._change_password(grandfathered_student, 'Different')
            self.assertFalse(
                PasswordHistory.should_user_reset_password_now(
                    grandfathered_student))

            self._change_password(staff, 'Different')
            self.assertFalse(
                PasswordHistory.should_user_reset_password_now(staff))