def test_get_api_keys_should_return_all_keys_for_service(notify_api, notify_db, notify_db_session, sample_api_key): with notify_api.test_request_context(): with notify_api.test_client() as client: another_user = create_user(notify_db, notify_db_session, email='*****@*****.**') another_service = create_sample_service(notify_db, notify_db_session, service_name='another', user=another_user) create_sample_api_key(notify_db, notify_db_session, service=another_service) api_key2 = ApiKey(**{'service_id': sample_api_key.service_id, 'name': 'second_api_key'}) api_key3 = ApiKey(**{'service_id': sample_api_key.service_id, 'name': 'third_api_key', 'expiry_date': datetime.utcnow() + timedelta(hours=-1)}) save_model_api_key(api_key2) save_model_api_key(api_key3) assert ApiKey.query.count() == 4 auth_header = create_authorization_header(path=url_for('service.get_api_keys', service_id=sample_api_key.service_id), method='GET') response = client.get(url_for('service.get_api_keys', service_id=sample_api_key.service_id), headers=[('Content-Type', 'application/json'), auth_header]) assert response.status_code == 200 json_resp = json.loads(response.get_data(as_text=True)) assert len(json_resp['apiKeys']) == 3
def test_authentication_passes_when_service_has_multiple_keys_some_expired( notify_api, notify_db, notify_db_session, sample_api_key): with notify_api.test_request_context(): with notify_api.test_client() as client: exprired_key = { 'service_id': sample_api_key.service_id, 'name': 'expired_key', 'expiry_date': datetime.now() } expired_api_key = ApiKey(**exprired_key) save_model_api_key(expired_api_key) another_key = { 'service_id': sample_api_key.service_id, 'name': 'another_key' } api_key = ApiKey(**another_key) save_model_api_key(api_key) token = create_jwt_token( request_method="GET", request_path=url_for('service.get_service'), secret=get_unsigned_secret(api_key.id), client_id=sample_api_key.service_id) response = client.get( url_for('service.get_service'), headers={'Authorization': 'Bearer {}'.format(token)}) assert response.status_code == 200
def test_authentication_returns_token_expired_when_service_uses_expired_key_and_has_multiple_keys( client, sample_api_key): expired_key = { 'service': sample_api_key.service, 'name': 'expired_key', 'created_by': sample_api_key.created_by, 'key_type': KEY_TYPE_NORMAL } expired_api_key = ApiKey(**expired_key) save_model_api_key(expired_api_key) another_key = { 'service': sample_api_key.service, 'name': 'another_key', 'created_by': sample_api_key.created_by, 'key_type': KEY_TYPE_NORMAL } api_key = ApiKey(**another_key) save_model_api_key(api_key) token = create_jwt_token(secret=get_unsigned_secret(expired_api_key.id), client_id=str(sample_api_key.service_id)) expire_api_key(service_id=sample_api_key.service_id, api_key_id=expired_api_key.id) request.headers = {'Authorization': 'Bearer {}'.format(token)} with pytest.raises(AuthError) as exc: validate_service_api_key_auth() assert exc.value.short_message == 'Invalid token: API key revoked' assert exc.value.service_id == expired_api_key.service_id assert exc.value.api_key_id == expired_api_key.id
def test_authentication_returns_token_expired_when_service_uses_expired_key_and_has_multiple_keys( client, sample_api_key): expired_key = { "service": sample_api_key.service, "name": "expired_key", "created_by": sample_api_key.created_by, "key_type": KEY_TYPE_NORMAL, } expired_api_key = ApiKey(**expired_key) save_model_api_key(expired_api_key) another_key = { "service": sample_api_key.service, "name": "another_key", "created_by": sample_api_key.created_by, "key_type": KEY_TYPE_NORMAL, } api_key = ApiKey(**another_key) save_model_api_key(api_key) token = create_jwt_token( secret=get_unsigned_secret(expired_api_key.id), client_id=str(sample_api_key.service_id), ) expire_api_key(service_id=sample_api_key.service_id, api_key_id=expired_api_key.id) request.headers = {"Authorization": "Bearer {}".format(token)} with pytest.raises(AuthError) as exc: requires_auth() assert exc.value.short_message == "Invalid token: API key revoked" assert exc.value.service_id == expired_api_key.service_id assert exc.value.api_key_id == expired_api_key.id
def test_authentication_returns_token_expired_when_service_uses_expired_key_and_has_multiple_keys( notify_api, notify_db, notify_db_session, sample_api_key): with notify_api.test_request_context(): with notify_api.test_client() as client: expired_key = { 'service_id': sample_api_key.service_id, 'name': 'expired_key' } expired_api_key = ApiKey(**expired_key) save_model_api_key(expired_api_key) another_key = { 'service_id': sample_api_key.service_id, 'name': 'another_key' } api_key = ApiKey(**another_key) save_model_api_key(api_key) token = create_jwt_token( request_method="GET", request_path=url_for('service.get_service'), secret=get_unsigned_secret(expired_api_key.id), client_id=sample_api_key.service_id) # expire the key expire_the_key = { 'id': expired_api_key.id, 'service_id': sample_api_key.service_id, 'name': 'expired_key', 'expiry_date': datetime.now() + timedelta(hours=-2) } save_model_api_key(expired_api_key, expire_the_key) response = client.get( url_for('service.get_service'), headers={'Authorization': 'Bearer {}'.format(token)}) assert response.status_code == 403 data = json.loads(response.get_data()) assert data['error'] == 'Invalid token: signature'
def test_authentication_passes_when_service_has_multiple_keys_some_expired( client, sample_api_key): expired_key_data = {'service': sample_api_key.service, 'name': 'expired_key', 'expiry_date': datetime.utcnow(), 'created_by': sample_api_key.created_by, 'key_type': KEY_TYPE_NORMAL } expired_key = ApiKey(**expired_key_data) save_model_api_key(expired_key) another_key = {'service': sample_api_key.service, 'name': 'another_key', 'created_by': sample_api_key.created_by, 'key_type': KEY_TYPE_NORMAL } api_key = ApiKey(**another_key) save_model_api_key(api_key) token = create_jwt_token( secret=get_unsigned_secret(api_key.id), client_id=str(sample_api_key.service_id)) response = client.get( '/notifications', headers={'Authorization': 'Bearer {}'.format(token)}) assert response.status_code == 200
def test_get_notification_from_different_api_key_of_same_type_succeeds(client, sample_notification, key_type): creation_api_key = ApiKey(service=sample_notification.service, name='creation_api_key', created_by=sample_notification.service.created_by, key_type=key_type) save_model_api_key(creation_api_key) querying_api_key = ApiKey(service=sample_notification.service, name='querying_api_key', created_by=sample_notification.service.created_by, key_type=key_type) save_model_api_key(querying_api_key) sample_notification.api_key = creation_api_key sample_notification.key_type = key_type dao_update_notification(sample_notification) response = client.get( path='/notifications/{}'.format(sample_notification.id), headers=_create_auth_header_from_key(querying_api_key)) assert response.status_code == 200 notification = json.loads(response.get_data(as_text=True))['data']['notification'] assert sample_notification.api_key_id != querying_api_key.id assert notification['id'] == str(sample_notification.id)
def test_get_all_notifications_only_returns_notifications_of_matching_type( client, notify_db, notify_db_session, sample_service, key_type ): team_api_key = ApiKey(service=sample_service, name='team_api_key', created_by=sample_service.created_by, key_type=KEY_TYPE_TEAM) save_model_api_key(team_api_key) normal_api_key = ApiKey(service=sample_service, name='normal_api_key', created_by=sample_service.created_by, key_type=KEY_TYPE_NORMAL) save_model_api_key(normal_api_key) test_api_key = ApiKey(service=sample_service, name='test_api_key', created_by=sample_service.created_by, key_type=KEY_TYPE_TEST) save_model_api_key(test_api_key) normal_notification = create_sample_notification( notify_db, notify_db_session, api_key=normal_api_key, key_type=KEY_TYPE_NORMAL ) team_notification = create_sample_notification( notify_db, notify_db_session, api_key=team_api_key, key_type=KEY_TYPE_TEAM ) test_notification = create_sample_notification( notify_db, notify_db_session, api_key=test_api_key, key_type=KEY_TYPE_TEST ) notification_objs = { KEY_TYPE_NORMAL: normal_notification, KEY_TYPE_TEAM: team_notification, KEY_TYPE_TEST: test_notification } response = client.get( path='/notifications', headers=_create_auth_header_from_key(notification_objs[key_type].api_key)) assert response.status_code == 200 notifications = json.loads(response.get_data(as_text=True))['notifications'] assert len(notifications) == 1 assert notifications[0]['id'] == str(notification_objs[key_type].id)
def test_only_normal_api_keys_can_return_job_notifications( client, notify_db, notify_db_session, sample_service, sample_job, key_type): team_api_key = ApiKey( service=sample_service, name="team_api_key", created_by=sample_service.created_by, key_type=KEY_TYPE_TEAM, ) save_model_api_key(team_api_key) normal_api_key = ApiKey( service=sample_service, name="normal_api_key", created_by=sample_service.created_by, key_type=KEY_TYPE_NORMAL, ) save_model_api_key(normal_api_key) test_api_key = ApiKey( service=sample_service, name="test_api_key", created_by=sample_service.created_by, key_type=KEY_TYPE_TEST, ) save_model_api_key(test_api_key) create_sample_notification(notify_db, notify_db_session, job=sample_job) normal_notification = create_sample_notification(notify_db, notify_db_session, api_key=normal_api_key, key_type=KEY_TYPE_NORMAL) team_notification = create_sample_notification(notify_db, notify_db_session, api_key=team_api_key, key_type=KEY_TYPE_TEAM) test_notification = create_sample_notification(notify_db, notify_db_session, api_key=test_api_key, key_type=KEY_TYPE_TEST) notification_objs = { KEY_TYPE_NORMAL: normal_notification, KEY_TYPE_TEAM: team_notification, KEY_TYPE_TEST: test_notification, } response = client.get( path="/notifications?include_jobs=true", headers=_create_auth_header_from_key( notification_objs[key_type[0]].api_key), ) assert response.status_code == 200 notifications = json.loads( response.get_data(as_text=True))["notifications"] assert len(notifications) == key_type[1] assert notifications[0]["id"] == str(notification_objs[key_type[0]].id)
def initial_apikey_data(): pdns_proto = os.environ['PDNS_PROTO'] pdns_host = os.environ['PDNS_HOST'] pdns_port = os.environ['PDNS_PORT'] pdns_api_url = '{0}://{1}:{2}'.format(pdns_proto, pdns_host, pdns_port) api_url_setting = Setting('pdns_api_url', pdns_api_url) api_key_setting = Setting('pdns_api_key', os.environ['PDNS_API_KEY']) allow_create_domain_setting = Setting('allow_user_create_domain', True) try: with app.app_context(): flask_migrate.upgrade() db.session.add(api_url_setting) db.session.add(api_key_setting) db.session.add(allow_create_domain_setting) test_user_apikey = app.config.get('TEST_USER_APIKEY') test_admin_apikey = app.config.get('TEST_ADMIN_APIKEY') dummy_apikey = ApiKey( desc="dummy", role_name="Administrator" ) admin_key = dummy_apikey.get_hashed_password( plain_text_password=test_admin_apikey ).decode('utf-8') admin_apikey = ApiKey( key=admin_key, desc="test admin apikey", role_name="Administrator" ) admin_apikey.create() user_key = dummy_apikey.get_hashed_password( plain_text_password=test_user_apikey ).decode('utf-8') user_apikey = ApiKey( key=user_key, desc="test user apikey", role_name="User" ) user_apikey.create() except Exception as e: logging.error("Unexpected ERROR: {0}".format(e)) raise e yield db.session.close() os.unlink(app.config['TEST_DB_LOCATION'])
def admin_apikey(): data = admin_apikey_data() api_key = ApiKey(desc=data['description'], role_name=data['role'], domains=[]) headers = create_apikey_headers(api_key.plain_key) return headers
def test_should_persist_notification(client, sample_template, sample_email_template, fake_uuid, mocker, template_type, queue_name): mocked = mocker.patch( 'app.celery.provider_tasks.deliver_{}.apply_async'.format( template_type)) mocker.patch('app.notifications.process_notifications.uuid.uuid4', return_value=fake_uuid) template = sample_template if template_type == SMS_TYPE else sample_email_template to = sample_template.service.created_by.mobile_number if template_type == SMS_TYPE \ else sample_email_template.service.created_by.email_address data = {'to': to, 'template': template.id} api_key = ApiKey(service=template.service, name='team_key', created_by=template.created_by, key_type=KEY_TYPE_TEAM) save_model_api_key(api_key) auth_header = create_jwt_token(secret=api_key.secret, client_id=str(api_key.service_id)) response = client.post(path='/notifications/{}'.format(template_type), data=json.dumps(data), headers=[('Content-Type', 'application/json'), ('Authorization', 'Bearer {}'.format(auth_header))]) mocked.assert_called_once_with([fake_uuid], queue=queue_name) assert response.status_code == 201 notification = notifications_dao.get_notification_by_id(fake_uuid) assert notification.to == to assert notification.template_id == template.id assert notification.notification_type == template_type
def test_should_send_sms_if_team_api_key_and_a_service_user( client, sample_template, fake_uuid, mocker): mocker.patch('app.celery.provider_tasks.deliver_sms.apply_async') mocker.patch('app.notifications.process_notifications.uuid.uuid4', return_value=fake_uuid) data = { 'to': sample_template.service.created_by.mobile_number, 'template': sample_template.id } api_key = ApiKey(service=sample_template.service, name='team_key', created_by=sample_template.created_by, key_type=KEY_TYPE_TEAM) save_model_api_key(api_key) auth_header = create_jwt_token(secret=api_key.secret, client_id=str(api_key.service_id)) response = client.post(path='/notifications/sms', data=json.dumps(data), headers=[('Content-Type', 'application/json'), ('Authorization', 'Bearer {}'.format(auth_header))]) app.celery.provider_tasks.deliver_sms.apply_async.assert_called_once_with( [fake_uuid], queue='send-sms-tasks') assert response.status_code == 201
def test_should_send_email_to_anyone_with_test_key(client, sample_email_template, mocker, restricted, limit, fake_uuid): mocker.patch('app.celery.provider_tasks.deliver_email.apply_async') mocker.patch('app.notifications.process_notifications.uuid.uuid4', return_value=fake_uuid) data = { 'to': '*****@*****.**', 'template': sample_email_template.id } sample_email_template.service.restricted = restricted sample_email_template.service.message_limit = limit api_key = ApiKey(service=sample_email_template.service, name='test_key', created_by=sample_email_template.created_by, key_type=KEY_TYPE_TEST) save_model_api_key(api_key) auth_header = create_jwt_token(secret=api_key.secret, client_id=str(api_key.service_id)) response = client.post(path='/notifications/email', data=json.dumps(data), headers=[('Content-Type', 'application/json'), ('Authorization', 'Bearer {}'.format(auth_header))]) app.celery.provider_tasks.deliver_email.apply_async.assert_called_once_with( [fake_uuid], queue='research-mode-tasks') assert response.status_code == 201
def test_save_api_key_should_create_new_api_key(notify_api, notify_db, notify_db_session, sample_service): api_key = ApiKey(**{'service_id': sample_service.id, 'name': sample_service.name}) save_model_api_key(api_key) all_api_keys = get_model_api_keys(service_id=sample_service.id) assert len(all_api_keys) == 1 assert all_api_keys[0] == api_key
def sample_admin_service_id(notify_db, notify_db_session): admin_user = sample_user(notify_db, notify_db_session, email="*****@*****.**") admin_service = sample_service(notify_db, notify_db_session, service_name="Sample Admin Service", user=admin_user) data = {'service': admin_service, 'name': 'sample admin key'} api_key = ApiKey(**data) save_model_api_key(api_key) return admin_service.id
def common_data_mock(self): self.oauth_setting_patcher = patch('app.oauth.Setting', spec=app.models.Setting) self.views_setting_patcher = patch('app.views.Setting', spec=app.models.Setting) self.helpers_setting_patcher = patch('app.lib.helper.Setting', spec=app.models.Setting) self.models_setting_patcher = patch('app.models.Setting', spec=app.models.Setting) self.mock_apikey_patcher = patch('app.decorators.ApiKey', spec=app.models.ApiKey) self.mock_hist_patcher = patch('app.blueprints.api.History', spec=app.models.History) self.mock_oauth_setting = self.oauth_setting_patcher.start() self.mock_views_setting = self.views_setting_patcher.start() self.mock_helpers_setting = self.helpers_setting_patcher.start() self.mock_models_setting = self.models_setting_patcher.start() self.mock_apikey = self.mock_apikey_patcher.start() self.mock_hist = self.mock_hist_patcher.start() self.mock_oauth_setting.return_value.get.side_effect = load_data self.mock_views_setting.return_value.get.side_effect = load_data self.mock_helpers_setting.return_value.get.side_effect = load_data self.mock_models_setting.return_value.get.side_effect = load_data data = user_apikey_data() domain = Domain(name=data['domains'][0]) api_key = ApiKey(desc=data['description'], role_name=data['role'], domains=[domain]) api_key.role = Role(name=data['role']) self.mock_apikey.return_value.is_validate.return_value = api_key
def api_generate_apikey(): data = request.get_json() description = None role_name = None apikey = None domain_obj_list = [] abort(400) if 'domains' not in data else None abort(400) if not isinstance(data['domains'], (list, )) else None abort(400) if 'role' not in data else None description = data['description'] if 'description' in data else None role_name = data['role'] domains = data['domains'] if role_name == 'User' and len(domains) == 0: logging.error("Apikey with User role must have domains") raise ApiKeyNotUsable() elif role_name == 'User': domain_obj_list = Domain.query.filter(Domain.name.in_(domains)).all() if len(domain_obj_list) == 0: msg = "One of supplied domains does not exists" logging.error(msg) raise DomainNotExists(message=msg) if g.user.role.name not in ['Administrator', 'Operator']: # domain list of domain api key should be valid for # if not any domain error # role of api key, user cannot assign role above for api key if role_name != 'User': msg = "User cannot assign other role than User" logging.error(msg) raise NotEnoughPrivileges(message=msg) user_domain_obj_list = g.user.get_domains() domain_list = [item.name for item in domain_obj_list] user_domain_list = [item.name for item in user_domain_obj_list] logging.debug("Input domain list: {0}".format(domain_list)) logging.debug("User domain list: {0}".format(user_domain_list)) inter = set(domain_list).intersection(set(user_domain_list)) if not (len(inter) == len(domain_list)): msg = "You don't have access to one of domains" logging.error(msg) raise DomainAccessForbidden(message=msg) apikey = ApiKey(desc=description, role_name=role_name, domains=domain_obj_list) try: apikey.create() except Exception as e: logging.error('Error: {0}'.format(e)) raise ApiKeyCreateFail(message='Api key create failed') return json.dumps(apikey_plain_schema.dump([apikey])), 201
def test_should_delete_notification_and_return_error_if_sqs_fails( client, sample_email_template, sample_template, fake_uuid, mocker, template_type, queue_name): mocked = mocker.patch( 'app.celery.provider_tasks.deliver_{}.apply_async'.format( template_type), side_effect=Exception("failed to talk to SQS")) mocker.patch('app.notifications.process_notifications.uuid.uuid4', return_value=fake_uuid) template = sample_template if template_type == SMS_TYPE else sample_email_template to = sample_template.service.created_by.mobile_number if template_type == SMS_TYPE \ else sample_email_template.service.created_by.email_address data = {'to': to, 'template': template.id} api_key = ApiKey(service=template.service, name='team_key', created_by=template.created_by, key_type=KEY_TYPE_TEAM) save_model_api_key(api_key) auth_header = create_jwt_token(secret=api_key.secret, client_id=str(api_key.service_id)) with pytest.raises(Exception) as e: client.post(path='/notifications/{}'.format(template_type), data=json.dumps(data), headers=[('Content-Type', 'application/json'), ('Authorization', 'Bearer {}'.format(auth_header)) ]) assert str(e.value) == 'failed to talk to SQS' mocked.assert_called_once_with([fake_uuid], queue=queue_name) assert not notifications_dao.get_notification_by_id(fake_uuid) assert not NotificationHistory.query.get(fake_uuid)
def create_authorization_header(service_id=None, key_type=KEY_TYPE_NORMAL): if service_id: client_id = str(service_id) secrets = ApiKey.query.filter_by(service_id=service_id, key_type=key_type).all() if secrets: secret = secrets[0].secret else: service = dao_fetch_service_by_id(service_id) data = { 'service': service, 'name': uuid.uuid4(), 'created_by': service.created_by, 'key_type': key_type } api_key = ApiKey(**data) save_model_api_key(api_key) secret = api_key.secret else: client_id = current_app.config['ADMIN_CLIENT_USER_NAME'] secret = current_app.config['ADMIN_CLIENT_SECRET'] token = create_jwt_token(secret=secret, client_id=client_id) return 'Authorization', 'Bearer {}'.format(token)
def create_authorization_header(service_id=None, key_type=KEY_TYPE_NORMAL): if service_id: client_id = str(service_id) secrets = ApiKey.query.filter_by(service_id=service_id, key_type=key_type).all() if secrets: secret = secrets[0].secret else: service = dao_fetch_service_by_id(service_id) data = { "service": service, "name": uuid.uuid4(), "created_by": service.created_by, "key_type": key_type, } api_key = ApiKey(**data) save_model_api_key(api_key) secret = api_key.secret else: client_id = current_app.config["ADMIN_CLIENT_USER_NAME"] secret = current_app.config["ADMIN_CLIENT_SECRET"] token = create_jwt_token(secret=secret, client_id=client_id) return "Authorization", "Bearer {}".format(token)
def _get_notification(client, notification, url): save_model_api_key( ApiKey(service=notification.service, name='api_key', created_by=notification.service.created_by, key_type=KEY_TYPE_NORMAL)) auth_header = create_authorization_header( service_id=notification.service_id) return client.get(url, headers=[auth_header])
def sample_api_key(notify_db, notify_db_session, service=None): if service is None: service = sample_service(notify_db, notify_db_session) data = {'service_id': service.id, 'name': uuid.uuid4()} api_key = ApiKey(**data) save_model_api_key(api_key) return api_key
def api_key(app, users): with app.app_context(): db.session.add( ApiKey( key= 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', user_id=users[0].id)) db.session.commit() yield ApiKey.query.first()
def sample_api_key(notify_db_session): service = create_service(check_if_service_exists=True) data = { 'service': service, 'name': uuid.uuid4(), 'created_by': service.created_by, 'key_type': KEY_TYPE_NORMAL } api_key = ApiKey(**data) save_model_api_key(api_key) return api_key
def sample_api_key(notify_db, notify_db_session, service=None, key_type=KEY_TYPE_NORMAL, name=None): if service is None: service = sample_service(notify_db, notify_db_session) data = {'service': service, 'name': name or uuid.uuid4(), 'created_by': service.created_by, 'key_type': key_type} api_key = ApiKey(**data) save_model_api_key(api_key) return api_key
def test_should_not_allow_duplicate_key_names_per_service(notify_api, notify_db, notify_db_session, sample_api_key): api_key = ApiKey( **{'id': sample_api_key.id + 1, 'service_id': sample_api_key.service_id, 'name': sample_api_key.name}) try: save_model_api_key(api_key) fail("should throw IntegrityError") except: pass
def create_api_key(service, key_type=KEY_TYPE_NORMAL): id_ = uuid.uuid4() api_key = ApiKey(service=service, name='{} api key {}'.format(key_type, id_), created_by=service.created_by, key_type=key_type, id=id_, secret=uuid.uuid4()) db.session.add(api_key) db.session.commit() return api_key
def test_save_api_key_can_create_key_with_same_name_if_other_is_expired( sample_service): expired_api_key = ApiKey( **{ 'service': sample_service, 'name': "normal api key", 'created_by': sample_service.created_by, 'key_type': KEY_TYPE_NORMAL, 'expiry_date': datetime.utcnow() }) save_model_api_key(expired_api_key) api_key = ApiKey( **{ 'service': sample_service, 'name': "normal api key", 'created_by': sample_service.created_by, 'key_type': KEY_TYPE_NORMAL }) save_model_api_key(api_key) keys = ApiKey.query.all() assert len(keys) == 2
def test_should_not_allow_duplicate_key_names_per_service( sample_api_key, fake_uuid): api_key = ApiKey( **{ 'id': fake_uuid, 'service': sample_api_key.service, 'name': sample_api_key.name, 'created_by': sample_api_key.created_by, 'key_type': KEY_TYPE_NORMAL }) with pytest.raises(IntegrityError): save_model_api_key(api_key)