Esempio n. 1
0
    def setUp(self):
        super(ComPAIRLearningRecordTestCase, self).setUp()
        self.data = SimpleAnswersTestData()
        self.auth_data = ThirdPartyAuthTestData()
        self.lti_data = LTITestData()
        self.course = self.data.main_course
        self.assignment = self.data.assignments[0]

        self.user = self.data.create_user(SystemRole.instructor)
        self.data.enrol_user(self.user, self.data.get_course(),
                             CourseRole.instructor)

        self.global_unique_identifier = 'mock_puid_è_global_unique_identifier'
Esempio n. 2
0
    def test_cas_login(self):
        auth_data = ThirdPartyAuthTestData()
        user = self.data.create_user(SystemRole.instructor)
        third_party_user = auth_data.create_third_party_user(user=user)

        with mock.patch('flask_cas.CAS.username', new_callable=mock.PropertyMock) as mocked_cas_username:
            # test cas login disabled
            self.app.config['CAS_LOGIN_ENABLED'] = False
            mocked_cas_username.return_value = third_party_user.unique_identifier
            rv = self.client.get('/api/auth/cas', data={}, content_type='application/json', follow_redirects=True)
            self.assert403(rv)

            # test cas login enabled
            self.app.config['CAS_LOGIN_ENABLED'] = True
            mocked_cas_username.return_value = third_party_user.unique_identifier
            rv = self.client.get('/api/auth/cas', data={}, content_type='application/json', follow_redirects=True)
            self.assert200(rv)
Esempio n. 3
0
    def setUp(self):
        super(ComPAIRLearningRecordTestCase, self).setUp()
        self.data = SimpleAnswersTestData()
        self.auth_data = ThirdPartyAuthTestData()
        self.course = self.data.main_course
        self.assignment = self.data.assignments[0]

        self.cas_user_auth = self.auth_data.create_cas_user_auth(
            SystemRole.instructor)
        self.cas_user = self.cas_user_auth.user
        self.data.enrol_user(self.cas_user, self.data.get_course(),
                             CourseRole.instructor)

        self.saml_user_auth = self.auth_data.create_saml_user_auth(
            SystemRole.instructor)
        self.saml_user = self.saml_user_auth.user
        self.data.enrol_user(self.saml_user, self.data.get_course(),
                             CourseRole.instructor)
Esempio n. 4
0
    def setUp(self):
        super(ComPAIRLearningRecordTestCase, self).setUp()
        self.data = SimpleAnswersTestData()
        self.auth_data = ThirdPartyAuthTestData()
        self.course = self.data.main_course
        self.assignment = self.data.assignments[0]

        self.cas_user_auth = self.auth_data.create_cas_user_auth(SystemRole.instructor)
        self.cas_user = self.cas_user_auth.user
        self.data.enrol_user(self.cas_user, self.data.get_course(), CourseRole.instructor)

        self.saml_user_auth = self.auth_data.create_saml_user_auth(SystemRole.instructor)
        self.saml_user = self.saml_user_auth.user
        self.data.enrol_user(self.saml_user, self.data.get_course(), CourseRole.instructor)
Esempio n. 5
0
    def test_cas_login(self):
        auth_data = ThirdPartyAuthTestData()
        user = self.data.create_user(SystemRole.instructor)
        third_party_user = auth_data.create_third_party_user(user=user)

        response_mock = mock.MagicMock()
        response_mock.success = True
        response_mock.user = third_party_user.unique_identifier
        response_mock.attributes = None

        with mock.patch('compair.api.login.validate_cas_ticket',
                        return_value=response_mock):
            # test cas login disabled
            self.app.config['CAS_LOGIN_ENABLED'] = False
            rv = self.client.get('/api/cas/auth?ticket=mock_ticket',
                                 follow_redirects=True)
            self.assert403(rv)

            # test cas login enabled
            self.app.config['CAS_LOGIN_ENABLED'] = True
            rv = self.client.get('/api/cas/auth?ticket=mock_ticket',
                                 follow_redirects=True)
            self.assert200(rv)
Esempio n. 6
0
class AccountLearningRecordTests(ComPAIRLearningRecordTestCase):
    def setUp(self):
        super(ComPAIRLearningRecordTestCase, self).setUp()
        self.data = SimpleAnswersTestData()
        self.auth_data = ThirdPartyAuthTestData()
        self.course = self.data.main_course
        self.assignment = self.data.assignments[0]

        self.cas_user_auth = self.auth_data.create_cas_user_auth(
            SystemRole.instructor)
        self.cas_user = self.cas_user_auth.user
        self.data.enrol_user(self.cas_user, self.data.get_course(),
                             CourseRole.instructor)

        self.saml_user_auth = self.auth_data.create_saml_user_auth(
            SystemRole.instructor)
        self.saml_user = self.saml_user_auth.user
        self.data.enrol_user(self.saml_user, self.data.get_course(),
                             CourseRole.instructor)

    def test_actor_accounts(self):
        for user, third_party_auth in [(self.cas_user, self.cas_user_auth),
                                       (self.saml_user, self.saml_user_auth)]:

            # test without homepage set
            # (should use compair actor account)
            self.app.config[
                'LRS_ACTOR_ACCOUNT_USE_GLOBAL_UNIQUE_IDENTIFIER'] = True
            self.app.config[
                'LRS_ACTOR_ACCOUNT_GLOBAL_UNIQUE_IDENTIFIER_HOMEPAGE'] = None
            expected_actor = self.get_compair_xapi_actor(user)

            on_assignment_modified.send(current_app._get_current_object(),
                                        event_name=on_assignment_modified.name,
                                        user=user,
                                        assignment=self.assignment)
            statements = self.get_and_clear_xapi_statement_log()
            self.assertEqual(len(statements), 1)
            self.assertEqual(statements[0]['actor'], expected_actor)

            # test with homepage set and global unique identifier not set
            # (should use compair actor account)
            self.app.config[
                'LRS_ACTOR_ACCOUNT_GLOBAL_UNIQUE_IDENTIFIER_HOMEPAGE'] = "http://third.party.homepage"
            on_assignment_modified.send(current_app._get_current_object(),
                                        event_name=on_assignment_modified.name,
                                        user=user,
                                        assignment=self.assignment)
            statements = self.get_and_clear_xapi_statement_log()
            self.assertEqual(len(statements), 1)
            self.assertEqual(statements[0]['actor'], expected_actor)
            expected_actor = self.get_compair_xapi_actor(user)

            # test with homepage set and global unique identifier set
            # (should use cas/saml actor account with overridden value used for name)
            user.global_unique_identifier = 'mock_puid_è_' + third_party_auth.third_party_type.value
            db.session.commit()
            expected_actor = self.get_unique_identifier_xapi_actor(
                user, "http://third.party.homepage/",
                'mock_puid_è_' + third_party_auth.third_party_type.value)
            on_assignment_modified.send(current_app._get_current_object(),
                                        event_name=on_assignment_modified.name,
                                        user=user,
                                        assignment=self.assignment)
            statements = self.get_and_clear_xapi_statement_log()
            self.assertEqual(len(statements), 1)
            self.assertEqual(statements[0]['actor'], expected_actor)

            # disabling LRS_ACTOR_ACCOUNT_USE_GLOBAL_UNIQUE_IDENTIFIER should skip checking global unique identifer
            self.app.config[
                'LRS_ACTOR_ACCOUNT_USE_GLOBAL_UNIQUE_IDENTIFIER'] = False
            expected_actor = self.get_compair_xapi_actor(user)
            on_assignment_modified.send(current_app._get_current_object(),
                                        event_name=on_assignment_modified.name,
                                        user=user,
                                        assignment=self.assignment)
            statements = self.get_and_clear_xapi_statement_log()
            self.assertEqual(len(statements), 1)
            self.assertEqual(statements[0]['actor'], expected_actor)
