예제 #1
0
class ForgotPasswordAPiTests(TestCase):
    def setUp(self):
        self.user = BaseUserFactory()
        self.unknown_user_email = faker.email()
        self.url = reverse('api:auth:forgot-password-reset')
        self.data = {
            'user': self.user.email
        }

    def test_unknown_user_cannot_initiate_password_reset(self):
        data = {
            'user': self.unknown_user_email
        }

        response = self.post(self.url, data=data)
        self.assertEqual(response.status_code, 400)

    def test_inactive_user_cannot_initiate_password_reset(self):
        response = self.post(self.url, data=self.data)
        self.assertEqual(response.status_code, 400)

    @patch('odin.authentication.apis.initiate_reset_user_password')
    def test_active_user_can_initiate_password_reset(self, mock_object):
        self.user.is_active = True
        self.user.save()

        response = self.post(self.url, data=self.data)
        self.assertTrue(mock_object.called)
        self.assertEqual(response.status_code, 202)
예제 #2
0
class LoginApiTest(TestCase):
    def setUp(self):
        self.test_password = faker.password()
        self.test_email = faker.email()
        self.user = BaseUserFactory(email=self.test_email)
        self.user.set_password(self.test_password)
        self.user.is_active = True
        self.user.save()
        self.login_url = reverse('api:auth:login')

    def test_user_cannot_login_with_wrong_email(self):
        email = faker.email()
        data = {
            'email': email,
            'password': self.test_password,
        }

        response = client.post(self.login_url, data=data)

        self.assertEqual(response.status_code, 400)

    def test_user_cannot_login_with_wrong_password(self):
        password = faker.password()
        data = {
            'email': self.user.email,
            'password': password,
        }

        response = client.post(self.login_url, data=data)

        self.assertEqual(response.status_code, 400)

    def test_user_cannot_login_if_account_is_inactive(self):
        self.user.is_active = False
        self.user.save()

        data = {
            'email': self.user.email,
            'password': self.test_password,
        }

        response = client.post(self.login_url, data=data)

        self.assertEqual(response.status_code, 400)

    @patch('odin.authentication.apis.get_user_data')
    def test_user_can_login_when_account_is_active(self, mock_object):
        mock_object.return_value = {}

        data = {
            'email': self.user.email,
            'password': self.test_password,
        }

        response = self.post(self.login_url, data=data)

        self.assertTrue(mock_object.called)
        self.assertEqual(response.status_code, 200)
예제 #3
0
class LogoutApiTest(TestCase):
    def setUp(self):
        self.test_email = faker.email()
        self.test_password = faker.password()
        self.user = BaseUserFactory(email=self.test_email)
        self.user.set_password(self.test_password)
        self.user.is_active = True
        self.user.save()
        self.login_url = reverse('api:auth:login')
        self.logout_url = reverse('api:auth:logout')
        self.data = {
            'email': self.test_email,
            'password': self.test_password,
        }

    @patch('odin.authentication.apis.get_user_data')
    def test_logout_user_with_invalid_token(self, mock_object):
        mock_object.return_value = {}
        # performing api login
        login_response = client.post(self.login_url, data=self.data)

        token = login_response.data['token']

        # that should invalidate all generated tokens until now
        self.user.rotate_secret_key()

        # try to perform api logout
        logout_response = client.post(self.logout_url,
                                      **{'HTTP_AUTHORIZATION': f'JWT {token}'})

        self.assertEqual(logout_response.status_code, 401)

    @patch('odin.authentication.apis.get_user_data')
    @patch('odin.authentication.apis.logout')
    def test_logout_user_with_valid_token(self, mock1, mock2):
        mock2.return_value = {}

        # performing api login
        login_response = client.post(self.login_url, data=self.data)

        token = login_response.data['token']

        # try to perform api logout
        logout_response = client.post(self.logout_url,
                                      **{'HTTP_AUTHORIZATION': f'JWT {token}'})

        self.assertEqual(mock1.called, True)
        self.assertEqual(mock2.called, True)
        self.assertEqual(logout_response.status_code, 202)
