def test_study_sponsors_script_ensure_delete(self, mock_get): mock_get.return_value.ok = True mock_get.return_value.text = self.protocol_builder_response( 'sponsors.json') flask.g.user = UserModel(uid='dhf8r') app.config['PB_ENABLED'] = True self.load_example_data() study = session.query(StudyModel).first() workflow_spec_model = self.load_test_spec( "study_sponsors_associates_delete") workflow_model = StudyService._create_workflow_model( study, workflow_spec_model) WorkflowService.test_spec("study_sponsors_associates_delete") processor = WorkflowProcessor(workflow_model) processor.do_engine_steps() # change user and make sure we can access the study flask.g.user = UserModel(uid='lb3dp') flask.g.token = 'my spiffy token' app.config['PB_ENABLED'] = False output = user_studies() self.assertEqual(len(output), 0) flask.g.token = 'my spiffy token' app.config['PB_ENABLED'] = False output = user_studies() self.assertEqual(len(output), 0)
def verify_token(token=None): """ Verifies the token for the user (if provided). If in production environment and token is not provided, gets user from the SSO headers and returns their token. Args: token: Optional[str] Returns: token: str Raises: ApiError. If not on production and token is not valid, returns an 'invalid_token' 403 error. If on production and user is not authenticated, returns a 'no_user' 403 error. """ failure_error = ApiError( "invalid_token", "Unable to decode the token you provided. Please re-authenticate", status_code=403) if not _is_production() and (token is None or 'user' not in g): g.user = UserModel.query.first() token = g.user.encode_auth_token() if token: try: token_info = UserModel.decode_auth_token(token) g.user = UserModel.query.filter_by(uid=token_info['sub']).first() except: raise failure_error if g.user is not None: return token_info else: raise failure_error # If there's no token and we're in production, get the user from the SSO headers and return their token if not token and _is_production(): uid = _get_request_uid(request) if uid is not None: db_user = UserModel.query.filter_by(uid=uid).first() if db_user is not None: g.user = db_user token = g.user.encode_auth_token().decode() token_info = UserModel.decode_auth_token(token) return token_info else: raise ApiError( "no_user", "User not found. Please login via the frontend app before accessing this feature.", status_code=403)
def load_example_data(self, use_crc_data=False, use_rrt_data=False): """use_crc_data will cause this to load the mammoth collection of documents we built up developing crc, use_rrt_data will do the same for hte rrt project, otherwise it depends on a small setup for running tests.""" from example_data import ExampleDataLoader ExampleDataLoader.clean_db() # If in production mode, only add the first user. if app.config['PRODUCTION']: ldap_info = LdapService.user_info(self.users[0]['uid']) session.add( UserModel(uid=self.users[0]['uid'], ldap_info=ldap_info)) else: for user_json in self.users: ldap_info = LdapService.user_info(user_json['uid']) session.add( UserModel(uid=user_json['uid'], ldap_info=ldap_info)) if use_crc_data: ExampleDataLoader().load_all() elif use_rrt_data: ExampleDataLoader().load_rrt() else: ExampleDataLoader().load_test_data() session.commit() for study_json in self.studies: study_model = StudyModel(**study_json) session.add(study_model) StudyService._add_all_workflow_specs_to_study(study_model) session.commit() update_seq = f"ALTER SEQUENCE %s RESTART WITH %s" % ( StudyModel.__tablename__ + '_id_seq', study_model.id + 1) print("Update Sequence." + update_seq) session.execute(update_seq) session.flush() specs = session.query(WorkflowSpecModel).all() self.assertIsNotNone(specs) for spec in specs: files = session.query(FileModel).filter_by( workflow_spec_id=spec.id).all() self.assertIsNotNone(files) self.assertGreater(len(files), 0) for file in files: # file_data = session.query(FileDataModel).filter_by(file_model_id=file.id).all() file_data = SpecFileService().get_spec_file_data(file.id).data self.assertIsNotNone(file_data) self.assertGreater(len(file_data), 0)
def _upsert_user(ldap_info): user = session.query(UserModel).filter(UserModel.uid == ldap_info.uid).first() if user is None: # Add new user user = UserModel() else: user = session.query(UserModel).filter(UserModel.uid == ldap_info.uid).with_for_update().first() user.uid = ldap_info.uid user.ldap_info = ldap_info session.add(user) session.commit() return user
def test_auth_token(self): # Save the orginal timeout setting orig_ttl = float(app.config['TOKEN_AUTH_TTL_HOURS']) self.load_example_data() # Set the timeout to something else new_ttl = 4.0 app.config['TOKEN_AUTH_TTL_HOURS'] = new_ttl user_1 = UserModel(uid="dhf8r") expected_exp_1 = timegm( (datetime.utcnow() + timedelta(hours=new_ttl)).utctimetuple()) auth_token_1 = user_1.encode_auth_token() self.assertTrue(isinstance(auth_token_1, str)) self.assertEqual("dhf8r", user_1.decode_auth_token(auth_token_1).get("sub"))
def create_user(self, uid="dhf8r", email="*****@*****.**", display_name="Hoopy Frood"): user = session.query(UserModel).filter(UserModel.uid == uid).first() if user is None: ldap_user = LdapService.user_info(uid) user = UserModel(uid=uid, ldap_info=ldap_user) session.add(user) session.commit() return user
def test_study_sponsors_script_validation(self, mock_get): mock_get.return_value.ok = True mock_get.return_value.text = self.protocol_builder_response('sponsors.json') app.config['PB_ENABLED'] = True flask.g.user = UserModel(uid='dhf8r') self.load_example_data() # study_info script complains if irb_documents.xls is not loaded # during the validate phase I'm going to assume that we will never # have a case where irb_documents.xls is not loaded ?? self.load_test_spec("study_sponsors_data_store") WorkflowService.test_spec("study_sponsors_data_store") # This would raise errors if it didn't validate
def create_user(self, uid="dhf8r", email="*****@*****.**", display_name="Hoopy Frood"): user = session.query(UserModel).filter(UserModel.uid == uid).first() if user is None: user = UserModel(uid=uid, email_address=email, display_name=display_name) db.session.add(user) db.session.commit() return user
def make_test_workflow(spec_id): user = db.session.query(UserModel).filter_by(uid="test").first() if not user: db.session.add(UserModel(uid="test")) study = db.session.query(StudyModel).filter_by(user_uid="test").first() if not study: db.session.add(StudyModel(user_uid="test", title="test")) db.session.commit() workflow_model = WorkflowModel(status=WorkflowStatus.not_started, workflow_spec_id=spec_id, last_updated=datetime.now(), study=study) return workflow_model
def test_current_user_status(self): self.load_example_data() rv = self.app.get('/v1.0/user') self.assert_failure(rv, 401) rv = self.app.get('/v1.0/user', headers=self.logged_in_headers()) self.assert_success(rv) # User must exist in the mock ldap responses. user = UserModel(uid="dhf8r", ldap_info=LdapService.user_info("dhf8r")) rv = self.app.get('/v1.0/user', headers=self.logged_in_headers( user, redirect_url='http://omg.edu/lolwut')) self.assert_success(rv) user_data = json.loads(rv.get_data(as_text=True)) self.assertTrue(user_data['is_admin'])
def verify_token_admin(token=None): """ Verifies the token for the user (if provided) in non-production environment. If in production environment, checks that the user is in the list of authorized admins Args: token: Optional[str] Returns: token: str """ verify_token(token) if "user" in g and g.user.is_admin(): token = g.user.encode_auth_token() token_info = UserModel.decode_auth_token(token) return token_info
def test_current_user_status(self): self.load_example_data() rv = self.app.get('/v1.0/user') self.assert_failure(rv, 401) rv = self.app.get('/v1.0/user', headers=self.logged_in_headers()) self.assert_success(rv) # User must exist in the mock ldap responses. user = UserModel(uid="dhf8r", first_name='Dan', last_name='Funk', email_address='*****@*****.**') rv = self.app.get('/v1.0/user', headers=self.logged_in_headers( user, redirect_url='http://omg.edu/lolwut')) self.assert_success(rv)
def test_study_sponsors_script_fail(self, mock_get): mock_get.return_value.ok = True mock_get.return_value.text = self.protocol_builder_response( 'sponsors.json') flask.g.user = UserModel(uid='dhf8r') app.config['PB_ENABLED'] = True self.load_example_data() study = session.query(StudyModel).first() workflow_spec_model = self.load_test_spec( "study_sponsors_associate_fail") workflow_model = StudyService._create_workflow_model( study, workflow_spec_model) WorkflowService.test_spec("study_sponsors_associate_fail") processor = WorkflowProcessor(workflow_model) with self.assertRaises(ApiError): processor.do_engine_steps()
def create_user_with_study_and_workflow(self): # clear it all out. from example_data import ExampleDataLoader ExampleDataLoader.clean_db() # Assure some basic models are in place, This is a damn mess. Our database models need an overhaul to make # this easier - better relationship modeling is now critical. self.load_test_spec("top_level_workflow", master_spec=True) user = db.session.query(UserModel).filter( UserModel.uid == "dhf8r").first() if not user: user = UserModel(uid="dhf8r", email_address="*****@*****.**", display_name="Stayathome Smellalots") db.session.add(user) db.session.commit() else: for study in db.session.query(StudyModel).all(): StudyService().delete_study(study.id) study = StudyModel( title="My title", protocol_builder_status=ProtocolBuilderStatus.ACTIVE, user_uid=user.uid) db.session.add(study) cat = WorkflowSpecCategoryModel(name="approvals", display_name="Approvals", display_order=0) db.session.add(cat) db.session.commit() self.assertIsNotNone(cat.id) self.load_test_spec("random_fact", category_id=cat.id) self.assertIsNotNone(study.id) workflow = WorkflowModel(workflow_spec_id="random_fact", study_id=study.id, status=WorkflowStatus.not_started, last_updated=datetime.now()) db.session.add(workflow) db.session.commit() # Assure there is a master specification, one standard spec, and lookup tables. ExampleDataLoader().load_reference_documents() return user
def load_example_data(self, use_crc_data=False, use_rrt_data=False): """use_crc_data will cause this to load the mammoth collection of documents we built up developing crc, use_rrt_data will do the same for hte rrt project, otherwise it depends on a small setup for running tests.""" from example_data import ExampleDataLoader ExampleDataLoader.clean_db() if use_crc_data: ExampleDataLoader().load_all() elif use_rrt_data: ExampleDataLoader().load_rrt() else: ExampleDataLoader().load_test_data() for user_json in self.users: db.session.add(UserModel(**user_json)) db.session.commit() for study_json in self.studies: study_model = StudyModel(**study_json) db.session.add(study_model) StudyService._add_all_workflow_specs_to_study(study_model) db.session.execute(Sequence(StudyModel.__tablename__ + '_id_seq')) db.session.commit() db.session.flush() specs = session.query(WorkflowSpecModel).all() self.assertIsNotNone(specs) for spec in specs: files = session.query(FileModel).filter_by( workflow_spec_id=spec.id).all() self.assertIsNotNone(files) self.assertGreater(len(files), 0) for spec in specs: files = session.query(FileModel).filter_by( workflow_spec_id=spec.id).all() self.assertIsNotNone(files) self.assertGreater(len(files), 0) for file in files: file_data = session.query(FileDataModel).filter_by( file_model_id=file.id).all() self.assertIsNotNone(file_data) self.assertGreater(len(file_data), 0)
def test_study_sponsors_script(self, mock_get): mock_get.return_value.ok = True mock_get.return_value.text = self.protocol_builder_response('sponsors.json') flask.g.user = UserModel(uid='dhf8r') app.config['PB_ENABLED'] = True self.load_example_data() study = session.query(StudyModel).first() workflow_spec_model = self.load_test_spec("study_sponsors_data_store") workflow_model = StudyService._create_workflow_model(study, workflow_spec_model) WorkflowService.test_spec("study_sponsors_data_store") processor = WorkflowProcessor(workflow_model) processor.do_engine_steps() self.assertTrue(processor.bpmn_workflow.is_completed()) data = processor.next_task().data self.assertIn('sponsors', data) self.assertIn('out', data) self.assertEqual('empty', data['empty']) self.assertEqual('newval', data['out']) self.assertEqual(3, len(data['sponsors']))
def test_study_sponsors_script_primary_user(self, mock_get): mock_get.return_value.ok = True mock_get.return_value.text = self.protocol_builder_response( 'sponsors.json') flask.g.user = UserModel(uid='dhf8r') app.config['PB_ENABLED'] = True self.load_example_data() study = session.query(StudyModel).first() workflow_spec_model = self.load_test_spec( "study_sponsors_associate_switch_user") workflow_model = StudyService._create_workflow_model( study, workflow_spec_model) WorkflowService.test_spec("study_sponsors_associate_switch_user") processor = WorkflowProcessor(workflow_model) processor.do_engine_steps() tasks = processor.next_user_tasks() self.assertEqual(len(tasks), 1) processor.complete_task(tasks[0]) processor.do_engine_steps() self.assertTrue(processor.bpmn_workflow.is_completed())
def _upsert_user(user_info): user = db.session.query(UserModel).filter( UserModel.uid == user_info.uid).first() if user is None: # Add new user user = UserModel() else: user = db.session.query(UserModel).filter( UserModel.uid == user_info.uid).with_for_update().first() user.uid = user_info.uid user.display_name = user_info.display_name user.email_address = user_info.email_address user.affiliation = user_info.affiliation user.title = user_info.title db.session.add(user) db.session.commit() return user
def test_study_sponsors_script_valid_users(self, mock_get): mock_get.return_value.ok = True mock_get.return_value.text = self.protocol_builder_response( 'sponsors.json') flask.g.user = UserModel(uid='dhf8r') app.config['PB_ENABLED'] = True self.load_example_data() study = session.query(StudyModel).first() workflow_spec_model = self.load_test_spec( "study_sponsors_associate_switch_user") workflow_model = StudyService._create_workflow_model( study, workflow_spec_model) WorkflowService.test_spec("study_sponsors_associate_switch_user") processor = WorkflowProcessor(workflow_model) processor.do_engine_steps() tasks = processor.next_user_tasks() self.assertEqual(len(tasks), 1) users = WorkflowService.get_users_assigned_to_task(processor, tasks[0]) self.assertFalse('cah3us' in users) self.assertFalse('lje5u' in users) self.assertTrue('lb3dp' in users) self.assertTrue('dhf8r' in users)
def load_default_user(self): ldap_info = LdapModel(uid="dhf8r", email_address="*****@*****.**", display_name="Development User") user = UserModel(uid="dhf8r", ldap_info=ldap_info) db.session.add(ldap_info) db.session.add(user) db.session.commit()
def test_auth_token(self): # Save the orginal timeout setting orig_ttl = float(app.config['TOKEN_AUTH_TTL_HOURS']) self.load_example_data() # Set the timeout to something else new_ttl = 4.0 app.config['TOKEN_AUTH_TTL_HOURS'] = new_ttl user_1 = UserModel(uid="dhf8r") expected_exp_1 = timegm( (datetime.utcnow() + timedelta(hours=new_ttl)).utctimetuple()) auth_token_1 = user_1.encode_auth_token() self.assertTrue(isinstance(auth_token_1, bytes)) self.assertEqual("dhf8r", user_1.decode_auth_token(auth_token_1).get("sub")) actual_exp_1 = user_1.decode_auth_token(auth_token_1).get("exp") self.assertTrue( expected_exp_1 - 1000 <= actual_exp_1 <= expected_exp_1 + 1000) # Set the timeout to something else neg_ttl = -0.01 app.config['TOKEN_AUTH_TTL_HOURS'] = neg_ttl user_2 = UserModel(uid="dhf8r") expected_exp_2 = timegm( (datetime.utcnow() + timedelta(hours=neg_ttl)).utctimetuple()) auth_token_2 = user_2.encode_auth_token() self.assertTrue(isinstance(auth_token_2, bytes)) with self.assertRaises(ApiError) as api_error: with self.assertRaises(jwt.exceptions.ExpiredSignatureError): user_2.decode_auth_token(auth_token_2) self.assertEqual(api_error.exception.status_code, 400, 'Should raise an API Error if token is expired') # Set the timeout back to where it was app.config['TOKEN_AUTH_TTL_HOURS'] = orig_ttl user_3 = UserModel(uid="dhf8r") expected_exp_3 = timegm( (datetime.utcnow() + timedelta(hours=new_ttl)).utctimetuple()) auth_token_3 = user_3.encode_auth_token() self.assertTrue(isinstance(auth_token_3, bytes)) actual_exp_3 = user_3.decode_auth_token(auth_token_1).get("exp") self.assertTrue( expected_exp_3 - 1000 <= actual_exp_3 <= expected_exp_3 + 1000)
def test_study_sponsors_script(self, mock_get): mock_get.return_value.ok = True mock_get.return_value.text = self.protocol_builder_response( 'sponsors.json') flask.g.user = UserModel(uid='dhf8r') app.config['PB_ENABLED'] = True self.load_example_data() study = session.query(StudyModel).first() workflow_spec_model = self.load_test_spec("study_sponsors_associate") workflow_model = StudyService._create_workflow_model( study, workflow_spec_model) WorkflowService.test_spec("study_sponsors_associate") processor = WorkflowProcessor(workflow_model) processor.do_engine_steps() self.assertTrue(processor.bpmn_workflow.is_completed()) data = processor.next_task().data self.assertIn('sponsors', data) self.assertIn('out', data) print(data['out']) dhf8r_info = LdapSchema().dump(LdapService.user_info('dhf8r')) lb3dp_info = LdapSchema().dump(LdapService.user_info('lb3dp')) self.assertDictEqual( { 'uid': 'dhf8r', 'role': 'owner', 'send_email': True, 'access': True, 'ldap_info': dhf8r_info }, data['out'][1]) self.assertDictEqual( { 'uid': 'lb3dp', 'role': 'SuperDude', 'send_email': False, 'access': True, 'ldap_info': lb3dp_info }, data['out'][0]) self.assertDictEqual( { 'uid': 'lb3dp', 'role': 'SuperDude', 'send_email': False, 'access': True, 'ldap_info': lb3dp_info }, data['out2']) self.assertDictEqual( { 'uid': 'dhf8r', 'role': 'owner', 'send_email': True, 'access': True, 'ldap_info': dhf8r_info }, data['out3'][1]) self.assertDictEqual( { 'uid': 'lb3dp', 'role': 'SuperGal', 'send_email': False, 'access': True, 'ldap_info': lb3dp_info }, data['out3'][0]) self.assertDictEqual( { 'uid': 'lb3dp', 'role': 'SuperGal', 'send_email': False, 'access': True, 'ldap_info': lb3dp_info }, data['out4']) self.assertEqual(3, len(data['sponsors']))
def verify_token(token=None): """ Verifies the token for the user (if provided). If in production environment and token is not provided, gets user from the SSO headers and returns their token. Args: token: Optional[str] Returns: token: str Raises: ApiError. If not on production and token is not valid, returns an 'invalid_token' 403 error. If on production and user is not authenticated, returns a 'no_user' 403 error. """ failure_error = ApiError("invalid_token", "Unable to decode the token you provided. Please re-authenticate", status_code=403) if token: try: token_info = UserModel.decode_auth_token(token) g.user = UserModel.query.filter_by(uid=token_info['sub']).first() # If the user is valid, store the token for this session if g.user: g.token = token except: raise failure_error if g.user is not None: return token_info else: raise failure_error # If there's no token and we're in production, get the user from the SSO headers and return their token elif _is_production(): uid = _get_request_uid(request) if uid is not None: db_user = UserModel.query.filter_by(uid=uid).first() # If the user is valid, store the user and token for this session if db_user is not None: g.user = db_user token = g.user.encode_auth_token().decode() g.token = token token_info = UserModel.decode_auth_token(token) return token_info else: raise ApiError("no_user", "User not found. Please login via the frontend app before accessing this feature.", status_code=403) else: # Fall back to a default user if this is not production. g.user = UserModel.query.first() if not g.user: raise ApiError("no_user", "You are in development mode, but there are no users in the database. Add one, and it will use it.") token = g.user.encode_auth_token() token_info = UserModel.decode_auth_token(token) return token_info