From 1dfdf8ed453daa901f327deb8b022070392a52bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Lombra=C3=B1a=20Gonz=C3=A1lez?= Date: Sun, 29 Sep 2019 22:21:02 +0200 Subject: [PATCH 1/5] fix(werkzeug): address CVE issue. --- setup.py | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/setup.py b/setup.py index d346cf8fef..e3dcb32979 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,8 @@ "beautifulsoup4>=4.3.2, <5.0", "blinker>=1.3, <2.0", "Flask-Babel>=0.9, <0.10", - "flask-login", # was pinned to Flask-Login==0.2.3 in the past. GitHub version 3.0+ is used now. + # was pinned to Flask-Login==0.2.3 in the past. GitHub version 3.0+ is used now. + "flask-login", "Flask-Mail>=0.9.0, <1.0", "misaka>=1.0.0, <2.0.0", "Flask-Misaka>=0.2.0, <0.4.0", @@ -43,7 +44,8 @@ "unidecode>=0.04.16, <0.05", "flask-plugins", "humanize", - "pbr>=1.0, <2.0", # keep an eye on pbr: https://github.com/rackspace/pyrax/issues/561 + # keep an eye on pbr: https://github.com/rackspace/pyrax/issues/561 + "pbr>=1.0, <2.0", "feedparser", "twitter>=1.17.1, <1.18", "google-api-python-client>=1.5.0, <1.6.0", @@ -65,36 +67,36 @@ "wtforms-components>=0.10.3, <0.10.4", "yacryptopan", "Faker", - "Werkzeug>=0.14.1, <0.14.2", + "Werkzeug>=0.15.3, <0.15.4", "keyring>=13.2.1, <13.2.2", "iiif-prezi>=0.2.9, <1.0.0" ] setup( - name = 'pybossa', - version = '2.12.0', - packages = find_packages(), - install_requires = requirements, + name='pybossa', + version='2.12.0', + packages=find_packages(), + install_requires=requirements, # only needed when installing directly from setup.py (PyPi, eggs?) and pointing to e.g. a git repo. # Keep in mind that dependency_links are not used when installing with requirements.txt # and need to be added redundant to requirements.txt in this case! # Example: # dependency_links = ['git+https://github.com/Hypernode/M2Crypto#egg=M2Crypto-0.22.dev'], - dependency_links = ['git+https://github.com/maxcountryman/flask-login.git@13af160b3fd14dfb5f35f9cdc3863771efe194eb#egg=Flask-Login', - 'git+https://github.com/Scifabric/rq-dashboard.git#egg=rq-dashboard', - 'git+https://github.com/Scifabric/flatten.git@5d57cc6336df277822305ad70b86adf8c6a1c947#egg=flatten_json', - ], + dependency_links=['git+https://github.com/maxcountryman/flask-login.git@13af160b3fd14dfb5f35f9cdc3863771efe194eb#egg=Flask-Login', + 'git+https://github.com/Scifabric/rq-dashboard.git#egg=rq-dashboard', + 'git+https://github.com/Scifabric/flatten.git@5d57cc6336df277822305ad70b86adf8c6a1c947#egg=flatten_json', + ], # metadata for upload to PyPI - author = 'Scifabric LTD', - author_email = 'info@scifabric.com', - description = 'Open Source CrowdSourcing framework', - long_description = '''PYBOSSA is the ultimate crowdsourcing framework to analyze or enrich data that can't be processed by machines alone.''', - license = 'AGPLv3', - url = 'http://pybossa.com', - download_url = 'https://github.com/Scifabric/pybossa', - include_package_data = True, - classifiers = [ + author='Scifabric LTD', + author_email='info@scifabric.com', + description='Open Source CrowdSourcing framework', + long_description='''PYBOSSA is the ultimate crowdsourcing framework to analyze or enrich data that can't be processed by machines alone.''', + license='AGPLv3', + url='http://pybossa.com', + download_url='https://github.com/Scifabric/pybossa', + include_package_data=True, + classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Console', 'Intended Audience :: Developers', @@ -103,6 +105,6 @@ 'Programming Language :: Python', 'Topic :: Software Development :: Libraries :: Python Modules' ], - entry_points = ''' + entry_points=''' ''' ) From a7447b9826cb9c28bdb605c6b20e1420b1a2b8ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Lombra=C3=B1a=20Gonz=C3=A1lez?= Date: Sun, 29 Sep 2019 22:56:47 +0200 Subject: [PATCH 2/5] fix(werkzeug): handle request_headers Werkzeug >= 0.15 resturns None when there's no request_headers. This fixes it. --- pybossa/api/api_base.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/pybossa/api/api_base.py b/pybossa/api/api_base.py index f50a28830c..3349aead2d 100644 --- a/pybossa/api/api_base.py +++ b/pybossa/api/api_base.py @@ -104,10 +104,10 @@ class APIBase(MethodView): def refresh_cache(self, cls_name, oid): """Refresh the cache.""" if caching.get(cls_name): - if cls_name != 'Category': - caching.get(cls_name)['refresh'](oid) - else: - caching.get(cls_name)['refresh'] + if cls_name != 'Category': + caching.get(cls_name)['refresh'](oid) + else: + caching.get(cls_name)['refresh'] def valid_args(self): """Check if the domain object args are valid.""" @@ -187,7 +187,8 @@ def _add_hateoas_links(self, item): obj['task_runs'] = [] obj['result'] = None task_runs = task_repo.filter_task_runs_by(task_id=item.id) - results = result_repo.filter_by(task_id=item.id, last_version=True) + results = result_repo.filter_by( + task_id=item.id, last_version=True) for tr in task_runs: obj['task_runs'].append(tr.dictize()) for r in results: @@ -195,7 +196,8 @@ def _add_hateoas_links(self, item): if item.__class__.__name__ == 'TaskRun': tasks = task_repo.filter_tasks_by(id=item.task_id) - results = result_repo.filter_by(task_id=item.task_id, last_version=True) + results = result_repo.filter_by( + task_id=item.task_id, last_version=True) obj['task'] = None obj['result'] = None for t in tasks: @@ -215,7 +217,8 @@ def _add_hateoas_links(self, item): stats = request.args.get('stats') if stats: if item.__class__.__name__ == 'Project': - stats = project_stats_repo.filter_by(project_id=item.id, limit=1) + stats = project_stats_repo.filter_by( + project_id=item.id, limit=1) obj['stats'] = stats[0].dictize() if stats else {} links, link = self.hateoas.create_links(item) @@ -290,7 +293,8 @@ def _set_limit_and_offset(self): except (ValueError, TypeError): offset = 0 try: - orderby = request.args.get('orderby') if request.args.get('orderby') else 'id' + orderby = request.args.get( + 'orderby') if request.args.get('orderby') else 'id' except (ValueError, TypeError): orderby = 'updated' return limit, offset, orderby @@ -488,7 +492,10 @@ def _file_upload(self, data): only a few classes.""" cls_name = self.__class__.__name__.lower() content_type = 'multipart/form-data' - if (content_type in request.headers.get('Content-Type') and + request_headers = request.headers.get('Content-Type') + if request_headers is None: + request_headers = [] + if (content_type in request_headers and cls_name in self.allowed_classes_upload): tmp = dict() for key in request.form.keys(): From fb001083459777526fe5a1a27230d92229de8bd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Lombra=C3=B1a=20Gonz=C3=A1lez?= Date: Mon, 21 Oct 2019 05:50:51 +0200 Subject: [PATCH 3/5] fix(cve): Use new werkzeug. --- pybossa/api/api_base.py | 2 + pybossa/api/task_run.py | 12 ++- test/test_api/test_task_api.py | 140 +++++++++++++++++------------- test/test_api/test_taskrun_api.py | 3 +- 4 files changed, 92 insertions(+), 65 deletions(-) diff --git a/pybossa/api/api_base.py b/pybossa/api/api_base.py index 3349aead2d..d4a635542e 100644 --- a/pybossa/api/api_base.py +++ b/pybossa/api/api_base.py @@ -326,6 +326,8 @@ def post(self): return Response(json_response, mimetype='application/json') except Exception as e: content_type = request.headers.get('Content-Type') + if content_type is None: + content_type = [] if (cls_name == 'TaskRun' and 'multipart/form-data' in content_type and data): diff --git a/pybossa/api/task_run.py b/pybossa/api/task_run.py index 7378a92da4..06017aea18 100644 --- a/pybossa/api/task_run.py +++ b/pybossa/api/task_run.py @@ -52,7 +52,8 @@ def check_can_post(self, project_id, task_id, user_ip_or_id): def _update_object(self, taskrun): """Update task_run object with user id or ip.""" - self.check_can_post(taskrun.project_id, taskrun.task_id, get_user_id_or_ip()) + self.check_can_post(taskrun.project_id, + taskrun.task_id, get_user_id_or_ip()) task = task_repo.get_task(taskrun.task_id) guard = ContributionsGuard(sentinel.master) @@ -72,8 +73,8 @@ def _validate_project_and_task(self, taskrun, task): if (task.project_id != taskrun.project_id): raise Forbidden('Invalid project_id') if taskrun.external_uid: - resp = jwt_authorize_project(task.project, - request.headers.get('Authorization')) + request_headers = request.headers.get('Authorization') + resp = jwt_authorize_project(task.project, request_headers) if type(resp) == Response: msg = json.loads(resp.data)['description'] raise Forbidden(msg) @@ -102,7 +103,10 @@ def _file_upload(self, data): only a few classes.""" cls_name = self.__class__.__name__.lower() content_type = 'multipart/form-data' - if (content_type in request.headers.get('Content-Type') and + request_headers = request.headers.get('Content-Type') + if request_headers is None: + request_headers = [] + if (content_type in request_headers and cls_name in self.allowed_classes_upload): data = dict() for key in request.form.keys(): diff --git a/test/test_api/test_task_api.py b/test/test_api/test_task_api.py index 16af508e3a..b7599dd5bb 100644 --- a/test/test_api/test_task_api.py +++ b/test/test_api/test_task_api.py @@ -85,7 +85,8 @@ def test_task_query_list_project_ids(self): task_run = TaskRunFactory.create(task=task_orig, user=user) project_ids = [project.id for project in projects] - url = '/api/task?project_id=%s&limit=100&participated=true&api_key=%s' % (project_ids, user.api_key) + url = '/api/task?project_id=%s&limit=100&participated=true&api_key=%s' % ( + project_ids, user.api_key) res = self.app.get(url) data = json.loads(res.data) assert len(data) == (3 * 2) - 1, len(data) @@ -103,9 +104,9 @@ def test_task_query_participated_user_ip(self): admin, owner, user = UserFactory.create_batch(3) project = ProjectFactory.create(owner=owner) tasks1 = TaskFactory.create_batch(10, project=project, - info=dict(foo='fox')) + info=dict(foo='fox')) tasks2 = TaskFactory.create_batch(10, project=project, - info=dict(foo='dog')) + info=dict(foo='dog')) tasks = tasks1 + tasks2 AnonymousTaskRunFactory.create(task=tasks[0]) AnonymousTaskRunFactory.create(task=tasks[1]) @@ -180,9 +181,9 @@ def test_task_query_participated_external_uid(self): admin, owner, user = UserFactory.create_batch(3) project = ProjectFactory.create(owner=owner) tasks1 = TaskFactory.create_batch(10, project=project, - info=dict(foo='fox')) + info=dict(foo='fox')) tasks2 = TaskFactory.create_batch(10, project=project, - info=dict(foo='dog')) + info=dict(foo='dog')) tasks = tasks1 + tasks2 ExternalUidTaskRunFactory.create(task=tasks[0]) ExternalUidTaskRunFactory.create(task=tasks[1]) @@ -212,7 +213,8 @@ def test_task_query_participated_external_uid(self): assert task['id'] not in participated_tasks, task['id'] # last_id - url = '/api/task?external_uid=1xa&participated=1&all=1&last_id=%s' % (tasks[0].id) + url = '/api/task?external_uid=1xa&participated=1&all=1&last_id=%s' % ( + tasks[0].id) res = self.app.get(url) data = json.loads(res.data) @@ -251,16 +253,15 @@ def test_task_query_participated_external_uid(self): for task in data: assert task['id'] not in participated_tasks, task['id'] - @with_context def test_task_query_participated(self): """Test API Task query with participated arg.""" admin, owner, user = UserFactory.create_batch(3) project = ProjectFactory.create(owner=owner) tasks1 = TaskFactory.create_batch(10, project=project, - info=dict(foo='fox')) + info=dict(foo='fox')) tasks2 = TaskFactory.create_batch(10, project=project, - info=dict(foo='dog')) + info=dict(foo='dog')) tasks = tasks1 + tasks2 TaskRunFactory.create(task=tasks[0], user=user) TaskRunFactory.create(task=tasks[1], user=user) @@ -290,7 +291,8 @@ def test_task_query_participated(self): assert task['id'] not in participated_tasks, task['id'] # last_id - url = '/api/task?api_key=%s&participated=1&all=1&last_id=%s' % (user.api_key, tasks[0].id) + url = '/api/task?api_key=%s&participated=1&all=1&last_id=%s' % ( + user.api_key, tasks[0].id) res = self.app.get(url) data = json.loads(res.data) @@ -333,27 +335,30 @@ def test_task_query_participated(self): def test_task_query_without_params(self): """ Test API Task query""" project = ProjectFactory.create() - t1 = TaskFactory.create(created='2015-01-01T14:37:30.642119', info={'question': 'answer'}) - tasks = TaskFactory.create_batch(8, project=project, info={'question': 'answer'}) + t1 = TaskFactory.create( + created='2015-01-01T14:37:30.642119', info={'question': 'answer'}) + tasks = TaskFactory.create_batch( + 8, project=project, info={'question': 'answer'}) t2 = TaskFactory.create(created='2019-01-01T14:37:30.642119', info={'question': 'answer'}, - fav_user_ids=[1,2,3,4]) + fav_user_ids=[1, 2, 3, 4]) t3 = TaskFactory.create(created='2018-01-01T14:37:30.642119', info={'question': 'answer'}, - fav_user_ids=[1,2]) + fav_user_ids=[1, 2]) + t4 = TaskFactory.create(fav_user_ids=[1]) tasks.insert(0, t1) tasks.append(t2) tasks.append(t3) + tasks.append(t4) res = self.app.get('/api/task') tasks = json.loads(res.data) - assert len(tasks) == 11, tasks + assert len(tasks) == 12, tasks task = tasks[0] assert task['info']['question'] == 'answer', task - # The output should have a mime-type: application/json assert res.mimetype == 'application/json', res @@ -413,7 +418,7 @@ def test_task_query_without_params(self): res = self.app.get(url) data = json.loads(res.data) err_msg = "It should get the last item first." - assert data[0]['id'] == tasks[1]['id'], err_msg + assert data[0]['id'] == t4.id, err_msg # Related taskruns = TaskRunFactory.create_batch(8, project=project, task=t2) @@ -437,8 +442,10 @@ def test_task_query_without_params_with_context(self): user = UserFactory.create() project_oc = ProjectFactory.create(owner=user) project_two = ProjectFactory.create() - TaskFactory.create_batch(10, project=project_oc, info={'question': 'answer'}) - TaskFactory.create_batch(10, project=project_two, info={'question': 'answer'}) + TaskFactory.create_batch(10, project=project_oc, info={ + 'question': 'answer'}) + TaskFactory.create_batch(10, project=project_two, info={ + 'question': 'answer'}) res = self.app.get('/api/task?api_key=' + user.api_key) tasks = json.loads(res.data) assert len(tasks) == 10, tasks @@ -453,7 +460,6 @@ def test_task_query_without_params_with_context(self): tasks = json.loads(res.data) assert len(tasks) == 20, tasks - @with_context def test_task_query_with_params(self): """Test API query for task with params works""" @@ -497,7 +503,6 @@ def test_task_query_with_params(self): assert len(data) == 5, data assert data[0]['id'] == tasks[5].id, data - @with_context def test_task_query_with_params_with_context(self): """Test API query for task with params works with context""" @@ -508,7 +513,8 @@ def test_task_query_with_params_with_context(self): tasks = TaskFactory.create_batch(10, project=project_oc) TaskFactory.create_batch(10, project=project_two) # Test for real field - res = self.app.get("/api/task?project_id="+ str(project_oc.id) + "&api_key=" + user.api_key) + res = self.app.get("/api/task?project_id=" + + str(project_oc.id) + "&api_key=" + user.api_key) data = json.loads(res.data) # Should return then results assert len(data) == 10, data @@ -521,14 +527,15 @@ def test_task_query_with_params_with_context(self): # Should return one result assert len(data) == 20, data - # Valid field but wrong value - res = self.app.get("/api/task?project_id=99999999&api_key=" + user.api_key) + res = self.app.get( + "/api/task?project_id=99999999&api_key=" + user.api_key) data = json.loads(res.data) assert len(data) == 0, data # Multiple fields - res = self.app.get('/api/task?project_id=1&state=ongoing&api_key=' + user.api_key) + res = self.app.get( + '/api/task?project_id=1&state=ongoing&api_key=' + user.api_key) data = json.loads(res.data) # One result assert len(data) == 10, data @@ -538,14 +545,16 @@ def test_task_query_with_params_with_context(self): assert t['state'] == u'ongoing', data # Limits - res = self.app.get("/api/task?project_id=1&limit=5&api_key=" + user.api_key) + res = self.app.get( + "/api/task?project_id=1&limit=5&api_key=" + user.api_key) data = json.loads(res.data) assert len(data) == 5, data for item in data: assert item['project_id'] == project_oc.id, item # Keyset pagination - url = "/api/task?project_id=1&limit=5&last_id=%s&api_key=%s" % (tasks[4].id, user.api_key) + url = "/api/task?project_id=1&limit=5&last_id=%s&api_key=%s" % ( + tasks[4].id, user.api_key) res = self.app.get(url) data = json.loads(res.data) assert len(data) == 5, data @@ -554,12 +563,14 @@ def test_task_query_with_params_with_context(self): assert item['project_id'] == project_oc.id, item # Test for real field with user_two - res = self.app.get("/api/task?project_id="+ str(project_oc.id) + "&api_key=" + user_two.api_key) + res = self.app.get("/api/task?project_id=" + + str(project_oc.id) + "&api_key=" + user_two.api_key) data = json.loads(res.data) # Should return then results assert len(data) == 0, data # Test for real field with user_two - res = self.app.get("/api/task?all=1&project_id="+ str(project_oc.id) + "&api_key=" + user_two.api_key) + res = self.app.get("/api/task?all=1&project_id=" + + str(project_oc.id) + "&api_key=" + user_two.api_key) data = json.loads(res.data) # Should return then results assert len(data) == 10, data @@ -572,18 +583,20 @@ def test_task_query_with_params_with_context(self): # Should return one result assert len(data) == 20, data - # Valid field but wrong value - res = self.app.get("/api/task?project_id=99999999&api_key=" + user_two.api_key) + res = self.app.get( + "/api/task?project_id=99999999&api_key=" + user_two.api_key) data = json.loads(res.data) assert len(data) == 0, data # Multiple fields - res = self.app.get('/api/task?project_id=1&state=ongoing&api_key=' + user_two.api_key) + res = self.app.get( + '/api/task?project_id=1&state=ongoing&api_key=' + user_two.api_key) data = json.loads(res.data) # One result assert len(data) == 0, data - res = self.app.get('/api/task?all=1&project_id=1&state=ongoing&api_key=' + user_two.api_key) + res = self.app.get( + '/api/task?all=1&project_id=1&state=ongoing&api_key=' + user_two.api_key) data = json.loads(res.data) # One result assert len(data) == 10, data @@ -593,21 +606,25 @@ def test_task_query_with_params_with_context(self): assert t['state'] == u'ongoing', data # Limits - res = self.app.get("/api/task?project_id=1&limit=5&api_key=" + user_two.api_key) + res = self.app.get( + "/api/task?project_id=1&limit=5&api_key=" + user_two.api_key) data = json.loads(res.data) assert len(data) == 0, data - res = self.app.get("/api/task?all=1&project_id=1&limit=5&api_key=" + user_two.api_key) + res = self.app.get( + "/api/task?all=1&project_id=1&limit=5&api_key=" + user_two.api_key) data = json.loads(res.data) assert len(data) == 5, data for item in data: assert item['project_id'] == project_oc.id, item # Keyset pagination - url = "/api/task?project_id=1&limit=5&last_id=%s&api_key=%s" % (tasks[4].id, user_two.api_key) + url = "/api/task?project_id=1&limit=5&last_id=%s&api_key=%s" % ( + tasks[4].id, user_two.api_key) res = self.app.get(url) data = json.loads(res.data) assert len(data) == 0, data - url = "/api/task?all=1&project_id=1&limit=5&last_id=%s&api_key=%s" % (tasks[4].id, user_two.api_key) + url = "/api/task?all=1&project_id=1&limit=5&last_id=%s&api_key=%s" % ( + tasks[4].id, user_two.api_key) res = self.app.get(url) data = json.loads(res.data) assert len(data) == 5, data @@ -615,7 +632,6 @@ def test_task_query_with_params_with_context(self): for item in data: assert item['project_id'] == project_oc.id, item - @with_context def test_task_post(self): """Test API Task creation""" @@ -632,7 +648,7 @@ def test_task_post(self): error_msg = 'Should not be allowed to create' assert_equal(res.status, '401 UNAUTHORIZED', error_msg) - ### real user but not allowed as not owner! + # real user but not allowed as not owner! res = self.app.post('/api/task?api_key=' + non_owner.api_key, data=json.dumps(data)) @@ -717,7 +733,6 @@ def test_task_post_with_reserved_fav_user_ids(self): error = json.loads(res.data) assert error['exception_msg'] == "Reserved keys in payload", error - @with_context def test_task_put_with_reserved_fields_returns_error(self): user = UserFactory.create() @@ -740,7 +755,7 @@ def test_task_put_with_fav_user_ids_fields_returns_error(self): project = ProjectFactory.create(owner=user) task = TaskFactory.create(project=project) url = '/api/task/%s?api_key=%s' % (task.id, user.api_key) - data = {'fav_user_ids': [1,2,3]} + data = {'fav_user_ids': [1, 2, 3]} res = self.app.put(url, data=json.dumps(data)) @@ -748,7 +763,6 @@ def test_task_put_with_fav_user_ids_fields_returns_error(self): error = json.loads(res.data) assert error['exception_msg'] == "Reserved keys in payload", error - @with_context def test_task_update(self): """Test API task update""" @@ -763,15 +777,15 @@ def test_task_update(self): root_data = {'n_answers': 4} root_datajson = json.dumps(root_data) - ## anonymous + # anonymous res = self.app.put('/api/task/%s' % task.id, data=data) assert_equal(res.status, '401 UNAUTHORIZED', res.status) - ### real user but not allowed as not owner! + # real user but not allowed as not owner! url = '/api/task/%s?api_key=%s' % (task.id, non_owner.api_key) res = self.app.put(url, data=datajson) assert_equal(res.status, '403 FORBIDDEN', res.status) - ### real user + # real user url = '/api/task/%s?api_key=%s' % (task.id, user.api_key) res = self.app.put(url, data=datajson) out = json.loads(res.data) @@ -780,7 +794,7 @@ def test_task_update(self): assert_equal(task.state, 'ongoing') assert task.id == out['id'], out - ### root + # root res = self.app.put('/api/task/%s?api_key=%s' % (root_task.id, admin.api_key), data=root_datajson) assert_equal(res.status, '200 OK', res.data) @@ -856,7 +870,6 @@ def test_task_update_state(self): assert_equal(task.state, 'ongoing') assert task.id == out['id'], out - @with_context def test_task_delete(self): """Test API task delete""" @@ -867,19 +880,19 @@ def test_task_delete(self): task = TaskFactory.create(project=project) root_task = TaskFactory.create(project=project) - ## anonymous + # anonymous res = self.app.delete('/api/task/%s' % task.id) error_msg = 'Anonymous should not be allowed to delete' print res.status assert_equal(res.status, '401 UNAUTHORIZED', error_msg) - ### real user but not allowed as not owner! + # real user but not allowed as not owner! url = '/api/task/%s?api_key=%s' % (task.id, non_owner.api_key) res = self.app.delete(url) error_msg = 'Should not be able to update tasks of others' assert_equal(res.status, '403 FORBIDDEN', error_msg) - #### real user + # real user # DELETE with not allowed args res = self.app.delete(url + "&foo=bar") err = json.loads(res.data) @@ -895,7 +908,7 @@ def test_task_delete(self): assert_equal(res.status, '204 NO CONTENT', res.data) assert res.data == '', res.data - #### root user + # root user url = '/api/task/%s?api_key=%s' % (root_task.id, admin.api_key) res = self.app.delete(url) assert_equal(res.status, '204 NO CONTENT', res.data) @@ -904,7 +917,6 @@ def test_task_delete(self): assert task not in tasks, tasks assert root_task not in tasks, tasks - @with_context @patch('pybossa.repositories.task_repository.uploader') def test_task_delete_deletes_zip_files(self, uploader): @@ -994,16 +1006,19 @@ def test_counter_table(self): project = ProjectFactory.create() task = TaskFactory.create(project=project) - items = db.session.query(Counter).filter_by(project_id=project.id).all() + items = db.session.query(Counter).filter_by( + project_id=project.id).all() assert len(items) == 1 TaskFactory.create_batch(9, project=project) - items = db.session.query(Counter).filter_by(project_id=project.id).all() + items = db.session.query(Counter).filter_by( + project_id=project.id).all() assert len(items) == 10 task_id = task.id task_repo.delete(task) - items = db.session.query(Counter).filter_by(project_id=project.id).all() + items = db.session.query(Counter).filter_by( + project_id=project.id).all() assert len(items) == 9 items = db.session.query(Counter).filter_by(task_id=task_id).all() assert len(items) == 0 @@ -1023,21 +1038,26 @@ def test_counter_table_api(self): assert data.get('id') is not None, res.data - items = db.session.query(Counter).filter_by(project_id=project.id).all() + items = db.session.query(Counter).filter_by( + project_id=project.id).all() assert len(items) == 1 - items = db.session.query(Counter).filter_by(task_id=data.get('id')).all() + items = db.session.query(Counter).filter_by( + task_id=data.get('id')).all() assert len(items) == 1 assert items[0].task_id == data.get('id') for i in range(9): res = self.app.post(url, data=json.dumps(task)) created_task = json.loads(res.data) - items = db.session.query(Counter).filter_by(project_id=project.id).all() + items = db.session.query(Counter).filter_by( + project_id=project.id).all() assert len(items) == 10, len(items) res = self.app.delete('/api/task/%s?api_key=%s' % (created_task['id'], project.owner.api_key)) - items = db.session.query(Counter).filter_by(project_id=project.id).all() + items = db.session.query(Counter).filter_by( + project_id=project.id).all() assert len(items) == 9 - items = db.session.query(Counter).filter_by(task_id=created_task.get('id')).all() + items = db.session.query(Counter).filter_by( + task_id=created_task.get('id')).all() assert len(items) == 0 diff --git a/test/test_api/test_taskrun_api.py b/test/test_api/test_taskrun_api.py index 2675582d45..8d04dceafc 100644 --- a/test/test_api/test_taskrun_api.py +++ b/test/test_api/test_taskrun_api.py @@ -1181,7 +1181,8 @@ def test_post_taskrun_can_create_result_for_published_project(self, guard): info='my task result') datajson = json.dumps(data) - self.app.post(url, data=datajson) + res = self.app.post(url, data=datajson) + print(res.data, 'hola') result = result_repo.get_by(project_id=project.id, task_id=task.id) From 92a308d07cd5c46958ecae36db75c42cca783137 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Lombra=C3=B1a=20Gonz=C3=A1lez?= Date: Fri, 25 Oct 2019 23:29:49 +0200 Subject: [PATCH 4/5] fix(tests): mock with return string. --- test/test_ldap.py | 4 ++-- test/test_view/test_twitter.py | 18 ++++++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/test/test_ldap.py b/test/test_ldap.py index d94eb25402..c331b0bcac 100644 --- a/test/test_ldap.py +++ b/test/test_ldap.py @@ -76,7 +76,6 @@ def test_signin_create(self, ldap_mock): assert data['flash'] == msg, data assert data['status'] == 'info', data - @with_context @patch('pybossa.view.account._create_account') @patch('pybossa.view.account.ldap') @@ -109,10 +108,11 @@ def test_twitter_login(self): def test_twitter_no_login(self, mock_twitter, url_for_mock): """Test Twitter no_login arg allows using Twitter importer.""" url = '/twitter/?no_login=1' - mock_twitter.authorize.return_value = Response(302) + mock_twitter.authorize.return_value = "OK" url_for_mock.return_value = 'url' with patch.dict(self.flask_app.config, {'LDAP_HOST': '127.0.0.1'}): res = self.app.get(url) + assert res.data == 'OK' assert mock_twitter.authorize.called_with('url') @with_context diff --git a/test/test_view/test_twitter.py b/test/test_view/test_twitter.py index 2cd18315c4..d023cf924a 100644 --- a/test/test_view/test_twitter.py +++ b/test/test_view/test_twitter.py @@ -44,7 +44,8 @@ def test_manage_user_twitter_registered_user(self): user_data = dict(user_id=1, screen_name='twitter') initial_token = dict(oauth_token='token', oauth_token_secret='secret') manage_user(initial_token, user_data) - updated_token = dict(oauth_token='token2', oauth_token_secret='secret2') + updated_token = dict(oauth_token='token2', + oauth_token_secret='secret2') user = manage_user(updated_token, user_data) assert user.email_addr == user_data['screen_name'], user assert user.name == user_data['screen_name'], user @@ -177,7 +178,7 @@ def test_manage_user_login_with_newsletter_twice(self, redirect, @with_context @patch('pybossa.view.twitter.twitter.oauth') def test_twitter_signin_with_no_login_param(self, oauth): - oauth.authorize.return_value = Response(302) + oauth.authorize.return_value = "OK" self.app.get('/twitter/?no_login=1') oauth.authorize.assert_called_once_with( @@ -191,8 +192,8 @@ def test_twitter_signin_oauth_callback_no_login_calls_manage_user_no_login( oauth.authorized_response.return_value = { 'oauth_token': 'token', 'oauth_token_secret': 'secret' - } - manage_user_no_login.return_value = Response(302) + } + manage_user_no_login.return_value = "OK" self.app.get('/twitter/oauth-authorized?no_login=1') manage_user_no_login.assert_called_once_with( @@ -209,8 +210,8 @@ def test_twitter_signin_no_login_param_missing( 'oauth_token_secret': 'secret', 'screen_name': 'john_doe', 'user_id': 1 - } - manage_user_no_login.return_value = Response(302) + } + manage_user_no_login.return_value = "OK" assert_not_raises(Exception, self.app.get, '/twitter/oauth-authorized') @@ -218,10 +219,11 @@ def test_twitter_signin_no_login_param_missing( @patch('pybossa.view.twitter.current_user') @patch('pybossa.view.twitter.redirect', return_value=True) def test_manage_user_no_login_stores_twitter_token_in_current_user_info( - self, redirect, current_user): + self, redirect, current_user): user = UserFactory.create(info={}) current_user.id = user.id - token_and_secret = {'oauth_token_secret': 'secret', 'oauth_token': 'token'} + token_and_secret = { + 'oauth_token_secret': 'secret', 'oauth_token': 'token'} next_url = '/' manage_user_no_login(token_and_secret, next_url) From cee56e1509478f2d5e94a47e8ed66ac0c8378ac4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Lombra=C3=B1a=20Gonz=C3=A1lez?= Date: Fri, 25 Oct 2019 23:47:42 +0200 Subject: [PATCH 5/5] fix(tests): return String instead of Response. It returns a stream and the tests fail. --- test/test_view/test_flickr.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/test/test_view/test_flickr.py b/test/test_view/test_flickr.py index a1c807a54c..1f6ceffc62 100644 --- a/test/test_view/test_flickr.py +++ b/test/test_view/test_flickr.py @@ -20,17 +20,17 @@ from flask import Response, session from default import flask_app, with_context + class TestFlickrOauth(object): @with_context @patch('pybossa.view.flickr.flickr.oauth') def test_flickr_login_specifies_callback_and_read_permissions(self, oauth): - oauth.authorize.return_value = Response(302) + oauth.authorize.return_value = "OK" flask_app.test_client().get('/flickr/') oauth.authorize.assert_called_with( callback='/flickr/oauth-authorized', perms='read') - @with_context def test_logout_removes_token_and_user_from_session(self): with flask_app.test_client() as c: @@ -46,23 +46,21 @@ def test_logout_removes_token_and_user_from_session(self): assert 'flickr_token' not in session assert 'flickr_user' not in session - @with_context @patch('pybossa.view.flickr.redirect') def test_logout_redirects_to_url_specified_by_next_param(self, redirect): - redirect.return_value = Response(302) + redirect.return_value = "OK" flask_app.test_client().get('/flickr/revoke-access?next=http://mynext_url') redirect.assert_called_with('http://mynext_url') - @with_context @patch('pybossa.view.flickr.flickr.oauth') def test_oauth_authorized_saves_token_and_user_to_session(self, oauth): fake_resp = {'oauth_token_secret': u'secret', 'username': u'palotespaco', 'fullname': u'paco palotes', - 'oauth_token':u'token', + 'oauth_token': u'token', 'user_nsid': u'user'} oauth.authorized_response.return_value = fake_resp expected_token = { @@ -77,7 +75,6 @@ def test_oauth_authorized_saves_token_and_user_to_session(self, oauth): assert session['flickr_token'] == expected_token, session['flickr_token'] assert session['flickr_user'] == expected_user, session['flickr_user'] - @with_context @patch('pybossa.view.flickr.flickr') @patch('pybossa.view.flickr.redirect') @@ -86,22 +83,21 @@ def test_oauth_authorized_redirects_to_url_next_param_on_authorization( fake_resp = {'oauth_token_secret': u'secret', 'username': u'palotespaco', 'fullname': u'paco palotes', - 'oauth_token':u'token', + 'oauth_token': u'token', 'user_nsid': u'user'} flickr.authorized_response.return_value = fake_resp - redirect.return_value = Response(302) + redirect.return_value = "OK" flask_app.test_client().get('/flickr/oauth-authorized?next=http://next') redirect.assert_called_with('http://next') - @with_context @patch('pybossa.view.flickr.flickr') @patch('pybossa.view.flickr.redirect') def test_oauth_authorized_redirects_to_url_next_param_on_user_no_authorizing( self, redirect, flickr): flickr.authorized_response.return_value = None - redirect.return_value = Response(302) + redirect.return_value = "OK" flask_app.test_client().get('/flickr/oauth-authorized?next=http://next') redirect.assert_called_with('http://next')