예제 #4
0
class TestIsStudentOrTeacherPermission(TestCase):
    def setUp(self):
        self.user = BaseUserFactory()
        self.user.is_active = True
        self.user.save()
        self.request = Mock()
        self.request.user = self.user

    def test_permission_if_user_is_not_student_or_teacher(self):
        permissions = IsStudentOrTeacherPermission()

        self.assertFalse(self.user.is_student())
        self.assertFalse(self.user.is_teacher())
        self.assertFalse(permissions.has_permission(self.request, None))

    def test_permission_if_user_is_student(self):
        student = Student.objects.create_from_user(self.user)
        student.save()

        permissions = IsStudentOrTeacherPermission()

        self.assertTrue(self.user.is_student())
        self.assertFalse(self.user.is_teacher())
        self.assertTrue(permissions.has_permission(self.request, None))

    def test_permission_if_user_is_teacher(self):
        teacher = Teacher.objects.create_from_user(self.user)
        teacher.save()

        permissions = IsStudentOrTeacherPermission()

        self.assertTrue(self.user.is_teacher())
        self.assertFalse(self.user.is_student())
        self.assertTrue(permissions.has_permission(self.request, None))

    def test_permission_if_user_is_teacher_and_student(self):
        teacher = Teacher.objects.create_from_user(self.user)
        teacher.save()

        student = Student.objects.create_from_user(self.user)
        student.save()

        permissions = IsStudentOrTeacherPermission()

        self.assertTrue(self.user.is_student())
        self.assertTrue(self.user.is_teacher())
        self.assertTrue(permissions.has_permission(self.request, None))
예제 #5
0
class TestUserDetailApi(TestCase):
    def setUp(self):
        self.test_email = faker.email()
        self.test_password = faker.password()
        self.user = BaseUserFactory(email=self.test_email)
        self.user.set_password(self.test_password)
        self.user.is_active = True
        self.user.save()
        self.login_url = reverse('api:auth:login')
        self.user_detail_url = reverse('api:auth:user-detail')
        self.data = {
            'email': self.test_email,
            'password': self.test_password,
        }

    @patch('odin.authentication.apis.get_user_data')
    def test_cannot_fetch_user_data_with_invalid_token(self, mock_object):
        mock_object.return_value = {}

        # perform login
        login_response = client.post(self.login_url, data=self.data)

        token = login_response.data['token']

        # that should invalidate all previously aquired tokens
        self.user.rotate_secret_key()

        me_response = client.get(self.user_detail_url, **{'HTTP_AUTHORIZATION': f'JWT {token}'})

        self.assertTrue(mock_object.called)
        self.assertEqual(me_response.status_code, 401)
        self.assertEqual(me_response.data['errors'][0]['code'], 'authentication_failed')

    @patch('odin.authentication.apis.get_user_data')
    def test_can_fetch_use_data_with_valid_token(self, mock_object):
        mock_object.return_value = {}
        # perform login
        login_response = client.post(self.login_url, data=self.data)

        token = login_response.data['token']

        me_response = client.get(self.user_detail_url, **{'HTTP_AUTHORIZATION': f'JWT {token}'})

        self.assertTrue(mock_object.called)
        self.assertEqual(me_response.status_code, 200)
예제 #6
0
class InitiateResetUserPasswordTests(TestCase):
    def setUp(self):
        self.user = BaseUserFactory()

    def test_inactive_user_cannot_initiate_password_reset(self):
        self.user.is_active = False
        self.user.save()

        with self.assertRaises(ValidationError):
            initiate_reset_user_password(user=self.user)

    @patch('odin.authentication.services.send_mail')
    def test_active_user_can_initiate_password_reset(self, mock_object):
        self.user.is_active = True
        self.user.save()

        token = initiate_reset_user_password(user=self.user)

        self.assertTrue(mock_object.called)
        self.assertEqual(isinstance(token, PasswordResetToken), True)
        self.assertIsNone(token.voided_at)
        self.assertIsNone(token.used_at)
예제 #7
0
    def test_user_can_decode_only_own_tokens(self):
        response1 = self.post(self.login_url, data=self.data)

        user = BaseUserFactory()
        user.is_active = True
        user.passwd = faker.password()
        user.set_password(user.passwd)
        user.save()

        data = {
            'email': user.email,
            'password': user.passwd,
        }

        response2 = self.post(self.login_url, data=data)

        token_user1 = response1.data['token']
        token_user2 = response2.data['token']

        self.assertNotEqual(token_user1, token_user2)
        self.assertNotEqual(self.user.secret_key, user.secret_key)

        with self.assertRaises(InvalidSignatureError):
            jwt.decode(token_user1, key=str(user.secret_key))

        with self.assertRaises(InvalidSignatureError):
            jwt.decode(token_user2, key=str(self.user.secret_key))

        self.assertEqual(
            self.user.email,
            jwt.decode(token_user1, key=str(self.user.secret_key))['email']
        )

        self.assertEqual(
            user.email,
            jwt.decode(token_user2, key=str(user.secret_key))['email']
        )