Esempio n. 7
0
    def test_cas_login(self):
        auth_data = ThirdPartyAuthTestData()

        # test login new user
        for system_role in [SystemRole.student, SystemRole.instructor, SystemRole.sys_admin]:
            unique_identifier = system_role.value + "_no_attributes"
            response_mock = mock.MagicMock()
            response_mock.success = True
            response_mock.user = unique_identifier
            response_mock.attributes = None

            self.app.config['CAS_ATTRIBUTE_FIRST_NAME'] = None
            self.app.config['CAS_ATTRIBUTE_LAST_NAME'] = None
            self.app.config['CAS_ATTRIBUTE_STUDENT_NUMBER'] = None
            self.app.config['CAS_ATTRIBUTE_EMAIL'] = None
            self.app.config['CAS_ATTRIBUTE_USER_ROLE'] = None
            self.app.config['CAS_INSTRUCTOR_ROLE_VALUES'] = {}
            self.app.config['CAS_GLOBAL_UNIQUE_IDENTIFIER_FIELD'] = None

            with mock.patch('compair.api.login.validate_cas_ticket', return_value=response_mock):
                # test cas login disabled
                self.app.config['CAS_LOGIN_ENABLED'] = False
                rv = self.client.get('/api/cas/auth?ticket=mock_ticket', follow_redirects=True)
                self.assert403(rv)

                # test cas login enabled with unsuccessful login
                self.app.config['CAS_LOGIN_ENABLED'] = True
                response_mock.success = False
                rv = self.client.get('/api/cas/auth?ticket=mock_ticket', follow_redirects=True)
                self.assert200(rv)

                # check session
                with self.client.session_transaction() as sess:
                    # check that user is logged in
                    self.assertEqual(sess.get('THIRD_PARTY_AUTH_ERROR_TYPE'), 'CAS')
                    self.assertEqual(sess.get('THIRD_PARTY_AUTH_ERROR_MSG'), 'Login Failed. CAS ticket was invalid.')

                # test cas login enabled with unsuccessful login
                response_mock.success = True
                response_mock.user = None
                rv = self.client.get('/api/cas/auth?ticket=mock_ticket', follow_redirects=True)
                self.assert200(rv)

                # check session
                with self.client.session_transaction() as sess:
                    # check that user is logged in
                    self.assertEqual(sess.get('THIRD_PARTY_AUTH_ERROR_TYPE'), 'CAS')
                    self.assertEqual(sess.get('THIRD_PARTY_AUTH_ERROR_MSG'), 'Login Failed. Expecting CAS username to be set.')

                # test cas login enabled with successful login
                response_mock.user = unique_identifier
                rv = self.client.get('/api/cas/auth?ticket=mock_ticket', follow_redirects=True)
                self.assert200(rv)

                third_party_user = ThirdPartyUser.query \
                    .filter_by(unique_identifier=unique_identifier, third_party_type=ThirdPartyType.cas)\
                    .one()

                self.assertIsNotNone(third_party_user)
                self.assertIsNotNone(third_party_user.user)
                self.assertEqual(third_party_user.user.system_role, SystemRole.student)
                self.assertIsNone(third_party_user.user.firstname)
                self.assertIsNone(third_party_user.user.lastname)
                six.assertRegex(self, third_party_user.user.displayname, r"^Student_\d{8}")
                self.assertIsNone(third_party_user.user.email)
                self.assertIsNone(third_party_user.user.student_number)
                self.assertIsNone(third_party_user.user.global_unique_identifier)

                with self.client.session_transaction() as sess:
                    self.assertEqual(sess.get('user_id'), str(third_party_user.user.id))

                # unused attributes
                unique_identifier = system_role.value + "_with_unused_attributes"
                response_mock.user = unique_identifier
                response_mock.attributes = {
                    'firstName': 'f_name',
                    'lastName': 'l_name',
                    'studentNumber': "student1" if system_role == SystemRole.student else None,
                    'email': '*****@*****.**',
                    'system_role_field': system_role.value,
                    'puid': system_role.value+"_puid",
                }

                rv = self.client.get('/api/cas/auth?ticket=mock_ticket', follow_redirects=True)
                self.assert200(rv)
                third_party_user = ThirdPartyUser.query \
                    .filter_by(unique_identifier=unique_identifier, third_party_type=ThirdPartyType.cas)\
                    .one()

                self.assertIsNotNone(third_party_user)
                self.assertIsNotNone(third_party_user.user)
                self.assertEqual(third_party_user.user.system_role, SystemRole.student)
                self.assertIsNone(third_party_user.user.firstname)
                self.assertIsNone(third_party_user.user.lastname)
                six.assertRegex(self, third_party_user.user.displayname, r"^Student_\d{8}")
                self.assertIsNone(third_party_user.user.email)
                self.assertIsNone(third_party_user.user.student_number)
                self.assertIsNone(third_party_user.user.global_unique_identifier)

                # used attributes and no valid instructor values
                self.app.config['CAS_ATTRIBUTE_FIRST_NAME'] = 'firstName'
                self.app.config['CAS_ATTRIBUTE_LAST_NAME'] = 'lastName'
                self.app.config['CAS_ATTRIBUTE_STUDENT_NUMBER'] = 'studentNumber'
                self.app.config['CAS_ATTRIBUTE_EMAIL'] = 'email'
                self.app.config['CAS_ATTRIBUTE_USER_ROLE'] = 'system_role_field'
                self.app.config['CAS_INSTRUCTOR_ROLE_VALUES'] = {}
                self.app.config['CAS_GLOBAL_UNIQUE_IDENTIFIER_FIELD'] = 'puid'
                unique_identifier = system_role.value + "_with_used_attributes"
                response_mock.user = unique_identifier

                rv = self.client.get('/api/cas/auth?ticket=mock_ticket', follow_redirects=True)
                self.assert200(rv)
                third_party_user = ThirdPartyUser.query \
                    .filter_by(unique_identifier=unique_identifier, third_party_type=ThirdPartyType.cas) \
                    .one()

                self.assertIsNotNone(third_party_user)
                self.assertIsNotNone(third_party_user.user)
                self.assertEqual(third_party_user.user.system_role, SystemRole.student)
                self.assertEqual(third_party_user.user.firstname, 'f_name')
                self.assertEqual(third_party_user.user.lastname, 'l_name')
                self.assertEqual(third_party_user.user.email, '*****@*****.**')
                self.assertEqual(third_party_user.user.global_unique_identifier, system_role.value+"_puid")
                if system_role == SystemRole.student:
                    self.assertEqual(third_party_user.user.student_number, 'student1')
                else:
                    self.assertIsNone(third_party_user.user.student_number)
                six.assertRegex(self, third_party_user.user.displayname, r"^Student_\d{8}")

                # used attributes and valid instructor values
                unique_identifier = system_role.value + "_with_used_attributes2"
                response_mock.user = unique_identifier
                if system_role == SystemRole.student:
                    response_mock.attributes['studentNumber'] = "student2"
                response_mock.attributes['puid'] = system_role.value+"_puid2"
                self.app.config['CAS_INSTRUCTOR_ROLE_VALUES'] = {SystemRole.sys_admin.value, SystemRole.instructor.value}

                rv = self.client.get('/api/cas/auth?ticket=mock_ticket', follow_redirects=True)
                self.assert200(rv)
                third_party_user = ThirdPartyUser.query \
                    .filter_by(unique_identifier=unique_identifier, third_party_type=ThirdPartyType.cas)\
                    .one()

                self.assertIsNotNone(third_party_user)
                self.assertIsNotNone(third_party_user.user)
                self.assertEqual(third_party_user.user.firstname, 'f_name')
                self.assertEqual(third_party_user.user.lastname, 'l_name')
                self.assertEqual(third_party_user.user.email, '*****@*****.**')
                self.assertEqual(third_party_user.user.global_unique_identifier, system_role.value+"_puid2")
                if system_role == SystemRole.student:
                    self.assertEqual(third_party_user.user.system_role, SystemRole.student)
                    six.assertRegex(self, third_party_user.user.displayname, r"^Student_\d{8}")
                    self.assertEqual(third_party_user.user.student_number, 'student2')
                else:
                    self.assertEqual(third_party_user.user.system_role, SystemRole.instructor)
                    self.assertEqual(third_party_user.user.displayname, "f_name l_name")
                    self.assertIsNone(third_party_user.user.student_number)

        # test login existing user
        for system_role in [SystemRole.student, SystemRole.instructor, SystemRole.sys_admin]:
            self.app.config['CAS_ATTRIBUTE_FIRST_NAME'] = None
            self.app.config['CAS_ATTRIBUTE_LAST_NAME'] = None
            self.app.config['CAS_ATTRIBUTE_STUDENT_NUMBER'] = None
            self.app.config['CAS_ATTRIBUTE_EMAIL'] = None
            self.app.config['CAS_ATTRIBUTE_USER_ROLE'] = None
            self.app.config['CAS_INSTRUCTOR_ROLE_VALUES'] = {}
            self.app.config['CAS_GLOBAL_UNIQUE_IDENTIFIER_FIELD'] = None

            user = self.data.create_user(system_role)
            third_party_user = auth_data.create_third_party_user(user=user, third_party_type=ThirdPartyType.cas)

            original_firstname = user.firstname
            original_lastname = user.lastname
            original_email = user.email
            original_student_number = user.student_number
            new_student_number = original_student_number+"123" if user.student_number else None

            response_mock = mock.MagicMock()
            response_mock.success = True
            response_mock.user = third_party_user.unique_identifier
            response_mock.attributes = None

            with mock.patch('compair.api.login.validate_cas_ticket', return_value=response_mock):
                # test cas login disabled
                self.app.config['CAS_LOGIN_ENABLED'] = False
                rv = self.client.get('/api/cas/auth?ticket=mock_ticket', follow_redirects=True)
                self.assert403(rv)

                # test cas login enabled
                self.app.config['CAS_LOGIN_ENABLED'] = True
                rv = self.client.get('/api/cas/auth?ticket=mock_ticket', follow_redirects=True)
                self.assert200(rv)

                # test overwrite disabled with no attributes
                self.app.config['ALLOW_STUDENT_CHANGE_DISPLAY_NAME'] = True
                self.app.config['ALLOW_STUDENT_CHANGE_NAME'] = True
                self.app.config['ALLOW_STUDENT_CHANGE_STUDENT_NUMBER'] = True
                self.app.config['ALLOW_STUDENT_CHANGE_EMAIL'] = True
                rv = self.client.get('/api/cas/auth?ticket=mock_ticket', follow_redirects=True)
                self.assert200(rv)
                self.assertEqual(user.firstname, original_firstname)
                self.assertEqual(user.lastname, original_lastname)
                self.assertEqual(user.email, original_email)
                self.assertEqual(user.student_number, original_student_number)
                self.assertIsNone(user.global_unique_identifier)

                # test overwrite enabled with no attributes
                self.app.config['ALLOW_STUDENT_CHANGE_DISPLAY_NAME'] = False
                self.app.config['ALLOW_STUDENT_CHANGE_NAME'] = False
                self.app.config['ALLOW_STUDENT_CHANGE_STUDENT_NUMBER'] = False
                self.app.config['ALLOW_STUDENT_CHANGE_EMAIL'] = False
                rv = self.client.get('/api/cas/auth?ticket=mock_ticket', follow_redirects=True)
                self.assert200(rv)
                self.assertEqual(user.firstname, original_firstname)
                self.assertEqual(user.lastname, original_lastname)
                self.assertEqual(user.email, original_email)
                self.assertEqual(user.student_number, original_student_number)
                self.assertIsNone(user.global_unique_identifier)

                # test overwrite enabled with no attributes
                self.app.config['CAS_ATTRIBUTE_FIRST_NAME'] = 'firstName'
                self.app.config['CAS_ATTRIBUTE_LAST_NAME'] = 'lastName'
                self.app.config['CAS_ATTRIBUTE_STUDENT_NUMBER'] = 'studentNumber'
                self.app.config['CAS_ATTRIBUTE_EMAIL'] = 'email'
                self.app.config['CAS_GLOBAL_UNIQUE_IDENTIFIER_FIELD'] = 'puid'
                rv = self.client.get('/api/cas/auth?ticket=mock_ticket', follow_redirects=True)
                self.assert200(rv)
                self.assertEqual(user.firstname, original_firstname)
                self.assertEqual(user.lastname, original_lastname)
                self.assertEqual(user.email, original_email)
                self.assertEqual(user.student_number, original_student_number)
                self.assertIsNone(user.global_unique_identifier)

                response_mock.attributes = {
                    'firstName': 'f_name',
                    'lastName': 'l_name',
                    'studentNumber': new_student_number,
                    'email': '*****@*****.**',
                    'puid': 'should_be_ignored_since_already_linked'
                }

                # test overwrite disabled with attributes
                self.app.config['ALLOW_STUDENT_CHANGE_DISPLAY_NAME'] = True
                self.app.config['ALLOW_STUDENT_CHANGE_NAME'] = True
                self.app.config['ALLOW_STUDENT_CHANGE_STUDENT_NUMBER'] = True
                self.app.config['ALLOW_STUDENT_CHANGE_EMAIL'] = True
                rv = self.client.get('/api/cas/auth?ticket=mock_ticket', follow_redirects=True)
                self.assert200(rv)
                self.assertEqual(user.firstname, original_firstname)
                self.assertEqual(user.lastname, original_lastname)
                self.assertEqual(user.email, original_email)
                self.assertEqual(user.student_number, original_student_number)
                self.assertIsNone(user.global_unique_identifier)

                # test overwrite enabled with attributes
                self.app.config['ALLOW_STUDENT_CHANGE_DISPLAY_NAME'] = False
                self.app.config['ALLOW_STUDENT_CHANGE_NAME'] = False
                self.app.config['ALLOW_STUDENT_CHANGE_STUDENT_NUMBER'] = False
                self.app.config['ALLOW_STUDENT_CHANGE_EMAIL'] = False
                rv = self.client.get('/api/cas/auth?ticket=mock_ticket', follow_redirects=True)
                self.assert200(rv)

                if system_role == SystemRole.student:
                    self.assertEqual(user.firstname, 'f_name')
                    self.assertEqual(user.lastname, 'l_name')
                    self.assertEqual(user.email, '*****@*****.**')
                    self.assertEqual(user.student_number, new_student_number)
                else:
                    self.assertEqual(user.firstname, original_firstname)
                    self.assertEqual(user.lastname, original_lastname)
                    self.assertEqual(user.email, original_email)
                    self.assertEqual(user.student_number, original_student_number)
                self.assertIsNone(user.global_unique_identifier)

            self.app.config['CAS_ATTRIBUTE_USER_ROLE'] = 'system_role_field'
            self.app.config['CAS_INSTRUCTOR_ROLE_VALUES'] = {SystemRole.instructor.value}
            # test automatic upgrading of system role for existing accounts
            for third_party_system_role in [SystemRole.student, SystemRole.instructor, SystemRole.sys_admin]:
                user = self.data.create_user(system_role)
                third_party_user = auth_data.create_third_party_user(user=user, third_party_type=ThirdPartyType.cas)
                db.session.commit()

                response_mock.user = third_party_user.unique_identifier
                response_mock.attributes = {
                    'system_role_field': third_party_system_role.value
                }

                with mock.patch('compair.api.login.validate_cas_ticket', return_value=response_mock):
                    rv = self.client.get('/api/cas/auth?ticket=mock_ticket', follow_redirects=True)
                    self.assert200(rv)

                    # compair user system role will upgrade
                    if system_role == SystemRole.student:
                        # cannot upgrade to admin
                        if third_party_system_role == SystemRole.instructor:
                            self.assertEqual(user.system_role, SystemRole.instructor)
                        else:
                            self.assertEqual(user.system_role, SystemRole.student)
                    elif system_role == SystemRole.instructor:
                        # cannot upgrade to admin and shouldn't downgrade to student
                        self.assertEqual(user.system_role, SystemRole.instructor)
                    elif system_role == SystemRole.sys_admin:
                        # shouldn't downgrade
                        self.assertEqual(user.system_role, SystemRole.sys_admin)
