def get_dataset_list_details(self): """Get a detailed list of datasets in the service""" url = '{}/datasets?simple=true'.format(self.url) headers = { "Content-Type": "application/json", "Accept": "application/json" } try: response = g.requests.get(url, headers=headers, timeout=self.timeout) response.raise_for_status() except requests.exceptions.HTTPError as error: current_app.logger.error( 'Encountered non 2xx http code from ulapd_api when retrieving list of datasets' ) raise ApplicationError(*errors.get( "verification_api", "ULAPD_API_HTTP_ERROR", filler=str(error))) except requests.exceptions.ConnectionError as error: current_app.logger.error( 'Encountered an error connecting to ulapd_api when retrieving list of datasets' ) raise ApplicationError(*errors.get( "verification_api", "ULAPD_API_CONN_ERROR", filler=str(error))) except requests.exceptions.Timeout as error: current_app.logger.error( 'Encountered a timeout with ulapd_api when retrieving list of datasets' ) raise ApplicationError(*errors.get( "verification_api", "ULAPD_API_TIMEOUT", filler=str(error))) else: app.logger.info( "Retrieved detailed list of datasets in the service") return response.json()
def decorated(*args, **kwargs): try: ulapd_api = UlapdAPI() api_key = request.headers.get('Authorization') name = kwargs['dataset'] dataset = ulapd_api.get_dataset_by_name(name) # Prevent 'open' datasets being accessed via API if dataset['type'] == 'open': raise ApplicationError(*errors.get('ulapd_ui', 'DATASET_NOT_FOUND', filler=name), http_code=404) # Prevent 'confidential' datasets being available via API if user doesn't have access if dataset['type'] == 'confidential': try: user_access = ulapd_api.get_user_details( 'api_key', api_key)['datasets'] except ApplicationError: raise ApplicationError(*errors.get('ulapd_ui', 'API_KEY_ERROR', filler=api_key), http_code=404) if not user_access.get(name, False): raise ApplicationError(*errors.get('ulapd_ui', 'DATASET_NOT_FOUND', filler=name), http_code=404) return f(*args, **kwargs) except ApplicationError as e: return jsonify({'success': False, 'error': e.message}), e.http_code
def delete_user(user_id): try: UserTermsLink.delete_user_by_user_id(user_id) Activity.delete_user_by_user_id(user_id) Contact.delete_contact_preferences_for_user(user_id) user = UserDetails.get_user_details_by_id(user_id) if user: db.session.delete(user) else: app.logger.error("No user: {} found for deletion".format(user_id)) db.session.rollback() db.session.close() raise ApplicationError(*errors.get('ulapd_api', 'USER_NOT_FOUND', filler=user_id), http_code=404) db.session.commit() return {'user_id': user_id, 'message': 'user deleted'} except Exception as e: db.session.rollback() db.session.close() raise ApplicationError(*errors.get('ulapd_api', 'DELETE_USER_ERROR', filler=e), http_code=400)
def update_dataset_access(self, data): """Update dataset access for user (add/remove licences to ulapd database LDAP roles)""" url = '{0}/users/licence'.format(self.url) headers = { "Content-Type": "application/json", "Accept": "application/json" } try: response = g.requests.post(url, json=data, headers=headers, timeout=self.timeout) response.raise_for_status() except requests.exceptions.HTTPError as error: current_app.logger.error( 'Encountered non 2xx http code from ulapd_api while updating dataset access' ) raise ApplicationError(*errors.get( "verification_api", "ULAPD_API_HTTP_ERROR", filler=str(error))) except requests.exceptions.ConnectionError as error: current_app.logger.error( 'Encountered an error connecting to ulapd_api while updating dataset access' ) raise ApplicationError(*errors.get( "verification_api", "ULAPD_API_CONN_ERROR", filler=str(error))) except requests.exceptions.Timeout as error: current_app.logger.error( 'Encountered a timeout with ulapd_api getting while updating dataset access' ) raise ApplicationError(*errors.get( "verification_api", "ULAPD_API_TIMEOUT", filler=str(error))) else: app.logger.info("Updated user dataset access") return response.json()
def create(self, data): """Activate user.""" url = '{0}/{1}/users'.format(self.url, self.version) headers = { "Content-Type": "application/json", "Accept": "application/json" } payload = _create_user_data(data) try: response = g.requests.post(url, json=payload, headers=headers, timeout=self.timeout) response.raise_for_status() except requests.exceptions.HTTPError as error: current_app.logger.error('Encountered non 2xx http code from account_api for user activation') raise ApplicationError(*errors.get("ulapd_api", "ACCOUNT_API_HTTP_ERROR", filler=str(error))) except requests.exceptions.ConnectionError as error: current_app.logger.error('Encountered an error connecting to account_api for user activation') raise ApplicationError(*errors.get("ulapd_api", "ACCOUNT_API_CONN_ERROR", filler=str(error))) except requests.exceptions.Timeout as error: current_app.logger.error('Encountered a timeout when writing to account_api for user activation') raise ApplicationError(*errors.get("ulapd_api", "ACCOUNT_API_TIMEOUT", filler=str(error))) else: app.logger.info("Created user {}".format(payload['email'])) return response.json()
def update_groups(self, ldap_id, groups): """Update users account""" url = '{0}/{1}/users/update_groups'.format(self.url, self.version) headers = { "Content-Type": "application/json", "Accept": "application/json" } data = { 'ldap_id': ldap_id, 'groups': groups } try: response = g.requests.patch(url, json=data, headers=headers, timeout=self.timeout) response.raise_for_status() except requests.exceptions.HTTPError as error: current_app.logger.error('Encountered non 2xx http code from account_api for updating groups') raise ApplicationError(*errors.get("ulapd_api", "ACCOUNT_API_HTTP_ERROR", filler=str(error))) except requests.exceptions.ConnectionError as error: current_app.logger.error('Encountered an error connecting to account_api for updating group') raise ApplicationError(*errors.get("ulapd_api", "ACCOUNT_API_CONN_ERROR", filler=str(error))) except requests.exceptions.Timeout as error: current_app.logger.error('Encountered a timeout when writing to account_api for updating group') raise ApplicationError(*errors.get("ulapd_api", "ACCOUNT_API_TIMEOUT", filler=str(error))) else: app.logger.info("groups updated for user {}".format(ldap_id)) return {'message': 'groups updated'}
def get(self, ldap_id): url = '{0}/{1}/users?id={2}'.format(self.url, self.version, ldap_id) headers = { "Accept": "application/json", 'Authorization': 'Bearer ' + self.key } try: response = g.requests.get(url, headers=headers, timeout=self.timeout) response.raise_for_status() except requests.exceptions.HTTPError as error: current_app.logger.error( 'Encountered non 2xx http code from account_api for retrieving user details' ) raise ApplicationError(*errors.get("verification_api", "ACCOUNT_API_HTTP_ERROR", filler=str(error))) except requests.exceptions.ConnectionError as error: current_app.logger.error( 'Encountered an error connecting to account_api for retrieving user details' ) raise ApplicationError(*errors.get("verification_api", "ACCOUNT_API_CONN_ERROR", filler=str(error))) except requests.exceptions.Timeout as error: current_app.logger.error( 'Encountered a timeout when writing to account_api for retrieving user details' ) raise ApplicationError(*errors.get( "verification_api", "ACCOUNT_API_TIMEOUT", filler=str(error))) else: return response.json()
def update(self, data): """Update user.""" url = '{0}/users/contact_preference'.format(self.url) headers = { "Content-Type": "application/json", "Accept": "application/json" } try: response = g.requests.patch(url, json=data, headers=headers, timeout=self.timeout) response.raise_for_status() except requests.exceptions.HTTPError as error: current_app.logger.error( 'Encountered non 2xx http code from ulapd_api for user activation' ) raise ApplicationError(*errors.get( "verification_api", "ULAPD_API_HTTP_ERROR", filler=str(error))) except requests.exceptions.ConnectionError as error: current_app.logger.error( 'Encountered an error connecting to ulapd_api for user activation' ) raise ApplicationError(*errors.get( "verification_api", "ULAPD_API_CONN_ERROR", filler=str(error))) except requests.exceptions.Timeout as error: current_app.logger.error( 'Encountered a timeout when writing to ulapd_api for user activation' ) raise ApplicationError(*errors.get( "verification_api", "ULAPD_API_TIMEOUT", filler=str(error))) else: app.logger.info("Update user {}".format(data['user_id'])) return {'message': 'user updated'}
def get_dataset_activity(self, user_id): """Get a users licence agreements and download history""" url = '{0}/users/dataset-activity/{1}'.format(self.url, user_id) headers = { "Content-Type": "application/json", "Accept": "application/json" } try: response = g.requests.get(url, headers=headers, timeout=self.timeout) response.raise_for_status() except requests.exceptions.HTTPError as error: current_app.logger.error( 'Encountered non 2xx http code from ulapd_api getting users dataset activity' ) raise ApplicationError(*errors.get( "verification_api", "ULAPD_API_HTTP_ERROR", filler=str(error))) except requests.exceptions.ConnectionError as error: current_app.logger.error( 'Encountered an error connecting to ulapd_api getting users dataset activity' ) raise ApplicationError(*errors.get( "verification_api", "ULAPD_API_CONN_ERROR", filler=str(error))) except requests.exceptions.Timeout as error: current_app.logger.error( 'Encountered a timeout with ulapd_api getting users dataset activity' ) raise ApplicationError(*errors.get( "verification_api", "ULAPD_API_TIMEOUT", filler=str(error))) else: app.logger.info("Retrieved details of the users dataset activity") return response.json()
def func_wrapper(*args, **kwargs): if config.LOGIN_DISABLED == 'True': return func(*args, **kwargs) if current_user.is_authenticated: current_app.logger.info('LIST OF ROLES: ' + str(current_user.get_roles())) if current_user.has_role(role): return func(*args, **kwargs) else: error = errors.get("verification_ui", "ADFS_ROLE_LOGGED_IN_ERROR", filler=str(role)) raise ApplicationError(*error) else: raise ApplicationError(*errors.get("verification_ui", "ADFS_ROLE_LOGGED_OUT_ERROR", filler=str(role)))
def manage_licence_agreement(data): try: user = UserDetails.get_user_details_by_id(data['user_details_id']) if not user: app.logger.error("No user found for id {}".format( data['user_details_id'])) raise ApplicationError(*errors.get('ulapd_api', 'USER_NOT_FOUND', filler=data['user_details_id']), http_code=404) # check to see if user already has this type of licence ldap_update_needed = True licence_data = _extract_rows( Licence.get_licences_by_dataset_name(data['licence_id'])) for rows in licence_data: user_licence = UserTermsLink.get_user_terms_by_licence_name( user.user_details_id, rows['licence_id']) if user_licence: app.logger.info( 'User {} already has the role {} so not updating ldap...'. format(user.user_details_id, data['licence_id'])) ldap_update_needed = False stored_licence_id = data['licence_id'] if _check_freemium(data['licence_id']): data['licence_id'] += '_direct' licence = UserTermsLink(data) db.session.add(licence) if ldap_update_needed: app.logger.info( 'User {} does not have the role {} so update ldap...'.format( user.user_details_id, stored_licence_id)) _handle_ldap_group(stored_licence_id, user.ldap_id, True) db.session.commit() data['link_id'] = licence.user_terms_link_id return data except Exception as e: app.logger.error( 'Failed to manage licence for user {} with error - {}'.format( data['user_details_id'], str(e))) db.session.rollback() db.session.close() raise ApplicationError(*errors.get('ulapd_api', 'LICENCE_AGREE_ERROR', filler=e), http_code=400)
def create_new_user(user_data): try: account = AccountAPI() verification = VerificationAPI() app.logger.info('Creating user in ldap for {}'.format( user_data['email'])) response = account.create(user_data) user_data['ldap_id'] = response['user_id'] user_data = _process_new_user_data(user_data) app.logger.info('Adding user {} to the ulapd database...'.format( user_data['email'])) new_user_details = UserDetails(user_data) db.session.add(new_user_details) db.session.commit() user_data['user_details_id'] = new_user_details.user_details_id if user_data['contactable']: app.logger.info( 'Inserting the contact preferences for {}...'.format( user_data['email'])) for preference in user_data['contact_preferences']: contact = { 'user_details_id': user_data['user_details_id'], 'contact_type': preference } contact_preference = Contact(contact) db.session.add(contact_preference) db.session.commit() app.logger.info( 'Finished adding user {} to the ulapd database...'.format( user_data['email'])) # Add the user details to verification database for DST app.logger.info('Adding user {} to verification db...'.format( user_data['email'])) verification.create(user_data) # Call to account-api to activate or acknowledge user is_uk_org = 'organisation-uk' in user_data['user_type'] if is_uk_org: app.logger.info('Acknowledging user') account.acknowledge(user_data['ldap_id']) else: app.logger.info('Activating user') account.activate(user_data['ldap_id']) db.session.close() return user_data except Exception as e: app.logger.error('Failed to create user with error - {}'.format( str(e))) db.session.rollback() _delete_user_data(account, user_data) db.session.close() raise ApplicationError(*errors.get('ulapd_api', 'CREATE_USER_ERROR', filler=e), http_code=400)
def get_api_dataset_history(name): ulapd_api = UlapdAPI() # authenticate _authenticate(ulapd_api) history_details = ulapd_api.get_dataset_history(name) if history_details: history_data = [] for history in history_details['dataset_history']: for file in history['resource_list']: history_info = { "last_updated": history["last_updated"], "unsorted_date": history["unsorted_date"], "filename": file['file_name'], "file_size": file['file_size'] } history_data.append(history_info) response = { "success": True, "dataset": name, "dataset_history": history_data } return response else: current_app.logger.error('Dataset {} not found'.format(name)) raise ApplicationError(*errors.get('ulapd_ui', 'DATASET_NOT_FOUND', filler=name), http_code=404)
def get_api_datasets(external=False): ulapd_api = UlapdAPI() dataset_list = ulapd_api.get_datasets() user_details = _authenticate(ulapd_api) user_access = user_details['datasets'] if dataset_list: # Filter out 'open' datasets dataset_list = [d for d in dataset_list if d['type'] != 'open'] result_data = [] for dataset in dataset_list: # Don't show 'confidential' datasets unless user has access if dataset['type'] == 'confidential': if user_access.get(dataset['name']): result_data.append( dict(name=dataset['name'], title=dataset['title'])) else: result_data.append( dict(name=dataset['name'], title=dataset['title'])) response = {"success": True, "result": result_data} return response else: raise ApplicationError(*errors.get('ulapd_ui', 'NO_DATASETS_FOUND'), http_code=404)
def get_user_by_key(user_data): if user_data['key'] == 'user_details_id': user = UserDetails.get_user_details_by_id(user_data['value']) elif user_data['key'] == 'email': user = UserDetails.get_user_details_by_email(user_data['value']) elif user_data['key'] == 'api_key': user = UserDetails.get_user_details_by_api_key(user_data['value']) elif user_data['key'] == 'ldap_id': user = UserDetails.get_user_details_by_ldap_id(user_data['value']) else: raise ApplicationError('Incorrect key: {}'.format(user_data['key']), 'E101', http_code=404) if user: user_details = user.as_dict() user_type = get_user_type(user_details['user_type_id']) user_details['user_type'] = user_type contact_preferences = _build_contact_preferences( _extract_rows( Contact.get_contact_preferences_for_user( user_details['user_details_id']))) user_details['contact_preferences'] = contact_preferences user_dict = { 'user_details': user_details, 'datasets': _build_users_datasets(user_details['user_details_id']) } return user_dict else: raise ApplicationError(*errors.get('ulapd_api', 'USER_NOT_FOUND', filler=user_data['value']), http_code=404)
def _request(self, uri, data=None): url = '{}/{}'.format(self.base_url, uri) headers = {'Accept': 'application/json'} timeout = current_app.config['DEFAULT_TIMEOUT'] try: if data is None: response = g.requests.get(url, headers=headers, timeout=timeout) else: headers['Content-Type'] = 'application/json' response = g.requests.post(url, headers=headers, timeout=timeout, data=data) status = response.status_code if status == 204: return {} if status == 404: error = 'Not Found' raise ApplicationError(*errors.get('verification_ui', 'API_HTTP_ERROR', filler=str(error)), http_code=status) else: response.raise_for_status() return response.json() except requests.exceptions.HTTPError as error: current_app.logger.error( 'Encountered non-2xx HTTP code when accessing {}'.format(url)) current_app.logger.error('Error: {}'.format(error)) raise ApplicationError(*errors.get( 'verification_ui', 'API_HTTP_ERROR', filler=str(error))) except requests.exceptions.ConnectionError as error: current_app.logger.error( 'Encountered an error while connecting to Verification API') raise ApplicationError(*errors.get( 'verification_ui', 'API_CONN_ERROR', filler=str(error))) except requests.exceptions.Timeout as error: current_app.logger.error( 'Encountered a timeout while accessing {}'.format(url)) raise ApplicationError(*errors.get( 'verification_ui', 'API_TIMEOUT', filler=str(error)))
def update_api_key(user_id): try: user = UserDetails.get_user_details_by_id(user_id) if user: user.api_key = _create_api_key() db.session.commit() return get_user_details(user_id) else: raise ApplicationError(*errors.get('ulapd_api', 'USER_NOT_FOUND', filler=user_id), http_code=404) except Exception as e: raise ApplicationError(*errors.get('ulapd_api', 'RESET_API_KEY_ERROR', filler=e), http_code=400)
def get_dataset_history(name): dataset = Dataset.get_dataset_by_name(name) if dataset: return _metadata_extend_history(dataset.as_dict()) else: raise ApplicationError(*errors.get('ulapd_api', 'DATASET_NOT_FOUND', filler=name), http_code=404)
def get_dataset_activity(user_id): user = UserDetails.get_user_details_by_id(user_id) if user: return _build_user_dataset_activity(user_id) else: app.logger.error("No user found for id {}".format(user_id)) raise ApplicationError(*errors.get('ulapd_api', 'USER_NOT_FOUND', filler=user_id), http_code=404)
def decline(self, ldap_id, decline_reason, decline_advice, user_id): """Decline user.""" url = '{0}/{1}/users/decline'.format(self.url, self.version) headers = { "Content-Type": "application/json", "Accept": "application/json", 'Authorization': 'Bearer ' + self.key } reason = { 'ldap_id': ldap_id, 'reason': decline_reason, 'advice': decline_advice, 'user_id': user_id } try: response = g.requests.post(url, data=json.dumps(reason), headers=headers, timeout=self.timeout) response.raise_for_status() except requests.exceptions.HTTPError as error: current_app.logger.error( 'Encountered non 2xx http code from account_api for declining user' ) raise ApplicationError(*errors.get("verification_api", "ACCOUNT_API_HTTP_ERROR", filler=str(error))) except requests.exceptions.ConnectionError as error: current_app.logger.error( 'Encountered an error connecting to account_api for declining user' ) raise ApplicationError(*errors.get("verification_api", "ACCOUNT_API_CONN_ERROR", filler=str(error))) except requests.exceptions.Timeout as error: current_app.logger.error( 'Encountered a timeout when writing to account_api for declining user' ) raise ApplicationError(*errors.get( "verification_api", "ACCOUNT_API_TIMEOUT", filler=str(error))) else: app.logger.info("Declined user {}".format(ldap_id)) return {'message': 'declined'}
def test_update_groups_for_ldap_error(self, mock_account): error = ApplicationError( *errors.get("ulapd_api", "ACCOUNT_API_HTTP_ERROR")) mock_account.return_value.update_groups.side_effect = error with app.app_context() as ac: update_groups_retry = current_app.config["UPDATE_GROUPS_RETRY"] ac.g.trace_id = None update_groups_for_ldap('112-122', {'nps': True}) self.assertEqual(mock_account.return_value.update_groups.call_count, int(update_groups_retry))
def test_insert_case_error(self, mock_case, *_): error = ('verification_api', 'VERIFICATION_ERROR') mock_case.side_effect = ApplicationError( *errors.get(*error, filler='TEST ERR')) with self.assertRaises(ApplicationError) as context: service.insert_case({'foo': 'bar'}) self.assertEqual(context.exception.message, errors.get_message(*error, filler='TEST ERR')) self.assertEqual(context.exception.code, errors.get_code(*error))
def _request(self, uri, data=None): url = '{}/{}'.format(self.base_url, uri) headers = {'Accept': 'application/json'} timeout = current_app.config['DEFAULT_TIMEOUT'] try: if data is None: response = g.requests.get(url, headers=headers, timeout=timeout) else: headers['Content-Type'] = 'application/json' response = g.requests.post(url, headers=headers, timeout=timeout, data=data) status = response.status_code if status == 204: return {} if status == 404: raise ApplicationError(*errors.get('ulapd_ui', 'RESOURCE_NOT_FOUND', filler=uri), http_code=status) response.raise_for_status() return response.json() except requests.exceptions.HTTPError as e: raise ApplicationError(*errors.get('ulapd_ui', 'API_HTTP_ERROR', filler=e), http_code=status) except requests.exceptions.ConnectionError as e: raise ApplicationError(*errors.get('ulapd_ui', 'API_CONN_ERROR', filler=e), http_code=status) except requests.exceptions.Timeout as e: raise ApplicationError(*errors.get('ulapd_ui', 'API_TIMEOUT', filler=e), http_code=status)
def _authenticate(ulapd_api): api_key = request.headers.get('Authorization') try: user_details = ulapd_api.get_user_details('api_key', api_key) except ApplicationError: raise ApplicationError(*errors.get('ulapd_ui', 'API_KEY_ERROR', filler=api_key), http_code=404) return user_details
def close(self, ldap_id, user_id, requester): """Close users account""" url = '{0}/{1}/users/close'.format(self.url, self.version) headers = { "Content-Type": "application/json", "Accept": "application/json", 'Authorization': 'Bearer ' + self.key } closure_data = { 'ldap_id': ldap_id, 'user_id': user_id, 'requester': requester } try: response = g.requests.post(url, data=json.dumps(closure_data), headers=headers, timeout=self.timeout) response.raise_for_status() except requests.exceptions.HTTPError as error: current_app.logger.error( 'Encountered non 2xx http code from account_api for closing account' ) raise ApplicationError(*errors.get("verification_api", "ACCOUNT_API_HTTP_ERROR", filler=str(error))) except requests.exceptions.ConnectionError as error: current_app.logger.error( 'Encountered an error connecting to account_api for closing account' ) raise ApplicationError(*errors.get("verification_api", "ACCOUNT_API_CONN_ERROR", filler=str(error))) except requests.exceptions.Timeout as error: current_app.logger.error( 'Encountered a timeout when writing to account_api for closing account' ) raise ApplicationError(*errors.get( "verification_api", "ACCOUNT_API_TIMEOUT", filler=str(error))) else: app.logger.info("closed account for user {}".format(ldap_id)) return {'message': 'closed'}
def test_get_worklist_error(self, mock_service, *_): mock_service.get_pending.side_effect = ApplicationError(*errors.get( *self.test_error)) expected_err_msg = errors.get_message(*self.test_error) response = self.app.get('/v1/worklist', headers=self.headers) self.assertEqual(500, response.status_code) response_body = response.get_json() self.assertEqual("Failed to retrieve worklist - " + expected_err_msg, response_body['error'])
def test_get_dataset_list_details_error(self, mock_service, *_): mock_service.get_dataset_list_details.side_effect = ApplicationError( *errors.get(*self.test_error)) response = self.app.get('/v1/dataset-list-details', headers=self.headers) expected_err = 'Failed to get detailed dataset list - {}'.format( errors.get_message(*self.test_error)) self.assertEqual(response.get_json()['error'], expected_err) self.assertEqual(response.status_code, 500)
def test_decline_reason_error(self, mock_service, *_): mock_service.get_decline_reasons.side_effect = ApplicationError( *errors.get(*self.test_error)) expected_err_msg = errors.get_message(*self.test_error) response = self.app.get('/v1/decline-reasons', headers=self.headers) self.assertEqual(500, response.status_code) response_body = response.get_json() self.assertEqual("Failed to get decline reasons - " + expected_err_msg, response_body['error'])
def test_decline_reasons_error(self, mock_extract, *_): error = ('verification_api', 'VERIFICATION_ERROR') mock_extract.side_effect = ApplicationError( *errors.get(*error, filler='TEST ERR')) with self.assertRaises(ApplicationError) as context: service.get_decline_reasons() self.assertEqual(context.exception.message, errors.get_message(*error, filler='TEST ERR')) self.assertEqual(context.exception.code, errors.get_code(*error))
def test_get_all_metrics_error(self, mock_service): mock_service.retrieve_all_metrics.side_effect = ApplicationError( *errors.get(*self.sql_error)) expected_err_msg = errors.get_message(*self.sql_error) response = self.app.get('/v1/metric', headers=self.headers) self.assertEqual(500, response.status_code) response_body = response.get_json() self.assertEqual("Failed to retrieve metrics - " + expected_err_msg, response_body['error'])