예제 #8
0
class TestIsStudentOrTeacherInCoursePermission(TestCase):
    def setUp(self):
        self.user = BaseUserFactory()
        self.user.is_active = True
        self.user.save()
        self.request = Mock()
        self.request.user = self.user
        self.course = CourseFactory()
        self.view = make_mock_object()
        self.view.kwargs = {'course_id': self.course.id}

    def test_permission_if_user_is_not_student_or_teacher_in_course(self):

        permissions = IsStudentOrTeacherInCoursePermission()

        self.assertFalse(permissions.has_permission(self.request, self.view))

    def test_permission_if_user_is_student_in_course(self):
        student = Student.objects.create_from_user(self.user)
        student.save()

        ca = CourseAssignment()

        ca.course = self.course
        ca.student = student
        ca.save()

        permissions = IsStudentOrTeacherInCoursePermission()

        self.assertTrue(self.user.is_student())
        self.assertTrue(permissions.has_permission(self.request, self.view))

    def test_permission_if_user_is_teacher_in_course(self):
        teacher = Teacher.objects.create_from_user(self.user)
        teacher.save()

        ca = CourseAssignment()

        ca.course = self.course
        ca.teacher = teacher
        ca.save()

        permissions = IsStudentOrTeacherInCoursePermission()

        self.assertTrue(self.user.is_teacher())
        self.assertTrue(permissions.has_permission(self.request, self.view))

    def test_permission_if_user_is_student_and_teacher_in_course(self):
        teacher = Teacher.objects.create_from_user(self.user)
        teacher.save()

        student = Student.objects.create_from_user(self.user)
        student.save()

        ca1 = CourseAssignment()
        ca2 = CourseAssignment()

        ca1.course = self.course
        ca1.teacher = teacher
        ca1.save()

        ca2.course = self.course
        ca2.student = student
        ca2.save()

        permissions = IsStudentOrTeacherInCoursePermission()

        self.assertTrue(self.user.is_teacher())
        self.assertTrue(self.user.is_student())
        self.assertTrue(permissions.has_permission(self.request, self.view))
예제 #9
0
class TestJWTSecret(TestCase):
    def setUp(self):
        self.test_user_email = faker.email()
        self.test_password = faker.password()
        self.user = BaseUserFactory(email=self.test_user_email)
        self.user.set_password(self.test_password)
        self.user.is_active = True
        self.user.save()
        self.init_secret_key = self.user.secret_key
        self.login_url = reverse('api:auth:login')
        self.logout_url = reverse('api:auth:logout')
        self.user_detail_url = reverse('api:auth:user-detail')
        self.data = {
            'email': self.user.email,
            'password': self.test_password,
        }

    def test_user_can_decode_only_own_tokens(self):
        response1 = self.post(self.login_url, data=self.data)

        user = BaseUserFactory()
        user.is_active = True
        user.passwd = faker.password()
        user.set_password(user.passwd)
        user.save()

        data = {
            'email': user.email,
            'password': user.passwd,
        }

        response2 = self.post(self.login_url, data=data)

        token_user1 = response1.data['token']
        token_user2 = response2.data['token']

        self.assertNotEqual(token_user1, token_user2)
        self.assertNotEqual(self.user.secret_key, user.secret_key)

        with self.assertRaises(InvalidSignatureError):
            jwt.decode(token_user1, key=str(user.secret_key))

        with self.assertRaises(InvalidSignatureError):
            jwt.decode(token_user2, key=str(self.user.secret_key))

        self.assertEqual(
            self.user.email,
            jwt.decode(token_user1, key=str(self.user.secret_key))['email']
        )

        self.assertEqual(
            user.email,
            jwt.decode(token_user2, key=str(user.secret_key))['email']
        )

    def test_user_can_access_urls_with_token_only_after_login(self):
        self.response = self.post(self.login_url, data=self.data)

        token = self.response.data['token']

        response = client.get(self.user_detail_url, **{'HTTP_AUTHORIZATION': f'JWT {token}'})

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data['email'], self.user.email)

    def test_user_cannot_use_old_token_after_logout(self):
        self.response = self.post(self.login_url, data=self.data)
        token = self.response.data['token']

        # performs logout with jwt
        client.post(self.logout_url, **{'HTTP_AUTHORIZATION': f'JWT {token}'})

        response = client.get(self.user_detail_url, **{'HTTP_AUTHORIZATION': f'JWT {token}'})

        self.assertEqual(response.status_code, 401)

    def test_user_gets_new_user_secret_key_after_logout(self):
        self.response = self.post(self.login_url, data=self.data)
        token = self.response.data['token']

        # performs logout with jwt
        client.post(self.logout_url, **{'HTTP_AUTHORIZATION': f'JWT {token}'})

        self.user.refresh_from_db()

        self.assertNotEqual(self.init_secret_key, self.user.secret_key)