Esempio n. 8
0
    def test_saml_login(self):
        auth_data = ThirdPartyAuthTestData()

        # test login new user
        for system_role in [SystemRole.student, SystemRole.instructor, SystemRole.sys_admin]:
            unique_identifier = system_role.value + "_no_attributes"
            response_mock = mock.MagicMock()
            response_mock.__errors = []
            response_mock.is_authenticated.return_value = True
            response_mock.get_attributes.return_value = {
                'urn:oid:0.9.2342.19200300.100.1.1': [unique_identifier]
            }
            response_mock.get_nameid.return_value = "saml_mock_nameid"
            response_mock.get_session_index.return_value = "saml_session_index"

            self.app.config['SAML_ATTRIBUTE_FIRST_NAME'] = None
            self.app.config['SAML_ATTRIBUTE_LAST_NAME'] = None
            self.app.config['SAML_ATTRIBUTE_STUDENT_NUMBER'] = None
            self.app.config['SAML_ATTRIBUTE_EMAIL'] = None
            self.app.config['SAML_ATTRIBUTE_USER_ROLE'] = None
            self.app.config['SAML_INSTRUCTOR_ROLE_VALUES'] = {}
            self.app.config['SAML_GLOBAL_UNIQUE_IDENTIFIER_FIELD'] = None

            with mock.patch('compair.api.login.get_saml_auth_response', return_value=response_mock):
                # test saml login disabled
                self.app.config['SAML_LOGIN_ENABLED'] = False
                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert403(rv)

                # test saml login enabled with unsuccessful login
                self.app.config['SAML_LOGIN_ENABLED'] = True
                response_mock.is_authenticated.return_value = False
                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert200(rv)

                # check session
                with self.client.session_transaction() as sess:
                    # check that user is logged in
                    self.assertEqual(sess.get('THIRD_PARTY_AUTH_ERROR_TYPE'), 'SAML')
                    self.assertEqual(sess.get('THIRD_PARTY_AUTH_ERROR_MSG'), 'Login Failed.')

                # test saml login enabled with unsuccessful login
                response_mock.is_authenticated.return_value = True
                response_mock.__errors = ['error1', 'error2']
                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert200(rv)

                # check session
                with self.client.session_transaction() as sess:
                    # check that user is logged in
                    self.assertEqual(sess.get('THIRD_PARTY_AUTH_ERROR_TYPE'), 'SAML')
                    self.assertEqual(sess.get('THIRD_PARTY_AUTH_ERROR_MSG'), 'Login Failed.')

                # test saml login enabled with successful login
                response_mock.__errors = []
                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert200(rv)

                third_party_user = ThirdPartyUser.query \
                    .filter_by(unique_identifier=unique_identifier, third_party_type=ThirdPartyType.saml)\
                    .one()

                self.assertIsNotNone(third_party_user)
                self.assertIsNotNone(third_party_user.user)
                self.assertEqual(third_party_user.user.system_role, SystemRole.student)
                self.assertIsNone(third_party_user.user.firstname)
                self.assertIsNone(third_party_user.user.lastname)
                six.assertRegex(self, third_party_user.user.displayname, r"^Student_\d{8}")
                self.assertIsNone(third_party_user.user.email)
                self.assertIsNone(third_party_user.user.student_number)
                self.assertIsNone(third_party_user.user.global_unique_identifier)

                with self.client.session_transaction() as sess:
                    self.assertEqual(sess.get('user_id'), str(third_party_user.user.id))

                # unused attributes
                unique_identifier = system_role.value + "_with_unused_attributes"
                response_mock.get_attributes.return_value = {
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.9': ['*****@*****.**', '*****@*****.**'],
                    'urn:oid:2.5.4.42': ['f_name'],
                    'urn:oid:0.9.2342.19200300.100.1.1': [unique_identifier],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.1': ['Member'],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.10': [None],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.7': [system_role.value],
                    'urn:oid:2.5.4.4': ['l_name'],
                    'urn:oid:2.5.4.3': ['Me Myself And I'],
                    'urn:oid:2.5.4.20': ['555-5555'],
                    'urn:oid:2.16.840.1.113730.3.1.3': ["student1"] if system_role == SystemRole.student else [],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.6': ['*****@*****.**']
                }

                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert200(rv)
                third_party_user = ThirdPartyUser.query \
                    .filter_by(unique_identifier=unique_identifier, third_party_type=ThirdPartyType.saml)\
                    .one()

                self.assertIsNotNone(third_party_user)
                self.assertIsNotNone(third_party_user.user)
                self.assertEqual(third_party_user.user.system_role, SystemRole.student)
                self.assertIsNone(third_party_user.user.firstname)
                self.assertIsNone(third_party_user.user.lastname)
                six.assertRegex(self, third_party_user.user.displayname, r"^Student_\d{8}")
                self.assertIsNone(third_party_user.user.email)
                self.assertIsNone(third_party_user.user.student_number)
                self.assertIsNone(third_party_user.user.global_unique_identifier)

                # used attributes and no valid instructor values
                self.app.config['SAML_ATTRIBUTE_FIRST_NAME'] = 'urn:oid:2.5.4.42'
                self.app.config['SAML_ATTRIBUTE_LAST_NAME'] = 'urn:oid:2.5.4.4'
                self.app.config['SAML_ATTRIBUTE_STUDENT_NUMBER'] = 'urn:oid:2.16.840.1.113730.3.1.3'
                self.app.config['SAML_ATTRIBUTE_EMAIL'] = 'urn:oid:1.3.6.1.4.1.5923.1.1.1.6'
                self.app.config['SAML_ATTRIBUTE_USER_ROLE'] = 'urn:oid:1.3.6.1.4.1.5923.1.1.1.7'
                self.app.config['SAML_GLOBAL_UNIQUE_IDENTIFIER_FIELD'] = 'puid'
                self.app.config['SAML_INSTRUCTOR_ROLE_VALUES'] = {}
                unique_identifier = system_role.value + "_with_used_attributes"
                response_mock.get_attributes.return_value = {
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.9': ['*****@*****.**', '*****@*****.**'],
                    'urn:oid:2.5.4.42': ['f_name'],
                    'urn:oid:0.9.2342.19200300.100.1.1': [unique_identifier],
                    'puid': [system_role.value+"_puid"],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.1': ['Member'],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.10': [None],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.7': [system_role.value],
                    'urn:oid:2.5.4.4': ['l_name'],
                    'urn:oid:2.5.4.3': ['Me Myself And I'],
                    'urn:oid:2.5.4.20': ['555-5555'],
                    'urn:oid:2.16.840.1.113730.3.1.3': ["student1"] if system_role == SystemRole.student else [],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.6': ['*****@*****.**'],
                }

                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert200(rv)
                third_party_user = ThirdPartyUser.query \
                    .filter_by(unique_identifier=unique_identifier, third_party_type=ThirdPartyType.saml) \
                    .one()

                self.assertIsNotNone(third_party_user)
                self.assertIsNotNone(third_party_user.user)
                self.assertEqual(third_party_user.user.system_role, SystemRole.student)
                self.assertEqual(third_party_user.user.firstname, 'f_name')
                self.assertEqual(third_party_user.user.lastname, 'l_name')
                self.assertEqual(third_party_user.user.email, '*****@*****.**')
                self.assertEqual(third_party_user.user.global_unique_identifier, system_role.value+"_puid")
                if system_role == SystemRole.student:
                    self.assertEqual(third_party_user.user.student_number, 'student1')
                else:
                    self.assertIsNone(third_party_user.user.student_number)
                six.assertRegex(self, third_party_user.user.displayname, r"^Student_\d{8}")

                # used attributes and valid instructor values
                unique_identifier = system_role.value + "_with_used_attributes2"
                response_mock.get_attributes.return_value = {
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.9': ['*****@*****.**', '*****@*****.**'],
                    'urn:oid:2.5.4.42': ['f_name'],
                    'urn:oid:0.9.2342.19200300.100.1.1': [unique_identifier],
                    'puid': [system_role.value+"_puid2"],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.1': ['Member'],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.10': [None],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.7': [system_role.value],
                    'urn:oid:2.5.4.4': ['l_name'],
                    'urn:oid:2.5.4.3': ['Me Myself And I'],
                    'urn:oid:2.5.4.20': ['555-5555'],
                    'urn:oid:2.16.840.1.113730.3.1.3': ["student2"] if system_role == SystemRole.student else [],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.6': ['*****@*****.**']
                }
                self.app.config['SAML_INSTRUCTOR_ROLE_VALUES'] = {SystemRole.sys_admin.value, SystemRole.instructor.value}

                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert200(rv)
                third_party_user = ThirdPartyUser.query \
                    .filter_by(unique_identifier=unique_identifier, third_party_type=ThirdPartyType.saml)\
                    .one()

                self.assertIsNotNone(third_party_user)
                self.assertIsNotNone(third_party_user.user)
                self.assertEqual(third_party_user.user.firstname, 'f_name')
                self.assertEqual(third_party_user.user.lastname, 'l_name')
                self.assertEqual(third_party_user.user.email, '*****@*****.**')
                self.assertEqual(third_party_user.user.global_unique_identifier, system_role.value+"_puid2")
                if system_role == SystemRole.student:
                    self.assertEqual(third_party_user.user.system_role, SystemRole.student)
                    six.assertRegex(self, third_party_user.user.displayname, r"^Student_\d{8}")
                    self.assertEqual(third_party_user.user.student_number, 'student2')
                else:
                    self.assertEqual(third_party_user.user.system_role, SystemRole.instructor)
                    self.assertEqual(third_party_user.user.displayname, "f_name l_name")
                    self.assertIsNone(third_party_user.user.student_number)

        # test login existing user
        for system_role in [SystemRole.student, SystemRole.instructor, SystemRole.sys_admin]:
            self.app.config['SAML_ATTRIBUTE_FIRST_NAME'] = None
            self.app.config['SAML_ATTRIBUTE_LAST_NAME'] = None
            self.app.config['SAML_ATTRIBUTE_STUDENT_NUMBER'] = None
            self.app.config['SAML_ATTRIBUTE_EMAIL'] = None
            self.app.config['SAML_ATTRIBUTE_USER_ROLE'] = None
            self.app.config['SAML_INSTRUCTOR_ROLE_VALUES'] = {}
            self.app.config['SAML_GLOBAL_UNIQUE_IDENTIFIER_FIELD'] = None

            user = self.data.create_user(system_role)
            third_party_user = auth_data.create_third_party_user(user=user, third_party_type=ThirdPartyType.saml)

            original_firstname = user.firstname
            original_lastname = user.lastname
            original_email = user.email
            original_student_number = user.student_number
            new_student_number = original_student_number+"123" if user.student_number else None

            response_mock = mock.MagicMock()
            response_mock.__errors = []
            response_mock.is_authenticated.return_value = True
            response_mock.get_attributes.return_value = {
                'urn:oid:0.9.2342.19200300.100.1.1': [third_party_user.unique_identifier]
            }
            response_mock.get_nameid.return_value = "saml_mock_nameid"
            response_mock.get_session_index.return_value = "saml_session_index"

            with mock.patch('compair.api.login.get_saml_auth_response', return_value=response_mock):
                # test saml login disabled
                self.app.config['SAML_LOGIN_ENABLED'] = False
                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert403(rv)

                # test saml login enabled
                self.app.config['SAML_LOGIN_ENABLED'] = True
                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert200(rv)

                # test overwrite disabled with no attributes
                self.app.config['ALLOW_STUDENT_CHANGE_DISPLAY_NAME'] = True
                self.app.config['ALLOW_STUDENT_CHANGE_NAME'] = True
                self.app.config['ALLOW_STUDENT_CHANGE_STUDENT_NUMBER'] = True
                self.app.config['ALLOW_STUDENT_CHANGE_EMAIL'] = True
                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert200(rv)
                self.assertEqual(user.firstname, original_firstname)
                self.assertEqual(user.lastname, original_lastname)
                self.assertEqual(user.email, original_email)
                self.assertEqual(user.student_number, original_student_number)
                self.assertIsNone(user.global_unique_identifier)

                # test overwrite enabled with no attributes
                self.app.config['ALLOW_STUDENT_CHANGE_DISPLAY_NAME'] = False
                self.app.config['ALLOW_STUDENT_CHANGE_NAME'] = False
                self.app.config['ALLOW_STUDENT_CHANGE_STUDENT_NUMBER'] = False
                self.app.config['ALLOW_STUDENT_CHANGE_EMAIL'] = False
                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert200(rv)
                self.assertEqual(user.firstname, original_firstname)
                self.assertEqual(user.lastname, original_lastname)
                self.assertEqual(user.email, original_email)
                self.assertEqual(user.student_number, original_student_number)
                self.assertIsNone(user.global_unique_identifier)

                # test overwrite enabled with no attributes
                self.app.config['SAML_ATTRIBUTE_FIRST_NAME'] = 'urn:oid:2.5.4.42'
                self.app.config['SAML_ATTRIBUTE_LAST_NAME'] = 'urn:oid:2.5.4.4'
                self.app.config['SAML_ATTRIBUTE_STUDENT_NUMBER'] = 'urn:oid:2.16.840.1.113730.3.1.3'
                self.app.config['SAML_ATTRIBUTE_EMAIL'] = 'urn:oid:1.3.6.1.4.1.5923.1.1.1.6'
                self.app.config['SAML_GLOBAL_UNIQUE_IDENTIFIER_FIELD'] = 'puid'
                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert200(rv)
                self.assertEqual(user.firstname, original_firstname)
                self.assertEqual(user.lastname, original_lastname)
                self.assertEqual(user.email, original_email)
                self.assertEqual(user.student_number, original_student_number)
                self.assertIsNone(user.global_unique_identifier)

                response_mock.get_attributes.return_value = {
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.9': ['*****@*****.**', '*****@*****.**'],
                    'urn:oid:2.5.4.42': ['f_name'],
                    'urn:oid:0.9.2342.19200300.100.1.1': [third_party_user.unique_identifier],
                    'puid': ["should_be_ignored_since_already_linked"],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.1': ['Member', 'Staff'],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.10': [None],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.7': ['urn:mace:dir:entitlement:common-lib-terms'],
                    'urn:oid:2.5.4.4': ['l_name'],
                    'urn:oid:2.5.4.3': ['Me Myself And I'],
                    'urn:oid:2.5.4.20': ['555-5555'],
                    'urn:oid:2.16.840.1.113730.3.1.3': [new_student_number],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.6': ['*****@*****.**']
                }

                # test overwrite disabled with attributes
                self.app.config['ALLOW_STUDENT_CHANGE_DISPLAY_NAME'] = True
                self.app.config['ALLOW_STUDENT_CHANGE_NAME'] = True
                self.app.config['ALLOW_STUDENT_CHANGE_STUDENT_NUMBER'] = True
                self.app.config['ALLOW_STUDENT_CHANGE_EMAIL'] = True
                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert200(rv)
                self.assertEqual(user.firstname, original_firstname)
                self.assertEqual(user.lastname, original_lastname)
                self.assertEqual(user.email, original_email)
                self.assertEqual(user.student_number, original_student_number)
                self.assertIsNone(user.global_unique_identifier)

                # test overwrite enabled with attributes
                self.app.config['ALLOW_STUDENT_CHANGE_DISPLAY_NAME'] = False
                self.app.config['ALLOW_STUDENT_CHANGE_NAME'] = False
                self.app.config['ALLOW_STUDENT_CHANGE_STUDENT_NUMBER'] = False
                self.app.config['ALLOW_STUDENT_CHANGE_EMAIL'] = False
                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert200(rv)

                if system_role == SystemRole.student:
                    self.assertEqual(user.firstname, 'f_name')
                    self.assertEqual(user.lastname, 'l_name')
                    self.assertEqual(user.email, '*****@*****.**')
                    self.assertEqual(user.student_number, new_student_number)
                else:
                    self.assertEqual(user.firstname, original_firstname)
                    self.assertEqual(user.lastname, original_lastname)
                    self.assertEqual(user.email, original_email)
                    self.assertEqual(user.student_number, original_student_number)
                self.assertIsNone(user.global_unique_identifier)

            self.app.config['SAML_ATTRIBUTE_USER_ROLE'] = 'urn:oid:1.3.6.1.4.1.5923.1.1.1.7'
            self.app.config['SAML_INSTRUCTOR_ROLE_VALUES'] = {SystemRole.instructor.value}
            # test automatic upgrading of system role for existing accounts
            for third_party_system_role in [SystemRole.student, SystemRole.instructor, SystemRole.sys_admin]:
                user = self.data.create_user(system_role)
                third_party_user = auth_data.create_third_party_user(user=user, third_party_type=ThirdPartyType.saml)
                db.session.commit()

                response_mock.get_attributes.return_value = {
                    'urn:oid:0.9.2342.19200300.100.1.1': [third_party_user.unique_identifier],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.7': [third_party_system_role.value]
                }

                with mock.patch('compair.api.login.get_saml_auth_response', return_value=response_mock):
                    rv = self.client.post('/api/saml/auth', follow_redirects=True)
                    self.assert200(rv)

                    # compair user system role will upgrade
                    if system_role == SystemRole.student:
                        # cannot upgrade to admin
                        if third_party_system_role == SystemRole.instructor:
                            self.assertEqual(user.system_role, SystemRole.instructor)
                        else:
                            self.assertEqual(user.system_role, SystemRole.student)
                    elif system_role == SystemRole.instructor:
                        # cannot upgrade to admin and shouldn't downgrade to student
                        self.assertEqual(user.system_role, SystemRole.instructor)
                    elif system_role == SystemRole.sys_admin:
                        # shouldn't downgrade
                        self.assertEqual(user.system_role, SystemRole.sys_admin)
