def test_add_module_viewer_access_denied(self): workflow = Workflow.create_and_init(public=True) # tab-1 ModuleVersion.create_or_replace_from_spec({ "id_name": "amodule", "name": "A Module", "category": "Clean", "parameters": [{ "id_name": "foo", "type": "string" }], }) response = self.run_handler( add_module, workflow=workflow, tabSlug="tab-1", slug="step-1", position=3, moduleIdName="amodule", paramValues={"foo": "bar"}, ) self.assertResponse(response, error="AuthError: no write access to workflow")
def test_change_dependent_wf_modules(self): # tab slug: tab-1 workflow = Workflow.create_and_init(selected_tab_position=2) workflow.tabs.create(position=1, slug='tab-2') workflow.tabs.create(position=2, slug='tab-3') # Create `wf_module` depending on tabs 2+3 (and their order) ModuleVersion.create_or_replace_from_spec({ 'id_name': 'x', 'name': 'X', 'category': 'Clean', 'parameters': [ { 'id_name': 'tabs', 'type': 'multitab' }, ] }) wf_module = workflow.tabs.first().wf_modules.create( order=0, module_id_name='x', params={'tabs': ['tab-2', 'tab-3']}, last_relevant_delta_id=workflow.last_delta_id) cmd = self.run_with_async_db( ReorderTabsCommand.create(workflow=workflow, new_order=['tab-3', 'tab-1', 'tab-2'])) wf_module.refresh_from_db() self.assertEqual(wf_module.last_relevant_delta_id, cmd.id)
def test_change_parameters_deny_invalid_params(self, load_module): workflow = Workflow.create_and_init() wf_module = workflow.tabs.first().wf_modules.create( order=0, module_id_name='x', last_relevant_delta_id=workflow.last_delta_id, params={'x': 1} ) ModuleVersion.create_or_replace_from_spec({ 'id_name': 'x', 'name': 'x', 'category': 'Clean', 'parameters': [ {'id_name': 'x', 'type': 'integer'}, ] }) load_module.return_value = LoadedModule('x', '1', ParamDType.Dict({ 'x': ParamDType.Integer(), }), migrate_params_impl=lambda x: x) with self.assertRaises(ValueError): # Now the user requests to change params, giving an invalid param. self.run_with_async_db(ChangeParametersCommand.create( workflow=workflow, wf_module=wf_module, new_values={'x': 'Threeve'} ))
def test_delete_secret_writer_access_denied(self): user = User.objects.create(email='*****@*****.**') workflow = Workflow.create_and_init(public=True) workflow.acl.create(email=user.email, can_edit=True) ModuleVersion.create_or_replace_from_spec({ 'id_name': 'g', 'name': 'g', 'category': 'Clean', 'parameters': [ { 'id_name': 'google_credentials', 'type': 'secret', 'name': 'Google' }, ], }) wf_module = workflow.tabs.first().wf_modules.create( module_id_name='g', order=0, secrets={'google_credentials': { 'name': 'a', 'secret': 'hello' }}) response = self.run_handler(delete_secret, user=user, workflow=workflow, wfModuleId=wf_module.id, param='google_credentials') self.assertResponse(response, error='AuthError: no owner access to workflow')
def test_change_last_relevant_delta_ids_of_dependent_wf_modules(self): workflow = Workflow.create_and_init() delta_id = workflow.last_delta_id tab1 = workflow.tabs.first() tab2 = workflow.tabs.create(position=1, slug='tab-2', name='Tab 2') # Add a WfModule that depends on tab1 ModuleVersion.create_or_replace_from_spec({ 'id_name': 'x', 'name': 'x', 'category': 'Clean', 'parameters': [{ 'id_name': 'tab', 'type': 'tab' }] }) wf_module = tab2.wf_modules.create(order=0, module_id_name='x', params={'tab': tab1.slug}, last_relevant_delta_id=delta_id) cmd = self.run_with_async_db( SetTabNameCommand.create(workflow=workflow, tab=tab1, new_name=tab1.name + 'X')) wf_module.refresh_from_db() self.assertEqual(wf_module.last_relevant_delta_id, cmd.id)
def test_set_params(self): user = User.objects.create(username='******', email='*****@*****.**') workflow = Workflow.create_and_init(owner=user) wf_module = workflow.tabs.first().wf_modules.create(order=0, module_id_name='x') ModuleVersion.create_or_replace_from_spec({ 'id_name': 'x', 'name': 'x', 'category': 'Clean', 'parameters': [ { 'id_name': 'foo', 'type': 'string' }, ], }) response = self.run_handler(set_params, user=user, workflow=workflow, wfModuleId=wf_module.id, values={'foo': 'bar'}) self.assertResponse(response, data=None) command = ChangeParametersCommand.objects.first() self.assertEquals(command.new_values, {'foo': 'bar'}) self.assertEquals(command.old_values, {}) self.assertEquals(command.wf_module_id, wf_module.id) self.assertEquals(command.workflow_id, workflow.id)
def test_generate_secret_access_token_no_service_gives_error(self): user = User.objects.create() workflow = Workflow.create_and_init(owner=user) ModuleVersion.create_or_replace_from_spec({ 'id_name': 'g', 'name': 'g', 'category': 'Clean', 'parameters': [ { 'id_name': 'google_credentials', 'type': 'secret', 'name': 'Google' }, ], }) wf_module = workflow.tabs.first().wf_modules.create( module_id_name='g', order=0, secrets={'google_credentials': { 'name': 'a', 'secret': 'hello' }}) response = self.run_handler(generate_secret_access_token, user=user, workflow=workflow, wfModuleId=wf_module.id, param='google_credentials') self.assertResponse( response, error=('AuthError: we only support twitter_credentials'))
def test_add_module_param_values_not_object(self): user = User.objects.create(username='******', email='*****@*****.**') workflow = Workflow.create_and_init(owner=user) # tab-1 ModuleVersion.create_or_replace_from_spec({ 'id_name': 'amodule', 'name': 'A Module', 'category': 'Clean', 'parameters': [ { 'id_name': 'foo', 'type': 'string' }, ], }) response = self.run_handler(add_module, user=user, workflow=workflow, tabSlug='tab-1', position=3, moduleIdName='amodule', paramValues='foobar') self.assertResponse(response, error='BadRequest: paramValues must be an Object')
def test_set_params_invalid_params(self): user = User.objects.create(username="******", email="*****@*****.**") workflow = Workflow.create_and_init(owner=user) wf_module = workflow.tabs.first().wf_modules.create(order=0, slug="step-1", module_id_name="x") ModuleVersion.create_or_replace_from_spec({ "id_name": "x", "name": "x", "category": "Clean", "parameters": [{ "id_name": "foo", "type": "string" }], }) response = self.run_handler( set_params, user=user, workflow=workflow, wfModuleId=wf_module.id, values={"foo1": "bar"}, ) self.assertResponse( response, error=("ValueError: Value {'foo1': 'bar'} has wrong names: " "expected names {'foo'}"), )
def test_add_module_missing_tab(self): user = User.objects.create(username='******', email='*****@*****.**') workflow = Workflow.create_and_init(owner=user) other_workflow = Workflow.create_and_init(owner=user) # Create a "honeypot" tab -- make sure the module doesn't get inserted # in the other workflow's 'tab-2'! other_workflow.tabs.create(position=1, slug='tab-2') ModuleVersion.create_or_replace_from_spec({ 'id_name': 'amodule', 'name': 'A Module', 'category': 'Clean', 'parameters': [ { 'id_name': 'foo', 'type': 'string' }, ], }) response = self.run_handler(add_module, user=user, workflow=workflow, tabSlug='tab-2', position=3, moduleIdName='amodule', paramValues={'foo': 'bar'}) self.assertResponse(response, error='DoesNotExist: Tab not found')
def test_add_module_viewer_access_denied(self): workflow = Workflow.create_and_init(public=True) # tab-1 ModuleVersion.create_or_replace_from_spec({ 'id_name': 'amodule', 'name': 'A Module', 'category': 'Clean', 'parameters': [ { 'id_name': 'foo', 'type': 'string' }, ], }) response = self.run_handler(add_module, workflow=workflow, tabSlug='tab-1', position=3, moduleIdName='amodule', paramValues={'foo': 'bar'}) self.assertResponse(response, error='AuthError: no write access to workflow')
def test_oauth1a_token_request_denied(self, lookup): lookup.return_value = Mock(oauth.OAuthService) lookup.return_value.generate_redirect_url_and_state.side_effect = \ oauth.TokenRequestDenied('no!', {}) ModuleVersion.create_or_replace_from_spec({ 'id_name': 'ext', 'name': '', 'category': 'Clean', 'parameters': [{ 'id_name': 'auth', 'type': 'secret', 'name': 'Secret' }] }) user = User.objects.create(username='******', email='*****@*****.**') self.client.force_login(user) workflow = Workflow.objects.create(owner=user) tab = workflow.tabs.create(position=0) wf_module = tab.wf_modules.create(module_id_name='ext', order=0) response = self.client.get( f'/oauth/create-secret/{workflow.id}/{wf_module.id}/auth/') self.assertEqual(response.status_code, 403) self.assertRegex( response.content, b'The authorization server refused to let you log in')
def test_add_to_empty_tab_affects_dependent_tab_wf_modules(self): ModuleVersion.create_or_replace_from_spec({ 'id_name': 'tabby', 'name': 'Tabby', 'category': 'Clean', 'parameters': [{ 'id_name': 'tab', 'type': 'tab' }] }) wfm1 = self.workflow.tabs.first().wf_modules.create( order=0, module_id_name='tabby', last_relevant_delta_id=self.workflow.last_delta_id, params={'tab': 'tab-2'}) tab2 = self.workflow.tabs.create(position=1, slug='tab-2') # Now add a module to tab2. cmd = self.run_with_async_db( AddModuleCommand.create(workflow=self.workflow, tab=tab2, module_id_name=self.module_version.id_name, position=0, param_values={'url': 'https://x.com'})) # Tab1's "tabby" module depends on tab2, so it should update. wfm1.refresh_from_db() self.assertEqual(wfm1.last_relevant_delta_id, cmd.id)
def test_oauth1a_token_request_denied(self, lookup): lookup.return_value = Mock(oauth.OAuthService) lookup.return_value.generate_redirect_url_and_state.side_effect = oauth.TokenRequestDenied( "no!", {} ) ModuleVersion.create_or_replace_from_spec( { "id_name": "ext", "name": "", "category": "Clean", "parameters": [ { "id_name": "auth", "type": "secret", "secret_logic": {"provider": "oauth", "service": "twitter"}, } ], } ) user = User.objects.create(username="******", email="*****@*****.**") self.client.force_login(user) workflow = Workflow.create_and_init(owner=user) wf_module = workflow.tabs.first().wf_modules.create( module_id_name="ext", order=0, slug="step-1" ) response = self.client.get( f"/oauth/create-secret/{workflow.id}/{wf_module.id}/auth/" ) self.assertEqual(response.status_code, 403) self.assertRegex( response.content, b"The authorization server refused to let you log in" )
def test_delete_secret_happy_path(self, send_delta): send_delta.return_value = async_noop() user = User.objects.create() workflow = Workflow.create_and_init(owner=user) ModuleVersion.create_or_replace_from_spec({ 'id_name': 'g', 'name': 'g', 'category': 'Clean', 'parameters': [TestGoogleSecret], }) wf_module = workflow.tabs.first().wf_modules.create( module_id_name='g', order=0, secrets={'google_credentials': { 'name': 'a', 'secret': 'hello' }}) response = self.run_handler(delete_secret, user=user, workflow=workflow, wfModuleId=wf_module.id, param='google_credentials') self.assertResponse(response, data=None) wf_module.refresh_from_db() self.assertEqual(wf_module.secrets, {}) send_delta.assert_called() delta = send_delta.call_args[0][1] wf_module_delta = delta['updateWfModules'][str(wf_module.id)] self.assertEqual(wf_module_delta['secrets'], {})
def test_set_params(self): user = User.objects.create(username="******", email="*****@*****.**") workflow = Workflow.create_and_init(owner=user) wf_module = workflow.tabs.first().wf_modules.create(order=0, slug="step-1", module_id_name="x") ModuleVersion.create_or_replace_from_spec({ "id_name": "x", "name": "x", "category": "Clean", "parameters": [{ "id_name": "foo", "type": "string" }], }) response = self.run_handler( set_params, user=user, workflow=workflow, wfModuleId=wf_module.id, values={"foo": "bar"}, ) self.assertResponse(response, data=None) command = ChangeParametersCommand.objects.first() self.assertEquals(command.new_values, {"foo": "bar"}) self.assertEquals(command.old_values, {}) self.assertEquals(command.wf_module_id, wf_module.id) self.assertEquals(command.workflow_id, workflow.id)
def test_set_secret_error_not_a_secret(self): user = User.objects.create() workflow = Workflow.create_and_init(owner=user) ModuleVersion.create_or_replace_from_spec({ 'id_name': 'g', 'name': 'g', 'category': 'Clean', 'parameters': [ { 'id_name': 'string_secret', 'type': 'string' }, ], }) wf_module = workflow.tabs.first().wf_modules.create( module_id_name='g', order=0, params={'string_secret': 'bar'}, secrets={}) response = self.run_handler(set_secret, user=user, workflow=workflow, wfModuleId=wf_module.id, param='string_secret', secret='foo') self.assertResponse( response, error='BadRequest: param is not a secret string parameter') wf_module.refresh_from_db() self.assertEqual(wf_module.params, {'string_secret': 'bar'}) self.assertEqual(wf_module.secrets, {})
def test_generate_secret_access_token_wrong_param_type_gives_null(self): user = User.objects.create() workflow = Workflow.create_and_init(owner=user) ModuleVersion.create_or_replace_from_spec({ "id_name": "g", "name": "g", "category": "Clean", "parameters": [TestGoogleSecret], }) wf_module = workflow.tabs.first().wf_modules.create( module_id_name="g", order=0, slug="step-1", params={"s": '{"name":"a","secret":"hello"}'}, ) response = self.run_handler( generate_secret_access_token, user=user, workflow=workflow, wfModuleId=wf_module.id, param="a", ) self.assertResponse(response, data={"token": None})
def test_generate_secret_access_token_wrong_param_type_gives_null(self): user = User.objects.create() workflow = Workflow.create_and_init(owner=user) ModuleVersion.create_or_replace_from_spec({ 'id_name': 'g', 'name': 'g', 'category': 'Clean', 'parameters': [ { 'id_name': 'google_credentials', 'type': 'secret', 'name': 'Google' }, ], }) wf_module = workflow.tabs.first().wf_modules.create( module_id_name='g', order=0, params={'s': '{"name":"a","secret":"hello"}'}) response = self.run_handler(generate_secret_access_token, user=user, workflow=workflow, wfModuleId=wf_module.id, param='a') self.assertResponse(response, data={'token': None})
def test_generate_secret_access_token_no_service_gives_error(self): user = User.objects.create() workflow = Workflow.create_and_init(owner=user) ModuleVersion.create_or_replace_from_spec({ "id_name": "g", "name": "g", "category": "Clean", "parameters": [TestGoogleSecret], }) wf_module = workflow.tabs.first().wf_modules.create( module_id_name="g", order=0, slug="step-1", secrets={"google_credentials": { "name": "a", "secret": "hello" }}, ) response = self.run_handler( generate_secret_access_token, user=user, workflow=workflow, wfModuleId=wf_module.id, param="google_credentials", ) self.assertResponse(response, error=("AuthError: we only support twitter"))
def test_set_params_invalid_params(self): user = User.objects.create(username='******', email='*****@*****.**') workflow = Workflow.create_and_init(owner=user) wf_module = workflow.tabs.first().wf_modules.create(order=0, module_id_name='x') ModuleVersion.create_or_replace_from_spec({ 'id_name': 'x', 'name': 'x', 'category': 'Clean', 'parameters': [ { 'id_name': 'foo', 'type': 'string' }, ], }) response = self.run_handler(set_params, user=user, workflow=workflow, wfModuleId=wf_module.id, values={'foo1': 'bar'}) self.assertResponse( response, error=("ValueError: Value {'foo1': 'bar'} has wrong names: " "expected names {'foo'}"))
def test_generate_secret_access_token_auth_error_gives_error( self, factory): service = Mock(oauth.OAuth2) service.generate_access_token_or_str_error.return_value = "an error" factory.return_value = service user = User.objects.create() workflow = Workflow.create_and_init(owner=user) ModuleVersion.create_or_replace_from_spec({ "id_name": "g", "name": "g", "category": "Clean", "parameters": [TestGoogleSecret], }) wf_module = workflow.tabs.first().wf_modules.create( module_id_name="g", order=0, slug="step-1", secrets={"google_credentials": { "name": "a", "secret": "hello" }}, ) response = self.run_handler( generate_secret_access_token, user=user, workflow=workflow, wfModuleId=wf_module.id, param="google_credentials", ) self.assertResponse(response, error="AuthError: an error")
def test_delete_secret_ignore_non_secret(self): user = User.objects.create() workflow = Workflow.create_and_init(owner=user) ModuleVersion.create_or_replace_from_spec({ 'id_name': 'g', 'name': 'g', 'category': 'Clean', 'parameters': [ { 'id_name': 'google_credentials', 'type': 'string' }, ], }) wf_module = workflow.tabs.first().wf_modules.create( module_id_name='g', order=0, params={'foo': 'bar'}, secrets={}) response = self.run_handler(delete_secret, user=user, workflow=workflow, wfModuleId=wf_module.id, param='foo') self.assertResponse(response, data=None) wf_module.refresh_from_db() self.assertEqual(wf_module.params, {'foo': 'bar'})
def test_delete_secret_writer_access_denied(self): user = User.objects.create(email="*****@*****.**") workflow = Workflow.create_and_init(public=True) workflow.acl.create(email=user.email, can_edit=True) ModuleVersion.create_or_replace_from_spec({ "id_name": "g", "name": "g", "category": "Clean", "parameters": [TestGoogleSecret], }) wf_module = workflow.tabs.first().wf_modules.create( module_id_name="g", order=0, slug="step-1", secrets={"google_credentials": { "name": "a", "secret": "hello" }}, ) response = self.run_handler( delete_secret, user=user, workflow=workflow, wfModuleId=wf_module.id, param="google_credentials", ) self.assertResponse(response, error="AuthError: no owner access to workflow")
def test_change_last_relevant_delta_ids_of_self_wf_modules(self): """ Module render() accepts a `tab_name` argument: test it sees a new one. """ workflow = Workflow.create_and_init() delta_id = workflow.last_delta_id tab = workflow.tabs.first() # Add a WfModule that relies on `tab.name` through its 'render' method. ModuleVersion.create_or_replace_from_spec({ 'id_name': 'x', 'name': 'x', 'category': 'Clean', 'parameters': [] }) wf_module = tab.wf_modules.create(order=0, module_id_name='x', last_relevant_delta_id=delta_id) cmd = self.run_with_async_db( SetTabNameCommand.create(workflow=workflow, tab=tab, new_name=tab.name + 'X')) wf_module.refresh_from_db() self.assertEqual(wf_module.last_relevant_delta_id, cmd.id)
def test_generate_secret_access_token_wrong_param_name_gives_null(self): user = User.objects.create() workflow = Workflow.create_and_init(owner=user) ModuleVersion.create_or_replace_from_spec({ 'id_name': 'g', 'name': 'g', 'category': 'Clean', 'parameters': [TestGoogleSecret], }) wf_module = workflow.tabs.first().wf_modules.create( module_id_name='g', order=0, secrets={'google_credentials': { 'name': 'a', 'secret': 'hello' }}) response = self.run_handler(generate_secret_access_token, user=user, workflow=workflow, wfModuleId=wf_module.id, param='twitter_credentials') self.assertResponse(response, data={'token': None})
def test_workflow_anonymous_user(self): # Looking at example workflow as anonymous should create a new workflow num_workflows = Workflow.objects.count() self.other_workflow_public.example = True self.other_workflow_public.save() # Also ensure the anonymous users can't access the Python module; first we need to load it ModuleVersion.create_or_replace_from_spec({ 'id_name': 'pythoncode', 'name': 'Python', 'category': 'Clean', 'parameters': [], }) request = self._build_get('/workflows/%d/' % self.other_workflow_public.id, user=AnonymousUser()) response = render_workflow(request, workflow_id=self.other_workflow_public.id) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual( Workflow.objects.count(), num_workflows + 1) # should have duplicated the wf with this API call # Ensure the anonymous users can't access the Python module self.assertNotContains(response, '"pythoncode"')
def test_generate_secret_access_token_happy_path(self, factory): service = Mock(oauth.OAuth2) service.generate_access_token_or_str_error.return_value = { 'access_token': 'a-token', 'refresh_token': 'something we must never share', } factory.return_value = service user = User.objects.create() workflow = Workflow.create_and_init(owner=user) ModuleVersion.create_or_replace_from_spec({ 'id_name': 'g', 'name': 'g', 'category': 'Clean', 'parameters': [TestGoogleSecret], }) wf_module = workflow.tabs.first().wf_modules.create( module_id_name='g', order=0, secrets={'google_credentials': { 'name': 'a', 'secret': 'hello' }}) response = self.run_handler(generate_secret_access_token, user=user, workflow=workflow, wfModuleId=wf_module.id, param='google_credentials') self.assertResponse(response, data={'token': 'a-token'})
def test_change_parameters_on_soft_deleted_wf_module(self): workflow = Workflow.create_and_init() ModuleVersion.create_or_replace_from_spec({ 'id_name': 'loadurl', 'name': 'loadurl', 'category': 'Clean', 'parameters': [ {'id_name': 'url', 'type': 'string'}, ] }) wf_module = workflow.tabs.first().wf_modules.create( order=0, module_id_name='loadurl', last_relevant_delta_id=workflow.last_delta_id, is_deleted=True, params={'url': ''} ) cmd = self.run_with_async_db(ChangeParametersCommand.create( workflow=workflow, wf_module=wf_module, new_values={'url': 'https://example.com'} )) self.assertIsNone(cmd)
def test_add_module_missing_tab(self): user = User.objects.create(username="******", email="*****@*****.**") workflow = Workflow.create_and_init(owner=user) other_workflow = Workflow.create_and_init(owner=user) # Create a "honeypot" tab -- make sure the module doesn't get inserted # in the other workflow's 'tab-2'! other_workflow.tabs.create(position=1, slug="tab-2") ModuleVersion.create_or_replace_from_spec({ "id_name": "amodule", "name": "A Module", "category": "Clean", "parameters": [{ "id_name": "foo", "type": "string" }], }) response = self.run_handler( add_module, user=user, workflow=workflow, tabSlug="tab-2", slug="step-1", position=3, moduleIdName="amodule", paramValues={"foo": "bar"}, ) self.assertResponse(response, error="DoesNotExist: Tab not found")