예제 #10
0
class TestUserChangePasswordApi(TestCase):
    def setUp(self):
        self.test_email = faker.email()
        self.test_password = faker.password()
        self.user = BaseUserFactory(email=self.test_email)
        self.user.set_password(self.test_password)
        self.user.save()
        self.login_url = reverse('api:auth:login')
        self.logout_url = reverse('api:auth:logout')
        self.change_password_url = reverse('api:auth:change-password')
        self.data = {
            'email': self.test_email,
            'password': self.test_password,
        }

    @patch('odin.authentication.apis.get_user_data')
    def test_logged_out_user_cannot_change_password(self, mock_object):
        mock_object.return_value = {}
        self.user.is_active = True
        self.user.save()

        # this should perform login
        login_response = client.post(self.login_url, data=self.data)

        token = login_response.data['token']

        # perform logout
        client.post(self.logout_url, **{'HTTP_AUTHORIZATION': f'JWT {token}'})

        data = {
            "old_password": self.test_password,
            "new_password": faker.password(),
        }

        # this should perform change password
        change_password_response = client.post(
            self.change_password_url,
            json.dumps(data),
            **{
                'HTTP_AUTHORIZATION': f'JWT {token}',
                'content_type': 'application/json'
            },
        )

        self.assertEqual(change_password_response.status_code, 401)
        self.assertEqual(change_password_response.data['errors'][0]['code'],
                         'authentication_failed')

    @patch('odin.authentication.apis.get_user_data')
    def test_inactive_user_cannot_change_password(self, mock_object):
        mock_object.return_value = {}
        self.user.is_active = True
        self.user.save()

        # this should perform login
        login_response = client.post(self.login_url, data=self.data)

        token = login_response.data['token']

        self.user.is_active = False
        self.user.save()

        data = {
            'old_password': self.test_password,
            'new_password': faker.password(),
        }

        # this should perform change password
        change_password_response = client.post(
            self.change_password_url,
            json.dumps(data),
            **{
                'HTTP_AUTHORIZATION': f'JWT {token}',
                'content_type': 'application/json'
            },
        )

        self.assertEqual(change_password_response.status_code, 401)
        self.assertEqual(change_password_response.data['errors'][0]['message'],
                         'User account is disabled.')

    @patch('odin.authentication.apis.get_user_data')
    def test_user_cannot_change_password_with_invalid_token(self, mock_object):
        mock_object.return_value = {}
        self.user.is_active = True
        self.user.save()

        # this should perform login
        login_response = client.post(self.login_url, data=self.data)

        token = login_response.data['token']

        self.user.rotate_secret_key()

        data = {
            'old_password': self.test_password,
            'new_password': faker.password(),
        }

        # this should perform change password
        change_password_response = client.post(
            self.change_password_url,
            json.dumps(data),
            **{
                'HTTP_AUTHORIZATION': f'JWT {token}',
                'content_type': 'application/json'
            },
        )

        self.assertEqual(change_password_response.status_code, 401)
        self.assertEqual(change_password_response.data['errors'][0]['code'],
                         'authentication_failed')

    @patch('odin.authentication.apis.get_user_data')
    def test_user_cannot_change_password_with_wrong_old_password(
            self, mock_object):
        mock_object.return_value = {}
        self.user.is_active = True
        self.user.save()

        # this should perform login
        login_response = client.post(self.login_url, data=self.data)

        token = login_response.data['token']

        data = {
            "old_password": faker.password(),
            "new_password": faker.password(),
        }

        # this should perform change password
        change_password_response = client.post(
            self.change_password_url,
            json.dumps(data),
            **{
                'HTTP_AUTHORIZATION': f'JWT {token}',
                'content_type': 'application/json'
            },
        )

        self.assertEqual(change_password_response.status_code, 400)
        self.assertEqual(change_password_response.data['errors'][0]['message'],
                         'Old password is invalid.')

    @patch('odin.authentication.apis.change_user_password')
    @patch('odin.authentication.apis.get_user_data')
    def test_active_user_can_change_password_with_valid_token(
            self, mock1, mock2):
        mock1.return_value = {}
        self.user.is_active = True
        self.user.save()

        # this should perform login
        login_response = client.post(self.login_url, data=self.data)

        token = login_response.data['token']

        data = {
            "old_password": self.test_password,
            "new_password": faker.password(),
        }

        # this should perform change password
        change_password_response = client.post(
            self.change_password_url,
            json.dumps(data),
            **{
                'HTTP_AUTHORIZATION': f'JWT {token}',
                'content_type': 'application/json'
            },
        )

        self.assertTrue(mock2.called)
        self.assertEqual(change_password_response.status_code, 202)