Esempio n. 9
0
class AccountLearningRecordTests(ComPAIRLearningRecordTestCase):
    def setUp(self):
        super(ComPAIRLearningRecordTestCase, self).setUp()
        self.data = SimpleAnswersTestData()
        self.auth_data = ThirdPartyAuthTestData()
        self.lti_data = LTITestData()
        self.course = self.data.main_course
        self.assignment = self.data.assignments[0]

        self.user = self.data.create_user(SystemRole.instructor)
        self.data.enrol_user(self.user, self.data.get_course(),
                             CourseRole.instructor)

        self.global_unique_identifier = 'mock_puid_è_global_unique_identifier'

    def test_actor_accounts(self):
        user = self.user

        # test without homepage set
        # (should use compair actor account)
        self.app.config[
            'LRS_ACTOR_ACCOUNT_USE_GLOBAL_UNIQUE_IDENTIFIER'] = True
        self.app.config[
            'LRS_ACTOR_ACCOUNT_GLOBAL_UNIQUE_IDENTIFIER_HOMEPAGE'] = None
        expected_caliper_actor = self.get_compair_caliper_actor(user)
        expected_xapi_actor = self.get_compair_xapi_actor(user)

        on_assignment_modified.send(current_app._get_current_object(),
                                    event_name=on_assignment_modified.name,
                                    user=user,
                                    assignment=self.assignment)
        events = self.get_and_clear_caliper_event_log()
        self.assertEqual(len(events), 1)
        self.assertEqual(events[0]['actor'], expected_caliper_actor)

        statements = self.get_and_clear_xapi_statement_log()
        self.assertEqual(len(statements), 1)
        self.assertEqual(statements[0]['actor'], expected_xapi_actor)

        # test with homepage set and global unique identifier not set
        # (should use compair actor account)
        self.app.config[
            'LRS_ACTOR_ACCOUNT_GLOBAL_UNIQUE_IDENTIFIER_HOMEPAGE'] = "http://third.party.homepage"
        on_assignment_modified.send(current_app._get_current_object(),
                                    event_name=on_assignment_modified.name,
                                    user=user,
                                    assignment=self.assignment)
        events = self.get_and_clear_caliper_event_log()
        self.assertEqual(len(events), 1)
        self.assertEqual(events[0]['actor'], expected_caliper_actor)

        statements = self.get_and_clear_xapi_statement_log()
        self.assertEqual(len(statements), 1)
        self.assertEqual(statements[0]['actor'], expected_xapi_actor)

        # test with homepage set and global unique identifier set
        user.global_unique_identifier = self.global_unique_identifier
        db.session.commit()
        expected_caliper_actor = self.get_unique_identifier_caliper_actor(
            user, "http://third.party.homepage/",
            self.global_unique_identifier)
        expected_xapi_actor = self.get_unique_identifier_xapi_actor(
            user, "http://third.party.homepage/",
            self.global_unique_identifier)
        on_assignment_modified.send(current_app._get_current_object(),
                                    event_name=on_assignment_modified.name,
                                    user=user,
                                    assignment=self.assignment)
        events = self.get_and_clear_caliper_event_log()
        self.assertEqual(len(events), 1)
        self.assertEqual(events[0]['actor'], expected_caliper_actor)

        statements = self.get_and_clear_xapi_statement_log()
        self.assertEqual(len(statements), 1)
        self.assertEqual(statements[0]['actor'], expected_xapi_actor)

        # disabling LRS_ACTOR_ACCOUNT_USE_GLOBAL_UNIQUE_IDENTIFIER should skip checking global unique identifer
        self.app.config[
            'LRS_ACTOR_ACCOUNT_USE_GLOBAL_UNIQUE_IDENTIFIER'] = False
        expected_caliper_actor = self.get_compair_caliper_actor(user)
        expected_xapi_actor = self.get_compair_xapi_actor(user)
        on_assignment_modified.send(current_app._get_current_object(),
                                    event_name=on_assignment_modified.name,
                                    user=user,
                                    assignment=self.assignment)
        events = self.get_and_clear_caliper_event_log()
        self.assertEqual(len(events), 1)
        self.assertEqual(events[0]['actor'], expected_caliper_actor)

        statements = self.get_and_clear_xapi_statement_log()
        self.assertEqual(len(statements), 1)
        self.assertEqual(statements[0]['actor'], expected_xapi_actor)

        # test adding third party auths & lti user links
        # NOTE: xapi doesn't really add this extra info to actor since there
        # isn't anywhere to put it
        cas_auth = self.auth_data.create_third_party_user(
            user=user, third_party_type=ThirdPartyType.cas)
        saml_auth = self.auth_data.create_third_party_user(
            user=user, third_party_type=ThirdPartyType.saml)
        lti_user = self.lti_data.create_user(
            self.lti_data.lti_consumer,
            SystemRole.instructor,
            user,
        )
        lti_user.student_number = '1234567890'
        lti_user.global_unique_identifier = self.global_unique_identifier
        lti_user.lis_person_sourcedid = 'asdfghjkl'
        db.session.commit()

        expected_caliper_actor = self.get_compair_caliper_actor(
            user,
            third_party_users=[cas_auth, saml_auth],
            lti_users=[lti_user])
        expected_xapi_actor = self.get_compair_xapi_actor(
            user,
            third_party_users=[cas_auth, saml_auth],
            lti_users=[lti_user])

        on_assignment_modified.send(current_app._get_current_object(),
                                    event_name=on_assignment_modified.name,
                                    user=user,
                                    assignment=self.assignment)
        events = self.get_and_clear_caliper_event_log()
        self.assertEqual(len(events), 1)
        print(events[0]['actor'])
        self.assertEqual(events[0]['actor'], expected_caliper_actor)

        statements = self.get_and_clear_xapi_statement_log()
        self.assertEqual(len(statements), 1)
        self.assertEqual(statements[0]['actor'], expected_xapi_actor)
Esempio n. 10
0
    def test_import_groups(self):
        auth_data = ThirdPartyAuthTestData()
        url = '/api/courses/' + self.fixtures.course.uuid + '/groups'

        content = self.fixtures.students[0].username + "," + self.fixtures.groups[0]
        encoded_content = content.encode()
        filename = "groups.csv"

        # test login required
        uploaded_file = io.BytesIO(encoded_content)
        rv = self.client.post(url, data=dict(userIdentifier="username", file=(uploaded_file, filename)))
        self.assert401(rv)
        uploaded_file.close()

        # test unauthorized user
        uploaded_file = io.BytesIO(encoded_content)
        with self.login(self.fixtures.students[0].username):
            rv = self.client.post(url, data=dict(userIdentifier="username", file=(uploaded_file, filename)))
            self.assert403(rv)
            uploaded_file.close()

        uploaded_file = io.BytesIO(encoded_content)
        with self.login(self.fixtures.ta.username):
            rv = self.client.post(url, data=dict(userIdentifier="username", file=(uploaded_file, filename)))
            self.assert403(rv)
            uploaded_file.close()

        uploaded_file = io.BytesIO(encoded_content)
        with self.login(self.fixtures.unauthorized_instructor.username):
            rv = self.client.post(url, data=dict(userIdentifier="username", file=(uploaded_file, filename)))
            self.assert403(rv)
            uploaded_file.close()

        with self.login(self.fixtures.instructor.username):
            # test invalid course id
            invalid_url = '/api/courses/999/groups'
            uploaded_file = io.BytesIO(encoded_content)
            rv = self.client.post(invalid_url, data=dict(userIdentifier="username", file=(uploaded_file, filename)))
            self.assert404(rv)
            uploaded_file.close()

            # test invalid file type
            invalid_file = "groups.png"
            uploaded_file = io.BytesIO(encoded_content)
            rv = self.client.post(url, data=dict(userIdentifier="username", file=(uploaded_file, invalid_file)))
            self.assert400(rv)
            uploaded_file.close()

            # test invalid user identifier
            uploaded_file = io.BytesIO(encoded_content)
            rv = self.client.post(url, data=dict(userIdentifier="lastname", file=(uploaded_file, filename)))
            self.assert200(rv)
            self.assertEqual(0, rv.json['success'])
            self.assertEqual({}, rv.json['invalids'][0]['member'])
            self.assertEqual("A valid user identifier is not given.", rv.json['invalids'][0]['message'])
            uploaded_file.close()

            # test missing user identifier
            uploaded_file = io.BytesIO(encoded_content)
            rv = self.client.post(url, data=dict(file=(uploaded_file, filename)))
            self.assert400(rv)
            uploaded_file.close()

            # test duplicate users in file
            duplicate = "".join([content, "\n", content])
            uploaded_file = io.BytesIO(duplicate.encode())
            rv = self.client.post(url, data=dict(userIdentifier="username", file=(uploaded_file, filename)))
            self.assert200(rv)
            self.assertEqual(1, rv.json['success'])
            self.assertEqual(1, len(rv.json['invalids']))
            invalid = rv.json['invalids'][0]
            member = [
                '["', self.fixtures.students[0].username, '", "',
                self.fixtures.groups[0], '"]']
            self.assertEqual("".join(member), invalid['member'])
            self.assertEqual("This user already exists in the file.", invalid['message'])
            uploaded_file.close()

            # test missing username
            missing_username = "******" + self.fixtures.groups[0]
            uploaded_file = io.BytesIO(missing_username.encode())
            rv = self.client.post(url, data=dict(userIdentifier="username", file=(uploaded_file, filename)))
            self.assert200(rv)
            self.assertEqual(1, rv.json['success'])
            self.assertEqual(1, len(rv.json['invalids']))
            invalid = rv.json['invalids'][0]
            member = ['["", "', self.fixtures.groups[0], '"]']
            self.assertEqual("".join(member), invalid['member'])
            self.assertEqual("No user with this ComPAIR username exists.", invalid['message'])
            uploaded_file.close()

            # test missing group name
            missing_group = self.fixtures.students[0].username + ","
            uploaded_file = io.BytesIO(missing_group.encode())
            rv = self.client.post(url, data=dict(userIdentifier="username", file=(uploaded_file, filename)))
            self.assert200(rv)
            self.assertEqual(0, rv.json['success'])
            self.assertEqual(1, len(rv.json['invalids']))
            invalid = rv.json['invalids'][0]
            member = ['["', self.fixtures.students[0].username, '", ""]']
            self.assertEqual("".join(member), invalid['member'])
            self.assertEqual("The group name is invalid.", invalid['message'])
            uploaded_file.close()

            # test invalid user
            invalid_user = "******" + self.fixtures.groups[0]
            uploaded_file = io.BytesIO(invalid_user.encode())
            rv = self.client.post(url, data=dict(userIdentifier="username", file=(uploaded_file, filename)))
            self.assert200(rv)
            self.assertEqual(1, rv.json['success'])
            self.assertEqual(1, len(rv.json['invalids']))
            invalid = rv.json['invalids'][0]
            member = ['["username9999", "', self.fixtures.groups[0], '"]']
            self.assertEqual("".join(member), invalid['member'])
            self.assertEqual("No user with this ComPAIR username exists.", invalid['message'])
            uploaded_file.close()

            # test successful import with username
            with_username = self.fixtures.students[0].username + "," + self.fixtures.groups[0]
            uploaded_file = io.BytesIO(with_username.encode())
            rv = self.client.post(url, data=dict(userIdentifier="username", file=(uploaded_file, filename)))
            self.assert200(rv)
            self.assertEqual(1, rv.json['success'])
            self.assertEqual(0, len(rv.json['invalids']))
            uploaded_file.close()

            # test invalid import with username
            self.app.config['APP_LOGIN_ENABLED'] = False
            with_username = self.fixtures.students[0].username + "," + self.fixtures.groups[0]
            uploaded_file = io.BytesIO(with_username.encode())
            rv = self.client.post(url, data=dict(userIdentifier="username", file=(uploaded_file, filename)))
            self.assert400(rv)
            uploaded_file.close()
            self.app.config['APP_LOGIN_ENABLED'] = True

            # test successful import with student number
            with_studentno = self.fixtures.students[0].student_number + "," + self.fixtures.groups[0]
            uploaded_file = io.BytesIO(with_studentno.encode())
            rv = self.client.post(url, data=dict(userIdentifier="student_number", file=(uploaded_file, filename)))
            self.assert200(rv)
            self.assertEqual(1, rv.json['success'])
            self.assertEqual(0, len(rv.json['invalids']))
            uploaded_file.close()

            # test successful import with cas username
            cas_auth = auth_data.create_cas_user_auth(CourseRole.student)
            cas_user = cas_auth.user
            self.fixtures.enrol_user(cas_user, self.fixtures.course, CourseRole.student)

            with_cas_username = cas_auth.unique_identifier + "," + self.fixtures.groups[0]
            uploaded_file = io.BytesIO(with_cas_username.encode())
            rv = self.client.post(url, data=dict(userIdentifier=ThirdPartyType.cas.value, file=(uploaded_file, filename)))
            self.assert200(rv)
            self.assertEqual(1, rv.json['success'])
            self.assertEqual(0, len(rv.json['invalids']))
            uploaded_file.close()

            # test invalid import with cas username
            self.app.config['CAS_LOGIN_ENABLED'] = False
            with_cas_username = cas_auth.unique_identifier + "," + self.fixtures.groups[0]
            uploaded_file = io.BytesIO(with_cas_username.encode())
            rv = self.client.post(url, data=dict(userIdentifier=ThirdPartyType.cas.value, file=(uploaded_file, filename)))
            self.assert400(rv)
            uploaded_file.close()
            self.app.config['CAS_LOGIN_ENABLED'] = True

            # test import user not in course
            unauthorized_student = self.fixtures.unauthorized_student.username + "," + self.fixtures.groups[0]
            uploaded_file = io.BytesIO(unauthorized_student.encode())
            rv = self.client.post(url, data=dict(userIdentifier="username", file=(uploaded_file, filename)))
            self.assert200(rv)
            self.assertEqual(1, rv.json['success'])
            self.assertEqual(1, len(rv.json['invalids']))
            invalid = rv.json['invalids'][0]
            member = [
                '["', self.fixtures.unauthorized_student.username, '", "',
                self.fixtures.groups[0], '"]']
            self.assertEqual("".join(member), invalid['member'])
            self.assertEqual("The user is not enroled in the course", invalid['message'])
            uploaded_file.close()

            # test placing instructor in group
            add_instructor = self.fixtures.instructor.username + "," + self.fixtures.groups[0]
            uploaded_file = io.BytesIO(add_instructor.encode())
            rv = self.client.post(url, data=dict(userIdentifier="username", file=(uploaded_file, filename)))
            self.assert200(rv)
            self.assertEqual(1, rv.json['success'])
            self.assertEqual(0, len(rv.json['invalids']))
            uploaded_file.close()

            # test placing TA in group
            add_ta = self.fixtures.ta.username + "," + self.fixtures.groups[0]
            uploaded_file = io.BytesIO(add_ta.encode())
            rv = self.client.post(url, data=dict(userIdentifier="username", file=(uploaded_file, filename)))
            self.assert200(rv)
            self.assertEqual(1, rv.json['success'])
            self.assertEqual(0, len(rv.json['invalids']))
            uploaded_file.close()
Esempio n. 11
0
    def test_cas_login(self):
        auth_data = ThirdPartyAuthTestData()

        # test login new user
        for system_role in [
                SystemRole.student, SystemRole.instructor, SystemRole.sys_admin
        ]:
            unique_identifier = system_role.value + "_no_attributes"
            response_mock = mock.MagicMock()
            response_mock.success = True
            response_mock.user = unique_identifier
            response_mock.attributes = None

            self.app.config['CAS_ATTRIBUTE_FIRST_NAME'] = None
            self.app.config['CAS_ATTRIBUTE_LAST_NAME'] = None
            self.app.config['CAS_ATTRIBUTE_STUDENT_NUMBER'] = None
            self.app.config['CAS_ATTRIBUTE_EMAIL'] = None
            self.app.config['CAS_ATTRIBUTE_USER_ROLE'] = None
            self.app.config['CAS_INSTRUCTOR_ROLE_VALUES'] = {}
            self.app.config['CAS_GLOBAL_UNIQUE_IDENTIFIER_FIELD'] = None

            with mock.patch('compair.api.login.validate_cas_ticket',
                            return_value=response_mock):
                # test cas login disabled
                self.app.config['CAS_LOGIN_ENABLED'] = False
                rv = self.client.get('/api/cas/auth?ticket=mock_ticket',
                                     follow_redirects=True)
                self.assert403(rv)

                # test cas login enabled with unsuccessful login
                self.app.config['CAS_LOGIN_ENABLED'] = True
                response_mock.success = False
                rv = self.client.get('/api/cas/auth?ticket=mock_ticket',
                                     follow_redirects=True)
                self.assert200(rv)

                # check session
                with self.client.session_transaction() as sess:
                    # check that user is logged in
                    self.assertEqual(sess.get('THIRD_PARTY_AUTH_ERROR_TYPE'),
                                     'CAS')
                    self.assertEqual(sess.get('THIRD_PARTY_AUTH_ERROR_MSG'),
                                     'Login Failed. CAS ticket was invalid.')

                # test cas login enabled with unsuccessful login
                response_mock.success = True
                response_mock.user = None
                rv = self.client.get('/api/cas/auth?ticket=mock_ticket',
                                     follow_redirects=True)
                self.assert200(rv)

                # check session
                with self.client.session_transaction() as sess:
                    # check that user is logged in
                    self.assertEqual(sess.get('THIRD_PARTY_AUTH_ERROR_TYPE'),
                                     'CAS')
                    self.assertEqual(
                        sess.get('THIRD_PARTY_AUTH_ERROR_MSG'),
                        'Login Failed. Expecting CAS username to be set.')

                # test cas login enabled with successful login
                response_mock.user = unique_identifier
                rv = self.client.get('/api/cas/auth?ticket=mock_ticket',
                                     follow_redirects=True)
                self.assert200(rv)

                third_party_user = ThirdPartyUser.query \
                    .filter_by(unique_identifier=unique_identifier, third_party_type=ThirdPartyType.cas)\
                    .one()

                self.assertIsNotNone(third_party_user)
                self.assertIsNotNone(third_party_user.user)
                self.assertEqual(third_party_user.user.system_role,
                                 SystemRole.student)
                self.assertIsNone(third_party_user.user.firstname)
                self.assertIsNone(third_party_user.user.lastname)
                six.assertRegex(self, third_party_user.user.displayname,
                                r"^Student_\d{8}")
                self.assertIsNone(third_party_user.user.email)
                self.assertIsNone(third_party_user.user.student_number)
                self.assertIsNone(
                    third_party_user.user.global_unique_identifier)

                with self.client.session_transaction() as sess:
                    self.assertEqual(sess.get('_user_id'),
                                     str(third_party_user.user.id))

                # unused attributes
                unique_identifier = system_role.value + "_with_unused_attributes"
                response_mock.user = unique_identifier
                response_mock.attributes = {
                    'firstName':
                    'f_name',
                    'lastName':
                    'l_name',
                    'studentNumber':
                    "student1" if system_role == SystemRole.student else None,
                    'email':
                    '*****@*****.**',
                    'system_role_field':
                    system_role.value,
                    'puid':
                    system_role.value + "_puid",
                }

                rv = self.client.get('/api/cas/auth?ticket=mock_ticket',
                                     follow_redirects=True)
                self.assert200(rv)
                third_party_user = ThirdPartyUser.query \
                    .filter_by(unique_identifier=unique_identifier, third_party_type=ThirdPartyType.cas)\
                    .one()

                self.assertIsNotNone(third_party_user)
                self.assertIsNotNone(third_party_user.user)
                self.assertEqual(third_party_user.user.system_role,
                                 SystemRole.student)
                self.assertIsNone(third_party_user.user.firstname)
                self.assertIsNone(third_party_user.user.lastname)
                six.assertRegex(self, third_party_user.user.displayname,
                                r"^Student_\d{8}")
                self.assertIsNone(third_party_user.user.email)
                self.assertIsNone(third_party_user.user.student_number)
                self.assertIsNone(
                    third_party_user.user.global_unique_identifier)

                # used attributes and no valid instructor values
                self.app.config['CAS_ATTRIBUTE_FIRST_NAME'] = 'firstName'
                self.app.config['CAS_ATTRIBUTE_LAST_NAME'] = 'lastName'
                self.app.config[
                    'CAS_ATTRIBUTE_STUDENT_NUMBER'] = 'studentNumber'
                self.app.config['CAS_ATTRIBUTE_EMAIL'] = 'email'
                self.app.config[
                    'CAS_ATTRIBUTE_USER_ROLE'] = 'system_role_field'
                self.app.config['CAS_INSTRUCTOR_ROLE_VALUES'] = {}
                self.app.config['CAS_GLOBAL_UNIQUE_IDENTIFIER_FIELD'] = 'puid'
                unique_identifier = system_role.value + "_with_used_attributes"
                response_mock.user = unique_identifier

                rv = self.client.get('/api/cas/auth?ticket=mock_ticket',
                                     follow_redirects=True)
                self.assert200(rv)
                third_party_user = ThirdPartyUser.query \
                    .filter_by(unique_identifier=unique_identifier, third_party_type=ThirdPartyType.cas) \
                    .one()

                self.assertIsNotNone(third_party_user)
                self.assertIsNotNone(third_party_user.user)
                self.assertEqual(third_party_user.user.system_role,
                                 SystemRole.student)
                self.assertEqual(third_party_user.user.firstname, 'f_name')
                self.assertEqual(third_party_user.user.lastname, 'l_name')
                self.assertEqual(third_party_user.user.email,
                                 '*****@*****.**')
                self.assertEqual(
                    third_party_user.user.global_unique_identifier,
                    system_role.value + "_puid")
                if system_role == SystemRole.student:
                    self.assertEqual(third_party_user.user.student_number,
                                     'student1')
                else:
                    self.assertIsNone(third_party_user.user.student_number)
                six.assertRegex(self, third_party_user.user.displayname,
                                r"^Student_\d{8}")

                # used attributes and valid instructor values
                unique_identifier = system_role.value + "_with_used_attributes2"
                response_mock.user = unique_identifier
                if system_role == SystemRole.student:
                    response_mock.attributes['studentNumber'] = "student2"
                response_mock.attributes['puid'] = system_role.value + "_puid2"
                self.app.config['CAS_INSTRUCTOR_ROLE_VALUES'] = {
                    SystemRole.sys_admin.value, SystemRole.instructor.value
                }

                rv = self.client.get('/api/cas/auth?ticket=mock_ticket',
                                     follow_redirects=True)
                self.assert200(rv)
                third_party_user = ThirdPartyUser.query \
                    .filter_by(unique_identifier=unique_identifier, third_party_type=ThirdPartyType.cas)\
                    .one()

                self.assertIsNotNone(third_party_user)
                self.assertIsNotNone(third_party_user.user)
                self.assertEqual(third_party_user.user.firstname, 'f_name')
                self.assertEqual(third_party_user.user.lastname, 'l_name')
                self.assertEqual(third_party_user.user.email,
                                 '*****@*****.**')
                self.assertEqual(
                    third_party_user.user.global_unique_identifier,
                    system_role.value + "_puid2")
                if system_role == SystemRole.student:
                    self.assertEqual(third_party_user.user.system_role,
                                     SystemRole.student)
                    six.assertRegex(self, third_party_user.user.displayname,
                                    r"^Student_\d{8}")
                    self.assertEqual(third_party_user.user.student_number,
                                     'student2')
                else:
                    self.assertEqual(third_party_user.user.system_role,
                                     SystemRole.instructor)
                    self.assertEqual(third_party_user.user.displayname,
                                     "f_name l_name")
                    self.assertIsNone(third_party_user.user.student_number)

        # test login existing user
        for system_role in [
                SystemRole.student, SystemRole.instructor, SystemRole.sys_admin
        ]:
            self.app.config['CAS_ATTRIBUTE_FIRST_NAME'] = None
            self.app.config['CAS_ATTRIBUTE_LAST_NAME'] = None
            self.app.config['CAS_ATTRIBUTE_STUDENT_NUMBER'] = None
            self.app.config['CAS_ATTRIBUTE_EMAIL'] = None
            self.app.config['CAS_ATTRIBUTE_USER_ROLE'] = None
            self.app.config['CAS_INSTRUCTOR_ROLE_VALUES'] = {}
            self.app.config['CAS_GLOBAL_UNIQUE_IDENTIFIER_FIELD'] = None

            user = self.data.create_user(system_role)
            third_party_user = auth_data.create_third_party_user(
                user=user, third_party_type=ThirdPartyType.cas)

            original_firstname = user.firstname
            original_lastname = user.lastname
            original_email = user.email
            original_student_number = user.student_number
            new_student_number = original_student_number + "123" if user.student_number else None

            response_mock = mock.MagicMock()
            response_mock.success = True
            response_mock.user = third_party_user.unique_identifier
            response_mock.attributes = None

            with mock.patch('compair.api.login.validate_cas_ticket',
                            return_value=response_mock):
                # test cas login disabled
                self.app.config['CAS_LOGIN_ENABLED'] = False
                rv = self.client.get('/api/cas/auth?ticket=mock_ticket',
                                     follow_redirects=True)
                self.assert403(rv)

                # test cas login enabled
                self.app.config['CAS_LOGIN_ENABLED'] = True
                rv = self.client.get('/api/cas/auth?ticket=mock_ticket',
                                     follow_redirects=True)
                self.assert200(rv)

                # test overwrite disabled with no attributes
                self.app.config['ALLOW_STUDENT_CHANGE_DISPLAY_NAME'] = True
                self.app.config['ALLOW_STUDENT_CHANGE_NAME'] = True
                self.app.config['ALLOW_STUDENT_CHANGE_STUDENT_NUMBER'] = True
                self.app.config['ALLOW_STUDENT_CHANGE_EMAIL'] = True
                rv = self.client.get('/api/cas/auth?ticket=mock_ticket',
                                     follow_redirects=True)
                self.assert200(rv)
                self.assertEqual(user.firstname, original_firstname)
                self.assertEqual(user.lastname, original_lastname)
                self.assertEqual(user.email, original_email)
                self.assertEqual(user.student_number, original_student_number)
                self.assertIsNone(user.global_unique_identifier)

                # test overwrite enabled with no attributes
                self.app.config['ALLOW_STUDENT_CHANGE_DISPLAY_NAME'] = False
                self.app.config['ALLOW_STUDENT_CHANGE_NAME'] = False
                self.app.config['ALLOW_STUDENT_CHANGE_STUDENT_NUMBER'] = False
                self.app.config['ALLOW_STUDENT_CHANGE_EMAIL'] = False
                rv = self.client.get('/api/cas/auth?ticket=mock_ticket',
                                     follow_redirects=True)
                self.assert200(rv)
                self.assertEqual(user.firstname, original_firstname)
                self.assertEqual(user.lastname, original_lastname)
                self.assertEqual(user.email, original_email)
                self.assertEqual(user.student_number, original_student_number)
                self.assertIsNone(user.global_unique_identifier)

                # test overwrite enabled with no attributes
                self.app.config['CAS_ATTRIBUTE_FIRST_NAME'] = 'firstName'
                self.app.config['CAS_ATTRIBUTE_LAST_NAME'] = 'lastName'
                self.app.config[
                    'CAS_ATTRIBUTE_STUDENT_NUMBER'] = 'studentNumber'
                self.app.config['CAS_ATTRIBUTE_EMAIL'] = 'email'
                self.app.config['CAS_GLOBAL_UNIQUE_IDENTIFIER_FIELD'] = 'puid'
                rv = self.client.get('/api/cas/auth?ticket=mock_ticket',
                                     follow_redirects=True)
                self.assert200(rv)
                self.assertEqual(user.firstname, original_firstname)
                self.assertEqual(user.lastname, original_lastname)
                self.assertEqual(user.email, original_email)
                self.assertEqual(user.student_number, original_student_number)
                self.assertIsNone(user.global_unique_identifier)

                response_mock.attributes = {
                    'firstName': 'f_name',
                    'lastName': 'l_name',
                    'studentNumber': new_student_number,
                    'email': '*****@*****.**',
                    'puid': 'should_be_ignored_since_already_linked'
                }

                # test overwrite disabled with attributes
                self.app.config['ALLOW_STUDENT_CHANGE_DISPLAY_NAME'] = True
                self.app.config['ALLOW_STUDENT_CHANGE_NAME'] = True
                self.app.config['ALLOW_STUDENT_CHANGE_STUDENT_NUMBER'] = True
                self.app.config['ALLOW_STUDENT_CHANGE_EMAIL'] = True
                rv = self.client.get('/api/cas/auth?ticket=mock_ticket',
                                     follow_redirects=True)
                self.assert200(rv)
                self.assertEqual(user.firstname, original_firstname)
                self.assertEqual(user.lastname, original_lastname)
                self.assertEqual(user.email, original_email)
                self.assertEqual(user.student_number, original_student_number)
                self.assertIsNone(user.global_unique_identifier)

                # test overwrite enabled with attributes
                self.app.config['ALLOW_STUDENT_CHANGE_DISPLAY_NAME'] = False
                self.app.config['ALLOW_STUDENT_CHANGE_NAME'] = False
                self.app.config['ALLOW_STUDENT_CHANGE_STUDENT_NUMBER'] = False
                self.app.config['ALLOW_STUDENT_CHANGE_EMAIL'] = False
                rv = self.client.get('/api/cas/auth?ticket=mock_ticket',
                                     follow_redirects=True)
                self.assert200(rv)

                if system_role == SystemRole.student:
                    self.assertEqual(user.firstname, 'f_name')
                    self.assertEqual(user.lastname, 'l_name')
                    self.assertEqual(user.email, '*****@*****.**')
                    self.assertEqual(user.student_number, new_student_number)
                else:
                    self.assertEqual(user.firstname, original_firstname)
                    self.assertEqual(user.lastname, original_lastname)
                    self.assertEqual(user.email, original_email)
                    self.assertEqual(user.student_number,
                                     original_student_number)
                self.assertIsNone(user.global_unique_identifier)

            self.app.config['CAS_ATTRIBUTE_USER_ROLE'] = 'system_role_field'
            self.app.config['CAS_INSTRUCTOR_ROLE_VALUES'] = {
                SystemRole.instructor.value
            }
            # test automatic upgrading of system role for existing accounts
            for third_party_system_role in [
                    SystemRole.student, SystemRole.instructor,
                    SystemRole.sys_admin
            ]:
                user = self.data.create_user(system_role)
                third_party_user = auth_data.create_third_party_user(
                    user=user, third_party_type=ThirdPartyType.cas)
                db.session.commit()

                response_mock.user = third_party_user.unique_identifier
                response_mock.attributes = {
                    'system_role_field': third_party_system_role.value
                }

                with mock.patch('compair.api.login.validate_cas_ticket',
                                return_value=response_mock):
                    rv = self.client.get('/api/cas/auth?ticket=mock_ticket',
                                         follow_redirects=True)
                    self.assert200(rv)

                    # compair user system role will upgrade
                    if system_role == SystemRole.student:
                        # cannot upgrade to admin
                        if third_party_system_role == SystemRole.instructor:
                            self.assertEqual(user.system_role,
                                             SystemRole.instructor)
                        else:
                            self.assertEqual(user.system_role,
                                             SystemRole.student)
                    elif system_role == SystemRole.instructor:
                        # cannot upgrade to admin and shouldn't downgrade to student
                        self.assertEqual(user.system_role,
                                         SystemRole.instructor)
                    elif system_role == SystemRole.sys_admin:
                        # shouldn't downgrade
                        self.assertEqual(user.system_role,
                                         SystemRole.sys_admin)
Esempio n. 12
0
    def test_saml_login(self):
        auth_data = ThirdPartyAuthTestData()

        # test login new user
        for system_role in [
                SystemRole.student, SystemRole.instructor, SystemRole.sys_admin
        ]:
            unique_identifier = system_role.value + "_no_attributes"
            response_mock = mock.MagicMock()
            response_mock.__errors = []
            response_mock.is_authenticated.return_value = True
            response_mock.get_attributes.return_value = {
                'urn:oid:0.9.2342.19200300.100.1.1': [unique_identifier]
            }
            response_mock.get_nameid.return_value = "saml_mock_nameid"
            response_mock.get_session_index.return_value = "saml_session_index"

            self.app.config['SAML_ATTRIBUTE_FIRST_NAME'] = None
            self.app.config['SAML_ATTRIBUTE_LAST_NAME'] = None
            self.app.config['SAML_ATTRIBUTE_STUDENT_NUMBER'] = None
            self.app.config['SAML_ATTRIBUTE_EMAIL'] = None
            self.app.config['SAML_ATTRIBUTE_USER_ROLE'] = None
            self.app.config['SAML_INSTRUCTOR_ROLE_VALUES'] = {}
            self.app.config['SAML_GLOBAL_UNIQUE_IDENTIFIER_FIELD'] = None

            with mock.patch('compair.api.login.get_saml_auth_response',
                            return_value=response_mock):
                # test saml login disabled
                self.app.config['SAML_LOGIN_ENABLED'] = False
                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert403(rv)

                # test saml login enabled with unsuccessful login
                self.app.config['SAML_LOGIN_ENABLED'] = True
                response_mock.is_authenticated.return_value = False
                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert200(rv)

                # check session
                with self.client.session_transaction() as sess:
                    # check that user is logged in
                    self.assertEqual(sess.get('THIRD_PARTY_AUTH_ERROR_TYPE'),
                                     'SAML')
                    self.assertEqual(sess.get('THIRD_PARTY_AUTH_ERROR_MSG'),
                                     'Login Failed.')

                # test saml login enabled with unsuccessful login
                response_mock.is_authenticated.return_value = True
                response_mock.__errors = ['error1', 'error2']
                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert200(rv)

                # check session
                with self.client.session_transaction() as sess:
                    # check that user is logged in
                    self.assertEqual(sess.get('THIRD_PARTY_AUTH_ERROR_TYPE'),
                                     'SAML')
                    self.assertEqual(sess.get('THIRD_PARTY_AUTH_ERROR_MSG'),
                                     'Login Failed.')

                # test saml login enabled with successful login
                response_mock.__errors = []
                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert200(rv)

                third_party_user = ThirdPartyUser.query \
                    .filter_by(unique_identifier=unique_identifier, third_party_type=ThirdPartyType.saml)\
                    .one()

                self.assertIsNotNone(third_party_user)
                self.assertIsNotNone(third_party_user.user)
                self.assertEqual(third_party_user.user.system_role,
                                 SystemRole.student)
                self.assertIsNone(third_party_user.user.firstname)
                self.assertIsNone(third_party_user.user.lastname)
                six.assertRegex(self, third_party_user.user.displayname,
                                r"^Student_\d{8}")
                self.assertIsNone(third_party_user.user.email)
                self.assertIsNone(third_party_user.user.student_number)
                self.assertIsNone(
                    third_party_user.user.global_unique_identifier)

                with self.client.session_transaction() as sess:
                    self.assertEqual(sess.get('_user_id'),
                                     str(third_party_user.user.id))

                # unused attributes
                unique_identifier = system_role.value + "_with_unused_attributes"
                response_mock.get_attributes.return_value = {
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.9':
                    ['*****@*****.**', '*****@*****.**'],
                    'urn:oid:2.5.4.42': ['f_name'],
                    'urn:oid:0.9.2342.19200300.100.1.1': [unique_identifier],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.1': ['Member'],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.10': [None],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.7': [system_role.value],
                    'urn:oid:2.5.4.4': ['l_name'],
                    'urn:oid:2.5.4.3': ['Me Myself And I'],
                    'urn:oid:2.5.4.20': ['555-5555'],
                    'urn:oid:2.16.840.1.113730.3.1.3':
                    ["student1"] if system_role == SystemRole.student else [],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.6': ['*****@*****.**']
                }

                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert200(rv)
                third_party_user = ThirdPartyUser.query \
                    .filter_by(unique_identifier=unique_identifier, third_party_type=ThirdPartyType.saml)\
                    .one()

                self.assertIsNotNone(third_party_user)
                self.assertIsNotNone(third_party_user.user)
                self.assertEqual(third_party_user.user.system_role,
                                 SystemRole.student)
                self.assertIsNone(third_party_user.user.firstname)
                self.assertIsNone(third_party_user.user.lastname)
                six.assertRegex(self, third_party_user.user.displayname,
                                r"^Student_\d{8}")
                self.assertIsNone(third_party_user.user.email)
                self.assertIsNone(third_party_user.user.student_number)
                self.assertIsNone(
                    third_party_user.user.global_unique_identifier)

                # used attributes and no valid instructor values
                self.app.config[
                    'SAML_ATTRIBUTE_FIRST_NAME'] = 'urn:oid:2.5.4.42'
                self.app.config['SAML_ATTRIBUTE_LAST_NAME'] = 'urn:oid:2.5.4.4'
                self.app.config[
                    'SAML_ATTRIBUTE_STUDENT_NUMBER'] = 'urn:oid:2.16.840.1.113730.3.1.3'
                self.app.config[
                    'SAML_ATTRIBUTE_EMAIL'] = 'urn:oid:1.3.6.1.4.1.5923.1.1.1.6'
                self.app.config[
                    'SAML_ATTRIBUTE_USER_ROLE'] = 'urn:oid:1.3.6.1.4.1.5923.1.1.1.7'
                self.app.config['SAML_GLOBAL_UNIQUE_IDENTIFIER_FIELD'] = 'puid'
                self.app.config['SAML_INSTRUCTOR_ROLE_VALUES'] = {}
                unique_identifier = system_role.value + "_with_used_attributes"
                response_mock.get_attributes.return_value = {
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.9':
                    ['*****@*****.**', '*****@*****.**'],
                    'urn:oid:2.5.4.42': ['f_name'],
                    'urn:oid:0.9.2342.19200300.100.1.1': [unique_identifier],
                    'puid': [system_role.value + "_puid"],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.1': ['Member'],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.10': [None],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.7': [system_role.value],
                    'urn:oid:2.5.4.4': ['l_name'],
                    'urn:oid:2.5.4.3': ['Me Myself And I'],
                    'urn:oid:2.5.4.20': ['555-5555'],
                    'urn:oid:2.16.840.1.113730.3.1.3':
                    ["student1"] if system_role == SystemRole.student else [],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.6': ['*****@*****.**'],
                }

                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert200(rv)
                third_party_user = ThirdPartyUser.query \
                    .filter_by(unique_identifier=unique_identifier, third_party_type=ThirdPartyType.saml) \
                    .one()

                self.assertIsNotNone(third_party_user)
                self.assertIsNotNone(third_party_user.user)
                self.assertEqual(third_party_user.user.system_role,
                                 SystemRole.student)
                self.assertEqual(third_party_user.user.firstname, 'f_name')
                self.assertEqual(third_party_user.user.lastname, 'l_name')
                self.assertEqual(third_party_user.user.email,
                                 '*****@*****.**')
                self.assertEqual(
                    third_party_user.user.global_unique_identifier,
                    system_role.value + "_puid")
                if system_role == SystemRole.student:
                    self.assertEqual(third_party_user.user.student_number,
                                     'student1')
                else:
                    self.assertIsNone(third_party_user.user.student_number)
                six.assertRegex(self, third_party_user.user.displayname,
                                r"^Student_\d{8}")

                # used attributes and valid instructor values
                unique_identifier = system_role.value + "_with_used_attributes2"
                response_mock.get_attributes.return_value = {
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.9':
                    ['*****@*****.**', '*****@*****.**'],
                    'urn:oid:2.5.4.42': ['f_name'],
                    'urn:oid:0.9.2342.19200300.100.1.1': [unique_identifier],
                    'puid': [system_role.value + "_puid2"],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.1': ['Member'],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.10': [None],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.7': [system_role.value],
                    'urn:oid:2.5.4.4': ['l_name'],
                    'urn:oid:2.5.4.3': ['Me Myself And I'],
                    'urn:oid:2.5.4.20': ['555-5555'],
                    'urn:oid:2.16.840.1.113730.3.1.3':
                    ["student2"] if system_role == SystemRole.student else [],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.6': ['*****@*****.**']
                }
                self.app.config['SAML_INSTRUCTOR_ROLE_VALUES'] = {
                    SystemRole.sys_admin.value, SystemRole.instructor.value
                }

                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert200(rv)
                third_party_user = ThirdPartyUser.query \
                    .filter_by(unique_identifier=unique_identifier, third_party_type=ThirdPartyType.saml)\
                    .one()

                self.assertIsNotNone(third_party_user)
                self.assertIsNotNone(third_party_user.user)
                self.assertEqual(third_party_user.user.firstname, 'f_name')
                self.assertEqual(third_party_user.user.lastname, 'l_name')
                self.assertEqual(third_party_user.user.email,
                                 '*****@*****.**')
                self.assertEqual(
                    third_party_user.user.global_unique_identifier,
                    system_role.value + "_puid2")
                if system_role == SystemRole.student:
                    self.assertEqual(third_party_user.user.system_role,
                                     SystemRole.student)
                    six.assertRegex(self, third_party_user.user.displayname,
                                    r"^Student_\d{8}")
                    self.assertEqual(third_party_user.user.student_number,
                                     'student2')
                else:
                    self.assertEqual(third_party_user.user.system_role,
                                     SystemRole.instructor)
                    self.assertEqual(third_party_user.user.displayname,
                                     "f_name l_name")
                    self.assertIsNone(third_party_user.user.student_number)

        # test login existing user
        for system_role in [
                SystemRole.student, SystemRole.instructor, SystemRole.sys_admin
        ]:
            self.app.config['SAML_ATTRIBUTE_FIRST_NAME'] = None
            self.app.config['SAML_ATTRIBUTE_LAST_NAME'] = None
            self.app.config['SAML_ATTRIBUTE_STUDENT_NUMBER'] = None
            self.app.config['SAML_ATTRIBUTE_EMAIL'] = None
            self.app.config['SAML_ATTRIBUTE_USER_ROLE'] = None
            self.app.config['SAML_INSTRUCTOR_ROLE_VALUES'] = {}
            self.app.config['SAML_GLOBAL_UNIQUE_IDENTIFIER_FIELD'] = None

            user = self.data.create_user(system_role)
            third_party_user = auth_data.create_third_party_user(
                user=user, third_party_type=ThirdPartyType.saml)

            original_firstname = user.firstname
            original_lastname = user.lastname
            original_email = user.email
            original_student_number = user.student_number
            new_student_number = original_student_number + "123" if user.student_number else None

            response_mock = mock.MagicMock()
            response_mock.__errors = []
            response_mock.is_authenticated.return_value = True
            response_mock.get_attributes.return_value = {
                'urn:oid:0.9.2342.19200300.100.1.1':
                [third_party_user.unique_identifier]
            }
            response_mock.get_nameid.return_value = "saml_mock_nameid"
            response_mock.get_session_index.return_value = "saml_session_index"

            with mock.patch('compair.api.login.get_saml_auth_response',
                            return_value=response_mock):
                # test saml login disabled
                self.app.config['SAML_LOGIN_ENABLED'] = False
                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert403(rv)

                # test saml login enabled
                self.app.config['SAML_LOGIN_ENABLED'] = True
                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert200(rv)

                # test overwrite disabled with no attributes
                self.app.config['ALLOW_STUDENT_CHANGE_DISPLAY_NAME'] = True
                self.app.config['ALLOW_STUDENT_CHANGE_NAME'] = True
                self.app.config['ALLOW_STUDENT_CHANGE_STUDENT_NUMBER'] = True
                self.app.config['ALLOW_STUDENT_CHANGE_EMAIL'] = True
                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert200(rv)
                self.assertEqual(user.firstname, original_firstname)
                self.assertEqual(user.lastname, original_lastname)
                self.assertEqual(user.email, original_email)
                self.assertEqual(user.student_number, original_student_number)
                self.assertIsNone(user.global_unique_identifier)

                # test overwrite enabled with no attributes
                self.app.config['ALLOW_STUDENT_CHANGE_DISPLAY_NAME'] = False
                self.app.config['ALLOW_STUDENT_CHANGE_NAME'] = False
                self.app.config['ALLOW_STUDENT_CHANGE_STUDENT_NUMBER'] = False
                self.app.config['ALLOW_STUDENT_CHANGE_EMAIL'] = False
                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert200(rv)
                self.assertEqual(user.firstname, original_firstname)
                self.assertEqual(user.lastname, original_lastname)
                self.assertEqual(user.email, original_email)
                self.assertEqual(user.student_number, original_student_number)
                self.assertIsNone(user.global_unique_identifier)

                # test overwrite enabled with no attributes
                self.app.config[
                    'SAML_ATTRIBUTE_FIRST_NAME'] = 'urn:oid:2.5.4.42'
                self.app.config['SAML_ATTRIBUTE_LAST_NAME'] = 'urn:oid:2.5.4.4'
                self.app.config[
                    'SAML_ATTRIBUTE_STUDENT_NUMBER'] = 'urn:oid:2.16.840.1.113730.3.1.3'
                self.app.config[
                    'SAML_ATTRIBUTE_EMAIL'] = 'urn:oid:1.3.6.1.4.1.5923.1.1.1.6'
                self.app.config['SAML_GLOBAL_UNIQUE_IDENTIFIER_FIELD'] = 'puid'
                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert200(rv)
                self.assertEqual(user.firstname, original_firstname)
                self.assertEqual(user.lastname, original_lastname)
                self.assertEqual(user.email, original_email)
                self.assertEqual(user.student_number, original_student_number)
                self.assertIsNone(user.global_unique_identifier)

                response_mock.get_attributes.return_value = {
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.9':
                    ['*****@*****.**', '*****@*****.**'],
                    'urn:oid:2.5.4.42': ['f_name'],
                    'urn:oid:0.9.2342.19200300.100.1.1':
                    [third_party_user.unique_identifier],
                    'puid': ["should_be_ignored_since_already_linked"],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.1': ['Member', 'Staff'],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.10': [None],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.7':
                    ['urn:mace:dir:entitlement:common-lib-terms'],
                    'urn:oid:2.5.4.4': ['l_name'],
                    'urn:oid:2.5.4.3': ['Me Myself And I'],
                    'urn:oid:2.5.4.20': ['555-5555'],
                    'urn:oid:2.16.840.1.113730.3.1.3': [new_student_number],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.6': ['*****@*****.**']
                }

                # test overwrite disabled with attributes
                self.app.config['ALLOW_STUDENT_CHANGE_DISPLAY_NAME'] = True
                self.app.config['ALLOW_STUDENT_CHANGE_NAME'] = True
                self.app.config['ALLOW_STUDENT_CHANGE_STUDENT_NUMBER'] = True
                self.app.config['ALLOW_STUDENT_CHANGE_EMAIL'] = True
                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert200(rv)
                self.assertEqual(user.firstname, original_firstname)
                self.assertEqual(user.lastname, original_lastname)
                self.assertEqual(user.email, original_email)
                self.assertEqual(user.student_number, original_student_number)
                self.assertIsNone(user.global_unique_identifier)

                # test overwrite enabled with attributes
                self.app.config['ALLOW_STUDENT_CHANGE_DISPLAY_NAME'] = False
                self.app.config['ALLOW_STUDENT_CHANGE_NAME'] = False
                self.app.config['ALLOW_STUDENT_CHANGE_STUDENT_NUMBER'] = False
                self.app.config['ALLOW_STUDENT_CHANGE_EMAIL'] = False
                rv = self.client.post('/api/saml/auth', follow_redirects=True)
                self.assert200(rv)

                if system_role == SystemRole.student:
                    self.assertEqual(user.firstname, 'f_name')
                    self.assertEqual(user.lastname, 'l_name')
                    self.assertEqual(user.email, '*****@*****.**')
                    self.assertEqual(user.student_number, new_student_number)
                else:
                    self.assertEqual(user.firstname, original_firstname)
                    self.assertEqual(user.lastname, original_lastname)
                    self.assertEqual(user.email, original_email)
                    self.assertEqual(user.student_number,
                                     original_student_number)
                self.assertIsNone(user.global_unique_identifier)

            self.app.config[
                'SAML_ATTRIBUTE_USER_ROLE'] = 'urn:oid:1.3.6.1.4.1.5923.1.1.1.7'
            self.app.config['SAML_INSTRUCTOR_ROLE_VALUES'] = {
                SystemRole.instructor.value
            }
            # test automatic upgrading of system role for existing accounts
            for third_party_system_role in [
                    SystemRole.student, SystemRole.instructor,
                    SystemRole.sys_admin
            ]:
                user = self.data.create_user(system_role)
                third_party_user = auth_data.create_third_party_user(
                    user=user, third_party_type=ThirdPartyType.saml)
                db.session.commit()

                response_mock.get_attributes.return_value = {
                    'urn:oid:0.9.2342.19200300.100.1.1':
                    [third_party_user.unique_identifier],
                    'urn:oid:1.3.6.1.4.1.5923.1.1.1.7':
                    [third_party_system_role.value]
                }

                with mock.patch('compair.api.login.get_saml_auth_response',
                                return_value=response_mock):
                    rv = self.client.post('/api/saml/auth',
                                          follow_redirects=True)
                    self.assert200(rv)

                    # compair user system role will upgrade
                    if system_role == SystemRole.student:
                        # cannot upgrade to admin
                        if third_party_system_role == SystemRole.instructor:
                            self.assertEqual(user.system_role,
                                             SystemRole.instructor)
                        else:
                            self.assertEqual(user.system_role,
                                             SystemRole.student)
                    elif system_role == SystemRole.instructor:
                        # cannot upgrade to admin and shouldn't downgrade to student
                        self.assertEqual(user.system_role,
                                         SystemRole.instructor)
                    elif system_role == SystemRole.sys_admin:
                        # shouldn't downgrade
                        self.assertEqual(user.system_role,
                                         SystemRole.sys_admin)
Esempio n. 13
0
class AccountLearningRecordTests(ComPAIRLearningRecordTestCase):

    def setUp(self):
        super(ComPAIRLearningRecordTestCase, self).setUp()
        self.data = SimpleAnswersTestData()
        self.auth_data = ThirdPartyAuthTestData()
        self.course = self.data.main_course
        self.assignment = self.data.assignments[0]

        self.cas_user_auth = self.auth_data.create_cas_user_auth(SystemRole.instructor)
        self.cas_user = self.cas_user_auth.user
        self.data.enrol_user(self.cas_user, self.data.get_course(), CourseRole.instructor)

        self.saml_user_auth = self.auth_data.create_saml_user_auth(SystemRole.instructor)
        self.saml_user = self.saml_user_auth.user
        self.data.enrol_user(self.saml_user, self.data.get_course(), CourseRole.instructor)

    def test_actor_accounts(self):
        for user, third_party_auth in [(self.cas_user, self.cas_user_auth), (self.saml_user, self.saml_user_auth)]:

            # test without homepage set
            # (should use compair actor account)
            self.app.config['LRS_ACTOR_ACCOUNT_USE_GLOBAL_UNIQUE_IDENTIFIER'] = True
            self.app.config['LRS_ACTOR_ACCOUNT_GLOBAL_UNIQUE_IDENTIFIER_HOMEPAGE'] = None
            expected_actor = self.get_compair_xapi_actor(user)

            on_assignment_modified.send(
                current_app._get_current_object(),
                event_name=on_assignment_modified.name,
                user=user,
                assignment=self.assignment
            )
            statements = self.get_and_clear_xapi_statement_log()
            self.assertEqual(len(statements), 1)
            self.assertEqual(statements[0]['actor'], expected_actor)

            # test with homepage set and global unique identifier not set
            # (should use compair actor account)
            self.app.config['LRS_ACTOR_ACCOUNT_GLOBAL_UNIQUE_IDENTIFIER_HOMEPAGE'] = "http://third.party.homepage"
            on_assignment_modified.send(
                current_app._get_current_object(),
                event_name=on_assignment_modified.name,
                user=user,
                assignment=self.assignment
            )
            statements = self.get_and_clear_xapi_statement_log()
            self.assertEqual(len(statements), 1)
            self.assertEqual(statements[0]['actor'], expected_actor)
            expected_actor = self.get_compair_xapi_actor(user)

            # test with homepage set and global unique identifier set
            # (should use cas/saml actor account with overridden value used for name)
            user.global_unique_identifier = 'mock_puid_è_'+third_party_auth.third_party_type.value
            db.session.commit()
            expected_actor = self.get_unique_identifier_xapi_actor(
                user,
                "http://third.party.homepage/",
                'mock_puid_è_'+third_party_auth.third_party_type.value
            )
            on_assignment_modified.send(
                current_app._get_current_object(),
                event_name=on_assignment_modified.name,
                user=user,
                assignment=self.assignment
            )
            statements = self.get_and_clear_xapi_statement_log()
            self.assertEqual(len(statements), 1)
            self.assertEqual(statements[0]['actor'], expected_actor)

            # disabling LRS_ACTOR_ACCOUNT_USE_GLOBAL_UNIQUE_IDENTIFIER should skip checking global unique identifer
            self.app.config['LRS_ACTOR_ACCOUNT_USE_GLOBAL_UNIQUE_IDENTIFIER'] = False
            expected_actor = self.get_compair_xapi_actor(user)
            on_assignment_modified.send(
                current_app._get_current_object(),
                event_name=on_assignment_modified.name,
                user=user,
                assignment=self.assignment
            )
            statements = self.get_and_clear_xapi_statement_log()
            self.assertEqual(len(statements), 1)
            self.assertEqual(statements[0]['actor'], expected_actor)
Esempio n. 14
0
    def test_update_password(self):
        url = '/api/users/{}/password'
        data = {
            'oldpassword': '******',
            'newpassword': '******'
        }

        # test login required
        rv = self.client.post(
            url.format(self.data.authorized_instructor.uuid),
            data=json.dumps(data), content_type='application/json')
        self.assert401(rv)

        # test student update password
        with self.login(self.data.authorized_student.username):
            # test without old password
            rv = self.client.post(
                url.format(self.data.authorized_student.uuid),
                data=json.dumps({'newpassword': '******'}),
                content_type='application/json')
            self.assert403(rv)
            self.assertEqual(
                'The old password is incorrect or you do not have permission to change password.',
                rv.json['error'])

            # test incorrect old password
            invalid_input = data.copy()
            invalid_input['oldpassword'] = '******'
            rv = self.client.post(
                url.format(self.data.authorized_student.uuid),
                data=json.dumps(invalid_input), content_type='application/json')
            self.assert403(rv)
            self.assertEqual(
                'The old password is incorrect or you do not have permission to change password.',
                rv.json['error'])

            # test with old password
            rv = self.client.post(
                url.format(self.data.authorized_student.uuid),
                data=json.dumps(data),
                content_type='application/json')
            self.assert200(rv)
            self.assertEqual(self.data.get_authorized_student().uuid, rv.json['id'])

        # test instructor update password
        with self.login(self.data.get_authorized_instructor().username):
            rv = self.client.post(url.format(str(999)), data=json.dumps(data), content_type='application/json')
            self.assert404(rv)

            # test instructor changes the password of a student in the course
            rv = self.client.post(
                url.format(self.data.get_authorized_student().uuid), data=json.dumps(data),
                content_type='application/json')
            self.assert200(rv)
            self.assertEqual(self.data.get_authorized_student().uuid, rv.json['id'])

            # test changing own password
            rv = self.client.post(
                url.format(self.data.get_authorized_instructor().uuid),
                data=json.dumps(data), content_type='application/json')
            self.assert200(rv)
            self.assertEqual(self.data.get_authorized_instructor().uuid, rv.json['id'])

        # test instructor changes the password of a student not in the course
        with self.login(self.data.get_unauthorized_instructor().username):
            rv = self.client.post(
                url.format(self.data.get_authorized_student().uuid), data=json.dumps(data),
                content_type='application/json')
            self.assert403(rv)
            self.assertEqual(
                '<p>{} does not have edit access to {}</p>'.format(self.data.get_unauthorized_instructor().username,
                                                                   self.data.get_authorized_student().username),
                rv.json['message'])

        # test admin update password
        with self.login('root'):
            # test admin changes student password without old password
            rv = self.client.post(
                url.format(self.data.get_authorized_student().uuid), data=json.dumps({'newpassword': '******'}),
                content_type='application/json')
            self.assert200(rv)
            self.assertEqual(self.data.get_authorized_student().uuid, rv.json['id'])


        # test update password of user with no compair login
        auth_data = ThirdPartyAuthTestData()
        cas_user_auth = auth_data.create_cas_user_auth(SystemRole.student)
        user = cas_user_auth.user
        self.data.enrol_user(user, self.data.get_course(), CourseRole.student)
        url = 'api/users/' + user.uuid + '/password'

        # update own password as cas user
        with self.cas_login(cas_user_auth.unique_identifier):
            # cannot change password
            rv = self.client.post(url, data=json.dumps(data), content_type='application/json')
            self.assert400(rv)
            self.assertEqual("Cannot update password. User does not use ComPAIR account login authentication method.", rv.json['error'])
Esempio n. 15
0
    def test_edit_user(self):
        user = self.data.get_authorized_student()
        url = 'api/users/' + user.uuid
        expected = {
            'id': user.uuid,
            'username': user.username,
            'student_number': user.student_number,
            'system_role': user.system_role.value,
            'firstname': user.firstname,
            'lastname': user.lastname,
            'displayname': user.displayname,
            'email': user.email
        }
        instructor = self.data.get_authorized_instructor()
        instructor_url = 'api/users/' + instructor.uuid
        expected_instructor = {
            'id': instructor.uuid,
            'username': instructor.username,
            'student_number': instructor.student_number,
            'system_role': instructor.system_role.value,
            'firstname': instructor.firstname,
            'lastname': instructor.lastname,
            'displayname': instructor.displayname,
            'email': instructor.email
        }

        # test login required
        rv = self.client.post(url, data=json.dumps(expected), content_type='application/json')
        self.assert401(rv)

        # test unauthorized user
        with self.login(self.data.get_unauthorized_instructor().username):
            rv = self.client.post(url, data=json.dumps(expected), content_type='application/json')
            self.assert403(rv)

        # test invalid user id
        with self.login('root'):
            rv = self.client.post('/api/users/999', data=json.dumps(expected), content_type='application/json')
            self.assert404(rv)

            # test unmatched user's id
            invalid_url = '/api/users/' + self.data.get_unauthorized_instructor().uuid
            rv = self.client.post(invalid_url, data=json.dumps(expected), content_type='application/json')
            self.assert400(rv)

            # test duplicate username
            duplicate = expected.copy()
            duplicate['username'] = self.data.get_unauthorized_student().username
            rv = self.client.post(url, data=json.dumps(duplicate), content_type='application/json')
            self.assertStatus(rv, 409)
            self.assertEqual("This username already exists. Please pick another.", rv.json['error'])

            # test duplicate student number
            duplicate = expected.copy()
            duplicate['student_number'] = self.data.get_unauthorized_student().student_number
            rv = self.client.post(url, data=json.dumps(duplicate), content_type='application/json')
            self.assertStatus(rv, 409)
            self.assertEqual("This student number already exists. Please pick another.", rv.json['error'])

            # test successful update by admin
            valid = expected.copy()
            valid['displayname'] = "displayzzz"
            rv = self.client.post(url, data=json.dumps(valid), content_type='application/json')
            self.assert200(rv)
            self.assertEqual("displayzzz", rv.json['displayname'])

        # test successful update by user (as instructor)
        with self.login(self.data.get_authorized_instructor().username):
            valid = expected_instructor.copy()
            valid['displayname'] = "thebest"
            rv = self.client.post(instructor_url, data=json.dumps(valid), content_type='application/json')
            self.assert200(rv)
            self.assertEqual("thebest", rv.json['displayname'])

        # test successful update by user (as student)
        with self.login(self.data.get_authorized_student().username):
            valid = expected.copy()
            valid['displayname'] = "thebest"
            rv = self.client.post(url, data=json.dumps(valid), content_type='application/json')
            self.assert200(rv)
            self.assertEqual("thebest", rv.json['displayname'])

        # test updating username, student number, usertype for system - instructor
        with self.login(instructor.username):
            # for student
            valid = expected.copy()
            valid['username'] = "******"
            rv = self.client.post(url, data=json.dumps(valid), content_type='application/json')
            self.assert200(rv)
            self.assertEqual(user.username, rv.json['username'])

            valid = expected.copy()
            valid['student_number'] = "999999999999"
            rv = self.client.post(url, data=json.dumps(valid), content_type='application/json')
            self.assert200(rv)
            self.assertEqual(user.student_number, rv.json['student_number'])

            valid = expected.copy()
            valid['system_role'] = SystemRole.student.value
            rv = self.client.post(url, data=json.dumps(valid), content_type='application/json')
            self.assert200(rv)
            self.assertEqual(user.system_role.value, rv.json['system_role'])

            # for instructor
            valid = expected_instructor.copy()
            valid['username'] = "******"
            rv = self.client.post(instructor_url, data=json.dumps(valid), content_type='application/json')
            self.assert200(rv)
            self.assertEqual(instructor.username, rv.json['username'])

            ignored = expected_instructor.copy()
            ignored['student_number'] = "999999999999"
            rv = self.client.post(instructor_url, data=json.dumps(ignored), content_type='application/json')
            self.assert200(rv)
            self.assertIsNone(rv.json['student_number'])
            self.assertEqual(instructor.student_number, rv.json['student_number'])

            valid = expected_instructor.copy()
            valid['system_role'] = SystemRole.student.value
            rv = self.client.post(instructor_url, data=json.dumps(valid), content_type='application/json')
            self.assert200(rv)
            self.assertEqual(instructor.system_role.value, rv.json['system_role'])

        # test updating username, student number, usertype for system - admin
        with self.login('root'):
            # for student
            valid = expected.copy()
            valid['username'] = '******'
            rv = self.client.post(url, data=json.dumps(valid), content_type='application/json')
            self.assert200(rv)
            self.assertEqual('newUsername', rv.json['username'])

            valid = expected.copy()
            valid['student_number'] = '99999999'
            rv = self.client.post(url, data=json.dumps(valid), content_type='application/json')
            self.assert200(rv)
            self.assertEqual('99999999', rv.json['student_number'])

            valid = expected.copy()
            valid['system_role'] = SystemRole.student.value
            rv = self.client.post(url, data=json.dumps(valid), content_type='application/json')
            self.assert200(rv)
            self.assertEqual(user.system_role.value, rv.json['system_role'])

            # for instructor
            valid = expected_instructor.copy()
            valid['username'] = "******"
            rv = self.client.post(instructor_url, data=json.dumps(valid), content_type='application/json')
            self.assert200(rv)
            self.assertEqual(instructor.username, rv.json['username'])

            ignored = expected_instructor.copy()
            ignored['student_number'] = "999999999999"
            rv = self.client.post(instructor_url, data=json.dumps(ignored), content_type='application/json')
            self.assert200(rv)
            self.assertIsNone(rv.json['student_number'])
            self.assertEqual(instructor.student_number, rv.json['student_number'])

            valid = expected_instructor.copy()
            valid['system_role'] = SystemRole.student.value
            rv = self.client.post(instructor_url, data=json.dumps(valid), content_type='application/json')
            self.assert200(rv)
            self.assertEqual(instructor.system_role.value, rv.json['system_role'])

        # test edit user with no compair login
        auth_data = ThirdPartyAuthTestData()
        cas_user_auth = auth_data.create_cas_user_auth(SystemRole.student)
        user = cas_user_auth.user
        self.data.enrol_user(user, self.data.get_course(), CourseRole.student)

        url = 'api/users/' + user.uuid
        expected = {
            'id': user.uuid,
            'username': user.username,
            'student_number': user.student_number,
            'system_role': user.system_role.value,
            'firstname': user.firstname,
            'lastname': user.lastname,
            'displayname': user.displayname,
            'email': user.email
        }

        # edit own profile as cas user
        with self.cas_login(cas_user_auth.unique_identifier):
            # cannot change username (must be None)
            valid = expected.copy()
            valid['username'] = "******"
            rv = self.client.post(url, data=json.dumps(valid), content_type='application/json')
            self.assert200(rv)
            user = User.query.filter_by(uuid=rv.json['id']).one()
            self.assertIsNone(user.username)

        # test updating username as instructor
        with self.login(instructor.username):
            # cannot change username (must be None)
            valid = expected.copy()
            valid['username'] = "******"
            rv = self.client.post(url, data=json.dumps(valid), content_type='application/json')
            self.assert200(rv)
            user = User.query.filter_by(uuid=rv.json['id']).one()
            self.assertIsNone(user.username)

        # test updating username as system admin
        with self.login('root'):
            # admin can optionally change username
            valid = expected.copy()
            valid['username'] = ''
            rv = self.client.post(url, data=json.dumps(valid), content_type='application/json')
            self.assert200(rv)
            user = User.query.filter_by(uuid=rv.json['id']).one()
            self.assertIsNone(user.username)

            valid = expected.copy()
            valid['username'] = "******"
            rv = self.client.post(url, data=json.dumps(valid), content_type='application/json')
            self.assert200(rv)
            user = User.query.filter_by(uuid=rv.json['id']).one()
            self.assertEqual(user.username, "valid_username")