diff --git a/CHANGELOG b/CHANGELOG index ae86734434f..47185789da5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,14 @@ We follow the CalVer (https://calver.org/) versioning scheme: YY.MINOR.MICRO. +19.27.0 (2019-09-18) +=================== +- Automatically map subjects when a preprint is moved to a different + preprint provider in the admin app +- Gitlab: return all repos to which the user has access +- Upgrade Bower +- Py3 backwards compatible changes + 19.26.0 (2019-09-11) =================== - Create a custom through table for linking files and versions diff --git a/README-docker-compose.md b/README-docker-compose.md index d84a2670014..be1f2326c5a 100644 --- a/README-docker-compose.md +++ b/README-docker-compose.md @@ -81,6 +81,9 @@ `$ cp ./docker-compose-dist.override.yml ./docker-compose.override.yml` + For local tasks, (dev only) + `$ cp ./tasks/local-dist.py ./tasks/local.py` + 2. OPTIONAL (uncomment the below lines if you will use remote debugging) Environment variables (incl. remote debugging) - e.g. .docker-compose.env diff --git a/addons/base/generic_views.py b/addons/base/generic_views.py index a00bd2a088f..001194fe29e 100644 --- a/addons/base/generic_views.py +++ b/addons/base/generic_views.py @@ -1,6 +1,6 @@ """Generic add-on view factories""" # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status from flask import request @@ -28,12 +28,12 @@ def _import_auth(auth, node_addon, user_addon, **kwargs): ) if not user_addon.external_accounts.filter(id=external_account.id).exists(): - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) try: node_addon.set_auth(external_account, user_addon.owner) except PermissionsError: - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) node_addon.save() @@ -60,7 +60,7 @@ def folder_list(addon_short_name, addon_full_name, get_folders): def _folder_list(node_addon, **kwargs): """Returns a list of folders""" if not node_addon.has_auth: - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) folder_id = request.args.get('folderId') return get_folders(node_addon, folder_id) diff --git a/addons/base/tests/views.py b/addons/base/tests/views.py index 47d04e1ced3..8c3860b2826 100644 --- a/addons/base/tests/views.py +++ b/addons/base/tests/views.py @@ -1,5 +1,5 @@ -import httplib as http -import urlparse +from rest_framework import status as http_status +from future.moves.urllib.parse import urlparse, urljoin, parse_qs import mock import responses @@ -25,11 +25,11 @@ def test_oauth_start(self): service_name=self.ADDON_SHORT_NAME ) res = self.app.get(url, auth=self.user.auth) - assert res.status_code == http.FOUND - redirect_url = urlparse.urlparse(res.location) - redirect_params = urlparse.parse_qs(redirect_url.query) - provider_url = urlparse.urlparse(self.Provider().auth_url) - provider_params = urlparse.parse_qs(provider_url.query) + assert res.status_code == http_status.HTTP_302_FOUND + redirect_url = urlparse(res.location) + redirect_params = parse_qs(redirect_url.query) + provider_url = urlparse(self.Provider().auth_url) + provider_params = parse_qs(provider_url.query) for param, value in redirect_params.items(): if param == 'state': # state may change between calls continue @@ -43,7 +43,7 @@ def test_oauth_finish(self): with mock.patch.object(self.Provider, 'auth_callback') as mock_callback: mock_callback.return_value = True res = self.app.get(url, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) name, args, kwargs = mock_callback.mock_calls[0] assert_equal(kwargs['user']._id, self.user._id) @@ -53,7 +53,7 @@ def test_delete_external_account(self): external_account_id=self.external_account._id ) res = self.app.delete(url, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) self.user.reload() for account in self.user.external_accounts.all(): assert_not_equal(account._id, self.external_account._id) @@ -66,7 +66,7 @@ def test_delete_external_account_not_owner(self): external_account_id=self.external_account._id ) res = self.app.delete(url, auth=other_user.auth, expect_errors=True) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) class OAuthAddonConfigViewsTestCaseMixin(OAuthAddonTestCaseMixin): @@ -94,7 +94,7 @@ def test_import_auth(self): res = self.app.put_json(url, { 'external_account_id': ea._id }, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) assert_in('result', res.json) node_settings.reload() assert_equal(node_settings.external_account._id, ea._id) @@ -113,7 +113,7 @@ def test_import_auth_invalid_account(self): res = self.app.put_json(url, { 'external_account_id': ea._id }, auth=self.user.auth, expect_errors=True) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) def test_import_auth_cant_write_node(self): ea = self.ExternalAccountFactory() @@ -130,7 +130,7 @@ def test_import_auth_cant_write_node(self): res = self.app.put_json(url, { 'external_account_id': ea._id }, auth=user.auth, expect_errors=True) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) def test_set_config(self): self.node_settings.set_auth(self.external_account, self.user) @@ -138,7 +138,7 @@ def test_set_config(self): res = self.app.put_json(url, { 'selected': self.folder }, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) self.project.reload() assert_equal( self.project.logs.latest().action, @@ -150,7 +150,7 @@ def test_get_config(self): url = self.project.api_url_for('{0}_get_config'.format(self.ADDON_SHORT_NAME)) with mock.patch.object(type(self.Serializer()), 'credentials_are_valid', return_value=True): res = self.app.get(url, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) assert_in('result', res.json) serialized = self.Serializer().serialize_settings( self.node_settings, @@ -164,17 +164,17 @@ def test_get_config_unauthorized(self): user = AuthUserFactory() self.project.add_contributor(user, permissions=permissions.READ, auth=self.auth, save=True) res = self.app.get(url, auth=user.auth, expect_errors=True) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) def test_get_config_not_logged_in(self): url = self.project.api_url_for('{0}_get_config'.format(self.ADDON_SHORT_NAME)) res = self.app.get(url, auth=None, expect_errors=True) - assert_equal(res.status_code, http.FOUND) + assert_equal(res.status_code, http_status.HTTP_302_FOUND) def test_account_list_single(self): url = api_url_for('{0}_account_list'.format(self.ADDON_SHORT_NAME)) res = self.app.get(url, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) assert_in('accounts', res.json) assert_equal(len(res.json['accounts']), 1) @@ -185,14 +185,14 @@ def test_account_list_multiple(self): url = api_url_for('{0}_account_list'.format(self.ADDON_SHORT_NAME)) res = self.app.get(url, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) assert_in('accounts', res.json) assert_equal(len(res.json['accounts']), 2) def test_account_list_not_authorized(self): url = api_url_for('{0}_account_list'.format(self.ADDON_SHORT_NAME)) res = self.app.get(url, auth=None, expect_errors=True) - assert_equal(res.status_code, http.FOUND) + assert_equal(res.status_code, http_status.HTTP_302_FOUND) def test_folder_list(self): # Note: if your addon's folder_list view makes API calls @@ -202,13 +202,13 @@ def test_folder_list(self): self.node_settings.save() url = self.project.api_url_for('{0}_folder_list'.format(self.ADDON_SHORT_NAME)) res = self.app.get(url, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) # TODO test result serialization? def test_deauthorize_node(self): url = self.project.api_url_for('{0}_deauthorize_node'.format(self.ADDON_SHORT_NAME)) res = self.app.delete(url, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) self.node_settings.reload() assert_is_none(self.node_settings.external_account) assert_false(self.node_settings.has_auth) @@ -256,7 +256,7 @@ def test_set_config(self): 'external_list_id': self.folder.json['id'], 'external_list_name': self.folder.name, }, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) self.project.reload() assert_equal( self.project.logs.latest().action, @@ -271,7 +271,7 @@ def test_get_config(self): self.node_settings.save() url = self.project.api_url_for('{0}_get_config'.format(self.ADDON_SHORT_NAME)) res = self.app.get(url, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) assert_in('result', res.json) result = res.json['result'] serialized = self.Serializer( @@ -287,7 +287,7 @@ def test_folder_list(self): self.node_settings.save() url = self.project.api_url_for('{0}_citation_list'.format(self.ADDON_SHORT_NAME)) res = self.app.get(url, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) def test_check_credentials(self): with mock.patch.object(self.client, 'client', new_callable=mock.PropertyMock) as mock_client: @@ -412,4 +412,4 @@ def test_citation_list_non_linked_or_child_non_authorizer(self): auth=non_authorizing_user.auth, expect_errors=True ) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) diff --git a/addons/base/views.py b/addons/base/views.py index 763aec065a8..44a83d2c4a0 100644 --- a/addons/base/views.py +++ b/addons/base/views.py @@ -1,9 +1,9 @@ import datetime -import httplib +from rest_framework import status as http_status import os import uuid import markupsafe -import urllib +from future.moves.urllib.parse import quote from django.utils import timezone from flask import make_response @@ -112,7 +112,7 @@ def disable_addon(auth, **kwargs): addon_name = kwargs.get('addon') if addon_name is None: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) deleted = node.delete_addon(addon_name, auth) @@ -126,11 +126,11 @@ def get_addon_user_config(**kwargs): addon_name = kwargs.get('addon') if addon_name is None: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) addon = user.get_addon(addon_name) if addon is None: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) return addon.to_json(user) @@ -158,7 +158,7 @@ def check_access(node, auth, action, cas_resp): """ permission = permission_map.get(action, None) if permission is None: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) if cas_resp: if permission == permissions.READ: @@ -170,7 +170,7 @@ def check_access(node, auth, action, cas_resp): if not cas_resp.authenticated \ or required_scope not in oauth_scopes.normalize_scopes(cas_resp.attributes['accessTokenScope']): - raise HTTPError(httplib.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) if permission == permissions.READ: if node.can_view_files(auth): @@ -218,7 +218,7 @@ def check_access(node, auth, action, cas_resp): except RegistrationSchema.DoesNotExist: pass - raise HTTPError(httplib.FORBIDDEN if auth.user else httplib.UNAUTHORIZED) + raise HTTPError(http_status.HTTP_403_FORBIDDEN if auth.user else http_status.HTTP_401_UNAUTHORIZED) def make_auth(user): if user is not None: @@ -280,7 +280,7 @@ def get_auth(auth, **kwargs): )['data'] except (jwt.InvalidTokenError, KeyError) as err: sentry.log_message(str(err)) - raise HTTPError(httplib.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) if not auth.user: auth.user = OSFUser.from_cookie(data.get('cookie', '')) @@ -290,18 +290,18 @@ def get_auth(auth, **kwargs): node_id = data['nid'] provider_name = data['provider'] except KeyError: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) node = AbstractNode.load(node_id) or Preprint.load(node_id) if not node: - raise HTTPError(httplib.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) check_access(node, auth, action, cas_resp) provider_settings = None if hasattr(node, 'get_addon'): provider_settings = node.get_addon(provider_name) if not provider_settings: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) path = data.get('path') credentials = None @@ -321,7 +321,7 @@ def get_auth(auth, **kwargs): identifier=version ).select_related('region').get() except FileVersion.DoesNotExist: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) if auth.user: # mark fileversion as seen FileVersionUserMetadata.objects.get_or_create(user=auth.user, file_version=fileversion) @@ -404,11 +404,11 @@ def create_waterbutler_log(payload, **kwargs): user = OSFUser.load(auth['id']) if user is None: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) action = LOG_ACTION_MAP[payload['action']] except KeyError: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) auth = Auth(user=user) node = kwargs.get('node') or kwargs.get('project') or Preprint.load(kwargs.get('nid')) or Preprint.load(kwargs.get('pid')) @@ -418,7 +418,7 @@ def create_waterbutler_log(payload, **kwargs): for bundle in ('source', 'destination'): for key in ('provider', 'materialized', 'name', 'nid'): if key not in payload[bundle]: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) dest = payload['destination'] src = payload['source'] @@ -594,7 +594,7 @@ def addon_view_or_download_file_legacy(**kwargs): action=action, **query_params ), - code=httplib.MOVED_PERMANENTLY + code=http_status.HTTP_301_MOVED_PERMANENTLY ) @must_be_contributor_or_public @@ -674,7 +674,7 @@ def addon_deleted_file(auth, target, error_type='BLAME_PROVIDER', **kwargs): # TODO - serialize deleted metadata for future types of deleted file targets ret = {'error': error_msg} - return ret, httplib.GONE + return ret, http_status.HTTP_410_GONE @must_be_contributor_or_public @@ -691,7 +691,7 @@ def addon_view_or_download_file(auth, path, provider, **kwargs): path_safe = markupsafe.escape(path) if not path: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) if hasattr(target, 'get_addon'): @@ -699,19 +699,19 @@ def addon_view_or_download_file(auth, path, provider, **kwargs): if not isinstance(node_addon, BaseStorageAddon): object_text = markupsafe.escape(getattr(target, 'project_or_component', 'this object')) - raise HTTPError(httplib.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Bad Request', 'message_long': 'The {} add-on containing {} is no longer connected to {}.'.format(provider_safe, path_safe, object_text) }) if not node_addon.has_auth: - raise HTTPError(httplib.UNAUTHORIZED, data={ + raise HTTPError(http_status.HTTP_401_UNAUTHORIZED, data={ 'message_short': 'Unauthorized', 'message_long': 'The {} add-on containing {} is no longer authorized.'.format(provider_safe, path_safe) }) if not node_addon.complete: - raise HTTPError(httplib.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Bad Request', 'message_long': 'The {} add-on containing {} is no longer configured.'.format(provider_safe, path_safe) }) @@ -737,13 +737,13 @@ def addon_view_or_download_file(auth, path, provider, **kwargs): file_node = BaseFileNode.load(path) if not file_node: - raise HTTPError(httplib.NOT_FOUND, data={ + raise HTTPError(http_status.HTTP_404_NOT_FOUND, data={ 'message_short': 'File Not Found', 'message_long': 'The requested file could not be found.' }) if file_node.kind == 'folder': - raise HTTPError(httplib.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Bad Request', 'message_long': 'You cannot request a folder from this endpoint.' }) @@ -759,7 +759,7 @@ def addon_view_or_download_file(auth, path, provider, **kwargs): # TODO clean up these urls and unify what is used as a version identifier if request.method == 'HEAD': - return make_response(('', httplib.FOUND, { + return make_response(('', http_status.HTTP_302_FOUND, { 'Location': file_node.generate_waterbutler_url(**dict(extras, direct=None, version=version.identifier, _internal=extras.get('mode') == 'render')) })) @@ -768,7 +768,7 @@ def addon_view_or_download_file(auth, path, provider, **kwargs): _, extension = os.path.splitext(file_node.name) # avoid rendering files with the same format type. if format and '.{}'.format(format.lower()) != extension.lower(): - return redirect('{}/export?format={}&url={}'.format(get_mfr_url(target, provider), format, urllib.quote(file_node.generate_waterbutler_url( + return redirect('{}/export?format={}&url={}'.format(get_mfr_url(target, provider), format, quote(file_node.generate_waterbutler_url( **dict(extras, direct=None, version=version.identifier, _internal=extras.get('mode') == 'render') )))) return redirect(file_node.generate_waterbutler_url(**dict(extras, direct=None, version=version.identifier, _internal=extras.get('mode') == 'render'))) @@ -777,7 +777,7 @@ def addon_view_or_download_file(auth, path, provider, **kwargs): draft_id = extras.get('draft') draft = DraftRegistration.load(draft_id) if draft is None or draft.is_approved: - raise HTTPError(httplib.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Bad Request', 'message_long': 'File not associated with required object.' }) @@ -804,12 +804,12 @@ def persistent_file_download(auth, **kwargs): if guid: file = guid.referent else: - raise HTTPError(httplib.NOT_FOUND, data={ + raise HTTPError(http_status.HTTP_404_NOT_FOUND, data={ 'message_short': 'File Not Found', 'message_long': 'The requested file could not be found.' }) if not file.is_file: - raise HTTPError(httplib.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_long': 'Downloading folders is not permitted.' }) @@ -823,7 +823,7 @@ def persistent_file_download(auth, **kwargs): return redirect( file.generate_waterbutler_url(**query_params), - code=httplib.FOUND + code=http_status.HTTP_302_FOUND ) @@ -831,7 +831,7 @@ def addon_view_or_download_quickfile(**kwargs): fid = kwargs.get('fid', 'NOT_AN_FID') file_ = OsfStorageFile.load(fid) if not file_: - raise HTTPError(httplib.NOT_FOUND, data={ + raise HTTPError(http_status.HTTP_404_NOT_FOUND, data={ 'message_short': 'File Not Found', 'message_long': 'The requested file could not be found.' }) diff --git a/addons/bitbucket/api.py b/addons/bitbucket/api.py index b74d3cd6023..4a893ad991c 100644 --- a/addons/bitbucket/api.py +++ b/addons/bitbucket/api.py @@ -1,4 +1,4 @@ -import urllib +from future.moves.urllib.parse import urlencode from addons.bitbucket import settings @@ -184,7 +184,7 @@ def branches(self, user, repo): def ref_to_params(branch=None, sha=None): - params = urllib.urlencode({ + params = urlencode({ key: value for key, value in {'branch': branch, 'sha': sha}.items() if value diff --git a/addons/bitbucket/tests/test_views.py b/addons/bitbucket/tests/test_views.py index 6320799662b..b351bbe641d 100644 --- a/addons/bitbucket/tests/test_views.py +++ b/addons/bitbucket/tests/test_views.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status import mock import datetime @@ -68,7 +68,7 @@ def test_set_config(self, mock_account, mock_repo): 'bitbucket_user': 'octocat', 'bitbucket_repo': 'repo_name', }, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) self.project.reload() assert_equal( self.project.logs.latest().action, diff --git a/addons/bitbucket/utils.py b/addons/bitbucket/utils.py index 686973446bc..a7f8b213941 100644 --- a/addons/bitbucket/utils.py +++ b/addons/bitbucket/utils.py @@ -1,5 +1,5 @@ -import urllib -import httplib as http +from future.moves.urllib.parse import unquote_plus +from rest_framework import status as http_status from framework.exceptions import HTTPError @@ -9,9 +9,9 @@ def get_path(kwargs, required=True): path = kwargs.get('path') if path: - return urllib.unquote_plus(path) + return unquote_plus(path) elif required: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) def get_refs(addon, branch=None, sha=None, connection=None): @@ -26,7 +26,7 @@ def get_refs(addon, branch=None, sha=None, connection=None): connection = connection or BitbucketClient(access_token=addon.external_account.oauth_key) if sha and not branch: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # Get default branch if not provided if not branch: diff --git a/addons/bitbucket/views.py b/addons/bitbucket/views.py index 14248b12a1a..df555703c34 100644 --- a/addons/bitbucket/views.py +++ b/addons/bitbucket/views.py @@ -1,6 +1,6 @@ """Views for the node settings page.""" # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status import logging from flask import request, make_response @@ -78,14 +78,14 @@ def bitbucket_set_config(auth, **kwargs): if not user_settings: user_settings = node_settings.user_settings except AttributeError: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # Parse request bitbucket_user_name = request.json.get('bitbucket_user', '') bitbucket_repo_name = request.json.get('bitbucket_repo', '') if not bitbucket_user_name or not bitbucket_repo_name: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # Verify that repo exists and that user can access connection = BitbucketClient(access_token=node_settings.external_account.oauth_key) @@ -100,7 +100,7 @@ def bitbucket_set_config(auth, **kwargs): message = ( 'Cannot access repo.' ) - return {'message': message}, http.BAD_REQUEST + return {'message': message}, http_status.HTTP_400_BAD_REQUEST changed = ( bitbucket_user_name != node_settings.user or diff --git a/addons/box/models.py b/addons/box/models.py index cedb9a1ab9a..0ffb8de16c9 100644 --- a/addons/box/models.py +++ b/addons/box/models.py @@ -1,4 +1,4 @@ -import httplib as http +from rest_framework import status as http_status import logging import os @@ -144,14 +144,14 @@ def get_folders(self, **kwargs): oauth = OAuth2(client_id=settings.BOX_KEY, client_secret=settings.BOX_SECRET, access_token=self.external_account.oauth_key) client = Client(oauth) except BoxAPIException: - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) try: metadata = client.folder(folder_id).get() except BoxAPIException: - raise HTTPError(http.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) except MaxRetryError: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) folder_path = '/'.join( [ diff --git a/addons/box/tests/test_views.py b/addons/box/tests/test_views.py index 71cbb3c9e22..af7d646ae77 100644 --- a/addons/box/tests/test_views.py +++ b/addons/box/tests/test_views.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """Views tests for the Box addon.""" from django.utils import timezone -import httplib +from rest_framework import status as http_status from nose.tools import * # noqa (PEP8 asserts) import mock import pytest @@ -140,7 +140,7 @@ def test_box_list_folders_returns_error_if_invalid_path(self, mock_metadata): mock_metadata.side_effect = BoxAPIException(status=404, message='File not found') url = self.project.api_url_for('box_folder_list', folder_id='lolwut') res = self.app.get(url, auth=self.user.auth, expect_errors=True) - assert_equal(res.status_code, httplib.NOT_FOUND) + assert_equal(res.status_code, http_status.HTTP_404_NOT_FOUND) @mock.patch('addons.box.models.Client.folder') def test_box_list_folders_handles_max_retry_error(self, mock_metadata): @@ -148,7 +148,7 @@ def test_box_list_folders_handles_max_retry_error(self, mock_metadata): url = self.project.api_url_for('box_folder_list', folder_id='fo') mock_metadata.side_effect = MaxRetryError(mock_response, url) res = self.app.get(url, auth=self.user.auth, expect_errors=True) - assert_equal(res.status_code, httplib.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) class TestRestrictions(BoxAddonTestCase, OsfTestCase): @@ -180,13 +180,13 @@ def test_restricted_hgrid_data_contents(self, mock_auth): url = self.project.api_url_for('box_folder_list', path='foo bar') res = self.app.get(url, auth=self.contrib.auth, expect_errors=True) - assert_equal(res.status_code, httplib.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) def test_restricted_config_contrib_no_addon(self): url = api_url_for('box_set_config', pid=self.project._primary_key) res = self.app.put_json(url, {'selected': {'path': 'foo'}}, auth=self.contrib.auth, expect_errors=True) - assert_equal(res.status_code, httplib.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) def test_restricted_config_contrib_not_owner(self): # Contributor has box auth, but is not the node authorizer @@ -196,4 +196,4 @@ def test_restricted_config_contrib_not_owner(self): url = api_url_for('box_set_config', pid=self.project._primary_key) res = self.app.put_json(url, {'selected': {'path': 'foo'}}, auth=self.contrib.auth, expect_errors=True) - assert_equal(res.status_code, httplib.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) diff --git a/addons/dataverse/client.py b/addons/dataverse/client.py index 7e1e95eef8c..d8b2362d875 100644 --- a/addons/dataverse/client.py +++ b/addons/dataverse/client.py @@ -1,4 +1,4 @@ -import httplib as http +from rest_framework import status as http_status from dataverse import Connection from dataverse.exceptions import ConnectionError, UnauthorizedError, OperationFailedError @@ -32,10 +32,10 @@ def connect_or_error(host, token): try: connection = _connect(host, token) if not connection: - raise HTTPError(http.SERVICE_UNAVAILABLE) + raise HTTPError(http_status.HTTP_503_SERVICE_UNAVAILABLE) return connection except UnauthorizedError: - raise HTTPError(http.UNAUTHORIZED) + raise HTTPError(http_status.HTTP_401_UNAUTHORIZED) def connect_from_settings_or_401(node_settings): @@ -57,17 +57,17 @@ def publish_dataverse(dataverse): try: dataverse.publish() except OperationFailedError: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) def publish_dataset(dataset): if dataset.get_state() == 'RELEASED': - raise HTTPError(http.CONFLICT, data=dict( + raise HTTPError(http_status.HTTP_409_CONFLICT, data=dict( message_short='Dataset conflict', message_long='This version of the dataset has already been published.' )) if not dataset.dataverse.is_published: - raise HTTPError(http.METHOD_NOT_ALLOWED, data=dict( + raise HTTPError(http_status.HTTP_405_METHOD_NOT_ALLOWED, data=dict( message_short='Method not allowed', message_long='A dataset cannot be published until its parent Dataverse is published.' )) @@ -75,7 +75,7 @@ def publish_dataset(dataset): try: dataset.publish() except OperationFailedError: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) def get_datasets(dataverse): @@ -90,13 +90,13 @@ def get_dataset(dataverse, doi): dataset = dataverse.get_dataset_by_doi(doi, timeout=settings.REQUEST_TIMEOUT) try: if dataset and dataset.get_state() == 'DEACCESSIONED': - raise HTTPError(http.GONE, data=dict( + raise HTTPError(http_status.HTTP_410_GONE, data=dict( message_short='Dataset deaccessioned', message_long='This dataset has been deaccessioned and can no longer be linked to the OSF.' )) return dataset except UnicodeDecodeError: - raise HTTPError(http.NOT_ACCEPTABLE, data=dict( + raise HTTPError(http_status.HTTP_406_NOT_ACCEPTABLE, data=dict( message_short='Not acceptable', message_long='This dataset cannot be connected due to forbidden ' 'characters in one or more of the file names.' diff --git a/addons/dataverse/models.py b/addons/dataverse/models.py index 1b327aefbbe..147eddc1fff 100644 --- a/addons/dataverse/models.py +++ b/addons/dataverse/models.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status from addons.base.models import (BaseOAuthNodeSettings, BaseOAuthUserSettings, BaseStorageAddon) @@ -152,7 +152,7 @@ def _get_fileobj_child_metadata(self, filenode, user, cookie=None, version=None) return super(NodeSettings, self)._get_fileobj_child_metadata(filenode, user, cookie=cookie, version=version) except HTTPError as e: # The Dataverse API returns a 404 if the dataset has no published files - if e.code == http.NOT_FOUND and version == 'latest-published': + if e.code == http_status.HTTP_404_NOT_FOUND and version == 'latest-published': return [] raise diff --git a/addons/dataverse/tests/test_views.py b/addons/dataverse/tests/test_views.py index ae8a6cc6e89..bcf7e6ef1ca 100644 --- a/addons/dataverse/tests/test_views.py +++ b/addons/dataverse/tests/test_views.py @@ -4,7 +4,7 @@ import pytest import unittest -import httplib as http +from rest_framework import status as http_status from addons.base.tests.views import OAuthAddonConfigViewsTestCaseMixin @@ -98,7 +98,7 @@ def test_set_config(self, mock_connection): 'dataverse': {'alias': 'ALIAS3'}, 'dataset': {'doi': 'doi:12.3456/DVN/00003'}, }, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) self.project.reload() assert_equal( self.project.logs.latest().action, @@ -111,7 +111,7 @@ def test_set_config(self, mock_connection): def test_get_config(self): url = self.project.api_url_for('{0}_get_config'.format(self.ADDON_SHORT_NAME)) res = self.app.get(url, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) assert_in('result', res.json) serialized = self.Serializer().serialize_settings( self.node_settings, @@ -137,7 +137,7 @@ def test_set_config_no_dataset(self, mock_connection): self.node_settings.reload() # Old settings did not change - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) assert_equal(self.node_settings.dataverse_alias, 'ALIAS2') assert_equal(self.node_settings.dataset, 'Example (DVN/00001)') assert_equal(self.node_settings.dataset_doi, 'doi:12.3456/DVN/00001') @@ -303,4 +303,4 @@ def test_restricted_set_dataset_not_owner(self, mock_connection): } res = self.app.post_json(url, params, auth=self.contrib.auth, expect_errors=True) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) diff --git a/addons/dataverse/views.py b/addons/dataverse/views.py index 4d171fc6c10..a3d5e24834f 100644 --- a/addons/dataverse/views.py +++ b/addons/dataverse/views.py @@ -1,6 +1,6 @@ """Views for the node settings page.""" # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status from django.utils import timezone from django.core.exceptions import ValidationError @@ -69,7 +69,7 @@ def dataverse_user_config_get(auth, **kwargs): }, 'hosts': DEFAULT_HOSTS, }, - }, http.OK + }, http_status.HTTP_200_OK ## Config ## @@ -129,13 +129,13 @@ def dataverse_set_config(node_addon, auth, **kwargs): user = auth.user if user_settings and user_settings.owner != user: - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) alias = request.json.get('dataverse', {}).get('alias') doi = request.json.get('dataset', {}).get('doi') if doi is None or alias is None: - return HTTPError(http.BAD_REQUEST) + return HTTPError(http_status.HTTP_400_BAD_REQUEST) connection = client.connect_from_settings(node_addon) dataverse = client.get_dataverse(connection, alias) @@ -143,7 +143,7 @@ def dataverse_set_config(node_addon, auth, **kwargs): node_addon.set_folder(dataverse, dataset, auth) - return {'dataverse': dataverse.title, 'dataset': dataset.title}, http.OK + return {'dataverse': dataverse.title, 'dataset': dataset.title}, http_status.HTTP_200_OK @must_have_permission(WRITE) @@ -160,7 +160,7 @@ def dataverse_get_datasets(node_addon, **kwargs): 'alias': alias, # include alias to verify dataset container 'datasets': [{'title': dataset.title, 'doi': dataset.doi} for dataset in datasets], } - return ret, http.OK + return ret, http_status.HTTP_200_OK ## Crud ## @@ -196,7 +196,7 @@ def dataverse_publish_dataset(node_addon, auth, **kwargs): log_date=now, ) - return {'dataset': dataset.title}, http.OK + return {'dataset': dataset.title}, http_status.HTTP_200_OK ## HGRID ## @@ -299,7 +299,7 @@ def dataverse_get_widget_contents(node_addon, **kwargs): } if not node_addon.complete: - return {'data': data}, http.OK + return {'data': data}, http_status.HTTP_200_OK doi = node_addon.dataset_doi alias = node_addon.dataverse_alias @@ -309,7 +309,7 @@ def dataverse_get_widget_contents(node_addon, **kwargs): dataset = client.get_dataset(dataverse, doi) if dataset is None: - return {'data': data}, http.BAD_REQUEST + return {'data': data}, http_status.HTTP_400_BAD_REQUEST dataverse_host = node_addon.external_account.oauth_key dataverse_url = 'http://{0}/dataverse/{1}'.format(dataverse_host, alias) @@ -324,4 +324,4 @@ def dataverse_get_widget_contents(node_addon, **kwargs): 'datasetUrl': dataset_url, 'citation': dataset.citation, }) - return {'data': data}, http.OK + return {'data': data}, http_status.HTTP_200_OK diff --git a/addons/dropbox/models.py b/addons/dropbox/models.py index 0fdc62e0e28..9bdce337b1a 100644 --- a/addons/dropbox/models.py +++ b/addons/dropbox/models.py @@ -1,4 +1,4 @@ -import httplib as http +from rest_framework import status as http_status import logging import os @@ -90,9 +90,9 @@ def auth_callback(self, user): # Either way, return and display info about how to properly connect. return except (oauth.ProviderException, oauth.CsrfException): - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) except oauth.BadRequestException: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) self.client = Dropbox(access_token) @@ -187,12 +187,12 @@ def get_folders(self, **kwargs): list_folder = client.files_list_folder_continue(list_folder.cursor) contents += [x for x in list_folder.entries] except ApiError as error: - raise HTTPError(http.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': error.user_message_text, 'message_long': error.user_message_text, }) except DropboxException: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) return [ { diff --git a/addons/dropbox/tests/test_views.py b/addons/dropbox/tests/test_views.py index f74a8db4d1d..4f76022e93d 100644 --- a/addons/dropbox/tests/test_views.py +++ b/addons/dropbox/tests/test_views.py @@ -1,5 +1,5 @@ """Views tests for the Dropbox addon.""" -import httplib as http +from rest_framework import status as http_status import unittest from dropbox.exceptions import ApiError @@ -160,7 +160,7 @@ def test_dropbox_folder_list_returns_error_if_invalid_path(self, mock_metadata): url = self.project.api_url_for('dropbox_folder_list', folder_id='/fake_path') with mock.patch.object(type(self.node_settings), 'has_auth', True): res = self.app.get(url, auth=self.user.auth, expect_errors=True) - assert res.status_code == http.BAD_REQUEST + assert res.status_code == http_status.HTTP_400_BAD_REQUEST class TestRestrictions(DropboxAddonTestCase, OsfTestCase): @@ -186,13 +186,13 @@ def test_restricted_folder_list(self, mock_metadata): url = self.project.api_url_for('dropbox_folder_list', path='foo bar') res = self.app.get(url, auth=self.contrib.auth, expect_errors=True) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) def test_restricted_config_contrib_no_addon(self): url = self.project.api_url_for('dropbox_set_config') res = self.app.put_json(url, {'selected': {'path': 'foo'}}, auth=self.contrib.auth, expect_errors=True) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) def test_restricted_config_contrib_not_owner(self): # Contributor has dropbox auth, but is not the node authorizer @@ -202,4 +202,4 @@ def test_restricted_config_contrib_not_owner(self): url = self.project.api_url_for('dropbox_set_config') res = self.app.put_json(url, {'selected': {'path': 'foo'}}, auth=self.contrib.auth, expect_errors=True) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) diff --git a/addons/figshare/tests/test_views.py b/addons/figshare/tests/test_views.py index 66a1d6dfb7f..d5e1a0c7787 100644 --- a/addons/figshare/tests/test_views.py +++ b/addons/figshare/tests/test_views.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # encoding: utf-8 -import httplib as http +from rest_framework import status as http_status import mock from nose.tools import assert_equal import pytest @@ -38,7 +38,7 @@ def test_set_config(self, mock_about): res = self.app.put_json(url, { 'selected': self.folder }, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) self.project.reload() assert_equal( self.project.logs.latest().action, diff --git a/addons/forward/views/config.py b/addons/forward/views/config.py index e3e21872e84..5385086d229 100644 --- a/addons/forward/views/config.py +++ b/addons/forward/views/config.py @@ -1,6 +1,6 @@ """Views for the node settings page.""" # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status from flask import request from osf.exceptions import ValidationValueError @@ -39,14 +39,14 @@ def forward_config_put(auth, node_addon, **kwargs): node_addon.url = request.json['url'] node_addon.label = request.json.get('label') except (KeyError, TypeError, ValueError): - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # Save settings and get changed fields; crash if validation fails try: dirty_fields = node_addon.get_dirty_fields() node_addon.save(request=request) except ValidationValueError: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # Log change if URL updated if 'url' in dirty_fields: diff --git a/addons/github/api.py b/addons/github/api.py index 60cbad74944..15286c9eed2 100644 --- a/addons/github/api.py +++ b/addons/github/api.py @@ -1,4 +1,4 @@ -import urllib +from future.moves.urllib.parse import urlencode import github3 import cachecontrol @@ -156,7 +156,7 @@ def check_authorization(self): def ref_to_params(branch=None, sha=None): - params = urllib.urlencode({ + params = urlencode({ key: value for key, value in { 'branch': branch, diff --git a/addons/github/models.py b/addons/github/models.py index e4dd5bfbccc..504cd23141e 100644 --- a/addons/github/models.py +++ b/addons/github/models.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import os -import urlparse +from future.moves.urllib.parse import urljoin import markupsafe from addons.base.models import (BaseOAuthNodeSettings, BaseOAuthUserSettings, @@ -450,7 +450,7 @@ def add_hook(self, save=True): self.user, self.repo, 'web', { - 'url': urlparse.urljoin( + 'url': urljoin( hook_domain, os.path.join( self.owner.api_url, 'github', 'hook/' diff --git a/addons/github/tests/test_views.py b/addons/github/tests/test_views.py index 3ebf7e438a2..868cf0282d3 100644 --- a/addons/github/tests/test_views.py +++ b/addons/github/tests/test_views.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status import unittest from django.utils import timezone @@ -69,7 +69,7 @@ def test_set_config(self, mock_repo, mock_add_hook): 'github_user': 'octocat', 'github_repo': 'repo_name', }, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) self.project.reload() assert_equal( self.project.logs.latest().action, diff --git a/addons/github/utils.py b/addons/github/utils.py index cb621396f81..c09ede234f5 100644 --- a/addons/github/utils.py +++ b/addons/github/utils.py @@ -1,8 +1,8 @@ import hmac import uuid -import urllib +from future.moves.urllib.parse import unquote_plus import hashlib -import httplib as http +from rest_framework import status as http_status from github3.repos.branch import Branch from framework.exceptions import HTTPError @@ -47,9 +47,9 @@ def verify_hook_signature(node_settings, data, headers): def get_path(kwargs, required=True): path = kwargs.get('path') if path: - return urllib.unquote_plus(path) + return unquote_plus(path) elif required: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) def get_refs(addon, branch=None, sha=None, connection=None): @@ -64,7 +64,7 @@ def get_refs(addon, branch=None, sha=None, connection=None): connection = connection or GitHubClient(external_account=addon.external_account) if sha and not branch: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # Get default branch if not provided if not branch: @@ -85,7 +85,7 @@ def get_refs(addon, branch=None, sha=None, connection=None): ] # Fail if registered and branch not in registration data if registered_branches and branch not in registered_branch_names: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # Get data from GitHub API if not registered branches = registered_branches or connection.branches(addon.user, addon.repo) diff --git a/addons/github/views.py b/addons/github/views.py index ab14f56bc98..fee04373a7d 100644 --- a/addons/github/views.py +++ b/addons/github/views.py @@ -1,7 +1,7 @@ """Views for the node settings page.""" # -*- coding: utf-8 -*- from dateutil.parser import parse as dateparse -import httplib as http +from rest_framework import status as http_status import logging from flask import request, make_response @@ -74,14 +74,14 @@ def github_set_config(auth, **kwargs): if not user_settings: user_settings = node_settings.user_settings except AttributeError: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # Parse request github_user_name = request.json.get('github_user', '') github_repo_name = request.json.get('github_repo', '') if not github_user_name or not github_repo_name: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # Verify that repo exists and that user can access connection = GitHubClient(external_account=node_settings.external_account) @@ -96,7 +96,7 @@ def github_set_config(auth, **kwargs): message = ( 'Cannot access repo.' ) - return {'message': message}, http.BAD_REQUEST + return {'message': message}, http_status.HTTP_400_BAD_REQUEST changed = ( github_user_name != node_settings.user or @@ -191,7 +191,7 @@ def github_folder_list(node_addon, **kwargs): def github_create_repo(**kwargs): repo_name = request.json.get('name') if not repo_name: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) node_settings = kwargs['node_addon'] connection = GitHubClient(external_account=node_settings.external_account) @@ -200,7 +200,7 @@ def github_create_repo(**kwargs): repo = connection.create_repo(repo_name, auto_init=True) except GitHubError: # TODO: Check status code - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) return { 'user': repo.owner.login, diff --git a/addons/gitlab/api.py b/addons/gitlab/api.py index b9cb1da707a..b9a356d35d7 100644 --- a/addons/gitlab/api.py +++ b/addons/gitlab/api.py @@ -1,4 +1,4 @@ -import urllib +from future.moves.urllib.parse import urlencode import requests import gitlab @@ -55,7 +55,7 @@ def repo(self, repo_id): raise exc def repos(self): - return self.user().projects.list() + return self.gitlab.projects.list(membership=True) def branches(self, repo_id, branch=None): """List a repo's branches or get a single branch (in a list). @@ -132,7 +132,7 @@ def revoke_token(self): def ref_to_params(branch=None, sha=None): - params = urllib.urlencode({ + params = urlencode({ key: value for key, value in { 'branch': branch, diff --git a/addons/gitlab/models.py b/addons/gitlab/models.py index f812896e26b..bdc83d9758e 100644 --- a/addons/gitlab/models.py +++ b/addons/gitlab/models.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- import os -import urlparse +from future.moves.urllib.parse import urljoin from django.db import models import markupsafe @@ -413,7 +413,7 @@ def add_hook(self, save=True): self.user, self.repo, 'web', { - 'url': urlparse.urljoin( + 'url': urljoin( hook_domain, os.path.join( self.owner.api_url, 'gitlab', 'hook/' diff --git a/addons/gitlab/tests/test_views.py b/addons/gitlab/tests/test_views.py index 81e941179d7..04ae8f73e64 100644 --- a/addons/gitlab/tests/test_views.py +++ b/addons/gitlab/tests/test_views.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status import mock import datetime @@ -76,7 +76,7 @@ def test_set_config(self, mock_repo, mock_add_hook): 'gitlab_repo': 'repo_name', 'gitlab_repo_id': '123', }, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) self.project.reload() assert_equal( self.project.logs.latest().action, diff --git a/addons/gitlab/utils.py b/addons/gitlab/utils.py index 157fb86de35..e5b398b7e47 100644 --- a/addons/gitlab/utils.py +++ b/addons/gitlab/utils.py @@ -1,8 +1,8 @@ import hmac import uuid -import urllib +from future.moves.urllib.parse import unquote_plus import hashlib -import httplib as http +from rest_framework import status as http_status from framework.exceptions import HTTPError from addons.base.exceptions import HookError @@ -46,9 +46,9 @@ def verify_hook_signature(node_settings, data, headers): def get_path(kwargs, required=True): path = kwargs.get('path') if path: - return urllib.unquote_plus(path) + return unquote_plus(path) elif required: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) def get_refs(addon, branch=None, sha=None, connection=None): @@ -63,7 +63,7 @@ def get_refs(addon, branch=None, sha=None, connection=None): connection = connection or GitLabClient(external_account=addon.external_account) if sha and not branch: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # Get default branch if not provided if not branch: diff --git a/addons/gitlab/views.py b/addons/gitlab/views.py index b0b2adb1a4e..999cd343dc0 100644 --- a/addons/gitlab/views.py +++ b/addons/gitlab/views.py @@ -1,7 +1,7 @@ """Views for the node settings page.""" # -*- coding: utf-8 -*- from dateutil.parser import parse as dateparse -import httplib as http +from rest_framework import status as http_status import logging import gitlab @@ -84,7 +84,7 @@ def gitlab_user_config_get(auth, **kwargs): }, 'hosts': DEFAULT_HOSTS, }, - }, http.OK + }, http_status.HTTP_200_OK @must_be_logged_in def gitlab_add_user_account(auth, **kwargs): @@ -145,7 +145,7 @@ def gitlab_set_config(auth, **kwargs): if not user_settings: user_settings = node_settings.user_settings except AttributeError: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # Parse request gitlab_user_name = request.json.get('gitlab_user', '') @@ -153,7 +153,7 @@ def gitlab_set_config(auth, **kwargs): gitlab_repo_id = request.json.get('gitlab_repo_id', '') if not gitlab_user_name or not gitlab_repo_name or not gitlab_repo_id: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # Verify that repo exists and that user can access connection = GitLabClient(external_account=node_settings.external_account) @@ -163,7 +163,7 @@ def gitlab_set_config(auth, **kwargs): except gitlab.exceptions.GitlabError as exc: if exc.response_code == 403 and 'must accept the Terms of Service' in exc.error_message: return {'message': 'Your gitlab account does not have proper authentication. Ensure you have agreed to Gitlab\'s ' - 'current Terms of Service by disabling and re-enabling your account.'}, http.BAD_REQUEST + 'current Terms of Service by disabling and re-enabling your account.'}, http_status.HTTP_400_BAD_REQUEST if repo is None: if user_settings: @@ -175,7 +175,7 @@ def gitlab_set_config(auth, **kwargs): message = ( 'Cannot access repo.' ) - return {'message': message}, http.BAD_REQUEST + return {'message': message}, http_status.HTTP_400_BAD_REQUEST changed = ( gitlab_user_name != node_settings.user or diff --git a/addons/mendeley/tests/test_views.py b/addons/mendeley/tests/test_views.py index 5e23498c5a0..df2bb2baed8 100644 --- a/addons/mendeley/tests/test_views.py +++ b/addons/mendeley/tests/test_views.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import mock import pytest -import urlparse +from future.moves.urllib.parse import urlparse, urljoin from addons.base.tests import views from addons.base.tests.utils import MockFolder @@ -24,8 +24,8 @@ class TestConfigViews(MendeleyTestCase, views.OAuthCitationAddonConfigViewsTestC Serializer = MendeleySerializer client = Mendeley citationsProvider = MendeleyCitationsProvider - foldersApiUrl = urlparse.urljoin(API_URL, 'folders') - documentsApiUrl = urlparse.urljoin(API_URL, 'documents') + foldersApiUrl = urljoin(API_URL, 'folders') + documentsApiUrl = urljoin(API_URL, 'documents') mockResponses = mock_responses @mock.patch('addons.mendeley.models.NodeSettings._fetch_folder_name', mock.PropertyMock(return_value='Fake Name')) diff --git a/addons/onedrive/models.py b/addons/onedrive/models.py index 033a8f4947c..ce256837bb7 100644 --- a/addons/onedrive/models.py +++ b/addons/onedrive/models.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- import os -import urllib +from future.moves.urllib.parse import unquote import logging from django.db import models @@ -124,7 +124,7 @@ def folder_name(self): if self.folder_id != DEFAULT_ROOT_ID: # `urllib` does not properly handle unicode. # encode input to `str`, decode output back to `unicode` - return urllib.unquote(os.path.split(self.folder_path)[1].encode('utf-8')).decode('utf-8') + return unquote(os.path.split(self.folder_path)[1].encode('utf-8')).decode('utf-8') else: return '/ (Full OneDrive)' diff --git a/addons/osfstorage/decorators.py b/addons/osfstorage/decorators.py index 9493983d8ae..13727181e79 100644 --- a/addons/osfstorage/decorators.py +++ b/addons/osfstorage/decorators.py @@ -1,4 +1,4 @@ -import httplib +from rest_framework import status as http_status import functools from django.core.exceptions import ObjectDoesNotExist @@ -21,11 +21,11 @@ def wrapped(*args, **kwargs): try: return func(*args, **kwargs) except ObjectDoesNotExist: - raise HTTPError(httplib.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) except IntegrityError: - raise HTTPError(httplib.CONFLICT) + raise HTTPError(http_status.HTTP_409_CONFLICT) except exceptions.VersionNotFoundError: - raise HTTPError(httplib.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) return wrapped @@ -37,7 +37,7 @@ def wrapped(*args, **kwargs): target = getattr(Guid.load(guid), 'referent', None) if not target: raise HTTPError( - httplib.NOT_FOUND, + http_status.HTTP_404_NOT_FOUND, data={ 'message_short': 'Guid not resolved', 'message_long': 'No object with that guid could be found', @@ -63,7 +63,7 @@ def wrapped(*args, **kwargs): else: file_node = OsfStorageFileNode.get(kwargs.get('fid'), kwargs['target']) if must_be and file_node.kind != must_be: - raise HTTPError(httplib.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'incorrect type', 'message_long': 'FileNode must be of type {} not {}'.format(must_be, file_node.kind) }) @@ -99,7 +99,7 @@ def wrapped(payload, *args, **kwargs): 'name': payload['destination']['name'], }) except KeyError: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) return func(*args, **kwargs) return wrapped diff --git a/addons/osfstorage/utils.py b/addons/osfstorage/utils.py index da2d995a0fa..055ac3d32e3 100644 --- a/addons/osfstorage/utils.py +++ b/addons/osfstorage/utils.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals import os -import httplib +from rest_framework import status as http_status import logging import functools @@ -64,7 +64,7 @@ def serialize_revision(node, record, version, index, anon=False): SIGNED_REQUEST_ERROR = HTTPError( - httplib.SERVICE_UNAVAILABLE, + http_status.HTTP_503_SERVICE_UNAVAILABLE, data={ 'message_short': 'Upload service unavailable', 'message_long': ( diff --git a/addons/osfstorage/views.py b/addons/osfstorage/views.py index 8c5593946c9..b65657f420e 100644 --- a/addons/osfstorage/views.py +++ b/addons/osfstorage/views.py @@ -1,6 +1,6 @@ from __future__ import unicode_literals -import httplib +from rest_framework import status as http_status import logging from django.core.exceptions import ValidationError @@ -83,7 +83,7 @@ def osfstorage_update_metadata(payload, **kwargs): version_id = payload['version'] metadata = payload['metadata'] except KeyError: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) if check_select_for_update(): version = FileVersion.objects.filter(_id=version_id).select_for_update().first() @@ -91,7 +91,7 @@ def osfstorage_update_metadata(payload, **kwargs): version = FileVersion.objects.filter(_id=version_id).first() if version is None: - raise HTTPError(httplib.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) version.update_metadata(metadata) @@ -124,7 +124,7 @@ def osfstorage_get_revisions(file_node, payload, target, **kwargs): @decorators.waterbutler_opt_hook def osfstorage_copy_hook(source, destination, name=None, **kwargs): - ret = source.copy_under(destination, name=name).serialize(), httplib.CREATED + ret = source.copy_under(destination, name=name).serialize(), http_status.HTTP_201_CREATED update_storage_usage(destination.target) return ret @@ -133,13 +133,13 @@ def osfstorage_move_hook(source, destination, name=None, **kwargs): source_target = source.target try: - ret = source.move_under(destination, name=name).serialize(), httplib.OK + ret = source.move_under(destination, name=name).serialize(), http_status.HTTP_200_OK except exceptions.FileNodeCheckedOutError: - raise HTTPError(httplib.METHOD_NOT_ALLOWED, data={ + raise HTTPError(http_status.HTTP_405_METHOD_NOT_ALLOWED, data={ 'message_long': 'Cannot move file as it is checked out.' }) except exceptions.FileNodeIsPrimaryFile: - raise HTTPError(httplib.FORBIDDEN, data={ + raise HTTPError(http_status.HTTP_403_FORBIDDEN, data={ 'message_long': 'Cannot move file as it is the primary file of preprint.' }) @@ -288,7 +288,7 @@ def osfstorage_create_child(file_node, payload, **kwargs): if getattr(file_node.target, 'is_registration', False) and not getattr(file_node.target, 'archiving', False): raise HTTPError( - httplib.BAD_REQUEST, + http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Registered Nodes are immutable', 'message_long': "The operation you're trying to do cannot be applied to registered Nodes, which are immutable", @@ -296,10 +296,10 @@ def osfstorage_create_child(file_node, payload, **kwargs): ) if not (name or user) or '/' in name: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) if getattr(file_node.target, 'is_quickfiles', False) and is_folder: - raise HTTPError(httplib.BAD_REQUEST, data={'message_long': 'You may not create a folder for QuickFiles'}) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={'message_long': 'You may not create a folder for QuickFiles'}) try: # Create a save point so that we can rollback and unlock @@ -313,7 +313,7 @@ def osfstorage_create_child(file_node, payload, **kwargs): created, file_node = False, parent.find_child_by_name(name, kind=int(not is_folder)) if not created and is_folder: - raise HTTPError(httplib.CONFLICT, data={ + raise HTTPError(http_status.HTTP_409_CONFLICT, data={ 'message_long': 'Cannot create folder "{name}" because a file or folder already exists at path "{path}"'.format( name=file_node.name, path=file_node.materialized_path, @@ -321,7 +321,7 @@ def osfstorage_create_child(file_node, payload, **kwargs): }) if file_node.checkout and file_node.checkout._id != user._id: - raise HTTPError(httplib.FORBIDDEN, data={ + raise HTTPError(http_status.HTTP_403_FORBIDDEN, data={ 'message_long': 'File cannot be updated due to checkout status.' }) @@ -335,7 +335,7 @@ def osfstorage_create_child(file_node, payload, **kwargs): } )) except KeyError: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) current_version = file_node.get_version() new_version = file_node.create_version(user, location, metadata) @@ -354,7 +354,7 @@ def osfstorage_create_child(file_node, payload, **kwargs): 'archive': not archive_exists, # Should waterbutler also archive this file 'data': file_node.serialize(), 'version': version_id, - }, httplib.CREATED if created else httplib.OK + }, http_status.HTTP_201_CREATED if created else http_status.HTTP_200_OK @must_be_signed @@ -366,17 +366,17 @@ def osfstorage_delete(file_node, payload, target, **kwargs): #TODO Auth check? if not auth: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) if file_node == OsfStorageFolder.objects.get_root(target=target): - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) try: file_node.delete(user=user) except exceptions.FileNodeCheckedOutError: - raise HTTPError(httplib.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) except exceptions.FileNodeIsPrimaryFile: - raise HTTPError(httplib.FORBIDDEN, data={ + raise HTTPError(http_status.HTTP_403_FORBIDDEN, data={ 'message_long': 'Cannot delete file as it is the primary file of preprint.' }) @@ -401,7 +401,7 @@ def osfstorage_download(file_node, payload, **kwargs): try: version_id = int(request.args['version']) except ValueError: - raise make_error(httplib.BAD_REQUEST, message_short='Version must be an integer if not specified') + raise make_error(http_status.HTTP_400_BAD_REQUEST, message_short='Version must be an integer if not specified') version = file_node.get_version(version_id, required=True) file_version_thru = version.get_basefilenode_version(file_node) @@ -423,8 +423,8 @@ def osfstorage_download(file_node, payload, **kwargs): def osfstorage_add_tag(file_node, **kwargs): data = request.get_json() if file_node.add_tag(data['tag'], kwargs['auth']): - return {'status': 'success'}, httplib.OK - return {'status': 'failure'}, httplib.BAD_REQUEST + return {'status': 'success'}, http_status.HTTP_200_OK + return {'status': 'failure'}, http_status.HTTP_400_BAD_REQUEST @must_have_permission(WRITE) @decorators.autoload_filenode(must_be='file') @@ -433,11 +433,11 @@ def osfstorage_remove_tag(file_node, **kwargs): try: file_node.remove_tag(data['tag'], kwargs['auth']) except TagNotFoundError: - return {'status': 'failure'}, httplib.CONFLICT + return {'status': 'failure'}, http_status.HTTP_409_CONFLICT except InvalidTagError: - return {'status': 'failure'}, httplib.BAD_REQUEST + return {'status': 'failure'}, http_status.HTTP_400_BAD_REQUEST else: - return {'status': 'success'}, httplib.OK + return {'status': 'success'}, http_status.HTTP_200_OK @must_be_logged_in @@ -449,7 +449,7 @@ def update_region(auth, **kwargs): try: region_id = data['region_id'] except KeyError: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) try: user_settings.set_region(region_id) diff --git a/addons/owncloud/tests/test_views.py b/addons/owncloud/tests/test_views.py index 4473c433653..ab0f42f71c9 100644 --- a/addons/owncloud/tests/test_views.py +++ b/addons/owncloud/tests/test_views.py @@ -3,7 +3,7 @@ import mock import pytest -import httplib as http +from rest_framework import status as http_status from addons.base.tests.views import ( OAuthAddonAuthViewsTestCaseMixin, OAuthAddonConfigViewsTestCaseMixin @@ -57,7 +57,7 @@ def test_get_config(self): url = self.project.api_url_for( '{0}_get_config'.format(self.ADDON_SHORT_NAME)) res = self.app.get(url, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) assert_in('result', res.json) serialized = self.Serializer().serialize_settings( self.node_settings, diff --git a/addons/owncloud/views.py b/addons/owncloud/views.py index 3456380d285..6399c5bf8b0 100644 --- a/addons/owncloud/views.py +++ b/addons/owncloud/views.py @@ -1,6 +1,6 @@ """Views for the node settings page.""" # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status from django.core.exceptions import ValidationError from furl import furl @@ -62,11 +62,11 @@ def owncloud_add_user_account(auth, **kwargs): except requests.exceptions.ConnectionError: return { 'message': 'Invalid ownCloud server.' - }, http.BAD_REQUEST + }, http_status.HTTP_400_BAD_REQUEST except owncloud.owncloud.HTTPResponseError: return { 'message': 'ownCloud Login failed.' - }, http.UNAUTHORIZED + }, http_status.HTTP_401_UNAUTHORIZED provider = OwnCloudProvider(account=None, host=host.url, username=username, password=password) diff --git a/addons/s3/tests/test_view.py b/addons/s3/tests/test_view.py index 7619155861c..3057a0fa3aa 100644 --- a/addons/s3/tests/test_view.py +++ b/addons/s3/tests/test_view.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status from boto.exception import S3ResponseError import mock @@ -45,7 +45,7 @@ def test_s3_settings_input_empty_keys(self): 'access_key': '', 'secret_key': '' }, auth=self.user.auth, expect_errors=True) - assert_equals(rv.status_int, http.BAD_REQUEST) + assert_equals(rv.status_int, http_status.HTTP_400_BAD_REQUEST) assert_in('All the fields above are required.', rv.body) def test_s3_settings_input_empty_access_key(self): @@ -54,7 +54,7 @@ def test_s3_settings_input_empty_access_key(self): 'access_key': '', 'secret_key': 'Non-empty-secret-key' }, auth=self.user.auth, expect_errors=True) - assert_equals(rv.status_int, http.BAD_REQUEST) + assert_equals(rv.status_int, http_status.HTTP_400_BAD_REQUEST) assert_in('All the fields above are required.', rv.body) def test_s3_settings_input_empty_secret_key(self): @@ -63,7 +63,7 @@ def test_s3_settings_input_empty_secret_key(self): 'access_key': 'Non-empty-access-key', 'secret_key': '' }, auth=self.user.auth, expect_errors=True) - assert_equals(rv.status_int, http.BAD_REQUEST) + assert_equals(rv.status_int, http_status.HTTP_400_BAD_REQUEST) assert_in('All the fields above are required.', rv.body) def test_s3_set_bucket_no_settings(self): @@ -74,7 +74,7 @@ def test_s3_set_bucket_no_settings(self): url, {'s3_bucket': 'hammertofall'}, auth=user.auth, expect_errors=True ) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) def test_s3_set_bucket_no_auth(self): @@ -86,7 +86,7 @@ def test_s3_set_bucket_no_auth(self): url, {'s3_bucket': 'hammertofall'}, auth=user.auth, expect_errors=True ) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) def test_s3_set_bucket_registered(self): registration = self.project.register_node( @@ -99,7 +99,7 @@ def test_s3_set_bucket_registered(self): expect_errors=True, ) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) @mock.patch('addons.s3.views.utils.can_list', return_value=False) def test_user_settings_cant_list(self, mock_can_list): @@ -108,7 +108,7 @@ def test_user_settings_cant_list(self, mock_can_list): 'access_key': 'aldkjf', 'secret_key': 'las' }, auth=self.user.auth, expect_errors=True) - assert_equals(rv.status_int, http.BAD_REQUEST) + assert_equals(rv.status_int, http_status.HTTP_400_BAD_REQUEST) assert_in('Unable to list buckets.', rv.body) def test_s3_remove_node_settings_owner(self): @@ -159,7 +159,7 @@ def test_set_config(self, mock_location, mock_exists): res = self.app.put_json(url, { 'selected': self.folder }, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) self.project.reload() self.node_settings.reload() assert_equal( @@ -266,7 +266,7 @@ def test_create_bucket_pass(self, mock_names, mock_make): auth=self.user.auth ) - assert_equal(ret.status_int, http.OK) + assert_equal(ret.status_int, http_status.HTTP_200_OK) assert_equal(ret.json, {}) @mock.patch('addons.s3.views.utils.create_bucket') diff --git a/addons/s3/utils.py b/addons/s3/utils.py index d84773849b9..0738b5bf397 100644 --- a/addons/s3/utils.py +++ b/addons/s3/utils.py @@ -1,5 +1,5 @@ import re -import httplib +from rest_framework import status as http_status from boto import exception from boto.s3.connection import S3Connection @@ -26,7 +26,7 @@ def get_bucket_names(node_settings): try: buckets = connect_s3(node_settings=node_settings).get_all_buckets() except exception.NoAuthHandlerFound: - raise HTTPError(httplib.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) except exception.BotoServerError as e: raise HTTPError(e.status) diff --git a/addons/s3/views.py b/addons/s3/views.py index b022c4b9dd8..aba5ec8f63c 100644 --- a/addons/s3/views.py +++ b/addons/s3/views.py @@ -1,4 +1,4 @@ -import httplib +from rest_framework import status as http_status from boto import exception from django.core.exceptions import ValidationError @@ -65,12 +65,12 @@ def s3_add_user_account(auth, **kwargs): access_key = request.json['access_key'] secret_key = request.json['secret_key'] except KeyError: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) if not (access_key and secret_key): return { 'message': 'All the fields above are required.' - }, httplib.BAD_REQUEST + }, http_status.HTTP_400_BAD_REQUEST user_info = utils.get_user_info(access_key, secret_key) if not user_info: @@ -78,13 +78,13 @@ def s3_add_user_account(auth, **kwargs): 'message': ('Unable to access account.\n' 'Check to make sure that the above credentials are valid, ' 'and that they have permission to list buckets.') - }, httplib.BAD_REQUEST + }, http_status.HTTP_400_BAD_REQUEST if not utils.can_list(access_key, secret_key): return { 'message': ('Unable to list buckets.\n' 'Listing buckets is required permission that can be changed via IAM') - }, httplib.BAD_REQUEST + }, http_status.HTTP_400_BAD_REQUEST account = None try: @@ -130,14 +130,14 @@ def create_bucket(auth, node_addon, **kwargs): return { 'message': 'That bucket name is not valid.', 'title': 'Invalid bucket name', - }, httplib.BAD_REQUEST + }, http_status.HTTP_400_BAD_REQUEST # Get location and verify it is valid if not utils.validate_bucket_location(bucket_location): return { 'message': 'That bucket location is not valid.', 'title': 'Invalid bucket location', - }, httplib.BAD_REQUEST + }, http_status.HTTP_400_BAD_REQUEST try: utils.create_bucket(node_addon, bucket_name, bucket_location) @@ -145,16 +145,16 @@ def create_bucket(auth, node_addon, **kwargs): return { 'message': e.message, 'title': 'Problem connecting to S3', - }, httplib.BAD_REQUEST + }, http_status.HTTP_400_BAD_REQUEST except exception.S3CreateError as e: return { 'message': e.message, 'title': "Problem creating bucket '{0}'".format(bucket_name), - }, httplib.BAD_REQUEST + }, http_status.HTTP_400_BAD_REQUEST except exception.BotoClientError as e: # Base class catchall return { 'message': e.message, 'title': 'Error connecting to S3', - }, httplib.BAD_REQUEST + }, http_status.HTTP_400_BAD_REQUEST return {} diff --git a/addons/twofactor/tests/test_models.py b/addons/twofactor/tests/test_models.py index f581a4f2261..e501b5fdc57 100644 --- a/addons/twofactor/tests/test_models.py +++ b/addons/twofactor/tests/test_models.py @@ -1,5 +1,5 @@ import unittest -from urlparse import parse_qs, urlparse +from future.moves.urllib.parse import urlparse, parse_qs import pytest from addons.twofactor.tests.utils import _valid_code diff --git a/addons/twofactor/views.py b/addons/twofactor/views.py index 0c2c95837ad..d50fda014dc 100644 --- a/addons/twofactor/views.py +++ b/addons/twofactor/views.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status from flask import request @@ -16,13 +16,13 @@ def twofactor_settings_put(user_addon, *args, **kwargs): code = request.json.get('code') if code is None: - raise HTTPError(code=http.BAD_REQUEST) + raise HTTPError(code=http_status.HTTP_400_BAD_REQUEST) if user_addon.verify_code(code): user_addon.is_confirmed = True user_addon.save() - return {'message': 'Successfully verified two-factor authentication.'}, http.OK - raise HTTPError(http.FORBIDDEN, data=dict( + return {'message': 'Successfully verified two-factor authentication.'}, http_status.HTTP_200_OK + raise HTTPError(http_status.HTTP_403_FORBIDDEN, data=dict( message_short='Forbidden', message_long='The two-factor verification code you provided is invalid.' )) @@ -39,7 +39,7 @@ def twofactor_enable(auth, *args, **kwargs): user = auth.user if user.has_addon('twofactor'): - return HTTPError(http.BAD_REQUEST, data=dict(message_long='This user already has two-factor enabled')) + return HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict(message_long='This user already has two-factor enabled')) user.add_addon('twofactor', auth=auth) user_addon = user.get_addon('twofactor') @@ -57,6 +57,6 @@ def twofactor_disable(auth, *args, **kwargs): auth.user.save() return {} else: - raise HTTPError(http.INTERNAL_SERVER_ERROR, data=dict( + raise HTTPError(http_status.HTTP_500_INTERNAL_SERVER_ERROR, data=dict( message_long='Could not disable two-factor at this time' )) diff --git a/addons/wiki/tests/test_wiki.py b/addons/wiki/tests/test_wiki.py index a60b5aaa03d..de976becc25 100644 --- a/addons/wiki/tests/test_wiki.py +++ b/addons/wiki/tests/test_wiki.py @@ -3,7 +3,7 @@ # PEP8 asserts from copy import deepcopy -import httplib as http +from rest_framework import status as http_status import time import mock import pytest @@ -601,7 +601,7 @@ def test_rename_wiki_page_invalid(self, new_name=u'invalid/name'): auth=self.auth, expect_errors=True, ) - assert_equal(http.BAD_REQUEST, res.status_code) + assert_equal(http_status.HTTP_400_BAD_REQUEST, res.status_code) assert_equal(res.json['message_short'], 'Invalid name') assert_equal(res.json['message_long'], 'Page name cannot contain forward slashes.') self.project.reload() diff --git a/addons/wiki/utils.py b/addons/wiki/utils.py index 21f45f554f5..d4a02a45236 100644 --- a/addons/wiki/utils.py +++ b/addons/wiki/utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- import os -import urllib +from future.moves.urllib.parse import quote import uuid import ssl @@ -131,7 +131,7 @@ def broadcast_to_sharejs(action, sharejs_uuid, node=None, wiki_name='home', data ) if action == 'redirect' or action == 'delete': - redirect_url = urllib.quote( + redirect_url = quote( node.web_url_for('project_wiki_view', wname=wiki_name, _guid=True), safe='', ) diff --git a/addons/wiki/views.py b/addons/wiki/views.py index d40e611a42b..f4bcd90ea02 100644 --- a/addons/wiki/views.py +++ b/addons/wiki/views.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status import logging from flask import request @@ -44,27 +44,27 @@ logger = logging.getLogger(__name__) -WIKI_NAME_EMPTY_ERROR = HTTPError(http.BAD_REQUEST, data=dict( +WIKI_NAME_EMPTY_ERROR = HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_short='Invalid request', message_long='The wiki page name cannot be empty.' )) -WIKI_NAME_MAXIMUM_LENGTH_ERROR = HTTPError(http.BAD_REQUEST, data=dict( +WIKI_NAME_MAXIMUM_LENGTH_ERROR = HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_short='Invalid request', message_long='The wiki page name cannot be more than 100 characters.' )) -WIKI_PAGE_CANNOT_RENAME_ERROR = HTTPError(http.BAD_REQUEST, data=dict( +WIKI_PAGE_CANNOT_RENAME_ERROR = HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_short='Invalid request', message_long='The wiki page cannot be renamed.' )) -WIKI_PAGE_CONFLICT_ERROR = HTTPError(http.CONFLICT, data=dict( +WIKI_PAGE_CONFLICT_ERROR = HTTPError(http_status.HTTP_409_CONFLICT, data=dict( message_short='Page conflict', message_long='A wiki page with that name already exists.' )) -WIKI_PAGE_NOT_FOUND_ERROR = HTTPError(http.NOT_FOUND, data=dict( +WIKI_PAGE_NOT_FOUND_ERROR = HTTPError(http_status.HTTP_404_NOT_FOUND, data=dict( message_short='Not found', message_long='A wiki page could not be found.' )) -WIKI_INVALID_VERSION_ERROR = HTTPError(http.BAD_REQUEST, data=dict( +WIKI_INVALID_VERSION_ERROR = HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_short='Invalid request', message_long='The requested version of this wiki page does not exist.' )) @@ -163,7 +163,7 @@ def project_wiki_delete(auth, wname, **kwargs): sharejs_uuid = wiki_utils.get_sharejs_uuid(node, wiki_name) if not wiki_page: - raise HTTPError(http.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) wiki_page.delete(auth) wiki_utils.broadcast_to_sharejs('delete', sharejs_uuid, node) @@ -241,10 +241,10 @@ def project_wiki_view(auth, wname, path=None, **kwargs): raise WIKI_PAGE_NOT_FOUND_ERROR if 'edit' in request.args: if wiki_settings.is_publicly_editable: - raise HTTPError(http.UNAUTHORIZED) + raise HTTPError(http_status.HTTP_401_UNAUTHORIZED) if node.can_view(auth): return redirect(node.web_url_for('project_wiki_view', wname=wname, _guid=True)) - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) sharejs_uuid = None # Opens 'edit' panel when home wiki is empty @@ -313,7 +313,7 @@ def project_wiki_edit_post(auth, wname, **kwargs): # Create a wiki WikiPage.objects.create_for_node(node, wiki_name, form_wiki_content, auth) ret = {'status': 'success'} - return ret, http.FOUND, None, redirect_url + return ret, http_status.HTTP_302_FOUND, None, redirect_url @must_be_valid_project # injects node or project @must_have_permission(ADMIN) @@ -324,7 +324,7 @@ def edit_wiki_settings(node, auth, **kwargs): permissions = request.get_json().get('permission', None) if not wiki_settings: - raise HTTPError(http.BAD_REQUEST, data=dict( + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_short='Invalid request', message_long='Cannot change wiki settings without a wiki' )) @@ -334,7 +334,7 @@ def edit_wiki_settings(node, auth, **kwargs): elif permissions == 'private': permissions = False else: - raise HTTPError(http.BAD_REQUEST, data=dict( + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_short='Invalid request', message_long='Permissions flag used is incorrect.' )) @@ -342,7 +342,7 @@ def edit_wiki_settings(node, auth, **kwargs): try: wiki_settings.set_editing(permissions, auth, log=True) except NodeStateError as e: - raise HTTPError(http.BAD_REQUEST, data=dict( + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_short="Can't change privacy", message_long=str(e) )) @@ -416,7 +416,7 @@ def project_wiki_rename(auth, wname, **kwargs): except NameEmptyError: raise WIKI_NAME_EMPTY_ERROR except NameInvalidError as error: - raise HTTPError(http.BAD_REQUEST, data=dict( + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_short='Invalid name', message_long=error.args[0] )) @@ -429,7 +429,7 @@ def project_wiki_rename(auth, wname, **kwargs): except PageNotFoundError: raise WIKI_PAGE_NOT_FOUND_ERROR except ValidationError as err: - raise HTTPError(http.BAD_REQUEST, data=dict( + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_short='Invalid request', message_long=err.messages[0] )) @@ -447,7 +447,7 @@ def project_wiki_validate_name(wname, auth, node, **kwargs): wiki = WikiPage.objects.get_for_node(node, wiki_name) if wiki or wiki_name.lower() == 'home': - raise HTTPError(http.CONFLICT, data=dict( + raise HTTPError(http_status.HTTP_409_CONFLICT, data=dict( message_short='Wiki page name conflict.', message_long='A wiki page with that name already exists.' )) diff --git a/addons/zotero/provider.py b/addons/zotero/provider.py index 836688fa29d..6b1cc954d70 100644 --- a/addons/zotero/provider.py +++ b/addons/zotero/provider.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status from framework.exceptions import HTTPError from website.citations.providers import CitationsProvider @@ -102,7 +102,7 @@ def citation_list(self, node_addon, user, list_id, show='all'): while ancestor_id != attached_list_id: if ancestor_id is '__': - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) ancestor_id = folders[ancestor_id].get('parent_list_id') contents = [] diff --git a/addons/zotero/tests/test_views.py b/addons/zotero/tests/test_views.py index 6b35b72c213..959f1ab54ac 100644 --- a/addons/zotero/tests/test_views.py +++ b/addons/zotero/tests/test_views.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import mock import pytest -import urlparse +from future.moves.urllib.parse import urljoin import responses from framework.auth import Auth @@ -42,9 +42,9 @@ class TestConfigViews(ZoteroTestCase, views.OAuthCitationAddonConfigViewsTestCas def setUp(self): super(TestConfigViews, self).setUp() - self.foldersApiUrl = urlparse.urljoin(API_URL, 'users/{}/collections' + self.foldersApiUrl = urljoin(API_URL, 'users/{}/collections' .format(self.external_account.provider_id)) - self.documentsApiUrl = urlparse.urljoin(API_URL, 'users/{}/items/top' + self.documentsApiUrl = urljoin(API_URL, 'users/{}/items/top' .format(self.external_account.provider_id)) # Sets library key diff --git a/admin/base/settings/defaults.py b/admin/base/settings/defaults.py index 8f84988509c..ea7f7432f87 100644 --- a/admin/base/settings/defaults.py +++ b/admin/base/settings/defaults.py @@ -2,9 +2,6 @@ Django settings for the admin project. """ -import os -from urlparse import urlparse -from website import settings as osf_settings from django.contrib import messages from api.base.settings import * # noqa # TODO ALL SETTINGS FROM API WILL BE IMPORTED AND WILL NEED TO BE OVERRRIDEN @@ -249,7 +246,7 @@ INSTALLED_APPS += ('debug_toolbar', 'nplusone.ext.django',) MIDDLEWARE += ('debug_toolbar.middleware.DebugToolbarMiddleware', 'nplusone.ext.django.NPlusOneMiddleware',) DEBUG_TOOLBAR_CONFIG = { - 'SHOW_TOOLBAR_CALLBACK': lambda(_): True, + 'SHOW_TOOLBAR_CALLBACK': lambda _: True, 'DISABLE_PANELS': { 'debug_toolbar.panels.templates.TemplatesPanel', 'debug_toolbar.panels.redirects.RedirectsPanel' diff --git a/admin/base/urls.py b/admin/base/urls.py index d741d850eac..e39e1db38c0 100644 --- a/admin/base/urls.py +++ b/admin/base/urls.py @@ -1,9 +1,9 @@ from django.conf.urls import include, url from django.contrib import admin -import settings -from . import views +from admin.base.settings import ADMIN_BASE, DEBUG +from admin.base import views -base_pattern = '^{}'.format(settings.ADMIN_BASE) +base_pattern = '^{}'.format(ADMIN_BASE) urlpatterns = [ ### ADMIN ### @@ -38,7 +38,7 @@ ), ] -if settings.DEBUG: +if DEBUG: import debug_toolbar urlpatterns += [ diff --git a/admin/preprints/views.py b/admin/preprints/views.py index 303645d76df..98a9b3a455a 100644 --- a/admin/preprints/views.py +++ b/admin/preprints/views.py @@ -7,6 +7,7 @@ from django.shortcuts import redirect from django.views.defaults import page_not_found from django.core.exceptions import PermissionDenied +from django.contrib import messages from osf.models import SpamStatus, PreprintRequest from osf.models.preprint import Preprint, PreprintLog, OSFUser @@ -68,9 +69,14 @@ def get_success_url(self): return reverse_lazy('preprints:preprint', kwargs={'guid': self.kwargs.get('guid')}) def post(self, request, *args, **kwargs): + old_provider = self.get_object().provider if not request.user.has_perm('osf.change_preprint'): raise PermissionsError("This user does not have permission to update this preprint's provider.") - return super(PreprintView, self).post(request, *args, **kwargs) + response = super(PreprintView, self).post(request, *args, **kwargs) + new_provider = self.get_object().provider + if new_provider and old_provider.id != new_provider.id: + self.update_subjects_for_provider(request, old_provider, new_provider) + return response def get_context_data(self, **kwargs): preprint = Preprint.load(self.kwargs.get('guid')) @@ -81,6 +87,13 @@ def get_context_data(self, **kwargs): kwargs.update({'message': kwargs.get('message')}) return super(PreprintView, self).get_context_data(**kwargs) + def update_subjects_for_provider(self, request, old_provider, new_provider): + subject_problems = self.object.map_subjects_between_providers(old_provider, new_provider, auth=None) + if subject_problems: + messages.warning(request, 'Unable to find subjects in new provider for the following subject(s):') + for problem in subject_problems: + messages.warning(request, problem) + class PreprintSpamList(PermissionRequiredMixin, ListView): SPAM_STATE = SpamStatus.UNKNOWN diff --git a/admin/templates/preprints/preprint.html b/admin/templates/preprints/preprint.html index 578b690ec23..6181a0347d0 100644 --- a/admin/templates/preprints/preprint.html +++ b/admin/templates/preprints/preprint.html @@ -130,6 +130,13 @@

Preprint Details

Change preprint provider + {% if messages %} + + {% endif %}
diff --git a/admin_tests/preprints/test_views.py b/admin_tests/preprints/test_views.py index 3744bd1d54c..888c9f6bd0d 100644 --- a/admin_tests/preprints/test_views.py +++ b/admin_tests/preprints/test_views.py @@ -5,6 +5,7 @@ from django.core.urlresolvers import reverse from django.core.exceptions import PermissionDenied from django.contrib.auth.models import Permission, Group, AnonymousUser +from django.contrib.messages.storage.fallback import FallbackStorage from tests.base import AdminTestCase from osf.models import Preprint, OSFUser, PreprintLog @@ -14,6 +15,8 @@ PreprintProviderFactory, PreprintRequestFactory, NodeFactory, + SubjectFactory, + ) from osf.models.admin_log_entry import AdminLogEntry from osf.models.spam import SpamStatus @@ -213,6 +216,134 @@ def test_change_preprint_provider_form(self, plain_view, preprint): assert preprint.provider == new_provider + @pytest.fixture + def provider_one(self): + return PreprintProviderFactory() + + @pytest.fixture + def provider_two(self): + return PreprintProviderFactory() + + @pytest.fixture + def provider_osf(self): + return PreprintProviderFactory(_id='osf') + + @pytest.fixture + def preprint_user(self, user): + change_permission = Permission.objects.get(codename='change_preprint') + view_permission = Permission.objects.get(codename='view_preprint') + user.user_permissions.add(change_permission) + user.user_permissions.add(view_permission) + return user + + @pytest.fixture + def subject_osf(self, provider_osf): + return SubjectFactory(provider=provider_osf) + + @pytest.fixture + def subject_one(self, provider_one): + return SubjectFactory(provider=provider_one) + + def test_change_preprint_provider_subjects_custom_taxonomies(self, plain_view, preprint_user, provider_one, provider_two, subject_one): + """ Testing that subjects are changed when providers are changed between two custom taxonomies. + """ + + subject_two = SubjectFactory(provider=provider_two, + bepress_subject=subject_one.bepress_subject) + + preprint = PreprintFactory(subjects=[[subject_one._id]], provider=provider_one, creator=preprint_user) + request = RequestFactory().post(reverse('preprints:preprint', kwargs={'guid': preprint._id}), data={'provider': provider_two.id}) + request.user = preprint_user + response = plain_view.as_view()(request, guid=preprint._id) + + assert response.status_code == 302 + preprint.refresh_from_db() + assert preprint.provider == provider_two + assert subject_two in preprint.subjects.all() + + def test_change_preprint_provider_subjects_from_osf(self, plain_view, preprint_user, provider_one, provider_osf, subject_osf): + """ Testing that subjects are changed when a provider is changed from osf using the bepress subject id of the new subject. + """ + + subject_two = SubjectFactory(provider=provider_one, + bepress_subject=subject_osf) + + preprint = PreprintFactory(subjects=[[subject_osf._id]], provider=provider_osf, creator=preprint_user) + request = RequestFactory().post(reverse('preprints:preprint', kwargs={'guid': preprint._id}), data={'provider': provider_one.id}) + request.user = preprint_user + response = plain_view.as_view()(request, guid=preprint._id) + + assert response.status_code == 302 + preprint.refresh_from_db() + assert preprint.provider == provider_one + assert subject_two in preprint.subjects.all() + + def test_change_preprint_provider_subjects_to_osf(self, plain_view, preprint_user, provider_one, provider_osf, subject_osf): + """ Testing that subjects are changed when providers are changed to osf using the bepress subject id of the old subject + """ + + subject_one = SubjectFactory(provider=provider_one, + bepress_subject=subject_osf) + + preprint = PreprintFactory(subjects=[[subject_one._id]], provider=provider_one, creator=preprint_user) + + request = RequestFactory().post(reverse('preprints:preprint', kwargs={'guid': preprint._id}), data={'provider': provider_osf.id}) + request.user = preprint_user + response = plain_view.as_view()(request, guid=preprint._id) + + assert response.status_code == 302 + preprint.refresh_from_db() + assert preprint.provider == provider_osf + assert subject_osf in preprint.subjects.all() + + def test_change_preprint_provider_subjects_problem_subject(self, plain_view, preprint_user, provider_one, provider_osf, subject_osf): + """ Testing that subjects are changed when providers are changed and theres no related mapping between subjects, the old subject stays in place. + """ + + preprint = PreprintFactory(subjects=[[subject_osf._id]], provider=provider_osf, creator=preprint_user) + request = RequestFactory().post(reverse('preprints:preprint', kwargs={'guid': preprint._id}), data={'provider': provider_one.id}) + request.user = preprint_user + + # django.contrib.messages has a bug which effects unittests + # more info here -> https://code.djangoproject.com/ticket/17971 + setattr(request, 'session', 'session') + messages = FallbackStorage(request) + setattr(request, '_messages', messages) + + response = plain_view.as_view()(request, guid=preprint._id) + + assert response.status_code == 302 + preprint.refresh_from_db() + assert preprint.provider == provider_one + assert subject_osf in preprint.subjects.all() + + def test_change_preprint_provider_subjects_change_permissions(self, plain_view, preprint_user, provider_one, provider_osf, subject_osf): + """ Testing that subjects are changed when providers are changed and theres no related mapping between subjects, the old subject stays in place. + """ + auth_user = AuthUserFactory() + change_permission = Permission.objects.get(codename='change_preprint') + view_permission = Permission.objects.get(codename='view_preprint') + auth_user.user_permissions.add(change_permission) + auth_user.user_permissions.add(view_permission) + + preprint = PreprintFactory(subjects=[[subject_osf._id]], provider=provider_osf, creator=preprint_user) + request = RequestFactory().post(reverse('preprints:preprint', kwargs={'guid': preprint._id}), data={'provider': provider_one.id}) + request.user = auth_user + + # django.contrib.messages has a bug which effects unittests + # more info here -> https://code.djangoproject.com/ticket/17971 + setattr(request, 'session', 'session') + messages = FallbackStorage(request) + setattr(request, '_messages', messages) + + response = plain_view.as_view()(request, guid=preprint._id) + + assert response.status_code == 302 + preprint.refresh_from_db() + assert preprint.provider == provider_one + assert subject_osf in preprint.subjects.all() + + @pytest.mark.urls('admin.base.urls') class TestPreprintFormView: diff --git a/api/base/exceptions.py b/api/base/exceptions.py index 9c4b7085db2..8c73f23ae2e 100644 --- a/api/base/exceptions.py +++ b/api/base/exceptions.py @@ -1,4 +1,4 @@ -import httplib as http +from rest_framework import status as http_status from django.utils.translation import ugettext_lazy as _ from rest_framework import status @@ -164,12 +164,12 @@ def __init__(self, detail=None, attribute=None): class InvalidQueryStringError(JSONAPIParameterException): """Raised when client passes an invalid value to a query string parameter.""" default_detail = 'Query string contains an invalid value.' - status_code = http.BAD_REQUEST + status_code = http_status.HTTP_400_BAD_REQUEST class InvalidFilterOperator(JSONAPIParameterException): """Raised when client passes an invalid operator to a query param filter.""" - status_code = http.BAD_REQUEST + status_code = http_status.HTTP_400_BAD_REQUEST def __init__(self, detail=None, value=None, valid_operators=('eq', 'lt', 'lte', 'gt', 'gte', 'contains', 'icontains')): if value and not detail: @@ -183,7 +183,7 @@ def __init__(self, detail=None, value=None, valid_operators=('eq', 'lt', 'lte', class InvalidFilterValue(JSONAPIParameterException): """Raised when client passes an invalid value to a query param filter.""" - status_code = http.BAD_REQUEST + status_code = http_status.HTTP_400_BAD_REQUEST def __init__(self, detail=None, value=None, field_type=None): if not detail: @@ -199,7 +199,7 @@ def __init__(self, detail=None, value=None, field_type=None): class InvalidFilterError(JSONAPIParameterException): """Raised when client passes an malformed filter in the query string.""" default_detail = _('Query string contains a malformed filter.') - status_code = http.BAD_REQUEST + status_code = http_status.HTTP_400_BAD_REQUEST def __init__(self, detail=None): super(InvalidFilterError, self).__init__(detail=detail, parameter='filter') @@ -208,19 +208,19 @@ def __init__(self, detail=None): class InvalidFilterComparisonType(JSONAPIParameterException): """Raised when client tries to filter on a field that is not a date or number type""" default_detail = _('Comparison operators are only supported for dates and numbers.') - status_code = http.BAD_REQUEST + status_code = http_status.HTTP_400_BAD_REQUEST class InvalidFilterMatchType(JSONAPIParameterException): """Raised when client tries to do a match filter on a field that is not a string or a list""" default_detail = _('Match operators are only supported for strings and lists.') - status_code = http.BAD_REQUEST + status_code = http_status.HTTP_400_BAD_REQUEST class InvalidFilterFieldError(JSONAPIParameterException): """Raised when client tries to filter on a field that is not supported""" default_detail = _('Query contained one or more filters for invalid fields.') - status_code = http.BAD_REQUEST + status_code = http_status.HTTP_400_BAD_REQUEST def __init__(self, detail=None, parameter=None, value=None): if value and not detail: diff --git a/api/base/serializers.py b/api/base/serializers.py index 72386985968..2688347dd2a 100644 --- a/api/base/serializers.py +++ b/api/base/serializers.py @@ -1,6 +1,6 @@ import collections import re -from urlparse import urlparse +from future.moves.urllib.parse import urlparse import furl from django.core.urlresolvers import resolve, reverse, NoReverseMatch diff --git a/api/base/settings/__init__.py b/api/base/settings/__init__.py index 5c6c95a8505..0758997248b 100644 --- a/api/base/settings/__init__.py +++ b/api/base/settings/__init__.py @@ -7,7 +7,7 @@ 'v2/' """ import os -from urlparse import urlparse +from future.moves.urllib.parse import urlparse import warnings import itertools diff --git a/api/base/settings/defaults.py b/api/base/settings/defaults.py index 9c245a52007..80eb4f5964f 100644 --- a/api/base/settings/defaults.py +++ b/api/base/settings/defaults.py @@ -11,7 +11,7 @@ """ import os -from urlparse import urlparse +from future.moves.urllib.parse import urlparse from website import settings as osf_settings BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) diff --git a/api/base/settings/local-dist.py b/api/base/settings/local-dist.py index c6c5d062fac..9235cf419a5 100644 --- a/api/base/settings/local-dist.py +++ b/api/base/settings/local-dist.py @@ -18,7 +18,7 @@ 'nplusone.ext.django.NPlusOneMiddleware', ) DEBUG_TOOLBAR_CONFIG = { - 'SHOW_TOOLBAR_CALLBACK': lambda(_): True, + 'SHOW_TOOLBAR_CALLBACK': lambda _: True, } ALLOWED_HOSTS.append('localhost') diff --git a/api/base/storage.py b/api/base/storage.py index 77fa6dbcfbb..235bdced624 100644 --- a/api/base/storage.py +++ b/api/base/storage.py @@ -1,7 +1,7 @@ from django.core.files.storage import FileSystemStorage from django.utils.encoding import filepath_to_uri from storages.backends.gcloud import GoogleCloudStorage -from urlparse import urljoin +from future.moves.urllib.parse import urljoin from website.settings import DOMAIN diff --git a/api/base/utils.py b/api/base/utils.py index c7a3d6c0bcf..592de335b54 100644 --- a/api/base/utils.py +++ b/api/base/utils.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- -import urllib import furl -import urlparse +from future.moves.urllib.parse import urlunsplit, urlsplit, parse_qs, urlencode from distutils.version import StrictVersion from hashids import Hashids @@ -163,11 +162,11 @@ def default_node_list_permission_queryset(user, model_cls): return qs.annotate(region=F('addons_osfstorage_node_settings__region___id')) def extend_querystring_params(url, params): - scheme, netloc, path, query, _ = urlparse.urlsplit(url) - orig_params = urlparse.parse_qs(query) + scheme, netloc, path, query, _ = urlsplit(url) + orig_params = parse_qs(query) orig_params.update(params) - query = urllib.urlencode(orig_params, True) - return urlparse.urlunsplit([scheme, netloc, path, query, '']) + query = urlencode(orig_params, True) + return urlunsplit([scheme, netloc, path, query, '']) def extend_querystring_if_key_exists(url, request, key): if key in request.query_params.keys(): diff --git a/api/caching/tasks.py b/api/caching/tasks.py index a976cb20a63..54fecd3f50d 100644 --- a/api/caching/tasks.py +++ b/api/caching/tasks.py @@ -1,4 +1,4 @@ -import urlparse +from future.moves.urllib.parse import urlparse import requests import logging @@ -31,8 +31,8 @@ def get_bannable_urls(instance): for host in get_varnish_servers(): # add instance url - varnish_parsed_url = urlparse.urlparse(host) - parsed_absolute_url = urlparse.urlparse(instance.absolute_api_v2_url) + varnish_parsed_url = urlparse(host) + parsed_absolute_url = urlparse(instance.absolute_api_v2_url) url_string = '{scheme}://{netloc}{path}.*'.format( scheme=varnish_parsed_url.scheme, netloc=varnish_parsed_url.netloc, @@ -41,7 +41,7 @@ def get_bannable_urls(instance): bannable_urls.append(url_string) if isinstance(instance, Comment): try: - parsed_target_url = urlparse.urlparse(instance.target.referent.absolute_api_v2_url) + parsed_target_url = urlparse(instance.target.referent.absolute_api_v2_url) except AttributeError: # some referents don't have an absolute_api_v2_url # I'm looking at you NodeWikiPage @@ -56,7 +56,7 @@ def get_bannable_urls(instance): bannable_urls.append(url_string) try: - parsed_root_target_url = urlparse.urlparse(instance.root_target.referent.absolute_api_v2_url) + parsed_root_target_url = urlparse(instance.root_target.referent.absolute_api_v2_url) except AttributeError: # some root_targets don't have an absolute_api_v2_url pass diff --git a/api/citations/utils.py b/api/citations/utils.py index 9331611c69e..c4b781c6cd0 100644 --- a/api/citations/utils.py +++ b/api/citations/utils.py @@ -1,6 +1,6 @@ import os import re -import httplib as http +from rest_framework import status as http_status from citeproc import CitationStylesStyle, CitationStylesBibliography from citeproc import Citation, CitationItem @@ -112,7 +112,7 @@ def apa_reformat(node, cit): # throw error if there is no visible contributor if contributors_list_length == 0: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # handle only one contributor elif contributors_list_length == 1: name = process_name(node, contributors_list[0]) @@ -158,7 +158,7 @@ def mla_reformat(node, cit): # throw error if there is no visible contributor if contributors_list_length == 0: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # handle only one contributor elif contributors_list_length == 1: name = process_name(node, contributors_list[0]) @@ -187,7 +187,7 @@ def chicago_reformat(node, cit): contributors_list_length = len(contributors_list) # throw error if there is no visible contributor if contributors_list_length == 0: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # handle only one contributor elif contributors_list_length == 1: name = process_name(node, contributors_list[0]) diff --git a/api/guids/serializers.py b/api/guids/serializers.py index deb3c05e43a..65fbc5d7ba5 100644 --- a/api/guids/serializers.py +++ b/api/guids/serializers.py @@ -1,4 +1,4 @@ -import urlparse +from future.moves.urllib.parse import urljoin from django.urls import resolve, reverse @@ -73,7 +73,7 @@ def get_absolute_url(self, obj): def get_absolute_html_url(self, obj): if not isinstance(obj.referent, BaseFileNode): return obj.referent.absolute_url - return urlparse.urljoin(website_settings.DOMAIN, '/{}/'.format(obj._id)) + return urljoin(website_settings.DOMAIN, '/{}/'.format(obj._id)) def to_representation(self, obj): if self.context['view'].kwargs.get('is_embedded'): diff --git a/api_tests/alerts/views/test_alerts_detail.py b/api_tests/alerts/views/test_alerts_detail.py index 7689107fbe3..d616b87d3d8 100644 --- a/api_tests/alerts/views/test_alerts_detail.py +++ b/api_tests/alerts/views/test_alerts_detail.py @@ -1,5 +1,5 @@ import pytest -import httplib as http +from rest_framework import status as http_status from api.base.settings.defaults import API_BASE from osf_tests.factories import ( @@ -24,7 +24,7 @@ def test_dismissed_alerts_detail(self, app): # test_alerts_get_success res = app.get(url_alerts_detail, auth=user.auth) - assert res.status_code == http.OK + assert res.status_code == http_status.HTTP_200_OK assert res.json['data']['id'] == alert_id assert res.json['data']['attributes']['location'] == alert_location assert alert_id in res.json['data']['links']['self'] diff --git a/api_tests/alerts/views/test_alerts_list.py b/api_tests/alerts/views/test_alerts_list.py index aa95c2ac2c0..7c30cdb1f58 100644 --- a/api_tests/alerts/views/test_alerts_list.py +++ b/api_tests/alerts/views/test_alerts_list.py @@ -1,5 +1,5 @@ import pytest -import httplib as http +from rest_framework import status as http_status from api.base.settings.defaults import API_BASE from osf_tests.factories import ( @@ -44,20 +44,20 @@ def test_dismissed_alerts_list(self, app, user_one, user_two): # test_user_dismiss_alert res = app.post_json_api(url_alerts_list, params, auth=user_one.auth) - assert res.status_code == http.CREATED + assert res.status_code == http_status.HTTP_201_CREATED assert res.json['data']['id'] == alert_id assert res.json['data']['attributes']['location'] == alert_location # test_alerts_list_read_success res = app.get(url_alerts_list, auth=user_one.auth) - assert res.status_code == http.OK + assert res.status_code == http_status.HTTP_200_OK assert len(res.json['data']) == 4 assert res.json['links']['meta']['total'] == len(res.json['data']) # test_alerts_list_id_filter url = '{}?filter[id]={}'.format(url_alerts_list, alert_id) res = app.get(url, auth=user_one.auth) - assert res.status_code == http.OK + assert res.status_code == http_status.HTTP_200_OK assert len(res.json['data']) == 1 res_data = res.json['data'][0] assert res_data['attributes']['location'] == alert_location @@ -66,7 +66,7 @@ def test_dismissed_alerts_list(self, app, user_one, user_two): # test_alerts_list_location_filter url = '{}?filter[location]={}'.format(url_alerts_list, alert_location) res = app.get(url, auth=user_one.auth) - assert res.status_code == http.OK + assert res.status_code == http_status.HTTP_200_OK assert len(res.json['data']) == 1 res_data = res.json['data'][0] assert res_data['attributes']['location'] == alert_location @@ -74,25 +74,25 @@ def test_dismissed_alerts_list(self, app, user_one, user_two): # test_alerts_create_no_auth_gets_403 res = app.post_json_api(url_alerts_list, params, expect_errors=True) - assert res.status_code == http.UNAUTHORIZED + assert res.status_code == http_status.HTTP_401_UNAUTHORIZED # test_alerts_read_user_only_gets_own_alerts res = app.get(url_alerts_list, auth=user_two.auth) - assert res.status_code == http.OK + assert res.status_code == http_status.HTTP_200_OK assert not res.json['data'] # test_alerts_create_with_bad_data_gets_400 params_empty_id = params.copy() params_empty_id['data']['id'] = '' res = app.post_json_api(url_alerts_list, params_empty_id, auth=user_one.auth, expect_errors=True) - assert res.status_code == http.BAD_REQUEST + assert res.status_code == http_status.HTTP_400_BAD_REQUEST params_empty_loc = params.copy() params_empty_loc['data']['attributes']['location'] = '' res = app.post_json_api(url_alerts_list, params_empty_loc, auth=user_one.auth, expect_errors=True) - assert res.status_code == http.BAD_REQUEST + assert res.status_code == http_status.HTTP_400_BAD_REQUEST params_extra_attr = params.copy() params_extra_attr['data']['attributes']['what'] = 'extra' res = app.post_json_api(url_alerts_list, params_extra_attr, auth=user_one.auth, expect_errors=True) - assert res.status_code == http.BAD_REQUEST + assert res.status_code == http_status.HTTP_400_BAD_REQUEST diff --git a/api_tests/base/test_middleware.py b/api_tests/base/test_middleware.py index 64e7f9ef773..57d780974e0 100644 --- a/api_tests/base/test_middleware.py +++ b/api_tests/base/test_middleware.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from django.http import HttpResponse -from urlparse import urlparse +from future.moves.urllib.parse import urlparse import mock from nose.tools import * # noqa: from rest_framework.test import APIRequestFactory diff --git a/api_tests/base/test_serializers.py b/api_tests/base/test_serializers.py index 7ff4d4d74ce..ccc87c6dc95 100644 --- a/api_tests/base/test_serializers.py +++ b/api_tests/base/test_serializers.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status import importlib import pkgutil import pytest from pytz import utc from datetime import datetime -import urllib +from future.moves.urllib.parse import quote from nose.tools import * # noqa: import re @@ -278,7 +278,7 @@ def test_invalid_related_counts_value_raises_bad_request(self): params={'related_counts': 'fish'}, expect_errors=True ) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) def test_invalid_embed_value_raise_bad_request(self): res = self.app.get( @@ -286,7 +286,7 @@ def test_invalid_embed_value_raise_bad_request(self): params={'embed': 'foo'}, expect_errors=True ) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) assert_equal( res.json['errors'][0]['detail'], 'The following fields are not embeddable: foo' @@ -364,7 +364,7 @@ def test_error_when_requesting_related_counts_for_attribute_field(self): params={'related_counts': 'title'}, expect_errors=True ) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) assert_equal( res.json['errors'][0]['detail'], "Acceptable values for the related_counts query param are 'true', 'false', or any of the relationship fields; got 'title'" @@ -497,11 +497,11 @@ def test_field_with_two_filters(self): ).data['data'] field = data['relationships']['field_with_filters']['links'] assert_in( - urllib.quote('filter[target]=hello', safe='?='), + quote('filter[target]=hello', safe='?='), field['related']['href'] ) assert_in( - urllib.quote('filter[woop]=yea', safe='?='), + quote('filter[woop]=yea', safe='?='), field['related']['href'] ) diff --git a/api_tests/base/test_views.py b/api_tests/base/test_views.py index 2d25730424f..2f738c23c0e 100644 --- a/api_tests/base/test_views.py +++ b/api_tests/base/test_views.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status import pkgutil import mock @@ -131,7 +131,7 @@ def test_unconfirmed_user_gets_error(self): auth=user.auth, expect_errors=True ) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) @mock.patch( 'osf.models.OSFUser.is_disabled', @@ -146,7 +146,7 @@ def test_disabled_user_gets_error(self): auth=user.auth, expect_errors=True ) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) class TestStatusView(ApiTestCase): diff --git a/api_tests/collections/test_views.py b/api_tests/collections/test_views.py index b4dd2face8d..861cba21e81 100644 --- a/api_tests/collections/test_views.py +++ b/api_tests/collections/test_views.py @@ -1,5 +1,5 @@ import pytest -from urlparse import urlparse +from future.moves.urllib.parse import urlparse from django.utils.timezone import now diff --git a/api_tests/comments/views/test_comment_detail.py b/api_tests/comments/views/test_comment_detail.py index 4dc37659713..57ec2d2b2d7 100644 --- a/api_tests/comments/views/test_comment_detail.py +++ b/api_tests/comments/views/test_comment_detail.py @@ -1,6 +1,6 @@ import mock import pytest -from urlparse import urlparse +from future.moves.urllib.parse import urlparse from addons.wiki.tests.factories import WikiFactory from api.base.settings.defaults import API_BASE diff --git a/api_tests/identifiers/views/test_identifier_detail.py b/api_tests/identifiers/views/test_identifier_detail.py index c54c62c8a6f..30f5e315c09 100644 --- a/api_tests/identifiers/views/test_identifier_detail.py +++ b/api_tests/identifiers/views/test_identifier_detail.py @@ -1,5 +1,5 @@ import pytest -from urlparse import urlparse +from future.moves.urllib.parse import urlparse from django.utils import timezone from api.base.settings.defaults import API_BASE diff --git a/api_tests/identifiers/views/test_identifier_list.py b/api_tests/identifiers/views/test_identifier_list.py index 7264c213cfc..2f28a3694c9 100644 --- a/api_tests/identifiers/views/test_identifier_list.py +++ b/api_tests/identifiers/views/test_identifier_list.py @@ -1,7 +1,9 @@ import mock import pytest + +from future.moves.urllib.parse import urlparse + import responses -from urlparse import urlparse from django.utils import timezone from framework.auth.core import Auth diff --git a/api_tests/nodes/serializers/test_serializers.py b/api_tests/nodes/serializers/test_serializers.py index 0856c9fa136..91b7badcb04 100644 --- a/api_tests/nodes/serializers/test_serializers.py +++ b/api_tests/nodes/serializers/test_serializers.py @@ -1,6 +1,6 @@ from dateutil.parser import parse as parse_date import pytest -from urlparse import urlparse +from future.moves.urllib.parse import urlparse from api.base.settings.defaults import API_BASE from api.nodes.serializers import NodeSerializer diff --git a/api_tests/nodes/views/test_node_detail.py b/api_tests/nodes/views/test_node_detail.py index a7fb3d3d5c9..1ac056eaa6a 100644 --- a/api_tests/nodes/views/test_node_detail.py +++ b/api_tests/nodes/views/test_node_detail.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import mock import pytest -from urlparse import urlparse +from future.moves.urllib.parse import urlparse from addons.wiki.tests.factories import WikiFactory, WikiVersionFactory diff --git a/api_tests/nodes/views/test_node_registrations_list.py b/api_tests/nodes/views/test_node_registrations_list.py index 4084af35bc7..ffa15012595 100644 --- a/api_tests/nodes/views/test_node_registrations_list.py +++ b/api_tests/nodes/views/test_node_registrations_list.py @@ -1,5 +1,5 @@ import pytest -from urlparse import urlparse +from future.moves.urllib.parse import urlparse from api.base.settings.defaults import API_BASE from osf_tests.factories import ( diff --git a/api_tests/registrations/views/test_registration_detail.py b/api_tests/registrations/views/test_registration_detail.py index bad641fbbc7..a4653429a91 100644 --- a/api_tests/registrations/views/test_registration_detail.py +++ b/api_tests/registrations/views/test_registration_detail.py @@ -1,7 +1,7 @@ import mock import pytest import datetime -from urlparse import urlparse +from future.moves.urllib.parse import urlparse from rest_framework import exceptions from django.utils import timezone diff --git a/api_tests/registrations/views/test_registration_list.py b/api_tests/registrations/views/test_registration_list.py index 05f7dc777c1..ad85debb3c0 100644 --- a/api_tests/registrations/views/test_registration_list.py +++ b/api_tests/registrations/views/test_registration_list.py @@ -3,7 +3,8 @@ import mock from nose.tools import * # noqa: import pytest -from urlparse import urlparse + +from future.moves.urllib.parse import urlparse from api.base.settings.defaults import API_BASE from api_tests.nodes.views.test_node_draft_registration_list import DraftRegistrationTestCase diff --git a/api_tests/registrations/views/test_withdrawn_registrations.py b/api_tests/registrations/views/test_withdrawn_registrations.py index aeeaca49672..eccdb9909fe 100644 --- a/api_tests/registrations/views/test_withdrawn_registrations.py +++ b/api_tests/registrations/views/test_withdrawn_registrations.py @@ -1,5 +1,5 @@ import pytest -from urlparse import urlparse +from future.moves.urllib.parse import urlparse from api_tests.nodes.views.test_node_contributors_list import NodeCRUDTestCase from api.base.settings.defaults import API_BASE diff --git a/api_tests/users/views/test_user_detail.py b/api_tests/users/views/test_user_detail.py index e6c7ac2d89d..5f06bd45d6d 100644 --- a/api_tests/users/views/test_user_detail.py +++ b/api_tests/users/views/test_user_detail.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import mock import pytest -import urlparse +from future.moves.urllib.parse import urlparse, parse_qs import datetime as dt from django.db import connection, transaction @@ -98,8 +98,8 @@ def test_get(self, app, user_one, user_two, project, view_only_link): res = app.get(url) user_json = res.json['data'] profile_image_url = user_json['links']['profile_image'] - query_dict = urlparse.parse_qs( - urlparse.urlparse(profile_image_url).query) + query_dict = parse_qs( + urlparse(profile_image_url).query) assert int(query_dict.get('s')[0]) == size # test_profile_image_in_links @@ -1188,7 +1188,7 @@ def test_requesting_deactivated_user_returns_410_response_and_meta_info( assert res.json['errors'][0]['meta']['given_name'] == user_one.given_name assert res.json['errors'][0]['meta']['middle_names'] == user_one.middle_names assert res.json['errors'][0]['meta']['full_name'] == user_one.fullname - assert urlparse.urlparse( + assert urlparse( res.json['errors'][0]['meta']['profile_image']).netloc == 'secure.gravatar.com' assert res.json['errors'][0]['detail'] == 'The requested user is no longer available.' diff --git a/api_tests/users/views/test_user_list.py b/api_tests/users/views/test_user_list.py index 1c9d5a1e0ab..b409b6481a0 100644 --- a/api_tests/users/views/test_user_list.py +++ b/api_tests/users/views/test_user_list.py @@ -3,7 +3,7 @@ import mock import pytest import unittest -import urlparse +from future.moves.urllib.parse import urlparse, parse_qs from uuid import UUID from api.base.settings.defaults import API_BASE @@ -250,8 +250,8 @@ def test_users_list_takes_profile_image_size_param( user_json = res.json['data'] for user in user_json: profile_image_url = user['links']['profile_image'] - query_dict = urlparse.parse_qs( - urlparse.urlparse(profile_image_url).query) + query_dict = parse_qs( + urlparse(profile_image_url).query) assert int(query_dict.get('s')[0]) == size def test_users_list_filter_multiple_field(self, app, user_one, user_two): diff --git a/api_tests/utils.py b/api_tests/utils.py index e41437bf444..814e24179ae 100644 --- a/api_tests/utils.py +++ b/api_tests/utils.py @@ -1,5 +1,5 @@ from blinker import ANY -from urlparse import urlparse +from future.moves.urllib.parse import urlparse from contextlib import contextmanager from addons.osfstorage import settings as osfstorage_settings diff --git a/api_tests/wikis/views/test_wiki_detail.py b/api_tests/wikis/views/test_wiki_detail.py index 30eb2ea62eb..0c94f913e5b 100644 --- a/api_tests/wikis/views/test_wiki_detail.py +++ b/api_tests/wikis/views/test_wiki_detail.py @@ -3,7 +3,7 @@ import furl import pytz import datetime -from urlparse import urlparse +from future.moves.urllib.parse import urlparse from nose.tools import * # noqa: from addons.wiki.models import WikiPage diff --git a/api_tests/wikis/views/test_wiki_version_detail.py b/api_tests/wikis/views/test_wiki_version_detail.py index c92116b04ab..0cf0c91575a 100644 --- a/api_tests/wikis/views/test_wiki_version_detail.py +++ b/api_tests/wikis/views/test_wiki_version_detail.py @@ -2,7 +2,7 @@ import furl import datetime import pytz -from urlparse import urlparse +from future.moves.urllib.parse import urlparse from nose.tools import * # noqa: from api.base.settings.defaults import API_BASE diff --git a/framework/auth/cas.py b/framework/auth/cas.py index 90df58c20b6..f7aad03b838 100644 --- a/framework/auth/cas.py +++ b/framework/auth/cas.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- import furl -import httplib as http +from rest_framework import status as http_status import json -import urllib +from future.moves.urllib.parse import quote from lxml import etree import requests @@ -40,7 +40,7 @@ class CasTokenError(CasError): """Raised if an invalid token is passed by the client.""" def __init__(self, message): - super(CasTokenError, self).__init__(http.BAD_REQUEST, message) + super(CasTokenError, self).__init__(http_status.HTTP_400_BAD_REQUEST, message) class CasResponse(object): @@ -224,7 +224,7 @@ def get_login_url(*args, **kwargs): def get_institution_target(redirect_url): - return '/login?service={}&auto=true'.format(urllib.quote(redirect_url, safe='~()*!.\'')) + return '/login?service={}&auto=true'.format(quote(redirect_url, safe='~()*!.\'')) def get_logout_url(*args, **kwargs): diff --git a/framework/auth/decorators.py b/framework/auth/decorators.py index a7970077f60..19ad6a00a65 100644 --- a/framework/auth/decorators.py +++ b/framework/auth/decorators.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import time -import httplib +from rest_framework import status as http_status import functools from flask import request @@ -25,7 +25,7 @@ def wrapped(*args, **kwargs): user_agent = request.headers.get('User-Agent') if user_agent and ('BingPreview' in user_agent or 'MSIE 9.0' in user_agent): return HTTPError( - httplib.FORBIDDEN, + http_status.HTTP_403_FORBIDDEN, data={'message_long': 'Internet Explorer 9 and BingPreview cannot be used to access this page for security reasons. Please use another browser. If this should not have occurred and the issue persists, please report it to ' + settings.OSF_SUPPORT_EMAIL + '.'} ) return func(*args, **kwargs) @@ -54,12 +54,12 @@ def wrapped(*args, **kwargs): if user.is_confirmed: return func(*args, **kwargs) else: - raise HTTPError(httplib.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Account not yet confirmed', 'message_long': 'The profile page could not be displayed as the user has not confirmed the account.' }) else: - raise HTTPError(httplib.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) return wrapped @@ -95,16 +95,16 @@ def wrapped(*args, **kwargs): payload = signing.unserialize_payload(data['payload']) exp_time = payload['time'] except (KeyError, ValueError): - raise HTTPError(httplib.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Invalid payload', 'message_long': 'The request payload could not be deserialized.' }) if not signing.default_signer.verify_payload(sig, payload): - raise HTTPError(httplib.UNAUTHORIZED) + raise HTTPError(http_status.HTTP_401_UNAUTHORIZED) if time.time() > exp_time: - raise HTTPError(httplib.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Expired', 'message_long': 'Signature has expired.' }) diff --git a/framework/auth/utils.py b/framework/auth/utils.py index ccc27a62d30..ac6ca46383e 100644 --- a/framework/auth/utils.py +++ b/framework/auth/utils.py @@ -1,4 +1,4 @@ -import httplib +from rest_framework import status as http_status import re from nameparser.parser import HumanName @@ -128,7 +128,7 @@ def validate_recaptcha(response, remote_ip=None): if remote_ip: payload.update({'remoteip': remote_ip}) resp = requests.post(settings.RECAPTCHA_VERIFY_URL, data=payload) - return resp.status_code == httplib.OK and resp.json().get('success') + return resp.status_code == http_status.HTTP_200_OK and resp.json().get('success') def generate_csl_given_name(given_name, middle_names='', suffix=''): diff --git a/framework/auth/views.py b/framework/auth/views.py index 9e1bfeb4e24..79763936ce7 100644 --- a/framework/auth/views.py +++ b/framework/auth/views.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import furl -import httplib as http -import urllib +from rest_framework import status as http_status +from future.moves.urllib.parse import urlencode import markupsafe from django.core.exceptions import ValidationError @@ -47,7 +47,7 @@ def reset_password_get(auth, uid=None, token=None): :param uid: the user id :param token: the token in verification key :return - :raises: HTTPError(http.BAD_REQUEST) if verification key for the user is invalid, has expired or was used + :raises: HTTPError(http_status.HTTP_400_BAD_REQUEST) if verification key for the user is invalid, has expired or was used """ # if users are logged in, log them out and redirect back to this page @@ -61,7 +61,7 @@ def reset_password_get(auth, uid=None, token=None): 'message_short': 'Invalid Request.', 'message_long': 'The requested URL is invalid, has expired, or was already used', } - raise HTTPError(http.BAD_REQUEST, data=error_data) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=error_data) # refresh the verification key (v2) user_obj.verification_key_v2 = generate_verification_key(verification_type='password') @@ -85,7 +85,7 @@ def reset_password_post(uid=None, token=None): :param uid: the user id :param token: the token in verification key :return: - :raises: HTTPError(http.BAD_REQUEST) if verification key for the user is invalid, has expired or was used + :raises: HTTPError(http_status.HTTP_400_BAD_REQUEST) if verification key for the user is invalid, has expired or was used """ form = ResetPasswordForm(request.form) @@ -97,7 +97,7 @@ def reset_password_post(uid=None, token=None): 'message_short': 'Invalid Request.', 'message_long': 'The requested URL is invalid, has expired, or was already used', } - raise HTTPError(http.BAD_REQUEST, data=error_data) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=error_data) if not form.validate(): # Don't go anywhere @@ -211,15 +211,15 @@ def login_and_register_handler(auth, login=True, campaign=None, next_url=None, l :param next_url: the service url for CAS login or redirect url for OSF :param logout: used only for `claim_user_registered` :return: data object that contains actions for `auth_register` and `auth_login` - :raises: http.BAD_REQUEST + :raises: http_status.HTTP_400_BAD_REQUEST """ # Only allow redirects which are relative root or full domain. Disallows external redirects. if next_url and not validate_next_url(next_url): - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) data = { - 'status_code': http.FOUND if login else http.OK, + 'status_code': http_status.HTTP_302_FOUND if login else http_status.HTTP_200_OK, 'next_url': next_url, 'campaign': None, 'must_login_warning': False, @@ -233,7 +233,7 @@ def login_and_register_handler(auth, login=True, campaign=None, next_url=None, l if campaign == 'institution': if next_url is None: next_url = web_url_for('dashboard', _absolute=True) - data['status_code'] = http.FOUND + data['status_code'] = http_status.HTTP_302_FOUND if auth.logged_in: data['next_url'] = next_url else: @@ -243,7 +243,7 @@ def login_and_register_handler(auth, login=True, campaign=None, next_url=None, l destination = next_url if next_url else campaigns.campaign_url_for(campaign) if auth.logged_in: # if user is already logged in, go to the campaign landing page - data['status_code'] = http.FOUND + data['status_code'] = http_status.HTTP_302_FOUND data['next_url'] = destination else: # if user is logged out, go to the osf register page with campaign context @@ -264,7 +264,7 @@ def login_and_register_handler(auth, login=True, campaign=None, next_url=None, l else: # invalid campaign, inform sentry and redirect to non-campaign sign up or sign in redirect_view = 'auth_login' if login else 'auth_register' - data['status_code'] = http.FOUND + data['status_code'] = http_status.HTTP_302_FOUND data['next_url'] = web_url_for(redirect_view, campaigns=None, next=next_url) data['campaign'] = None sentry.log_message( @@ -281,24 +281,24 @@ def login_and_register_handler(auth, login=True, campaign=None, next_url=None, l data['status_code'] = 'auth_logout' else: # after logout, land on the register page with "must_login" warning - data['status_code'] = http.OK + data['status_code'] = http_status.HTTP_200_OK data['must_login_warning'] = True elif auth.logged_in: # if user is already logged in, redirect to `next_url` - data['status_code'] = http.FOUND + data['status_code'] = http_status.HTTP_302_FOUND data['next_url'] = next_url elif login: # `/login?next=next_url`: go to CAS login page with current request url as service url - data['status_code'] = http.FOUND + data['status_code'] = http_status.HTTP_302_FOUND data['next_url'] = cas.get_login_url(request.url) else: # `/register?next=next_url`: land on OSF register page with request url as next url - data['status_code'] = http.OK + data['status_code'] = http_status.HTTP_200_OK data['next_url'] = request.url else: # `/login/` or `/register/` without any parameter if auth.logged_in: - data['status_code'] = http.FOUND + data['status_code'] = http_status.HTTP_302_FOUND data['next_url'] = web_url_for('dashboard', _absolute=True) return data @@ -326,7 +326,7 @@ def auth_login(auth): next_url = request.args.get('next') data = login_and_register_handler(auth, login=True, campaign=campaign, next_url=next_url) - if data['status_code'] == http.FOUND: + if data['status_code'] == http_status.HTTP_302_FOUND: return redirect(data['next_url']) @@ -348,7 +348,7 @@ def auth_register(auth): :param auth: the auth context :return: land, redirect or `auth_logout` - :raise: http.BAD_REQUEST + :raise: http_status.HTTP_400_BAD_REQUEST """ context = {} @@ -361,12 +361,12 @@ def auth_register(auth): # logout must have next_url if logout and not next_url: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) data = login_and_register_handler(auth, login=False, campaign=campaign, next_url=next_url, logout=logout) # land on register page - if data['status_code'] == http.OK: + if data['status_code'] == http_status.HTTP_200_OK: if data['must_login_warning']: status.push_status_message(language.MUST_LOGIN, trust=False) destination = cas.get_login_url(data['next_url']) @@ -382,15 +382,15 @@ def auth_register(auth): 'logo_path': k.get_asset_url('square_color_no_transparent') } for k in PreprintProvider.objects.all() if k._id != 'osf'} context['campaign'] = data['campaign'] - return context, http.OK + return context, http_status.HTTP_200_OK # redirect to url - elif data['status_code'] == http.FOUND: + elif data['status_code'] == http_status.HTTP_302_FOUND: return redirect(data['next_url']) # go to other views elif data['status_code'] == 'auth_logout': return auth_logout(redirect_url=data['next_url']) - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) @collect_auth def auth_logout(auth, redirect_url=None, next_url=None): @@ -458,13 +458,13 @@ def auth_email_logout(token, user): try: unconfirmed_email = user.get_unconfirmed_email_for_token(token) except InvalidTokenError: - raise HTTPError(http.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Bad token', 'message_long': 'The provided token is invalid.' }) except ExpiredTokenError: status.push_status_message('The private link you used is expired.') - raise HTTPError(http.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Expired link', 'message_long': 'The private link you used is expired.' }) @@ -500,11 +500,11 @@ def external_login_confirm_email_get(auth, uid, token): user = OSFUser.load(uid) if not user: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) destination = request.args.get('destination') if not destination: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # if user is already logged in if auth and auth.user: @@ -525,20 +525,20 @@ def external_login_confirm_email_get(auth, uid, token): # token is invalid if token not in user.email_verifications: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) verification = user.email_verifications[token] email = verification['email'] provider = verification['external_identity'].keys()[0] provider_id = verification['external_identity'][provider].keys()[0] # wrong provider if provider not in user.external_identity: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) external_status = user.external_identity[provider][provider_id] try: ensure_external_identity_uniqueness(provider, provider_id, user) except ValidationError as e: - raise HTTPError(http.FORBIDDEN, e.message) + raise HTTPError(http_status.HTTP_403_FORBIDDEN, e.message) if not user.is_registered: user.register(email) @@ -565,7 +565,7 @@ def external_login_confirm_email_get(auth, uid, token): osf_support_email=settings.OSF_SUPPORT_EMAIL, storage_flag_is_active=storage_i18n_flag_active(), ) - service_url += '&{}'.format(urllib.urlencode({'new': 'true'})) + service_url += '&{}'.format(urlencode({'new': 'true'})) elif external_status == 'LINK': mails.send_mail( user=user, @@ -600,7 +600,7 @@ def confirm_email_get(token, auth=None, **kwargs): else: user = OSFUser.objects.filter(guids___id=kwargs['uid'], guids___id__isnull=False).select_for_update().get() except OSFUser.DoesNotExist: - raise HTTPError(http.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) is_initial_confirmation = not user.date_confirmed log_out = request.args.get('logout', None) @@ -629,7 +629,7 @@ def confirm_email_get(token, auth=None, **kwargs): try: user.confirm_email(token, merge=is_merge) except exceptions.EmailConfirmTokenError as e: - raise HTTPError(http.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': e.message_short, 'message_long': e.message_long }) @@ -672,7 +672,7 @@ def unconfirmed_email_remove(auth=None): try: given_token = json_body['token'] except KeyError: - raise HTTPError(http.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Missing token', 'message_long': 'Must provide a token' }) @@ -695,19 +695,19 @@ def unconfirmed_email_add(auth=None): try: token = json_body['token'] except KeyError: - raise HTTPError(http.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Missing token', 'message_long': 'Must provide a token' }) try: user.confirm_email(token, merge=True) except exceptions.InvalidTokenError: - raise InvalidTokenError(http.BAD_REQUEST, data={ + raise InvalidTokenError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Invalid user token', 'message_long': 'The user token is invalid' }) except exceptions.EmailConfirmTokenError as e: - raise HTTPError(http.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': e.message_short, 'message_long': e.message_long }) @@ -803,7 +803,7 @@ def register_user(**kwargs): :param-json str fullName: :param-json str campaign: - :raises: HTTPError(http.BAD_REQUEST) if validation fails or user already exists + :raises: HTTPError(http_status.HTTP_400_BAD_REQUEST) if validation fails or user already exists """ # Verify that email address match. @@ -812,14 +812,14 @@ def register_user(**kwargs): json_data = request.get_json() if str(json_data['email1']).lower() != str(json_data['email2']).lower(): raise HTTPError( - http.BAD_REQUEST, + http_status.HTTP_400_BAD_REQUEST, data=dict(message_long='Email addresses must match.') ) # Verify that captcha is valid if settings.RECAPTCHA_SITE_KEY and not validate_recaptcha(json_data.get('g-recaptcha-response'), remote_ip=request.remote_addr): raise HTTPError( - http.BAD_REQUEST, + http_status.HTTP_400_BAD_REQUEST, data=dict(message_long='Invalid Captcha') ) @@ -842,7 +842,7 @@ def register_user(**kwargs): framework_auth.signals.user_registered.send(user) except (ValidationValueError, DuplicateEmailError): raise HTTPError( - http.CONFLICT, + http_status.HTTP_409_CONFLICT, data=dict( message_long=language.ALREADY_REGISTERED.format( email=markupsafe.escape(request.json['email1']) @@ -851,12 +851,12 @@ def register_user(**kwargs): ) except BlacklistedEmailError as e: raise HTTPError( - http.BAD_REQUEST, + http_status.HTTP_400_BAD_REQUEST, data=dict(message_long=language.BLACKLISTED_EMAIL) ) except ValidationError as e: raise HTTPError( - http.BAD_REQUEST, + http_status.HTTP_400_BAD_REQUEST, data=dict(message_long=e.message) ) @@ -936,7 +936,7 @@ def external_login_email_get(): form = ResendConfirmationForm(request.form) session = get_session() if not session.is_external_first_login: - raise HTTPError(http.UNAUTHORIZED) + raise HTTPError(http_status.HTTP_401_UNAUTHORIZED) external_id_provider = session.data['auth_user_external_id_provider'] auth_user_fullname = session.data.get('auth_user_fullname') @@ -957,7 +957,7 @@ def external_login_email_post(): form = ResendConfirmationForm(request.form) session = get_session() if not session.is_external_first_login: - raise HTTPError(http.UNAUTHORIZED) + raise HTTPError(http_status.HTTP_401_UNAUTHORIZED) external_id_provider = session.data['auth_user_external_id_provider'] external_id = session.data['auth_user_external_id'] @@ -997,7 +997,7 @@ def external_login_email_post(): try: ensure_external_identity_uniqueness(external_id_provider, external_id, user) except ValidationError as e: - raise HTTPError(http.FORBIDDEN, e.message) + raise HTTPError(http_status.HTTP_403_FORBIDDEN, e.message) if user: # 1. update user oauth, with pending status external_identity[external_id_provider][external_id] = 'LINK' diff --git a/framework/database/__init__.py b/framework/database/__init__.py index ca46565110b..22eb98220b8 100644 --- a/framework/database/__init__.py +++ b/framework/database/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- import functools -import httplib as http +from rest_framework import status as http_status import markupsafe from django.core.paginator import Paginator @@ -37,17 +37,17 @@ def get_or_http_error(Model, pk_or_query, allow_deleted=False, display_name=None try: instance = Model.objects.filter(pk_or_query).select_for_update().get() if select_for_update else Model.objects.get(pk_or_query) except Model.DoesNotExist: - raise HTTPError(http.NOT_FOUND, data=dict( + raise HTTPError(http_status.HTTP_404_NOT_FOUND, data=dict( message_long='No {name} record matching that query could be found'.format(name=safe_name) )) except Model.MultipleObjectsReturned: - raise HTTPError(http.BAD_REQUEST, data=dict( + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_long='The query must match exactly one {name} record'.format(name=safe_name) )) else: instance = Model.load(pk_or_query, select_for_update=select_for_update) if not instance: - raise HTTPError(http.NOT_FOUND, data=dict( + raise HTTPError(http_status.HTTP_404_NOT_FOUND, data=dict( message_long='No {name} record with that primary key could be found'.format(name=safe_name) )) if getattr(instance, 'is_deleted', False) and getattr(instance, 'suspended', False): @@ -56,7 +56,7 @@ def get_or_http_error(Model, pk_or_query, allow_deleted=False, display_name=None message_long='This content has been removed' )) if not allow_deleted and getattr(instance, 'is_deleted', False): - raise HTTPError(http.GONE) + raise HTTPError(http_status.HTTP_410_GONE) return instance diff --git a/framework/exceptions/__init__.py b/framework/exceptions/__init__.py index d110382543d..cb9c4d6f09b 100644 --- a/framework/exceptions/__init__.py +++ b/framework/exceptions/__init__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """Custom exceptions for the framework.""" import copy -import httplib as http +from rest_framework import status as http_status from flask import request from website import language @@ -13,30 +13,30 @@ class FrameworkError(Exception): class HTTPError(FrameworkError): error_msgs = { - http.BAD_REQUEST: { + http_status.HTTP_400_BAD_REQUEST: { 'message_short': 'Bad request', 'message_long': ('If this should not have occurred and the issue persists, ' + language.SUPPORT_LINK), }, - http.FORBIDDEN: { + http_status.HTTP_403_FORBIDDEN: { 'message_short': 'Forbidden', 'message_long': ('You do not have permission to perform this action. ' 'If this should not have occurred and the issue persists, ' + language.SUPPORT_LINK), }, - http.NOT_FOUND: { + http_status.HTTP_404_NOT_FOUND: { 'message_short': 'Page not found', 'message_long': ('The requested resource could not be found. If this ' 'should not have occurred and the issue persists, ' + language.SUPPORT_LINK), }, - http.GONE: { + http_status.HTTP_410_GONE: { 'message_short': 'Resource deleted', 'message_long': ('User has deleted this content. If this should ' 'not have occurred and the issue persists, ' + language.SUPPORT_LINK), }, - http.SERVICE_UNAVAILABLE: { + http_status.HTTP_503_SERVICE_UNAVAILABLE: { 'message_short': 'Service is currently unavailable', 'message_long': ('The requested service is unavailable. If this ' 'should not have occurred and the issue persists, ' @@ -80,7 +80,7 @@ def to_data(self): 'message_short': self.error_msgs[self.code]['message_short'], 'message_long': self.error_msgs[self.code]['message_long'] } - elif self.code == http.UNAUTHORIZED: + elif self.code == http_status.HTTP_401_UNAUTHORIZED: data = { 'message_short': 'Unauthorized', 'message_long': 'You must log in to access this resource.'.format(request.url), diff --git a/framework/forms/utils.py b/framework/forms/utils.py index e26d8da7c1c..d7a1f50ab10 100644 --- a/framework/forms/utils.py +++ b/framework/forms/utils.py @@ -1,5 +1,5 @@ import bleach -import urllib +from future.moves.urllib.parse import quote, unquote # TODO: Test me @jmcarp @@ -24,14 +24,14 @@ def process_data(data, func): def process_payload(data): return process_data( data, - lambda value: urllib.quote(value.encode('utf-8') if value else '', safe=' ') + lambda value: quote(value.encode('utf-8') if value else '', safe=' ') ) def unprocess_payload(data): return process_data( data, - lambda value: urllib.unquote(value.encode('utf-8') if value else '') + lambda value: unquote(value.encode('utf-8') if value else '') ) diff --git a/framework/routing/__init__.py b/framework/routing/__init__.py index 233e7468faa..b908dd20273 100644 --- a/framework/routing/__init__.py +++ b/framework/routing/__init__.py @@ -2,7 +2,7 @@ import copy import functools -import httplib as http +from rest_framework import status as http_status import json import logging import os @@ -53,8 +53,8 @@ ) REDIRECT_CODES = [ - http.MOVED_PERMANENTLY, - http.FOUND, + http_status.HTTP_301_MOVED_PERMANENTLY, + http_status.HTTP_302_FOUND, ] class Rule(object): @@ -124,7 +124,7 @@ def wrapped(*args, **kwargs): if debug_mode: raise data = HTTPError( - http.INTERNAL_SERVER_ERROR, + http_status.HTTP_500_INTERNAL_SERVER_ERROR, message=repr(error), ) return renderer(data, **renderer_kwargs or {}) diff --git a/framework/sessions/__init__.py b/framework/sessions/__init__.py index d0ef59f7790..3c85a860fd5 100644 --- a/framework/sessions/__init__.py +++ b/framework/sessions/__init__.py @@ -1,8 +1,7 @@ # -*- coding: utf-8 -*- import datetime as dt -import httplib as http -import urllib -import urlparse +from rest_framework import status as http_status +from future.moves.urllib.parse import urlparse, parse_qs, urlunparse, urlencode from django.apps import apps from django.utils import timezone @@ -24,12 +23,12 @@ def add_key_to_url(url, scheme, key): query = request.args.to_dict() query['view_only'] = key - replacements = {'query': urllib.urlencode(query)} + replacements = {'query': urlencode(query)} if scheme: replacements['scheme'] = scheme - parsed_url = urlparse.urlparse(url) + parsed_url = urlparse(url) if parsed_url.fragment: # Fragments should exists server side so this mean some one set up a # in the url @@ -38,7 +37,7 @@ def add_key_to_url(url, scheme, key): replacements['fragment'] = '' parsed_redirect_url = parsed_url._replace(**replacements) - return urlparse.urlunparse(parsed_redirect_url) + return urlunparse(parsed_redirect_url) def prepare_private_key(): @@ -60,9 +59,9 @@ def prepare_private_key(): # Grab query key from previous request for not logged-in users if request.referrer: - referrer_parsed = urlparse.urlparse(request.referrer) + referrer_parsed = urlparse(request.referrer) scheme = referrer_parsed.scheme - key = urlparse.parse_qs(urlparse.urlparse(request.referrer).query).get('view_only') + key = parse_qs(urlparse(request.referrer).query).get('view_only') if key: key = key[0] else: @@ -72,7 +71,7 @@ def prepare_private_key(): # Update URL and redirect if key and not session.is_authenticated: new_url = add_key_to_url(request.url, scheme, key) - return redirect(new_url, code=http.TEMPORARY_REDIRECT) + return redirect(new_url, code=http_status.HTTP_307_TEMPORARY_REDIRECT) def get_session(): @@ -144,7 +143,7 @@ def before_request(): otp = request.headers.get('X-OSF-OTP') if otp is None or not user_addon.verify_code(otp): # Must specify two-factor authentication OTP code or invalid two-factor authentication OTP code. - user_session.data['auth_error_code'] = http.UNAUTHORIZED + user_session.data['auth_error_code'] = http_status.HTTP_401_UNAUTHORIZED return user_session.data['auth_user_username'] = user.username user_session.data['auth_user_fullname'] = user.fullname @@ -153,7 +152,7 @@ def before_request(): user_session.save() else: # Invalid key: Not found in database - user_session.data['auth_error_code'] = http.UNAUTHORIZED + user_session.data['auth_error_code'] = http_status.HTTP_401_UNAUTHORIZED return cookie = request.cookies.get(settings.COOKIE_NAME) diff --git a/framework/transactions/handlers.py b/framework/transactions/handlers.py index f46f92509e3..6c136b5d873 100644 --- a/framework/transactions/handlers.py +++ b/framework/transactions/handlers.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -import httplib +from rest_framework import status as http_status import logging from framework.exceptions import HTTPError @@ -9,7 +9,7 @@ from werkzeug.local import LocalProxy -LOCK_ERROR_CODE = httplib.BAD_REQUEST +LOCK_ERROR_CODE = http_status.HTTP_400_BAD_REQUEST NO_AUTO_TRANSACTION_ATTR = '_no_auto_transaction' logger = logging.getLogger(__name__) diff --git a/osf/management/commands/force_archive.py b/osf/management/commands/force_archive.py index d066830a050..a51fb8c68fb 100644 --- a/osf/management/commands/force_archive.py +++ b/osf/management/commands/force_archive.py @@ -19,7 +19,7 @@ from __future__ import unicode_literals from copy import deepcopy -import httplib as http +from rest_framework import status as http_status import json import logging import requests @@ -161,7 +161,7 @@ def perform_wb_copy(reg, node_settings): } url = waterbutler_api_url_for(src._id, node_settings.short_name, _internal=True, base_url=src.osfstorage_region.waterbutler_url, **params) res = requests.post(url, data=json.dumps(data)) - if res.status_code not in (http.OK, http.CREATED, http.ACCEPTED): + if res.status_code not in (http_status.HTTP_200_OK, http_status.HTTP_201_CREATED, http_status.HTTP_202_ACCEPTED): raise HTTPError(res.status_code) def manually_archive(tree, reg, node_settings, parent=None): diff --git a/osf/migrations/0107_add_dependent_styles.py b/osf/migrations/0107_add_dependent_styles.py index 38bde524b92..1311b833a9b 100644 --- a/osf/migrations/0107_add_dependent_styles.py +++ b/osf/migrations/0107_add_dependent_styles.py @@ -5,7 +5,7 @@ from lxml import etree from website import settings -from urlparse import urlparse +from future.moves.urllib.parse import urlparse logger = logging.getLogger(__file__) diff --git a/osf/models/external.py b/osf/models/external.py index b22f4a3fb5f..3c6b03a14b3 100644 --- a/osf/models/external.py +++ b/osf/models/external.py @@ -1,7 +1,7 @@ import abc import datetime as dt import functools -import httplib as http +from rest_framework import status as http_status import logging from django.contrib.postgres.fields import ArrayField @@ -21,6 +21,7 @@ from website.security import random_string from website.settings import ADDONS_OAUTH_NO_REDIRECT from website.util import web_url_for +from future.utils import with_metaclass logger = logging.getLogger(__name__) @@ -96,7 +97,7 @@ def __init__(cls, name, bases, dct): PROVIDER_LOOKUP[cls.short_name] = cls -class ExternalProvider(object): +class ExternalProvider(with_metaclass(ExternalProviderMeta)): """A connection to an external service (ex: GitHub). This object contains no credentials, and is not saved in the database. @@ -109,8 +110,6 @@ class ExternalProvider(object): and ``ExternalAccount`` instances are stored within a single collection. """ - __metaclass__ = ExternalProviderMeta - # Default to OAuth v2.0. _oauth_version = OAUTH2 @@ -283,7 +282,7 @@ def auth_callback(self, user, **kwargs): code=request.args.get('code'), ) except (MissingTokenError, RequestsHTTPError): - raise HTTPError(http.SERVICE_UNAVAILABLE) + raise HTTPError(http_status.HTTP_503_SERVICE_UNAVAILABLE) # pre-set as many values as possible for the ``ExternalAccount`` info = self._default_handle_callback(response) # call the hook for subclasses to parse values from the response diff --git a/osf/models/institution.py b/osf/models/institution.py index 60158abb160..98338486a3a 100644 --- a/osf/models/institution.py +++ b/osf/models/institution.py @@ -1,5 +1,5 @@ import logging -import urlparse +from future.moves.urllib.parse import urljoin from dirtyfields import DirtyFieldsMixin @@ -74,7 +74,7 @@ def api_v2_url(self): @property def absolute_url(self): - return urlparse.urljoin(website_settings.DOMAIN, 'institutions/{}/'.format(self._id)) + return urljoin(website_settings.DOMAIN, 'institutions/{}/'.format(self._id)) @property def absolute_api_v2_url(self): diff --git a/osf/models/mixins.py b/osf/models/mixins.py index 3f06ba6da20..62cc18ffbd0 100644 --- a/osf/models/mixins.py +++ b/osf/models/mixins.py @@ -818,7 +818,8 @@ def set_subjects(self, new_subjects, auth, add_log=True): :return: None """ - self.check_subject_perms(auth) + if auth: + self.check_subject_perms(auth) self.assert_subject_format(new_subjects, expect_list=True, error_msg='Expecting list of lists.') old_subjects = list(self.subjects.values_list('id', flat=True)) @@ -863,6 +864,41 @@ def set_subjects_from_relationships(self, subjects_list, auth, add_log=True): self.save(old_subjects=old_subjects) + def map_subjects_between_providers(self, old_provider, new_provider, auth=None): + """ + Maps subjects between preprint providers using bepress_subject_id. + + Loops through each subject hierarchy for a resource and attempts to find + a matching subject for the lowest tier subject. + + :params old_provider PreprintProvider + :params new_provider PreprintProvider + :params auth Authenticated User + + returns a list of any subjects that could not be mapped. + """ + new_subjects = [] + subject_problems = [] + for hierarchy in self.subject_hierarchy: + subject = hierarchy[-1] + current_bepress_id = getattr( + hierarchy[-1], + self.get_bepress_id_field(old_provider) + ) + try: + new_subject = new_provider.subjects.get(**{ + self.get_bepress_id_field(new_provider): current_bepress_id + }) + except Subject.DoesNotExist: + new_subject = subject + subject_problems.append(subject.text) + new_subjects.append(new_subject.hierarchy) + self.set_subjects(new_subjects, auth, add_log=False) + return subject_problems + + def get_bepress_id_field(self, provider): + return 'id' if provider._id == 'osf' else 'bepress_subject_id' + class ContributorMixin(models.Model): """ diff --git a/osf/models/node.py b/osf/models/node.py index 0473195bb2f..f5cb9735edd 100644 --- a/osf/models/node.py +++ b/osf/models/node.py @@ -2,9 +2,9 @@ import itertools import logging import re -import urlparse +from future.moves.urllib.parse import urljoin import warnings -import httplib +from rest_framework import status as http_status import bson from django.db.models import Q @@ -531,7 +531,7 @@ def absolute_api_v2_url(self): def absolute_url(self): if not self.url: return None - return urlparse.urljoin(settings.DOMAIN, self.url) + return urljoin(settings.DOMAIN, self.url) @property def deep_url(self): @@ -2372,10 +2372,10 @@ def create_waterbutler_log(self, auth, action, payload): metadata = payload['metadata'] node_addon = self.get_addon(payload['provider']) except KeyError: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) if node_addon is None: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) metadata['path'] = metadata['path'].lstrip('/') diff --git a/osf/models/oauth.py b/osf/models/oauth.py index de54e780b48..33b74d7b733 100644 --- a/osf/models/oauth.py +++ b/osf/models/oauth.py @@ -1,4 +1,3 @@ -import urlparse import uuid from website.util import api_v2_url @@ -100,7 +99,7 @@ def url(self): @property def absolute_url(self): - return urlparse.urljoin(settings.DOMAIN, self.url) + return urljoin(settings.DOMAIN, self.url) # Properties used by Django and DRF "Links: self" field @property @@ -164,7 +163,7 @@ def url(self): @property def absolute_url(self): - return urlparse.urljoin(settings.DOMAIN, self.url) + return urljoin(settings.DOMAIN, self.url) # Properties used by Django and DRF "Links: self" field @property diff --git a/osf/models/preprint.py b/osf/models/preprint.py index 423fa82820c..945a964f5da 100644 --- a/osf/models/preprint.py +++ b/osf/models/preprint.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- import functools -import urlparse +from future.moves.urllib.parse import urljoin import logging import re @@ -323,7 +323,7 @@ def url(self): @property def absolute_url(self): - return urlparse.urljoin( + return urljoin( self.provider.domain if self.provider.domain_redirect_enabled else settings.DOMAIN, self.url ) diff --git a/osf/models/registrations.py b/osf/models/registrations.py index f725eb96799..bb104dbe35b 100644 --- a/osf/models/registrations.py +++ b/osf/models/registrations.py @@ -1,6 +1,6 @@ import logging import datetime -import urlparse +from future.moves.urllib.parse import urljoin from django.core.exceptions import ValidationError from django.db import models @@ -527,7 +527,7 @@ def url(self): @property def absolute_url(self): - return urlparse.urljoin(settings.DOMAIN, self.url) + return urljoin(settings.DOMAIN, self.url) @property def absolute_api_v2_url(self): diff --git a/osf/models/sanctions.py b/osf/models/sanctions.py index 879d380fc58..b640912134b 100644 --- a/osf/models/sanctions.py +++ b/osf/models/sanctions.py @@ -1,6 +1,6 @@ import pytz import functools -import httplib as http +from rest_framework import status as http_status from dateutil.parser import parse as parse_date from django.apps import apps @@ -495,7 +495,7 @@ def reject(self, user, token): reg = self._get_registration() if reg.is_public: raise HTTPError( - http.BAD_REQUEST, + http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Registrations cannot be modified', 'message_long': 'This project has already been registered and cannot be deleted', diff --git a/osf/models/user.py b/osf/models/user.py index 9bce511537d..2d638c122c6 100644 --- a/osf/models/user.py +++ b/osf/models/user.py @@ -1,8 +1,7 @@ import datetime as dt import logging import re -import urllib -import urlparse +from future.moves.urllib.parse import urljoin, urlencode import uuid from copy import deepcopy from os.path import splitext @@ -409,7 +408,7 @@ def url(self): @property def absolute_url(self): - return urlparse.urljoin(website_settings.DOMAIN, self.url) + return urljoin(website_settings.DOMAIN, self.url) @property def absolute_api_v2_url(self): @@ -1284,7 +1283,7 @@ def get_confirmation_url(self, email, base = website_settings.DOMAIN if external else '/' token = self.get_confirmation_token(email, force=force, renew=renew) external = 'external/' if external_id_provider else '' - destination = '?{}'.format(urllib.urlencode({'destination': destination})) if destination else '' + destination = '?{}'.format(urlencode({'destination': destination})) if destination else '' return '{0}confirm/{1}{2}/{3}/{4}'.format(base, external, self._primary_key, token, destination) def register(self, username, password=None, accepted_terms_of_service=None): diff --git a/osf/utils/tokens/__init__.py b/osf/utils/tokens/__init__.py index 239f53f94a1..3b413869a23 100644 --- a/osf/utils/tokens/__init__.py +++ b/osf/utils/tokens/__init__.py @@ -1,4 +1,4 @@ -import httplib as http +from rest_framework import status as http_status import functools import jwt from flask import request @@ -33,7 +33,7 @@ def from_string(cls, encoded_token): payload = decode(encoded_token) except jwt.DecodeError as e: raise HTTPError( - http.BAD_REQUEST, + http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Bad request', 'message_long': e.message @@ -73,7 +73,7 @@ def wrapper(*args, **kwargs): res = handler.to_response() except TokenHandlerNotFound as e: raise HTTPError( - http.BAD_REQUEST, + http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Invalid Token', 'message_long': 'No token handler for action: {} found'.format(e.action) diff --git a/osf/utils/tokens/handlers.py b/osf/utils/tokens/handlers.py index a93050b2a86..2b7fdf01cbc 100644 --- a/osf/utils/tokens/handlers.py +++ b/osf/utils/tokens/handlers.py @@ -1,4 +1,4 @@ -import httplib as http +from rest_framework import status as http_status from flask import redirect, request import markupsafe @@ -66,14 +66,14 @@ def sanction_handler(kind, action, payload, encoded_token, auth, **kwargs): err_code = None err_message = None if not sanction: - err_code = http.BAD_REQUEST + err_code = http_status.HTTP_400_BAD_REQUEST err_message = 'There is no {0} associated with this token.'.format( markupsafe.escape(Model.DISPLAY_NAME)) elif sanction.is_approved: # Simply strip query params and redirect if already approved return redirect(request.base_url) elif sanction.is_rejected: - err_code = http.GONE if kind in ['registration', 'embargo'] else http.BAD_REQUEST + err_code = http_status.HTTP_410_GONE if kind in ['registration', 'embargo'] else http_status.HTTP_400_BAD_REQUEST err_message = 'This registration {0} has been rejected.'.format( markupsafe.escape(sanction.DISPLAY_NAME)) if err_code: @@ -88,12 +88,12 @@ def sanction_handler(kind, action, payload, encoded_token, auth, **kwargs): try: do_action(auth.user, encoded_token) except TokenError as e: - raise HTTPError(http.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': e.message_short, 'message_long': e.message_long }) except PermissionsError as e: - raise HTTPError(http.UNAUTHORIZED, data={ + raise HTTPError(http_status.HTTP_401_UNAUTHORIZED, data={ 'message_short': 'Unauthorized access', 'message_long': str(e) }) diff --git a/osf_tests/test_generate_sitemap.py b/osf_tests/test_generate_sitemap.py index a2b1469eba0..7a0fd6b2acc 100644 --- a/osf_tests/test_generate_sitemap.py +++ b/osf_tests/test_generate_sitemap.py @@ -5,7 +5,7 @@ import shutil import tempfile import xml -import urlparse +from future.moves.urllib.parse import urljoin from scripts import generate_sitemap from osf_tests.factories import (AuthUserFactory, ProjectFactory, RegistrationFactory, CollectionFactory, @@ -123,7 +123,7 @@ def all_included_links(self, user_admin_project_public, user_admin_project_priva '/{}/download/?format=pdf'.format(preprint_osf._id), '/{}/download/?format=pdf'.format(preprint_other._id) ]) - urls_to_include = [urlparse.urljoin(settings.DOMAIN, item) for item in urls_to_include] + urls_to_include = [urljoin(settings.DOMAIN, item) for item in urls_to_include] return urls_to_include @@ -146,32 +146,32 @@ def test_unconfirmed_user_not_included(self, create_tmp_directory, user_unconfir with mock.patch('website.settings.STATIC_FOLDER', create_tmp_directory): urls = get_all_sitemap_urls() - assert urlparse.urljoin(settings.DOMAIN, user_unconfirmed.url) not in urls + assert urljoin(settings.DOMAIN, user_unconfirmed.url) not in urls def test_collection_link_not_included(self, collection, create_tmp_directory): with mock.patch('website.settings.STATIC_FOLDER', create_tmp_directory): urls = get_all_sitemap_urls() - assert urlparse.urljoin(settings.DOMAIN, collection.url) not in urls + assert urljoin(settings.DOMAIN, collection.url) not in urls def test_private_project_link_not_included(self, project_private, create_tmp_directory): with mock.patch('website.settings.STATIC_FOLDER', create_tmp_directory): urls = get_all_sitemap_urls() - assert urlparse.urljoin(settings.DOMAIN, project_private.url) not in urls + assert urljoin(settings.DOMAIN, project_private.url) not in urls def test_embargoed_registration_link_not_included(self, registration_embargoed, create_tmp_directory): with mock.patch('website.settings.STATIC_FOLDER', create_tmp_directory): urls = get_all_sitemap_urls() - assert urlparse.urljoin(settings.DOMAIN, registration_embargoed.url) not in urls + assert urljoin(settings.DOMAIN, registration_embargoed.url) not in urls def test_deleted_project_link_not_included(self, project_deleted, create_tmp_directory): with mock.patch('website.settings.STATIC_FOLDER', create_tmp_directory): urls = get_all_sitemap_urls() - assert urlparse.urljoin(settings.DOMAIN, project_deleted.url) not in urls + assert urljoin(settings.DOMAIN, project_deleted.url) not in urls diff --git a/osf_tests/test_guid.py b/osf_tests/test_guid.py index 43b510b54dd..8e03b21cb4a 100644 --- a/osf_tests/test_guid.py +++ b/osf_tests/test_guid.py @@ -1,6 +1,6 @@ import mock import pytest -import urllib +from future.moves.urllib.parse import quote from django.utils import timezone from django.core.exceptions import MultipleObjectsReturned @@ -298,22 +298,22 @@ def test_resolve_guid_download_file_export(self): res = self.app.get(pp.url + 'download?format=asdf') assert res.status_code == 302 assert '{}/export?format=asdf&url='.format(MFR_SERVER_URL) in res.location - assert '{}/v1/resources/{}/providers/{}{}%3Faction%3Ddownload'.format(urllib.quote(WATERBUTLER_URL), pp._id, pp.primary_file.provider, pp.primary_file.path) in res.location + assert '{}/v1/resources/{}/providers/{}{}%3Faction%3Ddownload'.format(quote(WATERBUTLER_URL), pp._id, pp.primary_file.provider, pp.primary_file.path) in res.location res = self.app.get(pp.url + 'download/?format=asdf') assert res.status_code == 302 assert '{}/export?format=asdf&url='.format(MFR_SERVER_URL) in res.location - assert '{}/v1/resources/{}/providers/{}{}%3Faction%3Ddownload'.format(urllib.quote(WATERBUTLER_URL), pp._id, pp.primary_file.provider, pp.primary_file.path) in res.location + assert '{}/v1/resources/{}/providers/{}{}%3Faction%3Ddownload'.format(quote(WATERBUTLER_URL), pp._id, pp.primary_file.provider, pp.primary_file.path) in res.location res = self.app.get('/{}/download?format=asdf'.format(pp.primary_file.get_guid(create=True)._id)) assert res.status_code == 302 assert '{}/export?format=asdf&url='.format(MFR_SERVER_URL) in res.location - assert '{}/v1/resources/{}/providers/{}{}%3Faction%3Ddownload'.format(urllib.quote(WATERBUTLER_URL), pp._id, pp.primary_file.provider, pp.primary_file.path) in res.location + assert '{}/v1/resources/{}/providers/{}{}%3Faction%3Ddownload'.format(quote(WATERBUTLER_URL), pp._id, pp.primary_file.provider, pp.primary_file.path) in res.location res = self.app.get('/{}/download/?format=asdf'.format(pp.primary_file.get_guid(create=True)._id)) assert res.status_code == 302 assert '{}/export?format=asdf&url='.format(MFR_SERVER_URL) in res.location - assert '{}/v1/resources/{}/providers/{}{}%3Faction%3Ddownload'.format(urllib.quote(WATERBUTLER_URL), pp._id, pp.primary_file.provider, pp.primary_file.path) in res.location + assert '{}/v1/resources/{}/providers/{}{}%3Faction%3Ddownload'.format(quote(WATERBUTLER_URL), pp._id, pp.primary_file.provider, pp.primary_file.path) in res.location pp.primary_file.create_version( creator=pp.creator, @@ -325,7 +325,7 @@ def test_resolve_guid_download_file_export(self): res = self.app.get(pp.url + 'download/?format=asdf') assert res.status_code == 302 assert '{}/export?format=asdf&url='.format(MFR_SERVER_URL) in res.location - assert '{}/v1/resources/{}/providers/{}{}%3F'.format(urllib.quote(WATERBUTLER_URL), pp._id, pp.primary_file.provider, pp.primary_file.path) in res.location + assert '{}/v1/resources/{}/providers/{}{}%3F'.format(quote(WATERBUTLER_URL), pp._id, pp.primary_file.provider, pp.primary_file.path) in res.location quarams = res.location.split('%3F')[1].split('%26') assert 'action%3Ddownload' in quarams assert 'version%3D2' in quarams @@ -334,7 +334,7 @@ def test_resolve_guid_download_file_export(self): res = self.app.get(pp.url + 'download/?format=asdf&version=1') assert res.status_code == 302 assert '{}/export?format=asdf&url='.format(MFR_SERVER_URL) in res.location - assert '{}/v1/resources/{}/providers/{}{}%3F'.format(urllib.quote(WATERBUTLER_URL), pp._id, pp.primary_file.provider, pp.primary_file.path) in res.location + assert '{}/v1/resources/{}/providers/{}{}%3F'.format(quote(WATERBUTLER_URL), pp._id, pp.primary_file.provider, pp.primary_file.path) in res.location quarams = res.location.split('%3F')[1].split('%26') assert 'action%3Ddownload' in quarams assert 'version%3D1' in quarams @@ -345,7 +345,7 @@ def test_resolve_guid_download_file_export(self): assert res.status_code == 302 assert res.status_code == 302 assert '{}/export?format=asdf&url='.format(MFR_SERVER_URL) in res.location - assert '{}/v1/resources/{}/providers/{}{}%3F'.format(urllib.quote(WATERBUTLER_URL), unpub_pp._id, unpub_pp.primary_file.provider, unpub_pp.primary_file.path) in res.location + assert '{}/v1/resources/{}/providers/{}{}%3F'.format(quote(WATERBUTLER_URL), unpub_pp._id, unpub_pp.primary_file.provider, unpub_pp.primary_file.path) in res.location quarams = res.location.split('%3F')[1].split('%26') assert 'action%3Ddownload' in quarams assert 'version%3D1' in quarams diff --git a/osf_tests/test_user.py b/osf_tests/test_user.py index 7f44832f85e..24e06f128ea 100644 --- a/osf_tests/test_user.py +++ b/osf_tests/test_user.py @@ -3,7 +3,7 @@ import os import json import datetime as dt -import urlparse +from future.moves.urllib.parse import urlparse, urljoin, parse_qs from django.db import connection, transaction from django.contrib.auth.models import Group @@ -614,7 +614,7 @@ def test_url(self, user): def test_absolute_url(self, user): assert( user.absolute_url == - urlparse.urljoin(settings.DOMAIN, '/{0}/'.format(user._id)) + urljoin(settings.DOMAIN, '/{0}/'.format(user._id)) ) def test_profile_image_url(self, user): @@ -647,7 +647,7 @@ def test_profile_image_url_has_no_default_size(self, user): user, use_ssl=True) assert user.profile_image_url() == expected - size = urlparse.parse_qs(urlparse.urlparse(user.profile_image_url()).query).get('size') + size = parse_qs(urlparse(user.profile_image_url()).query).get('size') assert size is None def test_activity_points(self, user): diff --git a/package.json b/package.json index bc9777b8ec4..f2571a29d14 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "OSF", - "version": "19.26.0", + "version": "19.27.0", "description": "Facilitating Open Science", "repository": "https://github.com/CenterForOpenScience/osf.io", "author": "Center for Open Science", @@ -18,7 +18,7 @@ "body-parser": "~1.12.0", "bootbox": "^4.4.0", "bootstrap": "3.3.7", - "bower": "^1.3.12", + "bower": "^1.8.8", "css-loader": "^0.9.1", "diff": "~2.2.2", "dropzone": "git://github.com/CenterForOpenScience/dropzone.git#aba21eb6b82cc823ac7a7a53d6e035791c082bae", diff --git a/requirements.txt b/requirements.txt index 27c5f0c7613..2d48746d1ac 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,7 +21,6 @@ git+https://github.com/cos-forks/celery@v4.1.1+cos0 kombu==4.2.0 amqp==2.2.2 vine==1.1.4 -httplib2==0.10.3 itsdangerous==0.24 lxml==3.4.1 mailchimp==2.0.9 diff --git a/scripts/generate_sitemap.py b/scripts/generate_sitemap.py index a878b81d19c..1e539aa2a3d 100644 --- a/scripts/generate_sitemap.py +++ b/scripts/generate_sitemap.py @@ -6,7 +6,7 @@ import gzip import os import shutil -import urlparse +from future.moves.urllib.parse import urlparse, urljoin import xml import django @@ -123,7 +123,7 @@ def write_sitemap_index(self): loc = doc.createElement('loc') sitemap.appendChild(loc) - loc_text = self.doc.createTextNode(urlparse.urljoin(settings.DOMAIN, 'sitemaps/sitemap_{}.xml'.format(str(f)))) + loc_text = self.doc.createTextNode(urljoin(settings.DOMAIN, 'sitemaps/sitemap_{}.xml'.format(str(f)))) loc.appendChild(loc_text) datemod = doc.createElement('lastmod') @@ -163,7 +163,7 @@ def generate(self): # Static urls progress.start(len(settings.SITEMAP_STATIC_URLS), 'STAT: ') for config in settings.SITEMAP_STATIC_URLS: - config['loc'] = urlparse.urljoin(settings.DOMAIN, config['loc']) + config['loc'] = urljoin(settings.DOMAIN, config['loc']) self.add_url(config) progress.increment() progress.stop() @@ -174,7 +174,7 @@ def generate(self): for obj in objs: try: config = settings.SITEMAP_USER_CONFIG - config['loc'] = urlparse.urljoin(settings.DOMAIN, '/{}/'.format(obj)) + config['loc'] = urljoin(settings.DOMAIN, '/{}/'.format(obj)) self.add_url(config) except Exception as e: self.log_errors('USER', obj, e) @@ -190,7 +190,7 @@ def generate(self): for obj in objs: try: config = settings.SITEMAP_NODE_CONFIG - config['loc'] = urlparse.urljoin(settings.DOMAIN, '/{}/'.format(obj['guids___id'])) + config['loc'] = urljoin(settings.DOMAIN, '/{}/'.format(obj['guids___id'])) config['lastmod'] = obj['modified'].strftime('%Y-%m-%d') self.add_url(config) except Exception as e: @@ -213,14 +213,14 @@ def generate(self): domain = provider.domain if (provider.domain_redirect_enabled and provider.domain) else settings.DOMAIN if provider == osf: preprint_url = '/preprints/{}/'.format(obj._id) - config['loc'] = urlparse.urljoin(domain, preprint_url) + config['loc'] = urljoin(domain, preprint_url) config['lastmod'] = preprint_date self.add_url(config) # Preprint file urls try: file_config = settings.SITEMAP_PREPRINT_FILE_CONFIG - file_config['loc'] = urlparse.urljoin( + file_config['loc'] = urljoin( obj.provider.domain or settings.DOMAIN, os.path.join( obj._id, diff --git a/scripts/populate_institutions.py b/scripts/populate_institutions.py index c22e557aaf8..eb15ca0170e 100644 --- a/scripts/populate_institutions.py +++ b/scripts/populate_institutions.py @@ -5,7 +5,7 @@ import argparse import logging import sys -import urllib +from future.moves.urllib.parse import quote import django from django.db import transaction @@ -34,7 +34,7 @@ def encode_uri_component(val): - return urllib.quote(val, safe='~()*!.\'') + return quote(val, safe='~()*!.\'') def update_or_create(inst_data): diff --git a/tasks/__init__.py b/tasks/__init__.py index d115763bb01..1f771aee720 100755 --- a/tasks/__init__.py +++ b/tasks/__init__.py @@ -17,6 +17,13 @@ from website import settings from .utils import pip_install, bin_prefix + +try: + from tasks import local # noqa +except ImportError as error: + print('No tasks/local.py file found. ' + 'Did you remember to copy local-dist.py to local.py?') + logging.getLogger('invoke').setLevel(logging.CRITICAL) # gets the root path for all the scripts that rely on it @@ -26,6 +33,12 @@ NO_TESTS_COLLECTED = 5 ns = Collection() +try: + from tasks import local as local_tasks + ns.add_collection(Collection.from_module(local_tasks), name='local') +except ImportError: + pass + try: from admin import tasks as admin_tasks ns.add_collection(Collection.from_module(admin_tasks), name='admin') diff --git a/tasks/local-dist.py b/tasks/local-dist.py new file mode 100644 index 00000000000..f98dba7721c --- /dev/null +++ b/tasks/local-dist.py @@ -0,0 +1,12 @@ +from invoke import task + + +@task() +def example_task(ctx, echo_this): + """ + An example task which prints using bash. + :param ctx: + :param echo_this str: + :return: + """ + ctx.run('echo {}'.format(echo_this)) diff --git a/tests/framework_tests/test_modular_templates.py b/tests/framework_tests/test_modular_templates.py index 06d586fd90a..aa8e7d32b7b 100644 --- a/tests/framework_tests/test_modular_templates.py +++ b/tests/framework_tests/test_modular_templates.py @@ -11,7 +11,8 @@ from lxml.html import fragment_fromstring import werkzeug.wrappers -from framework.exceptions import HTTPError, http +from rest_framework import status as http_status +from framework.exceptions import HTTPError from framework.routing import ( Renderer, JSONRenderer, WebRenderer, render_mako_string, @@ -44,7 +45,7 @@ def test_redirect(self): def test_http_error_raised(self): """Subclasses of Renderer must implement ``.handle_error()``.""" with self.assertRaises(NotImplementedError): - self.r(HTTPError(http.NOT_FOUND)) + self.r(HTTPError(http_status.HTTP_404_NOT_FOUND)) def test_tuple(self): """Subclasses of Renderer must implement ``.render()``.""" @@ -77,21 +78,21 @@ def test_http_error_raised(self): associated HTTP status code. This mirrors the tuple format anticipated by Flask. """ - resp = self.r(HTTPError(http.NOT_FOUND)) + resp = self.r(HTTPError(http_status.HTTP_404_NOT_FOUND)) - msg = HTTPError.error_msgs[http.NOT_FOUND] + msg = HTTPError.error_msgs[http_status.HTTP_404_NOT_FOUND] self.assertEqual( ( { - 'code': http.NOT_FOUND, + 'code': http_status.HTTP_404_NOT_FOUND, 'referrer': None, 'message_short': msg['message_short'], 'message_long': msg['message_long'], }, - http.NOT_FOUND, + http_status.HTTP_404_NOT_FOUND, ), - (json.loads(resp[0]), http.NOT_FOUND, ), + (json.loads(resp[0]), http_status.HTTP_404_NOT_FOUND, ), ) def test_tuple(self): @@ -165,7 +166,7 @@ def test_http_error_raised(self): self.app.app.preprocess_request() - err = HTTPError(http.NOT_FOUND) + err = HTTPError(http_status.HTTP_404_NOT_FOUND) resp = self.r(err) @@ -174,7 +175,7 @@ def test_http_error_raised(self): resp[0], ) self.assertEqual( - http.NOT_FOUND, + http_status.HTTP_404_NOT_FOUND, resp[1], ) @@ -195,7 +196,7 @@ def test_http_error_raise_with_redirect(self): """ resp = self.r( - HTTPError(http.CREATED, redirect_url='http://google.com/') + HTTPError(http_status.HTTP_201_CREATED, redirect_url='http://google.com/') ) self.assertIsInstance( diff --git a/tests/test_addons.py b/tests/test_addons.py index 291207e0daf..df3d43c7c2f 100644 --- a/tests/test_addons.py +++ b/tests/test_addons.py @@ -2,7 +2,7 @@ import os import datetime -import httplib as http +from rest_framework import status as http_status import time import functools @@ -563,13 +563,13 @@ def test_has_permission_download_not_prereg_challenge_admin(self): with assert_raises(HTTPError) as exc_info: views.check_access(self.draft_registration.branched_from, Auth(user=new_user), 'download', None) - assert_equal(exc_info.exception.code, http.FORBIDDEN) + assert_equal(exc_info.exception.code, http_status.HTTP_403_FORBIDDEN) def test_has_permission_download_prereg_challenge_admin_not_draft(self): with assert_raises(HTTPError) as exc_info: views.check_access(self.node, Auth(user=self.prereg_challenge_admin_user), 'download', None) - assert_equal(exc_info.exception.code, http.FORBIDDEN) + assert_equal(exc_info.exception.code, http_status.HTTP_403_FORBIDDEN) def test_has_permission_write_prereg_challenge_admin(self): with assert_raises(HTTPError) as exc_info: diff --git a/tests/test_auth.py b/tests/test_auth.py index 5b60ccf993f..7098419d7a5 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -4,9 +4,8 @@ from nose.tools import * # noqa; PEP8 asserts from webtest_plus import TestApp as WebtestApp # py.test tries to collect `TestApp` import mock -import urllib -import urlparse -import httplib as http +from future.moves.urllib.parse import urlparse, urljoin, quote +from rest_framework import status as http_status from flask import Flask from werkzeug.wrappers import BaseResponse @@ -104,7 +103,7 @@ def test_confirm_email(self, mock_mail): res = res.follow() assert_equal(res.status_code, 302) - assert_equal('/', urlparse.urlparse(res.location).path) + assert_equal('/', urlparse(res.location).path) assert_equal(len(mock_mail.call_args_list), 1) session = Session.objects.filter(data__auth_user_id=user._id).order_by('-modified').first() assert_equal(len(session.data['status']), 1) @@ -143,7 +142,7 @@ def test_successful_external_login_cas_redirect(self, mock_service_validate, moc assert_in('/login?service=', resp.location) # the valid username will be double quoted as it is furl quoted in both get_login_url and get_logout_url in order - username_quoted = urllib.quote(urllib.quote(user.username, safe='@'), safe='@') + username_quoted = quote(quote(user.username, safe='@'), safe='@') assert_in('username={}'.format(username_quoted), resp.location) assert_in('verification_key={}'.format(user.verification_key), resp.location) @@ -193,7 +192,7 @@ def test_password_change_sends_email(self, mock_mail): @mock.patch('framework.auth.utils.requests.post') def test_validate_recaptcha_success(self, req_post): resp = mock.Mock() - resp.status_code = http.OK + resp.status_code = http_status.HTTP_200_OK resp.json = mock.Mock(return_value={'success': True}) req_post.return_value = resp assert_true(validate_recaptcha('a valid captcha')) @@ -201,7 +200,7 @@ def test_validate_recaptcha_success(self, req_post): @mock.patch('framework.auth.utils.requests.post') def test_validate_recaptcha_valid_req_failure(self, req_post): resp = mock.Mock() - resp.status_code = http.OK + resp.status_code = http_status.HTTP_200_OK resp.json = mock.Mock(return_value={'success': False}) req_post.return_value = resp assert_false(validate_recaptcha(None)) @@ -209,7 +208,7 @@ def test_validate_recaptcha_valid_req_failure(self, req_post): @mock.patch('framework.auth.utils.requests.post') def test_validate_recaptcha_invalid_req_failure(self, req_post): resp = mock.Mock() - resp.status_code = http.BAD_REQUEST + resp.status_code = http_status.HTTP_400_BAD_REQUEST resp.json = mock.Mock(return_value={'success': True}) req_post.return_value = resp assert_false(validate_recaptcha(None)) @@ -738,7 +737,7 @@ def test_must_have_permission_false(self, mock_from_kwargs, mock_to_nodes): mock_to_nodes.return_value = (None, project) with assert_raises(HTTPError) as ctx: thriller(node=project) - assert_equal(ctx.exception.code, http.FORBIDDEN) + assert_equal(ctx.exception.code, http_status.HTTP_403_FORBIDDEN) @mock.patch('website.project.decorators._kwargs_to_nodes') @mock.patch('framework.auth.decorators.Auth.from_kwargs') @@ -748,7 +747,7 @@ def test_must_have_permission_not_logged_in(self, mock_from_kwargs, mock_to_node mock_to_nodes.return_value = (None, project) with assert_raises(HTTPError) as ctx: thriller(node=project) - assert_equal(ctx.exception.code, http.UNAUTHORIZED) + assert_equal(ctx.exception.code, http_status.HTTP_401_UNAUTHORIZED) def needs_addon_view(**kwargs): diff --git a/tests/test_campaigns.py b/tests/test_campaigns.py index cf29443974e..69bf2afee3c 100644 --- a/tests/test_campaigns.py +++ b/tests/test_campaigns.py @@ -1,5 +1,5 @@ from datetime import timedelta -import httplib as http +from rest_framework import status as http_status from django.utils import timezone from nose.tools import * # noqa (PEP8 asserts) @@ -184,36 +184,36 @@ def setUp(self): def test_campaign_register_view_logged_in(self): for key, value in self.campaigns.items(): resp = self.app.get(value['url_register'], auth=self.user.auth) - assert_equal(resp.status_code, http.FOUND) + assert_equal(resp.status_code, http_status.HTTP_302_FOUND) assert_equal(value['url_landing'], resp.headers['Location']) def test_campaign_register_view_logged_out(self): for key, value in self.campaigns.items(): resp = self.app.get(value['url_register']) - assert_equal(resp.status_code, http.OK) + assert_equal(resp.status_code, http_status.HTTP_200_OK) assert_in(value['title_register'], resp) def test_campaign_login_logged_in(self): for key, value in self.campaigns.items(): resp = self.app.get(value['url_login'], auth=self.user.auth) - assert_equal(resp.status_code, http.FOUND) + assert_equal(resp.status_code, http_status.HTTP_302_FOUND) assert_in(value['url_landing'], resp.headers['Location']) def test_campaign_login_logged_out(self): for key, value in self.campaigns.items(): resp = self.app.get(value['url_login']) - assert_equal(resp.status_code, http.FOUND) + assert_equal(resp.status_code, http_status.HTTP_302_FOUND) assert_in(value['url_register'], resp.headers['Location']) def test_campaign_landing_logged_in(self): for key, value in self.campaigns.items(): resp = self.app.get(value['url_landing'], auth=self.user.auth) - assert_equal(resp.status_code, http.OK) + assert_equal(resp.status_code, http_status.HTTP_200_OK) def test_auth_prereg_landing_page_logged_out(self): for key, value in self.campaigns.items(): resp = self.app.get(value['url_landing']) - assert_equal(resp.status_code, http.OK) + assert_equal(resp.status_code, http_status.HTTP_200_OK) # tests for registration through campaigns @@ -233,7 +233,7 @@ def test_confirm_email_get_with_campaign(self): } with self.app.app.test_request_context(), mock_auth(user): res = auth_views.confirm_email_get(token, **kwargs) - assert_equal(res.status_code, http.FOUND) + assert_equal(res.status_code, http_status.HTTP_302_FOUND) assert_equal(res.location, campaigns.campaign_url_for(key)) @@ -249,7 +249,7 @@ def setUp(self): # go to CAS institution login page if not logged in def test_institution_not_logged_in(self): resp = self.app.get(self.url_login) - assert_equal(resp.status_code, http.FOUND) + assert_equal(resp.status_code, http_status.HTTP_302_FOUND) assert_in(cas.get_login_url(self.service_url, campaign='institution'), resp.headers['Location']) # register behave the same as login resp2 = self.app.get(self.url_register) @@ -258,7 +258,7 @@ def test_institution_not_logged_in(self): # go to target page (service url_ if logged in def test_institution_logged_in(self): resp = self.app.get(self.url_login) - assert_equal(resp.status_code, http.FOUND) + assert_equal(resp.status_code, http_status.HTTP_302_FOUND) assert_in(self.service_url, resp.headers['Location']) # register behave the same as login resp2 = self.app.get(self.url_register) diff --git a/tests/test_oauth.py b/tests/test_oauth.py index 0f90c6846a9..3784f6762ad 100644 --- a/tests/test_oauth.py +++ b/tests/test_oauth.py @@ -1,10 +1,11 @@ from datetime import datetime -import httplib as http +from rest_framework import status as http_status +import logging import json import logging import mock import time -import urlparse +from future.moves.urllib.parse import urlparse, urljoin, parse_qs import responses from nose.tools import * # noqa @@ -129,7 +130,7 @@ def test_disconnect(self): # Request succeeded assert_equal( response.status_code, - http.OK, + http_status.HTTP_200_OK, ) self.user.reload() @@ -167,7 +168,7 @@ def test_disconnect_with_multiple_connected(self): # Request succeeded assert_equal( response.status_code, - http.OK, + http_status.HTTP_200_OK, ) self.user.reload() @@ -364,8 +365,8 @@ def test_start_flow_oauth_standard(self): assert_in('state', creds) # The URL to which the user would be redirected - parsed = urlparse.urlparse(url) - params = urlparse.parse_qs(parsed.query) + parsed = urlparse(url) + params = parse_qs(parsed.query) # Check parameters expected_params = { @@ -405,8 +406,8 @@ def test_start_flow_oauth_no_redirect(self): assert_in('state', creds) # The URL to which the user would be redirected - parsed = urlparse.urlparse(url) - params = urlparse.parse_qs(parsed.query) + parsed = urlparse(url) + params = parse_qs(parsed.query) # Check parameters - the only difference from standard OAuth flow is no `redirect_uri`. expected_params = { diff --git a/tests/test_preprints.py b/tests/test_preprints.py index 795eb1be3b8..c998f1f93fc 100644 --- a/tests/test_preprints.py +++ b/tests/test_preprints.py @@ -5,7 +5,7 @@ import mock import furl import time -import urlparse +from future.moves.urllib.parse import urlparse, urljoin import datetime from django.utils import timezone import pytest @@ -145,7 +145,7 @@ def test_url_(self, preprint): assert preprint.url == '/preprints/{}/{}/'.format(preprint.provider._id, preprint._id) def test_absolute_url(self, preprint): - assert preprint.absolute_url == urlparse.urljoin( + assert preprint.absolute_url == urljoin( preprint.provider.domain if preprint.provider.domain_redirect_enabled else settings.DOMAIN, preprint.url ) @@ -1736,8 +1736,16 @@ def test_set_supplemental_node_preprint_permissions(self): class TestPreprintProvider(OsfTestCase): def setUp(self): super(TestPreprintProvider, self).setUp() + self.user = AuthUserFactory() + self.auth = Auth(user=self.user) + self.provider_osf = PreprintProviderFactory(_id='osf') self.preprint = PreprintFactory(provider=None, is_published=False) self.provider = PreprintProviderFactory(name='WWEArxiv') + self.provider_one = PreprintProviderFactory(name='DoughnutArxiv') + self.provider_two = PreprintProviderFactory(name='IceCreamArxiv') + self.subject_one = SubjectFactory(provider=self.provider_one) + self.subject_osf = SubjectFactory(provider=self.provider_osf) + def test_add_provider(self): assert_not_equal(self.preprint.provider, self.provider) @@ -1806,6 +1814,41 @@ def test_highlighted_subjects(self): assert self.provider.has_highlighted_subjects is True assert set(self.provider.highlighted_subjects) == set([subj_aaa]) + def test_change_preprint_provider_custom_taxonomies(self): + subject_two = SubjectFactory(provider=self.provider_two, + bepress_subject=self.subject_one.bepress_subject) + preprint = PreprintFactory(subjects=[[self.subject_one._id]], provider=self.provider_one, creator=self.user) + subject_problems = preprint.map_subjects_between_providers(self.provider_one, self.provider_two, self.auth) + preprint.refresh_from_db() + assert subject_problems == [] + assert subject_two in preprint.subjects.all() + + def test_change_preprint_provider_from_osf(self): + subject_two = SubjectFactory(provider=self.provider_one, + bepress_subject=self.subject_osf) + preprint = PreprintFactory(subjects=[[self.subject_osf._id]], provider=self.provider_osf, creator=self.user) + subject_problems = preprint.map_subjects_between_providers(self.provider_osf, self.provider_one, self.auth) + preprint.refresh_from_db() + assert subject_problems == [] + assert subject_two in preprint.subjects.all() + + def test_change_preprint_provider_to_osf(self): + subject_two = SubjectFactory(provider=self.provider_one, + bepress_subject=self.subject_osf) + preprint = PreprintFactory(subjects=[[subject_two._id]], provider=self.provider_one, creator=self.user) + subject_problems = preprint.map_subjects_between_providers(self.provider_one, self.provider_osf, self.auth) + preprint.refresh_from_db() + assert subject_problems == [] + assert self.subject_osf in preprint.subjects.all() + + def test_change_preprint_provider_problem_subject(self): + subject_two = SubjectFactory(provider=self.provider_one, + bepress_subject=self.subject_osf) + preprint = PreprintFactory(subjects=[[subject_two._id]], provider=self.provider_one, creator=self.user) + subject_problems = preprint.map_subjects_between_providers(self.provider_one, self.provider_two, self.auth) + preprint.refresh_from_db() + assert subject_problems == [subject_two.text] + assert subject_two in preprint.subjects.all() class TestPreprintIdentifiers(OsfTestCase): def setUp(self): @@ -1990,7 +2033,7 @@ def test_format_preprint(self): assert related_doi['creative_work'] == related_work workidentifiers = [nodes.pop(k)['uri'] for k, v in nodes.items() if v['@type'] == 'workidentifier'] - assert workidentifiers == [urlparse.urljoin(settings.DOMAIN, self.preprint._id + '/')] + assert workidentifiers == [urljoin(settings.DOMAIN, self.preprint._id + '/')] relation = nodes.pop(nodes.keys()[0]) assert relation == {'@id': relation['@id'], '@type': 'workrelation', 'related': {'@id': related_work['@id'], '@type': related_work['@type']}, 'subject': {'@id': preprint['@id'], '@type': preprint['@type']}} @@ -2088,7 +2131,7 @@ def test_format_preprint_nones(self): workidentifiers = {nodes.pop(k)['uri'] for k, v in nodes.items() if v['@type'] == 'workidentifier'} # URLs should *always* be osf.io/guid/ - assert workidentifiers == set([urlparse.urljoin(settings.DOMAIN, self.preprint._id) + '/', 'https://doi.org/{}'.format(self.preprint.get_identifier('doi').value)]) + assert workidentifiers == set([urljoin(settings.DOMAIN, self.preprint._id) + '/', 'https://doi.org/{}'.format(self.preprint.get_identifier('doi').value)]) assert nodes == {} diff --git a/tests/test_registrations/test_embargoes.py b/tests/test_registrations/test_embargoes.py index 09cf1c9e083..1db3162a943 100644 --- a/tests/test_registrations/test_embargoes.py +++ b/tests/test_registrations/test_embargoes.py @@ -1,6 +1,6 @@ """Tests related to embargoes of registrations""" import datetime -import httplib as http +from rest_framework import status as http_status import json import pytz @@ -1211,7 +1211,7 @@ def test_non_contributor_GET_approval_returns_HTTPError(self): res = self.app.get(approval_url, auth=non_contributor.auth, expect_errors=True) self.registration.reload() - assert_equal(http.UNAUTHORIZED, res.status_code) + assert_equal(http_status.HTTP_401_UNAUTHORIZED, res.status_code) assert_true(self.registration.is_pending_embargo) assert_equal(self.registration.embargo.state, Embargo.UNAPPROVED) @@ -1228,6 +1228,6 @@ def test_non_contributor_GET_disapproval_returns_HTTPError(self): approval_url = self.registration.web_url_for('token_action', token=rejection_token) res = self.app.get(approval_url, auth=non_contributor.auth, expect_errors=True) - assert_equal(http.UNAUTHORIZED, res.status_code) + assert_equal(http_status.HTTP_401_UNAUTHORIZED, res.status_code) assert_true(self.registration.is_pending_embargo) assert_equal(self.registration.embargo.state, Embargo.UNAPPROVED) diff --git a/tests/test_registrations/test_retractions.py b/tests/test_registrations/test_retractions.py index b944276dfae..297a1a272a0 100644 --- a/tests/test_registrations/test_retractions.py +++ b/tests/test_registrations/test_retractions.py @@ -1,7 +1,7 @@ """Tests related to retraction of public registrations""" import datetime -import httplib as http +from rest_framework import status as http_status import mock import pytest @@ -636,7 +636,7 @@ def test_GET_approve_from_unauthorized_user_returns_HTTPError_UNAUTHORIZED(self) auth=unauthorized_user.auth, expect_errors=True ) - assert_equal(res.status_code, http.UNAUTHORIZED) + assert_equal(res.status_code, http_status.HTTP_401_UNAUTHORIZED) def test_GET_approve_registration_without_retraction_returns_HTTPError_BAD_REQUEST(self): assert_true(self.registration.is_pending_retraction) @@ -649,7 +649,7 @@ def test_GET_approve_registration_without_retraction_returns_HTTPError_BAD_REQUE auth=self.user.auth, expect_errors=True ) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) def test_GET_approve_with_invalid_token_returns_HTTPError_BAD_REQUEST(self): res = self.app.get( @@ -657,7 +657,7 @@ def test_GET_approve_with_invalid_token_returns_HTTPError_BAD_REQUEST(self): auth=self.user.auth, expect_errors=True ) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) def test_GET_approve_with_non_existant_sanction_returns_HTTPError_BAD_REQUEST(self): res = self.app.get( @@ -665,9 +665,9 @@ def test_GET_approve_with_non_existant_sanction_returns_HTTPError_BAD_REQUEST(se auth=self.user.auth, expect_errors=True ) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) - def test_GET_approve_with_valid_token_returns_200(self): + def test_GET_approve_with_valid_token_returns_302(self): res = self.app.get( self.registration.web_url_for('token_action', token=self.approval_token), auth=self.user.auth @@ -675,7 +675,7 @@ def test_GET_approve_with_valid_token_returns_200(self): self.registration.retraction.reload() assert_true(self.registration.is_retracted) assert_false(self.registration.is_pending_retraction) - assert_equal(res.status_code, 302) + assert_equal(res.status_code, http_status.HTTP_302_FOUND) # node_registration_retraction_disapprove_tests def test_GET_disapprove_from_unauthorized_user_returns_HTTPError_UNAUTHORIZED(self): @@ -686,7 +686,7 @@ def test_GET_disapprove_from_unauthorized_user_returns_HTTPError_UNAUTHORIZED(se auth=unauthorized_user.auth, expect_errors=True ) - assert_equal(res.status_code, http.UNAUTHORIZED) + assert_equal(res.status_code, http_status.HTTP_401_UNAUTHORIZED) def test_GET_disapprove_registration_without_retraction_returns_HTTPError_BAD_REQUEST(self): assert_true(self.registration.is_pending_retraction) @@ -699,7 +699,7 @@ def test_GET_disapprove_registration_without_retraction_returns_HTTPError_BAD_RE auth=self.user.auth, expect_errors=True ) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) def test_GET_disapprove_with_invalid_token_HTTPError_BAD_REQUEST(self): res = self.app.get( @@ -707,7 +707,7 @@ def test_GET_disapprove_with_invalid_token_HTTPError_BAD_REQUEST(self): auth=self.user.auth, expect_errors=True ) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) def test_GET_disapprove_with_valid_token_returns_redirect(self): res = self.app.get( @@ -718,7 +718,8 @@ def test_GET_disapprove_with_valid_token_returns_redirect(self): assert_false(self.registration.is_retracted) assert_false(self.registration.is_pending_retraction) assert_true(self.registration.retraction.is_rejected) - assert_equal(res.status_code, 302) + assert_equal(res.status_code, http_status.HTTP_302_FOUND) + @pytest.mark.enable_bookmark_creation class ComponentRegistrationRetractionViewsTestCase(OsfTestCase): @@ -756,7 +757,7 @@ def test_POST_retraction_to_component_returns_HTTPError_BAD_REQUEST(self): auth=self.auth, expect_errors=True, ) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) def test_POST_retraction_to_subproject_returns_HTTPError_BAD_REQUEST(self): res = self.app.post_json( @@ -764,7 +765,7 @@ def test_POST_retraction_to_subproject_returns_HTTPError_BAD_REQUEST(self): auth=self.auth, expect_errors=True, ) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) def test_POST_retraction_to_subproject_component_returns_HTTPError_BAD_REQUEST(self): res = self.app.post_json( @@ -772,7 +773,7 @@ def test_POST_retraction_to_subproject_component_returns_HTTPError_BAD_REQUEST(s auth=self.auth, expect_errors=True, ) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) @pytest.mark.enable_bookmark_creation class RegistrationRetractionViewsTestCase(OsfTestCase): @@ -800,7 +801,7 @@ def test_GET_retraction_page_when_pending_retraction_returns_HTTPError_BAD_REQUE auth=self.user.auth, expect_errors=True, ) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) def test_POST_retraction_to_private_registration_returns_HTTPError_FORBIDDEN(self): self.registration.is_public = False @@ -812,7 +813,7 @@ def test_POST_retraction_to_private_registration_returns_HTTPError_FORBIDDEN(sel auth=self.user.auth, expect_errors=True, ) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) self.registration.reload() assert_is_none(self.registration.retraction) @@ -850,7 +851,7 @@ def test_POST_pending_embargo_returns_HTTPError_HTTPOK(self): auth=self.user.auth, expect_errors=True, ) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) self.registration.reload() assert_true(self.registration.is_pending_retraction) @@ -873,19 +874,19 @@ def test_POST_active_embargo_returns_HTTPOK(self): auth=self.user.auth, expect_errors=True, ) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) self.registration.reload() assert_true(self.registration.is_pending_retraction) def test_POST_retraction_by_non_admin_retract_HTTPError_UNAUTHORIZED(self): res = self.app.post_json(self.retraction_post_url, expect_errors=True) - assert_equals(res.status_code, http.UNAUTHORIZED) + assert_equals(res.status_code, http_status.HTTP_401_UNAUTHORIZED) self.registration.reload() assert_is_none(self.registration.retraction) # group admin POST fails res = self.app.post_json(self.retraction_post_url, auth=self.group_mem.auth, expect_errors=True) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) @mock.patch('website.mails.send_mail') def test_POST_retraction_without_justification_returns_HTTPOK(self, mock_send): @@ -894,7 +895,7 @@ def test_POST_retraction_without_justification_returns_HTTPOK(self, mock_send): {'justification': ''}, auth=self.user.auth, ) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) self.registration.reload() assert_false(self.registration.is_retracted) assert_true(self.registration.is_pending_retraction) @@ -945,13 +946,13 @@ def test_non_contributor_GET_approval_returns_HTTPError_UNAUTHORIZED(self): approval_url = self.registration.web_url_for('token_action', token=approval_token) res = self.app.get(approval_url, auth=non_contributor.auth, expect_errors=True) - assert_equal(res.status_code, http.UNAUTHORIZED) + assert_equal(res.status_code, http_status.HTTP_401_UNAUTHORIZED) assert_true(self.registration.is_pending_retraction) assert_false(self.registration.is_retracted) # group admin on node fails disapproval GET res = self.app.get(approval_url, auth=self.group_mem.auth, expect_errors=True) - assert_equal(res.status_code, http.UNAUTHORIZED) + assert_equal(res.status_code, http_status.HTTP_401_UNAUTHORIZED) def test_non_contributor_GET_disapproval_returns_HTTPError_UNAUTHORIZED(self): non_contributor = AuthUserFactory() @@ -960,10 +961,10 @@ def test_non_contributor_GET_disapproval_returns_HTTPError_UNAUTHORIZED(self): disapproval_url = self.registration.web_url_for('token_action', token=rejection_token) res = self.app.get(disapproval_url, auth=non_contributor.auth, expect_errors=True) - assert_equal(res.status_code, http.UNAUTHORIZED) + assert_equal(res.status_code, http_status.HTTP_401_UNAUTHORIZED) assert_true(self.registration.is_pending_retraction) assert_false(self.registration.is_retracted) # group admin on node fails disapproval GET res = self.app.get(disapproval_url, auth=self.group_mem.auth, expect_errors=True) - assert_equal(res.status_code, http.UNAUTHORIZED) + assert_equal(res.status_code, http_status.HTTP_401_UNAUTHORIZED) diff --git a/tests/test_registrations/test_views.py b/tests/test_registrations/test_views.py index b199287c554..2616573ad94 100644 --- a/tests/test_registrations/test_views.py +++ b/tests/test_registrations/test_views.py @@ -4,7 +4,7 @@ from __future__ import absolute_import import datetime as dt import mock -import httplib as http +from rest_framework import status as http_status import pytz from django.utils import timezone @@ -39,14 +39,14 @@ class TestRegistrationViews(RegistrationsTestBase): def test_node_register_page_not_registration_redirects(self): url = self.node.web_url_for('node_register_page') res = self.app.get(url, auth=self.user.auth) - assert_equal(res.status_code, http.FOUND) + assert_equal(res.status_code, http_status.HTTP_302_FOUND) @mock.patch('website.archiver.tasks.archive') def test_node_register_page_registration(self, mock_archive): reg = self.node.register_node(get_default_metaschema(), self.auth, '', None) url = reg.web_url_for('node_register_page') res = self.app.get(url, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) def test_non_admin_can_view_node_register_page(self): non_admin = AuthUserFactory() @@ -59,7 +59,7 @@ def test_non_admin_can_view_node_register_page(self): reg = RegistrationFactory(project=self.node) url = reg.web_url_for('node_register_page') res = self.app.get(url, auth=non_admin.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) def test_is_public_node_register_page(self): self.node.is_public = True @@ -69,7 +69,7 @@ def test_is_public_node_register_page(self): reg.save() url = reg.web_url_for('node_register_page') res = self.app.get(url, auth=None) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) @mock.patch('framework.celery_tasks.handlers.enqueue_task', mock.Mock()) def test_register_template_page_backwards_comptability(self): @@ -84,7 +84,7 @@ def test_register_template_page_backwards_comptability(self): metaschema_id=_name_to_id(self.meta_schema.name), ) res = self.app.get(url, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) def test_register_template_page_redirects_if_not_registration(self): url = self.node.web_url_for( @@ -92,7 +92,7 @@ def test_register_template_page_redirects_if_not_registration(self): metaschema_id=self.meta_schema._id, ) res = self.app.get(url, auth=self.user.auth) - assert_equal(res.status_code, http.FOUND) + assert_equal(res.status_code, http_status.HTTP_302_FOUND) @pytest.mark.enable_bookmark_creation @@ -105,7 +105,7 @@ def test_submit_draft_for_review(self): self.embargo_payload, auth=self.user.auth ) - assert_equal(res.status_code, http.ACCEPTED) + assert_equal(res.status_code, http_status.HTTP_202_ACCEPTED) data = res.json assert_in('status', data) assert_equal(data['status'], 'initiated') @@ -126,7 +126,7 @@ def test_submit_draft_for_review_invalid(self): auth=self.user.auth, expect_errors=True ) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) # submitted by a group admin fails res = self.app.post_json( @@ -135,7 +135,7 @@ def test_submit_draft_for_review_invalid(self): auth=self.group_mem.auth, expect_errors=True ) - assert res.status_code == http.FORBIDDEN + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) def test_submit_draft_for_review_already_registered(self): self.draft.register(Auth(self.user), save=True) @@ -146,14 +146,14 @@ def test_submit_draft_for_review_already_registered(self): auth=self.user.auth, expect_errors=True ) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) assert_equal(res.json['message_long'], 'This draft has already been registered, if you wish to register it ' 'again or submit it for review please create a new draft.') def test_draft_before_register_page(self): url = self.draft_url('draft_before_register_page') res = self.app.get(url, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) def test_submit_draft_for_review_non_admin(self): url = self.draft_api_url('submit_draft_for_review') @@ -163,12 +163,12 @@ def test_submit_draft_for_review_non_admin(self): auth=self.non_admin.auth, expect_errors=True ) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) def test_get_draft_registration(self): url = self.draft_api_url('get_draft_registration') res = self.app.get(url, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) assert_equal(res.json['pk'], self.draft._id) def test_get_draft_registration_deleted(self): @@ -178,17 +178,17 @@ def test_get_draft_registration_deleted(self): url = self.draft_api_url('get_draft_registration') res = self.app.get(url, auth=self.user.auth, expect_errors=True) - assert_equal(res.status_code, http.GONE) + assert_equal(res.status_code, http_status.HTTP_410_GONE) def test_get_draft_registration_invalid(self): url = self.node.api_url_for('get_draft_registration', draft_id='13123123') res = self.app.get(url, auth=self.user.auth, expect_errors=True) - assert_equal(res.status_code, http.NOT_FOUND) + assert_equal(res.status_code, http_status.HTTP_404_NOT_FOUND) def test_get_draft_registration_not_admin(self): url = self.draft_api_url('get_draft_registration') res = self.app.get(url, auth=self.non_admin.auth, expect_errors=True) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) def test_get_draft_registrations_only_gets_drafts_for_that_node(self): dummy = NodeFactory() @@ -215,7 +215,7 @@ def test_get_draft_registrations_only_gets_drafts_for_that_node(self): url = self.node.api_url_for('get_draft_registrations') res = self.app.get(url, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) # 3 new, 1 from setUp assert_equal(len(res.json['drafts']), 4) for draft in res.json['drafts']: @@ -230,7 +230,7 @@ def test_new_draft_registration_POST(self): url = target.web_url_for('new_draft_registration') res = self.app.post(url, payload, auth=self.user.auth) - assert_equal(res.status_code, http.FOUND) + assert_equal(res.status_code, http_status.HTTP_302_FOUND) target.reload() draft = DraftRegistration.objects.get(branched_from=target) assert_equal(draft.registration_schema, self.meta_schema) @@ -243,7 +243,7 @@ def test_new_draft_registration_on_registration(self): } url = target.web_url_for('new_draft_registration') res = self.app.post(url, payload, auth=self.user.auth, expect_errors=True) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) def test_update_draft_registration_cant_update_registered(self): metadata = { @@ -259,13 +259,13 @@ def test_update_draft_registration_cant_update_registered(self): url = self.node.api_url_for('update_draft_registration', draft_id=self.draft._id) res = self.app.put_json(url, payload, auth=self.user.auth, expect_errors=True) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) def test_edit_draft_registration_page_already_registered(self): self.draft.register(self.auth, save=True) url = self.node.web_url_for('edit_draft_registration_page', draft_id=self.draft._id) res = self.app.get(url, auth=self.user.auth, expect_errors=True) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) def test_update_draft_registration(self): metadata = { @@ -283,7 +283,7 @@ def test_update_draft_registration(self): url = self.node.api_url_for('update_draft_registration', draft_id=self.draft._id) res = self.app.put_json(url, payload, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) open_ended_schema = RegistrationSchema.objects.get(name='Open-Ended Registration', schema_version=2) @@ -307,18 +307,18 @@ def test_update_draft_registration_non_admin(self): url = self.node.api_url_for('update_draft_registration', draft_id=self.draft._id) res = self.app.put_json(url, payload, auth=self.non_admin.auth, expect_errors=True) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) # group admin cannot update draft registration res = self.app.put_json(url, payload, auth=self.group_mem.auth, expect_errors=True) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) def test_delete_draft_registration(self): assert_equal(1, DraftRegistration.objects.filter(deleted__isnull=True).count()) url = self.node.api_url_for('delete_draft_registration', draft_id=self.draft._id) res = self.app.delete(url, auth=self.user.auth) - assert_equal(res.status_code, http.NO_CONTENT) + assert_equal(res.status_code, http_status.HTTP_204_NO_CONTENT) assert_equal(0, DraftRegistration.objects.filter(deleted__isnull=True).count()) def test_delete_draft_registration_non_admin(self): @@ -326,12 +326,12 @@ def test_delete_draft_registration_non_admin(self): url = self.node.api_url_for('delete_draft_registration', draft_id=self.draft._id) res = self.app.delete(url, auth=self.non_admin.auth, expect_errors=True) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) assert_equal(1, DraftRegistration.objects.filter(deleted__isnull=True).count()) # group admin cannot delete draft registration res = self.app.delete(url, auth=self.group_mem.auth, expect_errors=True) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) @mock.patch('website.archiver.tasks.archive') def test_delete_draft_registration_registered(self, mock_register_draft): @@ -339,7 +339,7 @@ def test_delete_draft_registration_registered(self, mock_register_draft): url = self.node.api_url_for('delete_draft_registration', draft_id=self.draft._id) res = self.app.delete(url, auth=self.user.auth, expect_errors=True) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) @mock.patch('website.archiver.tasks.archive') def test_delete_draft_registration_approved_and_registration_deleted(self, mock_register_draft): @@ -351,7 +351,7 @@ def test_delete_draft_registration_approved_and_registration_deleted(self, mock_ url = self.node.api_url_for('delete_draft_registration', draft_id=self.draft._id) res = self.app.delete(url, auth=self.user.auth) - assert_equal(res.status_code, http.NO_CONTENT) + assert_equal(res.status_code, http_status.HTTP_204_NO_CONTENT) assert_equal(0, DraftRegistration.objects.filter(deleted__isnull=True).count()) def test_only_admin_can_delete_registration(self): @@ -360,7 +360,7 @@ def test_only_admin_can_delete_registration(self): url = self.node.api_url_for('delete_draft_registration', draft_id=self.draft._id) res = self.app.delete(url, auth=non_admin.auth, expect_errors=True) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) assert_equal(1, DraftRegistration.objects.filter(deleted__isnull=True).count()) def test_get_metaschemas(self): @@ -374,7 +374,7 @@ def test_get_metaschemas(self): def test_get_metaschemas_all(self): url = api_url_for('get_metaschemas', include='all') res = self.app.get(url) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) assert_equal( len(res.json['meta_schemas']), RegistrationSchema.objects.filter(active=True).count() @@ -387,7 +387,7 @@ def test_validate_embargo_end_date_too_soon(self): try: draft_views.validate_embargo_end_date(too_soon.isoformat(), registration) except HTTPError as e: - assert_equal(e.code, http.BAD_REQUEST) + assert_equal(e.code, http_status.HTTP_400_BAD_REQUEST) else: self.fail() @@ -398,7 +398,7 @@ def test_validate_embargo_end_date_too_late(self): try: draft_views.validate_embargo_end_date(too_late.isoformat(), registration) except HTTPError as e: - assert_equal(e.code, http.BAD_REQUEST) + assert_equal(e.code, http_status.HTTP_400_BAD_REQUEST) else: self.fail() @@ -418,7 +418,7 @@ def test_check_draft_state_registered(self): try: draft_views.check_draft_state(self.draft) except HTTPError as e: - assert_equal(e.code, http.FORBIDDEN) + assert_equal(e.code, http_status.HTTP_403_FORBIDDEN) else: self.fail() @@ -438,7 +438,7 @@ def test_check_draft_state_pending_review(self): with mock.patch.object(DraftRegistration, 'requires_approval', mock.PropertyMock(return_value=True)): draft_views.check_draft_state(self.draft) except HTTPError as e: - assert_equal(e.code, http.FORBIDDEN) + assert_equal(e.code, http_status.HTTP_403_FORBIDDEN) else: self.fail() @@ -447,7 +447,7 @@ def test_check_draft_state_approved(self): with mock.patch.object(DraftRegistration, 'requires_approval', mock.PropertyMock(return_value=True)), mock.patch.object(DraftRegistration, 'is_approved', mock.PropertyMock(return_value=True)): draft_views.check_draft_state(self.draft) except HTTPError as e: - assert_equal(e.code, http.FORBIDDEN) + assert_equal(e.code, http_status.HTTP_403_FORBIDDEN) else: self.fail() @@ -479,6 +479,6 @@ def test_prereg_challenge_over(self): auth=self.user.auth, expect_errors=True ) - assert_equal(res.status_code, http.GONE) + assert_equal(res.status_code, http_status.HTTP_410_GONE) data = res.json assert_equal(data['message_short'], 'The Prereg Challenge has ended') diff --git a/tests/test_test_utils.py b/tests/test_test_utils.py index 695d1c32a4e..f41ec40fb31 100644 --- a/tests/test_test_utils.py +++ b/tests/test_test_utils.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- import mock -from urlparse import urlparse from nose.tools import * # noqa: F403 import unittest diff --git a/tests/test_tokens.py b/tests/test_tokens.py index 3ad121dd8e4..fe42432938a 100644 --- a/tests/test_tokens.py +++ b/tests/test_tokens.py @@ -1,5 +1,5 @@ import jwt -import httplib as http +from rest_framework import status as http_status import mock from django.db.models import Q @@ -105,7 +105,7 @@ def test_sanction_handler_no_sanction(self): try: handler.to_response() except HTTPError as e: - assert_equal(e.code, http.BAD_REQUEST) + assert_equal(e.code, http_status.HTTP_400_BAD_REQUEST) assert_equal(e.data['message_long'], NO_SANCTION_MSG.format(self.Model.DISPLAY_NAME)) def test_sanction_handler_sanction_approved(self): @@ -119,7 +119,7 @@ def test_sanction_handler_sanction_approved(self): try: handler.to_response() except HTTPError as e: - assert_equal(e.code, http.BAD_REQUEST if self.kind in ['embargo', 'registration_approval'] else http.GONE) + assert_equal(e.code, http_status.HTTP_400_BAD_REQUEST if self.kind in ['embargo', 'registration_approval'] else http_status.HTTP_410_GONE) assert_equal(e.data['message_long'], APPROVED_MSG.format(self.sanction.DISPLAY_NAME)) def test_sanction_handler_sanction_rejected(self): @@ -133,7 +133,7 @@ def test_sanction_handler_sanction_rejected(self): try: handler.to_response() except HTTPError as e: - assert_equal(e.code, http.GONE if self.kind in ['embargo', 'registration_approval'] else http.BAD_REQUEST) + assert_equal(e.code, http_status.HTTP_410_GONE if self.kind in ['embargo', 'registration_approval'] else http_status.HTTP_400_BAD_REQUEST) assert_equal(e.data['message_long'], REJECTED_MSG.format(self.sanction.DISPLAY_NAME)) class TestEmbargoTokenHandler(SanctionTokenHandlerBase): diff --git a/tests/test_views.py b/tests/test_views.py index 402a74a9b4b..544b95e99fd 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -5,11 +5,11 @@ from __future__ import absolute_import import datetime as dt -import httplib as http +from rest_framework import status as http_status import json import time import unittest -import urllib +from future.moves.urllib.parse import quote from flask import request import mock @@ -192,7 +192,7 @@ def test_not_logged_in_no_key(self): def test_logged_in_no_private_key(self): res = self.app.get(self.project_url, {'view_only': None}, auth=self.user.auth, expect_errors=True) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) def test_logged_in_has_key(self): res = self.app.get( @@ -372,7 +372,7 @@ def test_cannot_remove_only_visible_contributor(self): url, {'contributorID': self.user2._id, 'nodeIDs': [self.project._id]}, auth=self.auth, expect_errors=True ) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) assert_equal(res.json['message_long'], 'Must have at least one bibliographic contributor') assert_true(self.project.is_contributor(self.user2)) @@ -755,14 +755,14 @@ def test_remove_tag(self): def test_removal_empty_tag_throws_error(self): url = self.project.api_url_for('project_remove_tag') res = self.app.delete_json(url, {'tag': ''}, auth=self.auth, expect_errors=True) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) # Regression test for #OSF-5257 def test_removal_unknown_tag_throws_error(self): self.project.add_tag('narf', auth=self.consolidate_auth1, save=True) url = self.project.api_url_for('project_remove_tag') res = self.app.delete_json(url, {'tag': 'troz'}, auth=self.auth, expect_errors=True) - assert_equal(res.status_code, http.CONFLICT) + assert_equal(res.status_code, http_status.HTTP_409_CONFLICT) def test_suspended_project(self): node = NodeFactory(parent=self.project, creator=self.user1) @@ -857,7 +857,7 @@ def test_cant_remove_component_if_not_admin(self): expect_errors=True, ).maybe_follow() - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) assert_false(node.is_deleted) def test_view_project_returns_whether_to_show_wiki_widget(self): @@ -868,7 +868,7 @@ def test_view_project_returns_whether_to_show_wiki_widget(self): url = project.api_url_for('view_project') res = self.app.get(url, auth=user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) assert_in('show_wiki_widget', res.json['user']) def test_fork_grandcomponents_has_correct_root(self): @@ -1098,7 +1098,7 @@ def test_fmt_date_or_none(self): #enter a date before 1900 fmt_date_or_none(dt.datetime(1890, 10, 31, 18, 23, 29, 227)) # error should be raised because date is before 1900 - assert_equal(cm.exception.code, http.BAD_REQUEST) + assert_equal(cm.exception.code, http_status.HTTP_400_BAD_REQUEST) def test_unserialize_social(self): url = api_url_for('unserialize_social') @@ -1551,18 +1551,18 @@ def setUp(self): def test_non_owner_cant_access_detail_page(self): res = self.app.get(self.detail_url, auth=self.user2.auth, expect_errors=True) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) def test_owner_cant_access_deleted_application(self): self.platform_app.is_active = False self.platform_app.save() res = self.app.get(self.detail_url, auth=self.user.auth, expect_errors=True) - assert_equal(res.status_code, http.GONE) + assert_equal(res.status_code, http_status.HTTP_410_GONE) def test_owner_cant_access_nonexistent_application(self): url = web_url_for('oauth_application_detail', client_id='nonexistent') res = self.app.get(url, auth=self.user.auth, expect_errors=True) - assert_equal(res.status_code, http.NOT_FOUND) + assert_equal(res.status_code, http_status.HTTP_404_NOT_FOUND) def test_url_has_not_broken(self): assert_equal(self.platform_app.url, self.detail_url) @@ -2280,13 +2280,13 @@ def test_invite_contributor_post_if_user_is_already_contributor(self): res = self.app.post_json(self.invite_url, {'fullname': fake.name(), 'email': unreg_user.username}, auth=self.user.auth, expect_errors=True) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) def test_invite_contributor_with_no_email(self): name = fake.name() res = self.app.post_json(self.invite_url, {'fullname': name, 'email': None}, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) data = res.json assert_equal(data['status'], 'success') assert_equal(data['contributor']['fullname'], name) @@ -2297,7 +2297,7 @@ def test_invite_contributor_requires_fullname(self): res = self.app.post_json(self.invite_url, {'email': 'brian@queen.com', 'fullname': ''}, auth=self.user.auth, expect_errors=True) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) @mock.patch('website.project.views.contributor.mails.send_mail') def test_send_claim_email_to_given_email(self, send_mail): @@ -3242,7 +3242,7 @@ def test_register_scrubs_username(self, _): expected_scrub_username = "Eunice O' \"Cornwallis\"cornify_add()" user = OSFUser.objects.get(username=email) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) assert_equal(user.fullname, expected_scrub_username) def test_register_email_mismatch(self): @@ -3258,7 +3258,7 @@ def test_register_email_mismatch(self): }, expect_errors=True, ) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) users = OSFUser.objects.filter(username=email) assert_equal(users.count(), 0) @@ -3277,7 +3277,7 @@ def test_register_email_already_registered(self): }, expect_errors=True ) - assert_equal(res.status_code, http.CONFLICT) + assert_equal(res.status_code, http_status.HTTP_409_CONFLICT) users = OSFUser.objects.filter(username=email) assert_equal(users.count(), 1) @@ -3294,7 +3294,7 @@ def test_register_blacklisted_email_domain(self): }, expect_errors=True ) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) users = OSFUser.objects.filter(username=email) assert_equal(users.count(), 0) @@ -3316,7 +3316,7 @@ def test_register_good_captcha(self, _, validate_recaptcha): } ) validate_recaptcha.assert_called_with(captcha, remote_ip=None) - assert_equal(resp.status_code, http.OK) + assert_equal(resp.status_code, http_status.HTTP_200_OK) user = OSFUser.objects.get(username=email) assert_equal(user.fullname, name) @@ -3338,7 +3338,7 @@ def test_register_missing_captcha(self, _, validate_recaptcha): expect_errors=True ) validate_recaptcha.assert_called_with(None, remote_ip=None) - assert_equal(resp.status_code, http.BAD_REQUEST) + assert_equal(resp.status_code, http_status.HTTP_400_BAD_REQUEST) @mock.patch('framework.auth.views.validate_recaptcha', return_value=False) @mock.patch('framework.auth.views.mails.send_mail') @@ -3357,7 +3357,7 @@ def test_register_bad_captcha(self, _, validate_recaptcha): }, expect_errors=True ) - assert_equal(resp.status_code, http.BAD_REQUEST) + assert_equal(resp.status_code, http_status.HTTP_400_BAD_REQUEST) @mock.patch('osf.models.OSFUser.update_search_nodes') def test_register_after_being_invited_as_unreg_contributor(self, mock_update_search_nodes): @@ -3663,50 +3663,50 @@ def setUp(self): def test_osf_login_with_auth(self): # login: user with auth data = login_and_register_handler(self.auth) - assert_equal(data.get('status_code'), http.FOUND) + assert_equal(data.get('status_code'), http_status.HTTP_302_FOUND) assert_equal(data.get('next_url'), web_url_for('dashboard', _absolute=True)) def test_osf_login_without_auth(self): # login: user without auth data = login_and_register_handler(self.no_auth) - assert_equal(data.get('status_code'), http.FOUND) + assert_equal(data.get('status_code'), http_status.HTTP_302_FOUND) assert_equal(data.get('next_url'), web_url_for('dashboard', _absolute=True)) def test_osf_register_with_auth(self): # register: user with auth data = login_and_register_handler(self.auth, login=False) - assert_equal(data.get('status_code'), http.FOUND) + assert_equal(data.get('status_code'), http_status.HTTP_302_FOUND) assert_equal(data.get('next_url'), web_url_for('dashboard', _absolute=True)) def test_osf_register_without_auth(self): # register: user without auth data = login_and_register_handler(self.no_auth, login=False) - assert_equal(data.get('status_code'), http.OK) + assert_equal(data.get('status_code'), http_status.HTTP_200_OK) assert_equal(data.get('next_url'), web_url_for('dashboard', _absolute=True)) def test_next_url_login_with_auth(self): # next_url login: user with auth data = login_and_register_handler(self.auth, next_url=self.next_url) - assert_equal(data.get('status_code'), http.FOUND) + assert_equal(data.get('status_code'), http_status.HTTP_302_FOUND) assert_equal(data.get('next_url'), self.next_url) def test_next_url_login_without_auth(self): # login: user without auth request.url = web_url_for('auth_login', next=self.next_url, _absolute=True) data = login_and_register_handler(self.no_auth, next_url=self.next_url) - assert_equal(data.get('status_code'), http.FOUND) + assert_equal(data.get('status_code'), http_status.HTTP_302_FOUND) assert_equal(data.get('next_url'), get_login_url(request.url)) def test_next_url_register_with_auth(self): # register: user with auth data = login_and_register_handler(self.auth, login=False, next_url=self.next_url) - assert_equal(data.get('status_code'), http.FOUND) + assert_equal(data.get('status_code'), http_status.HTTP_302_FOUND) assert_equal(data.get('next_url'), self.next_url) def test_next_url_register_without_auth(self): # register: user without auth data = login_and_register_handler(self.no_auth, login=False, next_url=self.next_url) - assert_equal(data.get('status_code'), http.OK) + assert_equal(data.get('status_code'), http_status.HTTP_200_OK) assert_equal(data.get('next_url'), request.url) def test_institution_login_and_register(self): @@ -3715,13 +3715,13 @@ def test_institution_login_and_register(self): def test_institution_login_with_auth(self): # institution login: user with auth data = login_and_register_handler(self.auth, campaign='institution') - assert_equal(data.get('status_code'), http.FOUND) + assert_equal(data.get('status_code'), http_status.HTTP_302_FOUND) assert_equal(data.get('next_url'), web_url_for('dashboard', _absolute=True)) def test_institution_login_without_auth(self): # institution login: user without auth data = login_and_register_handler(self.no_auth, campaign='institution') - assert_equal(data.get('status_code'), http.FOUND) + assert_equal(data.get('status_code'), http_status.HTTP_302_FOUND) assert_equal( data.get('next_url'), get_login_url(web_url_for('dashboard', _absolute=True), campaign='institution')) @@ -3729,13 +3729,13 @@ def test_institution_login_without_auth(self): def test_institution_login_next_url_with_auth(self): # institution login: user with auth and next url data = login_and_register_handler(self.auth, next_url=self.next_url, campaign='institution') - assert_equal(data.get('status_code'), http.FOUND) + assert_equal(data.get('status_code'), http_status.HTTP_302_FOUND) assert_equal(data.get('next_url'), self.next_url) def test_institution_login_next_url_without_auth(self): # institution login: user without auth and next url data = login_and_register_handler(self.no_auth, next_url=self.next_url ,campaign='institution') - assert_equal(data.get('status_code'), http.FOUND) + assert_equal(data.get('status_code'), http_status.HTTP_302_FOUND) assert_equal( data.get('next_url'), get_login_url(self.next_url, campaign='institution')) @@ -3743,13 +3743,13 @@ def test_institution_login_next_url_without_auth(self): def test_institution_regsiter_with_auth(self): # institution register: user with auth data = login_and_register_handler(self.auth, login=False, campaign='institution') - assert_equal(data.get('status_code'), http.FOUND) + assert_equal(data.get('status_code'), http_status.HTTP_302_FOUND) assert_equal(data.get('next_url'), web_url_for('dashboard', _absolute=True)) def test_institution_register_without_auth(self): # institution register: user without auth data = login_and_register_handler(self.no_auth, login=False, campaign='institution') - assert_equal(data.get('status_code'), http.FOUND) + assert_equal(data.get('status_code'), http_status.HTTP_302_FOUND) assert_equal( data.get('next_url'), get_login_url(web_url_for('dashboard', _absolute=True), campaign='institution') @@ -3761,7 +3761,7 @@ def test_campaign_login_with_auth(self): continue # campaign login: user with auth data = login_and_register_handler(self.auth, campaign=campaign) - assert_equal(data.get('status_code'), http.FOUND) + assert_equal(data.get('status_code'), http_status.HTTP_302_FOUND) assert_equal(data.get('next_url'), campaign_url_for(campaign)) def test_campaign_login_without_auth(self): @@ -3770,7 +3770,7 @@ def test_campaign_login_without_auth(self): continue # campaign login: user without auth data = login_and_register_handler(self.no_auth, campaign=campaign) - assert_equal(data.get('status_code'), http.FOUND) + assert_equal(data.get('status_code'), http_status.HTTP_302_FOUND) assert_equal( data.get('next_url'), web_url_for('auth_register', campaign=campaign, next=campaign_url_for(campaign)) @@ -3782,7 +3782,7 @@ def test_campaign_register_with_auth(self): continue # campaign register: user with auth data = login_and_register_handler(self.auth, login=False, campaign=campaign) - assert_equal(data.get('status_code'), http.FOUND) + assert_equal(data.get('status_code'), http_status.HTTP_302_FOUND) assert_equal(data.get('next_url'), campaign_url_for(campaign)) def test_campaign_register_without_auth(self): @@ -3791,7 +3791,7 @@ def test_campaign_register_without_auth(self): continue # campaign register: user without auth data = login_and_register_handler(self.no_auth, login=False, campaign=campaign) - assert_equal(data.get('status_code'), http.OK) + assert_equal(data.get('status_code'), http_status.HTTP_200_OK) if is_native_login(campaign): # native campaign: prereg and erpc assert_equal(data.get('next_url'), campaign_url_for(campaign)) @@ -3809,7 +3809,7 @@ def test_campaign_next_url_login_with_auth(self): # campaign login: user with auth next_url = campaign_url_for(campaign) data = login_and_register_handler(self.auth, campaign=campaign, next_url=next_url) - assert_equal(data.get('status_code'), http.FOUND) + assert_equal(data.get('status_code'), http_status.HTTP_302_FOUND) assert_equal(data.get('next_url'), next_url) def test_campaign_next_url_login_without_auth(self): @@ -3819,7 +3819,7 @@ def test_campaign_next_url_login_without_auth(self): # campaign login: user without auth next_url = campaign_url_for(campaign) data = login_and_register_handler(self.no_auth, campaign=campaign, next_url=next_url) - assert_equal(data.get('status_code'), http.FOUND) + assert_equal(data.get('status_code'), http_status.HTTP_302_FOUND) assert_equal( data.get('next_url'), web_url_for('auth_register', campaign=campaign, next=next_url) @@ -3832,7 +3832,7 @@ def test_campaign_next_url_register_with_auth(self): # campaign register: user with auth next_url = campaign_url_for(campaign) data = login_and_register_handler(self.auth, login=False, campaign=campaign, next_url=next_url) - assert_equal(data.get('status_code'), http.FOUND) + assert_equal(data.get('status_code'), http_status.HTTP_302_FOUND) assert_equal(data.get('next_url'), next_url) def test_campaign_next_url_register_without_auth(self): @@ -3842,7 +3842,7 @@ def test_campaign_next_url_register_without_auth(self): # campaign register: user without auth next_url = campaign_url_for(campaign) data = login_and_register_handler(self.no_auth, login=False, campaign=campaign, next_url=next_url) - assert_equal(data.get('status_code'), http.OK) + assert_equal(data.get('status_code'), http_status.HTTP_200_OK) if is_native_login(campaign): # native campaign: prereg and erpc assert_equal(data.get('next_url'), next_url) @@ -3861,7 +3861,7 @@ def test_invalid_campaign_login_without_auth(self): next_url=self.next_url ) redirect_url = web_url_for('auth_login', campaigns=None, next=self.next_url) - assert_equal(data['status_code'], http.FOUND) + assert_equal(data['status_code'], http_status.HTTP_302_FOUND) assert_equal(data['next_url'], redirect_url) assert_equal(data['campaign'], None) @@ -3873,7 +3873,7 @@ def test_invalid_campaign_register_without_auth(self): next_url=self.next_url ) redirect_url = web_url_for('auth_register', campaigns=None, next=self.next_url) - assert_equal(data['status_code'], http.FOUND) + assert_equal(data['status_code'], http_status.HTTP_302_FOUND) assert_equal(data['next_url'], redirect_url) assert_equal(data['campaign'], None) @@ -3893,7 +3893,7 @@ def test_register_logout_flag_with_auth(self): def test_register_logout_flage_without(self): # the second step is to land user on register page with "MUST LOGIN" warning data = login_and_register_handler(self.no_auth, login=False, campaign=None, next_url=self.next_url, logout=True) - assert_equal(data.get('status_code'), http.OK) + assert_equal(data.get('status_code'), http_status.HTTP_200_OK) assert_equal(data.get('next_url'), self.next_url) assert_true(data.get('must_login_warning')) @@ -3916,37 +3916,37 @@ def tearDown(self): def test_logout_with_valid_next_url_logged_in(self): logout_url = web_url_for('auth_logout', _absolute=True, next=self.valid_next_url) resp = self.app.get(logout_url, auth=self.auth_user.auth) - assert_equal(resp.status_code, http.FOUND) + assert_equal(resp.status_code, http_status.HTTP_302_FOUND) assert_equal(cas.get_logout_url(logout_url), resp.headers['Location']) def test_logout_with_valid_next_url_logged_out(self): logout_url = web_url_for('auth_logout', _absolute=True, next=self.valid_next_url) resp = self.app.get(logout_url, auth=None) - assert_equal(resp.status_code, http.FOUND) + assert_equal(resp.status_code, http_status.HTTP_302_FOUND) assert_equal(self.valid_next_url, resp.headers['Location']) def test_logout_with_invalid_next_url_logged_in(self): logout_url = web_url_for('auth_logout', _absolute=True, next=self.invalid_next_url) resp = self.app.get(logout_url, auth=self.auth_user.auth) - assert_equal(resp.status_code, http.FOUND) + assert_equal(resp.status_code, http_status.HTTP_302_FOUND) assert_equal(cas.get_logout_url(self.goodbye_url), resp.headers['Location']) def test_logout_with_invalid_next_url_logged_out(self): logout_url = web_url_for('auth_logout', _absolute=True, next=self.invalid_next_url) resp = self.app.get(logout_url, auth=None) - assert_equal(resp.status_code, http.FOUND) + assert_equal(resp.status_code, http_status.HTTP_302_FOUND) assert_equal(cas.get_logout_url(self.goodbye_url), resp.headers['Location']) def test_logout_with_redirect_url(self): logout_url = web_url_for('auth_logout', _absolute=True, redirect_url=self.redirect_url) resp = self.app.get(logout_url, auth=self.auth_user.auth) - assert_equal(resp.status_code, http.FOUND) + assert_equal(resp.status_code, http_status.HTTP_302_FOUND) assert_equal(cas.get_logout_url(self.redirect_url), resp.headers['Location']) def test_logout_with_no_parameter(self): logout_url = web_url_for('auth_logout', _absolute=True) resp = self.app.get(logout_url, auth=None) - assert_equal(resp.status_code, http.FOUND) + assert_equal(resp.status_code, http_status.HTTP_302_FOUND) assert_equal(cas.get_logout_url(self.goodbye_url), resp.headers['Location']) @@ -4315,7 +4315,7 @@ def test_sync_data_from_mailchimp_fails_without_secret_key(self): 'email': 'freddie@cos.io'}}} url = api_url_for('sync_data_from_mailchimp') res = self.app.post_json(url, payload, auth=user.auth, expect_errors=True) - assert_equal(res.status_code, http.UNAUTHORIZED) + assert_equal(res.status_code, http_status.HTTP_401_UNAUTHORIZED) @classmethod def tearDownClass(cls): @@ -4335,7 +4335,7 @@ def setUp(self): def test_grid_data(self): url = self.project.api_url_for('grid_data') res = self.app.get(url, auth=self.user.auth).maybe_follow() - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) expected = rubeus.to_hgrid(self.project, auth=Auth(self.user)) data = res.json['data'] assert_equal(len(data), len(expected)) @@ -4679,7 +4679,7 @@ def test_can_view_profile(self): user = UnconfirmedUserFactory() url = web_url_for('profile_view_id', uid=user._id) res = self.app.get(url, expect_errors=True) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) class TestStaticFileViews(OsfTestCase): @@ -4892,7 +4892,7 @@ def test_can_reset_password_if_form_success(self, mock_service_validate): assert_equal(res.status_code, 302) location = res.headers.get('Location') assert_true('login?service=' in location) - assert_true('username={}'.format(urllib.quote(self.user.username, safe='@')) in location) + assert_true('username={}'.format(quote(self.user.username, safe='@')) in location) assert_true('verification_key={}'.format(self.user.verification_key) in location) # check if password was updated @@ -4996,7 +4996,7 @@ def test_deleted_quick_file_gone(self): url = web_url_for('resolve_guid', _guid=True, guid=guid) res = self.app.get(url, expect_errors=True) - assert_equal(res.status_code, http.GONE) + assert_equal(res.status_code, http_status.HTTP_410_GONE) assert_equal(res.request.path, '/{}/'.format(guid)) class TestConfirmationViewBlockBingPreview(OsfTestCase): diff --git a/tests/test_webtests.py b/tests/test_webtests.py index 3ebc0d41c67..8d624e8b431 100644 --- a/tests/test_webtests.py +++ b/tests/test_webtests.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- """Functional tests using WebTest.""" import datetime as dt -import httplib as http +from rest_framework import status as http_status import logging import unittest @@ -662,7 +662,7 @@ def test_error_page_if_confirm_link_is_used(self): res = self.app.get(self.confirmation_url, expect_errors=True) assert_in(auth_exc.InvalidTokenError.message_short, res) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) @pytest.mark.enable_implicit_clean diff --git a/website/archiver/tasks.py b/website/archiver/tasks.py index 5179266157d..d9b26e32881 100644 --- a/website/archiver/tasks.py +++ b/website/archiver/tasks.py @@ -1,6 +1,6 @@ import requests import json -import httplib as http +from rest_framework import status as http_status import celery from celery.utils.log import get_task_logger @@ -162,7 +162,7 @@ def make_copy_request(job_pk, url, data): src, dst, user = job.info() logger.info('Sending copy request for addon: {0} on node: {1}'.format(data['provider'], dst._id)) res = requests.post(url, data=json.dumps(data)) - if res.status_code not in (http.OK, http.CREATED, http.ACCEPTED): + if res.status_code not in (http_status.HTTP_200_OK, http_status.HTTP_201_CREATED, http_status.HTTP_202_ACCEPTED): raise HTTPError(res.status_code) def make_waterbutler_payload(dst_id, rename): diff --git a/website/citations/providers.py b/website/citations/providers.py index fd5e32b43dc..786572302ea 100644 --- a/website/citations/providers.py +++ b/website/citations/providers.py @@ -1,5 +1,5 @@ import abc -import httplib as http +from rest_framework import status as http_status from framework.auth import Auth from framework.exceptions import HTTPError @@ -141,12 +141,12 @@ def add_user_auth(self, node_addon, user, external_account_id): external_account = ExternalAccount.load(external_account_id) if not user.external_accounts.filter(id=external_account.id).all(): - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) try: node_addon.set_auth(external_account, user) except PermissionsError: - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) result = self.serializer( node_settings=node_addon, @@ -222,7 +222,7 @@ def citation_list(self, node_addon, user, list_id, show='all'): while ancestor_id != attached_list_id: if ancestor_id is '__': - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) ancestor_id = folders[ancestor_id].get('parent_list_id') contents = [] diff --git a/website/conferences/views.py b/website/conferences/views.py index 5f99b0eee81..54880c724aa 100644 --- a/website/conferences/views.py +++ b/website/conferences/views.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -import httplib +from rest_framework import status as http_status import logging from django.db import transaction, connection @@ -33,13 +33,13 @@ def meeting_hook(): message.verify() except ConferenceError as error: logger.error(error) - raise HTTPError(httplib.NOT_ACCEPTABLE) + raise HTTPError(http_status.HTTP_406_NOT_ACCEPTABLE) try: conference = Conference.get_by_endpoint(message.conference_name, active=False) except ConferenceError as error: logger.error(error) - raise HTTPError(httplib.NOT_ACCEPTABLE) + raise HTTPError(http_status.HTTP_406_NOT_ACCEPTABLE) if not conference.active: send_mail( @@ -50,7 +50,7 @@ def meeting_hook(): can_change_preferences=False, logo=settings.OSF_MEETINGS_LOGO, ) - raise HTTPError(httplib.NOT_ACCEPTABLE) + raise HTTPError(http_status.HTTP_406_NOT_ACCEPTABLE) add_poster_by_email(conference=conference, message=message) @@ -149,7 +149,7 @@ def conference_data(meeting): try: conf = Conference.objects.get(endpoint__iexact=meeting) except Conference.DoesNotExist: - raise HTTPError(httplib.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) return conference_submissions_sql(conf) @@ -282,7 +282,7 @@ def conference_results(meeting): try: conf = Conference.objects.get(endpoint__iexact=meeting) except Conference.DoesNotExist: - raise HTTPError(httplib.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) data = conference_data(meeting) diff --git a/website/filters/__init__.py b/website/filters/__init__.py index e1dd014d587..d209502972b 100644 --- a/website/filters/__init__.py +++ b/website/filters/__init__.py @@ -1,5 +1,5 @@ import hashlib -import urllib +from future.moves.urllib.parse import urlencode # Adapted from https://github.com/zzzsochi/Flask-Gravatar/blob/master/flaskext/gravatar.py def gravatar(user, use_ssl=False, d=None, r=None, size=None): @@ -23,7 +23,7 @@ def gravatar(user, use_ssl=False, d=None, r=None, size=None): params.append(('s', size)) if r: params.append(('r', r)) - url = base_url + hash_code + '?' + urllib.urlencode(params) + url = base_url + hash_code + '?' + urlencode(params) return url diff --git a/website/identifiers/views.py b/website/identifiers/views.py index 1c4b9d6605c..632a8e12cec 100644 --- a/website/identifiers/views.py +++ b/website/identifiers/views.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status from framework.exceptions import HTTPError from osf.models import NodeLog @@ -20,13 +20,13 @@ def node_identifiers_post(auth, node, **kwargs): # TODO this functionality exists in APIv2. When front end is entirely using # v2 for minting DOI's, remove this view. if not node.is_public or node.is_retracted: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) if node.get_identifier('doi') or node.get_identifier('ark'): - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) try: identifiers = get_or_create_identifiers(node) except HTTPError: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) for category, value in identifiers.items(): node.set_identifier_value(category, value) node.add_log( @@ -38,4 +38,4 @@ def node_identifiers_post(auth, node, **kwargs): }, auth=auth, ) - return identifiers, http.CREATED + return identifiers, http_status.HTTP_201_CREATED diff --git a/website/institutions/views.py b/website/institutions/views.py index 42c3b08273f..e28ca56cec4 100644 --- a/website/institutions/views.py +++ b/website/institutions/views.py @@ -1,4 +1,4 @@ -import httplib as http +from rest_framework import status as http_status from framework.exceptions import HTTPError @@ -19,5 +19,5 @@ def view_institution(inst_id, **kwargs): try: inst = Institution.objects.get(_id=inst_id, is_deleted=False) except Institution.DoesNotExist: - raise HTTPError(http.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) return serialize_institution(inst) diff --git a/website/notifications/views.py b/website/notifications/views.py index a155dcc2e2e..e2be194cf23 100644 --- a/website/notifications/views.py +++ b/website/notifications/views.py @@ -1,4 +1,4 @@ -import httplib as http +from rest_framework import status as http_status from flask import request @@ -44,7 +44,7 @@ def configure_subscription(auth): provider = json_data.get('provider') if not event or (notification_type not in NOTIFICATION_TYPES and notification_type != 'adopt_parent'): - raise HTTPError(http.BAD_REQUEST, data=dict( + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_long='Must provide an event and notification type for subscription.') ) @@ -61,24 +61,24 @@ def configure_subscription(auth): '{!r} attempted to subscribe to either a bad ' 'id or non-node non-self id, {}'.format(user, target_id) ) - raise HTTPError(http.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) if notification_type == 'adopt_parent': sentry.log_message( '{!r} attempted to adopt_parent of a none node id, {}'.format(user, target_id) ) - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) owner = user else: if not node.has_permission(user, READ): sentry.log_message('{!r} attempted to subscribe to private node, {}'.format(user, target_id)) - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) if isinstance(node, Registration): sentry.log_message( '{!r} attempted to subscribe to registration, {}'.format(user, target_id) ) - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) if notification_type != 'adopt_parent': owner = node @@ -92,7 +92,7 @@ def configure_subscription(auth): '{!r} attempted to adopt_parent of ' 'the parentless project, {!r}'.format(user, node) ) - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # If adopt_parent make sure that this subscription is None for the current User subscription = NotificationSubscription.load(event_id) diff --git a/website/oauth/views.py b/website/oauth/views.py index 4cd07c8c5c6..401b05ab3c4 100644 --- a/website/oauth/views.py +++ b/website/oauth/views.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status from flask import redirect @@ -16,10 +16,10 @@ def oauth_disconnect(external_account_id, auth): user = auth.user if account is None: - raise HTTPError(http.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) if not user.external_accounts.filter(id=account.id).exists(): - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) # iterate AddonUserSettings for addons for user_settings in user.get_oauth_addons(): diff --git a/website/preprints/tasks.py b/website/preprints/tasks.py index debb5871a02..a7257a7a88e 100644 --- a/website/preprints/tasks.py +++ b/website/preprints/tasks.py @@ -1,6 +1,6 @@ from django.apps import apps import logging -import urlparse +from future.moves.urllib.parse import urljoin import random import requests @@ -149,7 +149,7 @@ def format_preprint(preprint, share_type, old_subjects=None): }) to_visit = [ preprint_graph, - GraphNode('workidentifier', creative_work=preprint_graph, uri=urlparse.urljoin(settings.DOMAIN, preprint._id + '/')) + GraphNode('workidentifier', creative_work=preprint_graph, uri=urljoin(settings.DOMAIN, preprint._id + '/')) ] if preprint.get_identifier('doi'): diff --git a/website/profile/views.py b/website/profile/views.py index 5fd67f98053..7b4e4eab7fc 100644 --- a/website/profile/views.py +++ b/website/profile/views.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- import logging -import httplib -import httplib as http # TODO: Inconsistent usage of aliased import +from rest_framework import status as http_status from dateutil.parser import parse as parse_date from django.utils import timezone @@ -56,10 +55,10 @@ def validate_user(data, user): """Check if the user in request is the user who log in """ if 'id' in data: if data['id'] != user._id: - raise HTTPError(httplib.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) else: # raise an error if request doesn't have user id - raise HTTPError(httplib.BAD_REQUEST, data={'message_long': '"id" is required'}) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={'message_long': '"id" is required'}) @must_be_logged_in def resend_confirmation(auth): @@ -68,7 +67,7 @@ def resend_confirmation(auth): validate_user(data, user) if not throttle_period_expired(user.email_last_sent, settings.SEND_EMAIL_THROTTLE): - raise HTTPError(httplib.BAD_REQUEST, + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={'message_long': 'Too many requests. Please wait a while before sending another confirmation email.'}) try: @@ -76,10 +75,10 @@ def resend_confirmation(auth): confirmed = data['email']['confirmed'] address = data['email']['address'].strip().lower() except KeyError: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) if primary or confirmed: - raise HTTPError(httplib.BAD_REQUEST, data={'message_long': 'Cannnot resend confirmation for confirmed emails'}) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={'message_long': 'Cannnot resend confirmation for confirmed emails'}) user.add_unconfirmed_email(address) @@ -113,7 +112,7 @@ def update_user(auth): emails_list = [x['address'].strip().lower() for x in data['emails']] if user.username.strip().lower() not in emails_list: - raise HTTPError(httplib.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) available_emails = [ each.strip().lower() for each in @@ -127,14 +126,14 @@ def update_user(auth): ] if user.username.strip().lower() in removed_emails: - raise HTTPError(httplib.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) for address in removed_emails: if user.emails.filter(address=address): try: user.remove_email(address) except PermissionsError as e: - raise HTTPError(httplib.FORBIDDEN, str(e)) + raise HTTPError(http_status.HTTP_403_FORBIDDEN, str(e)) user.remove_unconfirmed_email(address) # additions @@ -148,7 +147,7 @@ def update_user(auth): try: user.add_unconfirmed_email(address) except (ValidationError, ValueError): - raise HTTPError(http.BAD_REQUEST, data=dict( + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_long='Invalid Email') ) except BlacklistedEmailError: @@ -159,14 +158,14 @@ def update_user(auth): 'address': address, } ) - raise HTTPError(http.BAD_REQUEST, data=dict( + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_long=language.BLACKLISTED_EMAIL) ) # TODO: This setting is now named incorrectly. if settings.CONFIRM_REGISTRATIONS_BY_EMAIL: if not throttle_period_expired(user.email_last_sent, settings.SEND_EMAIL_THROTTLE): - raise HTTPError(httplib.BAD_REQUEST, + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={'message_long': 'Too many requests. Please wait a while before adding an email to your account.'}) send_confirm_email(user, email=address) @@ -188,7 +187,7 @@ def update_user(auth): if primary_email: primary_email_address = primary_email['address'].strip().lower() if primary_email_address not in [each.strip().lower() for each in user.emails.values_list('address', flat=True)]: - raise HTTPError(httplib.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) username = primary_email_address # make sure the new username has already been confirmed @@ -236,7 +235,7 @@ def update_user(auth): def _profile_view(profile, is_profile=False, include_node_counts=False): if profile and profile.is_disabled: - raise HTTPError(http.GONE) + raise HTTPError(http_status.HTTP_410_GONE) if profile: profile_quickfilesnode = QuickFilesNode.objects.get_for_user(profile) @@ -252,7 +251,7 @@ def _profile_view(profile, is_profile=False, include_node_counts=False): }, } return ret - raise HTTPError(http.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) @must_be_logged_in def profile_view_json(auth): @@ -403,13 +402,13 @@ def oauth_application_detail(auth, **kwargs): try: record = ApiOAuth2Application.objects.get(client_id=client_id) except ApiOAuth2Application.DoesNotExist: - raise HTTPError(http.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) except ValueError: # Invalid client ID -- ApiOAuth2Application will not exist - raise HTTPError(http.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) if record.owner != auth.user: - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) if record.is_active is False: - raise HTTPError(http.GONE) + raise HTTPError(http_status.HTTP_410_GONE) app_detail_url = api_v2_url('applications/{}/'.format(client_id)) # Send request to this URL return {'app_list_url': '', @@ -443,11 +442,11 @@ def personal_access_token_detail(auth, **kwargs): try: record = ApiOAuth2PersonalToken.objects.get(_id=_id) except ApiOAuth2PersonalToken.DoesNotExist: - raise HTTPError(http.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) if record.owner != auth.user: - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) if record.is_active is False: - raise HTTPError(http.GONE) + raise HTTPError(http_status.HTTP_410_GONE) token_detail_url = api_v2_url('tokens/{}/'.format(_id)) # Send request to this URL return {'token_list_url': '', @@ -460,7 +459,7 @@ def delete_external_identity(auth, **kwargs): data = request.get_json() identity = data.get('identity') if not identity: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) for service in auth.user.external_identity: if identity in auth.user.external_identity[service]: @@ -470,7 +469,7 @@ def delete_external_identity(auth, **kwargs): auth.user.save() return - raise HTTPError(http.NOT_FOUND, 'Unable to find requested identity') + raise HTTPError(http_status.HTTP_404_NOT_FOUND, 'Unable to find requested identity') def collect_user_config_js(addon_configs): """Collect webpack bundles for each of the addons' user-cfg.js modules. Return @@ -513,7 +512,7 @@ def user_choose_mailing_lists(auth, **kwargs): else: update_mailchimp_subscription(user, list_name, subscribe) else: - raise HTTPError(http.BAD_REQUEST, data=dict( + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_long="Must provide a dictionary of the format {'mailing list name': Boolean}") ) @@ -547,7 +546,7 @@ def update_mailchimp_subscription(user, list_name, subscription, send_goodbye=Tr def mailchimp_get_endpoint(**kwargs): """Endpoint that the mailchimp webhook hits to check that the OSF is responding""" - return {}, http.OK + return {}, http_status.HTTP_200_OK def sync_data_from_mailchimp(**kwargs): @@ -579,7 +578,7 @@ def sync_data_from_mailchimp(**kwargs): # TODO: get tests to pass with sentry logging # sentry.log_exception() # sentry.log_message("Unauthorized request to the OSF.") - raise HTTPError(http.UNAUTHORIZED) + raise HTTPError(http_status.HTTP_401_UNAUTHORIZED) @must_be_logged_in @@ -607,7 +606,7 @@ def serialize_names(**kwargs): def get_target_user(auth, uid=None): target = OSFUser.load(uid) if uid else auth.user if target is None: - raise HTTPError(http.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) return target @@ -617,7 +616,7 @@ def fmt_date_or_none(date, fmt='%Y-%m-%d'): return date.strftime(fmt) except ValueError: raise HTTPError( - http.BAD_REQUEST, + http_status.HTTP_400_BAD_REQUEST, data=dict(message_long='Year entered must be after 1900') ) return None @@ -715,7 +714,7 @@ def unserialize_names(**kwargs): def verify_user_match(auth, **kwargs): uid = kwargs.get('uid') if uid and uid != auth.user._id: - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) @must_be_logged_in @@ -732,7 +731,7 @@ def unserialize_social(auth, **kwargs): try: user.save() except ValidationError as exc: - raise HTTPError(http.BAD_REQUEST, data=dict( + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_long=exc.messages[0] )) @@ -800,7 +799,7 @@ def unserialize_schools(auth, **kwargs): def request_export(auth): user = auth.user if not throttle_period_expired(user.email_last_sent, settings.SEND_EMAIL_THROTTLE): - raise HTTPError(httplib.BAD_REQUEST, + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={'message_long': 'Too many requests. Please wait a while before sending another account export request.', 'error_type': 'throttle_error'}) diff --git a/website/project/decorators.py b/website/project/decorators.py index f0e4068db4c..4602f5a07fb 100644 --- a/website/project/decorators.py +++ b/website/project/decorators.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- import functools -import httplib as http +from rest_framework import status as http_status from furl import furl from flask import request @@ -14,7 +14,7 @@ from osf.models import AbstractNode, Guid, Preprint, OSFGroup from osf.utils.permissions import WRITE -from website import settings, language +from website import language from website.util import web_url_for _load_node_or_fail = lambda pk: get_or_http_error(AbstractNode, pk) @@ -45,7 +45,7 @@ def _kwargs_to_nodes(kwargs): node = _load_node_or_fail(nid) elif not pid and not nid: raise HTTPError( - http.NOT_FOUND, + http_status.HTTP_404_NOT_FOUND, data={ 'message_short': 'Node not found', 'message_long': 'No Node with that primary key could be found', @@ -68,7 +68,7 @@ def wrapped(*args, **kwargs): node = get_or_http_error(AbstractNode, kwargs.get('nid', kwargs.get('pid')), allow_deleted=True) if node.sanction and node.sanction.is_rejected: - raise HTTPError(http.GONE, data=dict( + raise HTTPError(http_status.HTTP_410_GONE, data=dict( message_long='This registration has been rejected' )) @@ -97,12 +97,12 @@ def wrapped(*args, **kwargs): if getattr(kwargs['node'], 'is_collection', True) or (getattr(kwargs['node'], 'is_quickfiles', True) and not quickfiles_valid): raise HTTPError( - http.NOT_FOUND + http_status.HTTP_404_NOT_FOUND ) if not retractions_valid and getattr(kwargs['node'].retraction, 'is_retracted', False): raise HTTPError( - http.BAD_REQUEST, + http_status.HTTP_400_BAD_REQUEST, data=dict(message_long='Viewing withdrawn registrations is not permitted') ) else: @@ -127,7 +127,7 @@ def wrapped(*args, **kwargs): if not node.is_public or not node.is_registration: raise HTTPError( - http.BAD_REQUEST, + http_status.HTTP_400_BAD_REQUEST, data=dict(message_long='Must be a public registration to view') ) @@ -164,7 +164,7 @@ def wrapped(*args, **kwargs): if getattr(target, 'is_registration', False) and not getattr(target, 'archiving', False): raise HTTPError( - http.BAD_REQUEST, + http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Registrations cannot be changed', 'message_long': "The operation you're trying to do cannot be applied to registered projects, which are not allowed to be changed", @@ -183,7 +183,7 @@ def wrapped(*args, **kwargs): if not node.is_registration: raise HTTPError( - http.BAD_REQUEST, + http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Registered Nodes only', 'message_long': 'This view is restricted to registered Nodes only', @@ -218,7 +218,7 @@ def check_can_access(node, user, key=None, api_node=None, include_groups=True): if (not node.can_view(Auth(user=user)) and api_node != node) or (not include_groups and not node.is_contributor(user)): if node.is_deleted: - raise HTTPError(http.GONE, data={'message_long': 'The node for this file has been deleted.'}) + raise HTTPError(http_status.HTTP_410_GONE, data={'message_long': 'The node for this file has been deleted.'}) if getattr(node, 'private_link_keys_deleted', False) and key in node.private_link_keys_deleted: status.push_status_message('The view-only links you used are expired.', trust=False) @@ -235,13 +235,13 @@ def check_can_access(node, user, key=None, api_node=None, include_groups=True): } } raise TemplateHTTPError( - http.FORBIDDEN, + http_status.HTTP_403_FORBIDDEN, template='request_access.mako', data=data ) raise HTTPError( - http.FORBIDDEN, + http_status.HTTP_403_FORBIDDEN, data={'message_long': ('User has restricted access to this page. If this should not ' 'have occurred and the issue persists, ' + language.SUPPORT_LINK)} ) @@ -326,13 +326,13 @@ def wrapped(*args, **kwargs): auth = kwargs.get('auth') owner = auth.user if auth else None if owner is None: - raise HTTPError(http.UNAUTHORIZED) + raise HTTPError(http_status.HTTP_401_UNAUTHORIZED) else: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) addon = owner.get_addon(addon_name) if addon is None: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) kwargs['{0}_addon'.format(model)] = addon @@ -363,16 +363,16 @@ def wrapped(*args, **kwargs): node_addon = node.get_addon(addon_name) if not node_addon: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) if not node_addon.user_settings: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) auth = kwargs.get('auth') user = kwargs.get('user') or (auth.user if auth else None) if node_addon.user_settings.owner != user: - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) return func(*args, **kwargs) @@ -388,8 +388,8 @@ def must_have_permission(permission): :param list permissions: List of accepted permissions :returns: Decorator function for checking permissions - :raises: HTTPError(http.UNAUTHORIZED) if not logged in - :raises: HTTPError(http.FORBIDDEN) if missing permissions + :raises: HTTPError(http_status.HTTP_401_UNAUTHORIZED) if not logged in + :raises: HTTPError(http_status.HTTP_403_FORBIDDEN) if missing permissions """ def wrapper(func): @@ -406,11 +406,11 @@ def wrapped(*args, **kwargs): # User must be logged in if user is None: - raise HTTPError(http.UNAUTHORIZED) + raise HTTPError(http_status.HTTP_401_UNAUTHORIZED) # User must have permissions if not target.has_permission(user, permission): - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) # Call view function return func(*args, **kwargs) @@ -438,20 +438,6 @@ def wrapped(*args, **kwargs): # Return decorated function return wrapped -def http_error_if_disk_saving_mode(func): - - @functools.wraps(func) - def wrapper(*args, **kwargs): - _inject_nodes(kwargs) - node = kwargs['node'] - - if settings.DISK_SAVING_MODE: - raise HTTPError( - http.METHOD_NOT_ALLOWED, - redirect_url=node.url - ) - return func(*args, **kwargs) - return wrapper def check_contributor_auth(node, auth, include_public, include_view_only_anon, include_groups=True): response = None @@ -470,12 +456,12 @@ def check_contributor_auth(node, auth, include_public, include_view_only_anon, i if not node.is_public or not include_public: if not include_view_only_anon and link_anon: if not check_can_access(node=node, user=user, include_groups=include_groups): - raise HTTPError(http.UNAUTHORIZED) + raise HTTPError(http_status.HTTP_401_UNAUTHORIZED) elif not getattr(node, 'private_link_keys_active', False) or auth.private_key not in node.private_link_keys_active: if not check_can_access(node=node, user=user, key=auth.private_key, include_groups=include_groups): redirect_url = check_key_expired(key=auth.private_key, node=node, url=request.url) if request.headers.get('Content-Type') == 'application/json': - raise HTTPError(http.UNAUTHORIZED) + raise HTTPError(http_status.HTTP_401_UNAUTHORIZED) else: response = redirect(cas.get_login_url(redirect_url)) diff --git a/website/project/tasks.py b/website/project/tasks.py index a1a63cd2c10..ac02d4a1003 100644 --- a/website/project/tasks.py +++ b/website/project/tasks.py @@ -1,6 +1,6 @@ from django.apps import apps import logging -import urlparse +from future.moves.urllib.parse import urljoin import random import requests @@ -137,7 +137,7 @@ def format_registration(node): to_visit = [ registration_graph, - GraphNode('workidentifier', creative_work=registration_graph, uri=urlparse.urljoin(settings.DOMAIN, node.url)) + GraphNode('workidentifier', creative_work=registration_graph, uri=urljoin(settings.DOMAIN, node.url)) ] registration_graph.attrs['tags'] = [ @@ -152,7 +152,7 @@ def format_registration(node): parent = GraphNode('registration') to_visit.extend([ parent, - GraphNode('workidentifier', creative_work=parent, uri=urlparse.urljoin(settings.DOMAIN, node.parent_node.url)), + GraphNode('workidentifier', creative_work=parent, uri=urljoin(settings.DOMAIN, node.parent_node.url)), GraphNode('ispartof', subject=registration_graph, related=parent), ]) diff --git a/website/project/views/contributor.py b/website/project/views/contributor.py index 09c15913144..d12d307f1b9 100644 --- a/website/project/views/contributor.py +++ b/website/project/views/contributor.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status from flask import request from django.core.exceptions import ValidationError @@ -48,7 +48,7 @@ def get_node_contributors_abbrev(auth, node, **kwargs): users = node.visible_contributors if anonymous or not node.can_view(auth): - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) contributors = [] @@ -87,7 +87,7 @@ def get_contributors(auth, node, **kwargs): try: limit = int(request.args['limit']) except ValueError: - raise HTTPError(http.BAD_REQUEST, data=dict( + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_long='Invalid value for "limit": {}'.format(request.args['limit']) )) else: @@ -96,7 +96,7 @@ def get_contributors(auth, node, **kwargs): anonymous = has_anonymous_link(node, auth) if anonymous or not node.can_view(auth): - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) # Limit is either an int or None: # if int, contribs list is sliced to specified length @@ -123,10 +123,10 @@ def get_contributors_from_parent(auth, node, **kwargs): parent = node.parent_node if not parent: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) if not node.can_view(auth): - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) contribs = [ profile_utils.add_contributor_json(contrib, node=node) @@ -220,7 +220,7 @@ def project_contributors_post(auth, node, **kwargs): node_ids.remove(node._id) if user_dicts is None or node_ids is None: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # Prepare input data for `Node::add_contributors` try: @@ -283,7 +283,7 @@ def project_manage_contributors(auth, node, **kwargs): try: node.manage_contributors(contributors, auth=auth, save=True) except (ValueError, NodeStateError) as error: - raise HTTPError(http.BAD_REQUEST, data={'message_long': error.args[0]}) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={'message_long': error.args[0]}) # If user has removed herself from project, alert; redirect to # node summary if node is public, else to user's dashboard page @@ -323,7 +323,7 @@ def project_remove_contributor(auth, **kwargs): node_ids = request.get_json()['nodeIDs'] contributor = OSFUser.load(contributor_id) if contributor is None: - raise HTTPError(http.BAD_REQUEST, data={'message_long': 'Contributor not found.'}) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={'message_long': 'Contributor not found.'}) redirect_url = {} parent_id = node_ids[0] for node_id in node_ids: @@ -333,18 +333,18 @@ def project_remove_contributor(auth, **kwargs): # Forbidden unless user is removing herself if not node.has_permission(auth.user, ADMIN): if auth.user != contributor: - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) if node.visible_contributors.count() == 1 \ and node.visible_contributors[0] == contributor: - raise HTTPError(http.FORBIDDEN, data={ + raise HTTPError(http_status.HTTP_403_FORBIDDEN, data={ 'message_long': 'Must have at least one bibliographic contributor' }) nodes_removed = node.remove_contributor(contributor, auth=auth) # remove_contributor returns false if there is not one admin or visible contributor left after the move. if not nodes_removed: - raise HTTPError(http.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_long': 'Could not remove contributor.'}) # On parent node, if user has removed herself from project, alert; redirect to @@ -374,7 +374,7 @@ def send_claim_registered_email(claimer, unclaimed_user, node, throttle=24 * 360 :param node: the project node where the user account is claimed :param throttle: the time period in seconds before another claim for the account can be made :return: - :raise: http.BAD_REQUEST + :raise: http_status.HTTP_400_BAD_REQUEST """ unclaimed_record = unclaimed_user.get_unclaimed_record(node._primary_key) @@ -382,7 +382,7 @@ def send_claim_registered_email(claimer, unclaimed_user, node, throttle=24 * 360 # check throttle timestamp = unclaimed_record.get('last_sent') if not throttle_period_expired(timestamp, throttle): - raise HTTPError(http.BAD_REQUEST, data=dict( + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_long='User account can only be claimed with an existing user once every 24 hours' )) @@ -444,7 +444,7 @@ def send_claim_email(email, unclaimed_user, node, notify=True, throttle=24 * 360 emailed during which the referrer will not be emailed again. :param str email_template: the email template to use :return - :raise http.BAD_REQUEST + :raise http_status.HTTP_400_BAD_REQUEST """ @@ -484,7 +484,7 @@ def send_claim_email(email, unclaimed_user, node, notify=True, throttle=24 * 360 # check throttle timestamp = unclaimed_record.get('last_sent') if not throttle_period_expired(timestamp, throttle): - raise HTTPError(http.BAD_REQUEST, data=dict( + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_long='User account can only be claimed with an existing user once every 24 hours' )) # roll the valid token for each email, thus user cannot change email and approve a different email address @@ -685,7 +685,7 @@ def claim_user_registered(auth, node, **kwargs): 'message_long': ('The logged-in user is already a contributor to this ' 'project. Would you like to log out?').format(sign_out_url) } - raise HTTPError(http.BAD_REQUEST, data=data) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=data) # Logged in user is already a member of the OSF Group if hasattr(node, 'is_member') and node.is_member(current_user): @@ -694,7 +694,7 @@ def claim_user_registered(auth, node, **kwargs): 'message_long': ('The logged-in user is already a member of this OSF Group. ' 'Would you like to log out?').format(sign_out_url) } - raise HTTPError(http.BAD_REQUEST, data=data) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=data) uid, pid, token = kwargs['uid'], kwargs['pid'], kwargs['token'] unreg_user = OSFUser.load(uid) @@ -703,7 +703,7 @@ def claim_user_registered(auth, node, **kwargs): 'message_short': 'Invalid url.', 'message_long': 'The token in the URL is invalid or has expired.' } - raise HTTPError(http.BAD_REQUEST, data=error_data) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=error_data) # Store the unreg_user data on the session in case the user registers # a new account @@ -791,7 +791,7 @@ def claim_user_form(auth, **kwargs): 'message_short': 'Invalid url.', 'message_long': 'Claim user does not exists, the token in the URL is invalid or has expired.' } - raise HTTPError(http.BAD_REQUEST, data=error_data) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=error_data) # If user is logged in, redirect to 're-enter password' page if auth.logged_in: @@ -820,7 +820,7 @@ def claim_user_form(auth, **kwargs): else: username, password = claimer_email, form.password.data if not username: - raise HTTPError(http.BAD_REQUEST, data=dict( + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_long='No email associated with this account. Please claim this ' 'account on the project to which you were invited.' )) @@ -928,7 +928,7 @@ def claim_user_post(node, **kwargs): send_claim_registered_email(claimer, unclaimed_user, node) email = claimer.username else: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) return { 'status': 'success', diff --git a/website/project/views/drafts.py b/website/project/views/drafts.py index 06bae1bf0da..dd2118edf1b 100644 --- a/website/project/views/drafts.py +++ b/website/project/views/drafts.py @@ -1,5 +1,5 @@ import functools -import httplib as http +from rest_framework import status as http_status import itertools import waffle @@ -39,7 +39,7 @@ def get_schema_or_fail(schema_name, schema_version): try: meta_schema = RegistrationSchema.objects.get(name=schema_name, schema_version=schema_version) except RegistrationSchema.DoesNotExist: - raise HTTPError(http.NOT_FOUND, data=dict( + raise HTTPError(http_status.HTTP_200_OK, data=dict( message_long='No RegistrationSchema record matching that query could be found' )) return meta_schema @@ -52,10 +52,10 @@ def wrapper(*args, **kwargs): node = kwargs['node'] draft = kwargs['draft'] if draft.deleted: - raise HTTPError(http.GONE) + raise HTTPError(http_status.HTTP_410_GONE) if not draft.branched_from._id == node._id: raise HTTPError( - http.BAD_REQUEST, + http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Not a draft of this node', 'message_long': 'This draft registration is not created from the given node.' @@ -79,13 +79,13 @@ def validate_embargo_end_date(end_date_string, node): end_date = parse_date(end_date_string, ignoretz=True).replace(tzinfo=pytz.utc) today = timezone.now() if (end_date - today) <= settings.DRAFT_REGISTRATION_APPROVAL_PERIOD: - raise HTTPError(http.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Invalid embargo end date', 'message_long': 'Embargo end date for this submission must be at least {0} days in the future.'.format(settings.DRAFT_REGISTRATION_APPROVAL_PERIOD) }) elif not node._is_embargo_date_valid(end_date): max_end_date = today + settings.DRAFT_REGISTRATION_APPROVAL_PERIOD - raise HTTPError(http.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Invalid embargo end date', 'message_long': 'Embargo end date must on or before {0}.'.format(max_end_date.isoformat()) }) @@ -93,7 +93,7 @@ def validate_embargo_end_date(end_date_string, node): def validate_registration_choice(registration_choice): if registration_choice not in ('embargo', 'immediate'): raise HTTPError( - http.BAD_REQUEST, + http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': "Invalid 'registrationChoice'", 'message_long': "Values for 'registrationChoice' must be either 'embargo' or 'immediate'." @@ -103,17 +103,17 @@ def validate_registration_choice(registration_choice): def check_draft_state(draft): registered_and_deleted = draft.registered_node and draft.registered_node.is_deleted if draft.registered_node and not registered_and_deleted: - raise HTTPError(http.FORBIDDEN, data={ + raise HTTPError(http_status.HTTP_403_FORBIDDEN, data={ 'message_short': 'This draft has already been registered', 'message_long': 'This draft has already been registered and cannot be modified.' }) if draft.is_pending_review: - raise HTTPError(http.FORBIDDEN, data={ + raise HTTPError(http_status.HTTP_403_FORBIDDEN, data={ 'message_short': 'This draft is pending review', 'message_long': 'This draft is pending review and cannot be modified.' }) if draft.requires_approval and draft.is_approved and (not registered_and_deleted): - raise HTTPError(http.FORBIDDEN, data={ + raise HTTPError(http_status.HTTP_403_FORBIDDEN, data={ 'message_short': 'This draft has already been approved', 'message_long': 'This draft has already been approved and cannot be modified.' }) @@ -129,17 +129,17 @@ def submit_draft_for_review(auth, node, draft, *args, **kwargs): :raises: HTTPError if embargo end date is invalid """ if waffle.switch_is_active(features.OSF_PREREGISTRATION): - raise HTTPError(http.GONE, data={ + raise HTTPError(http_status.HTTP_410_GONE, data={ 'message_short': 'The Prereg Challenge has ended', 'message_long': 'The Prereg Challenge has ended. No new submissions are accepted at this time.' }) json_data = request.get_json() if 'data' not in json_data: - raise HTTPError(http.BAD_REQUEST, data=dict(message_long='Payload must include "data".')) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict(message_long='Payload must include "data".')) data = json_data['data'] if 'attributes' not in data: - raise HTTPError(http.BAD_REQUEST, data=dict(message_long='Payload must include "data/attributes".')) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict(message_long='Payload must include "data/attributes".')) attributes = data['attributes'] meta = {} registration_choice = attributes['registration_choice'] @@ -152,13 +152,13 @@ def submit_draft_for_review(auth, node, draft, *args, **kwargs): meta['registration_choice'] = registration_choice if draft.registered_node and not draft.registered_node.is_deleted: - raise HTTPError(http.BAD_REQUEST, data=dict(message_long='This draft has already been registered, if you wish to ' + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict(message_long='This draft has already been registered, if you wish to ' 'register it again or submit it for review please create ' 'a new draft.')) # Don't allow resubmission unless submission was rejected if draft.approval and draft.approval.state != Sanction.REJECTED: - raise HTTPError(http.CONFLICT, data=dict(message_long='Cannot resubmit previously submitted draft.')) + raise HTTPError(http_status.HTTP_409_CONFLICT, data=dict(message_long='Cannot resubmit previously submitted draft.')) draft.submit_for_review( initiated_by=auth.user, @@ -187,7 +187,7 @@ def submit_draft_for_review(auth, node, draft, *args, **kwargs): } }, 'status': 'initiated', - }, http.ACCEPTED + }, http_status.HTTP_202_ACCEPTED @must_have_permission(ADMIN) @must_be_contributor_and_not_group_member @@ -212,7 +212,7 @@ def get_draft_registration(auth, node, draft, *args, **kwargs): :return: serialized draft registration :rtype: dict """ - return serialize_draft_registration(draft, auth), http.OK + return serialize_draft_registration(draft, auth), http_status.HTTP_200_OK @must_have_permission(ADMIN) @must_be_valid_project @@ -229,7 +229,7 @@ def get_draft_registrations(auth, node, *args, **kwargs): sorted_serialized_drafts = sorted(serialized_drafts, key=itemgetter('updated'), reverse=True) return { 'drafts': sorted_serialized_drafts - }, http.OK + }, http_status.HTTP_200_OK @must_have_permission(ADMIN) @must_be_valid_project @@ -243,7 +243,7 @@ def new_draft_registration(auth, node, *args, **kwargs): :raises: HTTPError """ if node.is_registration: - raise HTTPError(http.FORBIDDEN, data={ + raise HTTPError(http_status.HTTP_403_FORBIDDEN, data={ 'message_short': "Can't create draft", 'message_long': 'Creating draft registrations on registered projects is not allowed.' }) @@ -252,7 +252,7 @@ def new_draft_registration(auth, node, *args, **kwargs): schema_name = data.get('schema_name') if not schema_name: raise HTTPError( - http.BAD_REQUEST, + http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Must specify a schema_name', 'message_long': 'Please specify a schema_name' @@ -312,7 +312,7 @@ def update_draft_registration(auth, node, draft, *args, **kwargs): draft.update_metadata(schema_data) draft.save() - return serialize_draft_registration(draft, auth), http.OK + return serialize_draft_registration(draft, auth), http_status.HTTP_200_OK @must_have_permission(ADMIN) @must_be_contributor_and_not_group_member @@ -325,7 +325,7 @@ def delete_draft_registration(auth, node, draft, *args, **kwargs): """ if draft.registered_node and not draft.registered_node.is_deleted: raise HTTPError( - http.FORBIDDEN, + http_status.HTTP_403_FORBIDDEN, data={ 'message_short': 'Can\'t delete draft', 'message_long': 'This draft has already been registered and cannot be deleted.' @@ -333,7 +333,7 @@ def delete_draft_registration(auth, node, draft, *args, **kwargs): ) draft.deleted = timezone.now() draft.save(update_fields=['deleted']) - return None, http.NO_CONTENT + return None, http_status.HTTP_204_NO_CONTENT def get_metaschemas(*args, **kwargs): """ @@ -355,4 +355,4 @@ def get_metaschemas(*args, **kwargs): 'meta_schemas': [ serialize_meta_schema(ms) for ms in meta_schemas[:count] ] - }, http.OK + }, http_status.HTTP_200_OK diff --git a/website/project/views/node.py b/website/project/views/node.py index 70c307ba9e5..6aadef2398d 100644 --- a/website/project/views/node.py +++ b/website/project/views/node.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import os import logging -import httplib as http +from rest_framework import status as http_status import math from collections import defaultdict from itertools import islice @@ -76,7 +76,7 @@ def edit_node(auth, node, **kwargs): node.set_title(value, auth=auth) except ValidationError as e: raise HTTPError( - http.BAD_REQUEST, + http_status.HTTP_400_BAD_REQUEST, data=dict(message_long=e.message) ) new_val = node.title @@ -90,7 +90,7 @@ def edit_node(auth, node, **kwargs): node.save() except ValidationError as e: raise HTTPError( - http.BAD_REQUEST, + http_status.HTTP_400_BAD_REQUEST, data=dict(message_long=e.message) ) return { @@ -143,14 +143,14 @@ def project_new_post(auth, **kwargs): project = new_node(category, title, user, description) except ValidationError as e: raise HTTPError( - http.BAD_REQUEST, + http_status.HTTP_400_BAD_REQUEST, data=dict(message_long=e.message) ) new_project = _view_project(project, auth) return { 'projectUrl': project.url, 'newNode': new_project['node'] if new_project else None - }, http.CREATED + }, http_status.HTTP_201_CREATED @must_be_logged_in @@ -160,7 +160,7 @@ def project_new_from_template(auth, node, **kwargs): auth=auth, changes=dict(), ) - return {'url': new_node.url}, http.CREATED, None + return {'url': new_node.url}, http_status.HTTP_201_CREATED, None ############################################################################## @@ -187,7 +187,7 @@ def project_new_node(auth, node, **kwargs): ) except ValidationError as e: raise HTTPError( - http.BAD_REQUEST, + http_status.HTTP_400_BAD_REQUEST, data=dict(message_long=e.message) ) redirect_url = node.url @@ -226,7 +226,7 @@ def project_new_node(auth, node, **kwargs): else: # TODO: This function doesn't seem to exist anymore? status.push_errors_to_status(form.errors) - raise HTTPError(http.BAD_REQUEST, redirect_url=node.url) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, redirect_url=node.url) @must_be_logged_in @@ -420,7 +420,7 @@ def configure_comments(node, **kwargs): elif comment_level in ['public', 'private']: node.comment_level = comment_level else: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) node.save() @must_have_permission(ADMIN) @@ -520,7 +520,7 @@ def project_reorder_components(node, **kwargs): ) if len(ordered_guids) > len(node_relations): - raise HTTPError(http.BAD_REQUEST, data=dict(message_long='Too many node IDs')) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict(message_long='Too many node IDs')) # Ordered NodeRelation pks, sorted according the order of guids passed in the request payload new_node_relation_ids = [ @@ -534,7 +534,7 @@ def project_reorder_components(node, **kwargs): return {'nodes': ordered_guids} logger.error('Got invalid node list in reorder components') - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) @must_be_valid_project @must_be_contributor_or_public @@ -555,12 +555,12 @@ def project_set_privacy(auth, node, **kwargs): permissions = kwargs.get('permissions') if permissions is None: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) try: node.set_privacy(permissions, auth) except NodeStateError as e: - raise HTTPError(http.BAD_REQUEST, data=dict( + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_short="Can't change privacy", message_long=str(e) )) @@ -605,7 +605,7 @@ def component_remove(auth, node, **kwargs): node.remove_node(auth) except NodeStateError as e: raise HTTPError( - http.BAD_REQUEST, + http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Error', 'message_long': 'Could not delete component: ' + str(e) @@ -636,7 +636,7 @@ def remove_private_link(*args, **kwargs): try: link = PrivateLink.objects.get(_id=link_id) except PrivateLink.DoesNotExist: - raise HTTPError(http.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) link.is_deleted = True link.save() @@ -1129,7 +1129,7 @@ def project_generate_private_link_post(auth, node, **kwargs): ) except ValidationError as e: raise HTTPError( - http.BAD_REQUEST, + http_status.HTTP_400_BAD_REQUEST, data=dict(message_long=e.message) ) @@ -1145,7 +1145,7 @@ def project_private_link_edit(auth, **kwargs): except ValidationError as e: message = 'Invalid link name.' if e.message == 'Invalid title.' else e.message raise HTTPError( - http.BAD_REQUEST, + http_status.HTTP_400_BAD_REQUEST, data=dict(message_long=message) ) @@ -1159,7 +1159,7 @@ def project_private_link_edit(auth, **kwargs): return new_name else: raise HTTPError( - http.BAD_REQUEST, + http_status.HTTP_400_BAD_REQUEST, data=dict(message_long='View-only link not found.') ) @@ -1266,14 +1266,14 @@ def add_pointer(auth): pointer_to_move = request.json.get('pointerID') if not (to_node_id and pointer_to_move): - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) pointer = AbstractNode.load(pointer_to_move) to_node = Guid.load(to_node_id).referent try: _add_pointers(to_node, [pointer], auth) except ValueError: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) @must_have_permission(WRITE) @@ -1285,7 +1285,7 @@ def add_pointers(auth, node, **kwargs): node_ids = request.json.get('nodeIds') if not node_ids: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) nodes = [ AbstractNode.load(node_id) @@ -1295,7 +1295,7 @@ def add_pointers(auth, node, **kwargs): try: _add_pointers(node, nodes, auth) except ValueError: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) return {} @@ -1311,16 +1311,16 @@ def remove_pointer(auth, node, **kwargs): # id in the URL instead pointer_id = request.json.get('pointerId') if pointer_id is None: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) pointer = AbstractNode.load(pointer_id) if pointer is None: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) try: node.rm_pointer(pointer, auth=auth) except ValueError: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) node.save() @@ -1343,18 +1343,18 @@ def fork_pointer(auth, node, **kwargs): if pointer is None: # TODO: Change this to 404? - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) try: fork = node.fork_pointer(pointer, auth=auth, save=True) except ValueError: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) return { 'data': { 'node': serialize_node_summary(node=fork, auth=auth, show_path=False) } - }, http.CREATED + }, http_status.HTTP_201_CREATED def abbrev_authors(node): lead_author = node.visible_contributors[0] diff --git a/website/project/views/register.py b/website/project/views/register.py index 5fdd69921b6..3ce84e31a56 100644 --- a/website/project/views/register.py +++ b/website/project/views/register.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status import itertools from flask import request @@ -74,12 +74,12 @@ def node_registration_retraction_get(auth, node, **kwargs): """ if not node.is_registration: - raise HTTPError(http.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Invalid Request', 'message_long': 'Withdrawal of non-registrations is not permitted.' }) if node.is_pending_retraction: - raise HTTPError(http.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Invalid Request', 'message_long': 'This registration is already pending withdrawal.' }) @@ -96,18 +96,18 @@ def node_registration_retraction_post(auth, node, **kwargs): :return: Redirect URL for successful POST """ if node.is_pending_retraction: - raise HTTPError(http.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Invalid Request', 'message_long': 'This registration is already pending withdrawal' }) if not node.is_registration: - raise HTTPError(http.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Invalid Request', 'message_long': 'Withdrawal of non-registrations is not permitted.' }) if node.root_id != node.id: - raise HTTPError(http.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Invalid Request', 'message_long': 'Withdrawal of non-parent registrations is not permitted.' }) @@ -118,7 +118,7 @@ def node_registration_retraction_post(auth, node, **kwargs): node.save() node.retraction.ask(node.get_active_contributors_recursive(unique_users=True)) except NodeStateError as err: - raise HTTPError(http.FORBIDDEN, data=dict(message_long=str(err))) + raise HTTPError(http_status.HTTP_403_FORBIDDEN, data=dict(message_long=str(err))) return {'redirectUrl': node.web_url_for('view_project')} @@ -137,12 +137,12 @@ def node_register_template_page(auth, node, metaschema_id, **kwargs): # backwards compatability for old urls, lookup by name meta_schema = RegistrationSchema.objects.filter(name=_id_to_name(metaschema_id)).order_by('-schema_version').first() if not meta_schema: - raise HTTPError(http.NOT_FOUND, data={ + raise HTTPError(http_status.HTTP_404_NOT_FOUND, data={ 'message_short': 'Invalid schema name', 'message_long': 'No registration schema with that name could be found.' }) if not node.registered_schema.filter(id=meta_schema.id).exists(): - raise HTTPError(http.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Invalid schema', 'message_long': 'This registration has no registration supplment with that name.' }) @@ -235,10 +235,10 @@ def get_referent_by_identifier(category, value): try: identifier = Identifier.objects.get(category=category, value=value) except Identifier.DoesNotExist: - raise HTTPError(http.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) if identifier.referent.url: return redirect(identifier.referent.url) - raise HTTPError(http.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) @fail_archive_on_error @must_be_signed diff --git a/website/project/views/tag.py b/website/project/views/tag.py index 9a545b0f6f6..3860a798099 100644 --- a/website/project/views/tag.py +++ b/website/project/views/tag.py @@ -1,4 +1,4 @@ -import httplib as http +from rest_framework import status as http_status from flask import request from django.core.exceptions import ValidationError @@ -39,9 +39,9 @@ def project_add_tag(auth, node, **kwargs): if tag: try: node.add_tag(tag=tag, auth=auth) - return {'status': 'success'}, http.CREATED + return {'status': 'success'}, http_status.HTTP_201_CREATED except ValidationError: - return {'status': 'error'}, http.BAD_REQUEST + return {'status': 'error'}, http_status.HTTP_400_BAD_REQUEST @must_be_valid_project # injects project @@ -52,8 +52,8 @@ def project_remove_tag(auth, node, **kwargs): try: node.remove_tag(tag=data['tag'], auth=auth) except TagNotFoundError: - return {'status': 'failure'}, http.CONFLICT + return {'status': 'failure'}, http_status.HTTP_409_CONFLICT except (InvalidTagError, NodeStateError): - return {'status': 'failure'}, http.BAD_REQUEST + return {'status': 'failure'}, http_status.HTTP_400_BAD_REQUEST else: - return {'status': 'success'}, http.OK + return {'status': 'success'}, http_status.HTTP_200_OK diff --git a/website/routes.py b/website/routes.py index fdc13cdf1e9..039d1821f6f 100644 --- a/website/routes.py +++ b/website/routes.py @@ -1,9 +1,11 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import import os -import httplib as http +from rest_framework import status as http_status import requests -import urlparse + +from future.moves.urllib.parse import urljoin + import json import waffle @@ -245,7 +247,7 @@ def sitemap_file(path): elif path.endswith('.xml'): mime = 'text/xml' else: - raise HTTPError(http.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) return send_from_directory( settings.STATIC_FOLDER + '/sitemaps/', path, @@ -265,11 +267,11 @@ def ember_app(path=None): break if not ember_app: - raise HTTPError(http.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) if settings.PROXY_EMBER_APPS: path = request.path[len(ember_app['path']):] - url = urlparse.urljoin(ember_app['server'], path) + url = urljoin(ember_app['server'], path) resp = requests.get(url, stream=True, timeout=EXTERNAL_EMBER_SERVER_TIMEOUT, headers={'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8'}) excluded_headers = ['content-encoding', 'content-length', 'transfer-encoding', 'connection'] headers = [(name, value) for (name, value) in resp.raw.headers.items() if name.lower() not in excluded_headers] @@ -278,11 +280,11 @@ def ember_app(path=None): ember_app_folder = os.path.abspath(os.path.join(os.getcwd(), ember_app['path'])) if not ember_app_folder: - raise HTTPError(http.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) if not os.path.abspath(os.path.join(ember_app_folder, fp)).startswith(ember_app_folder): # Prevent accessing files outside of the ember build dir - raise HTTPError(http.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) if not os.path.isfile(os.path.join(ember_app_folder, fp)): fp = 'index.html' @@ -308,13 +310,13 @@ def make_url_map(app): Rule( '/', ['get', 'post'], - HTTPError(http.NOT_FOUND), + HTTPError(http_status.HTTP_404_NOT_FOUND), OsfWebRenderer('', render_mako_string, trust=False) ), Rule( '/api/v1/', ['get', 'post'], - HTTPError(http.NOT_FOUND), + HTTPError(http_status.HTTP_404_NOT_FOUND), json_renderer ), ]) diff --git a/website/search/views.py b/website/search/views.py index 966d537583b..69f149a27b5 100644 --- a/website/search/views.py +++ b/website/search/views.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- import functools -import httplib as http +from rest_framework import status as http_status import logging import time @@ -33,12 +33,12 @@ def wrapped(*args, **kwargs): try: return func(*args, **kwargs) except exceptions.MalformedQueryError: - raise HTTPError(http.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Bad search query', 'message_long': language.SEARCH_QUERY_HELP, }) except exceptions.SearchUnavailableError: - raise HTTPError(http.SERVICE_UNAVAILABLE, data={ + raise HTTPError(http_status.HTTP_503_SERVICE_UNAVAILABLE, data={ 'message_short': 'Search unavailable', 'message_long': ('Our search service is currently unavailable, if the issue persists, ' + language.SUPPORT_LINK), @@ -48,7 +48,7 @@ def wrapped(*args, **kwargs): sentry.log_exception() sentry.log_message('Elasticsearch returned an unexpected error response') # TODO: Add a test; may need to mock out the error response due to inability to reproduce error code locally - raise HTTPError(http.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Could not perform search query', 'message_long': language.SEARCH_QUERY_HELP, }) @@ -96,7 +96,7 @@ def conditionally_add_query_item(query, item, condition, value): elif condition == 'either': return query - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) @must_be_logged_in diff --git a/website/security.py b/website/security.py index d15f9934cba..63760d0d892 100644 --- a/website/security.py +++ b/website/security.py @@ -7,7 +7,7 @@ random = SystemRandom() -def random_string(length=8, chars=string.letters + string.digits): +def random_string(length=8, chars=string.ascii_letters + string.digits): """Generate a random string of a given length. """ return ''.join([chars[random.randint(0, len(chars) - 1)] for i in range(length)]) diff --git a/website/util/__init__.py b/website/util/__init__.py index 37e572658fd..ba239736fd3 100644 --- a/website/util/__init__.py +++ b/website/util/__init__.py @@ -2,7 +2,7 @@ import logging import re -import urlparse +from future.moves.urllib.parse import urljoin from django.utils.http import urlencode from flask import request, url_for @@ -62,7 +62,7 @@ def api_url_for(view_name, _absolute=False, _xml=False, _internal=False, *args, # We do NOT use the url_for's _external kwarg because app.config['SERVER_NAME'] alters # behavior in an unknown way (currently breaks tests). /sloria /jspies domain = website_settings.INTERNAL_DOMAIN if _internal else website_settings.DOMAIN - return urlparse.urljoin(domain, url) + return urljoin(domain, url) return url # Move somewhere @@ -82,7 +82,7 @@ def api_v2_url(path_str, """ params = params or {} # Optional params dict for special-character param names, eg filter[fullname] - x = urlparse.urljoin(base_route, urlparse.urljoin(base_prefix, path_str.lstrip('/'))) + x = urljoin(base_route, urljoin(base_prefix, path_str.lstrip('/'))) if params or kwargs: x = '{}?{}'.format(x, urlencode(dict(params, **kwargs))) @@ -104,7 +104,7 @@ def web_url_for(view_name, _absolute=False, _internal=False, _guid=False, *args, # We do NOT use the url_for's _external kwarg because app.config['SERVER_NAME'] alters # behavior in an unknown way (currently breaks tests). /sloria /jspies domain = website_settings.INTERNAL_DOMAIN if _internal else website_settings.DOMAIN - return urlparse.urljoin(domain, url) + return urljoin(domain, url) return url diff --git a/website/views.py b/website/views.py index 674406eb173..5dc3a174a66 100644 --- a/website/views.py +++ b/website/views.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals import itertools -import httplib as http +from rest_framework import status as http_status import logging import math import os import requests -import urllib +from future.moves.urllib.parse import unquote from django.apps import apps from flask import request, send_from_directory, Response, stream_with_context @@ -190,7 +190,7 @@ def my_projects(auth): def validate_page_num(page, pages): if page < 0 or (pages and page >= pages): - raise HTTPError(http.BAD_REQUEST, data=dict( + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_long='Invalid value for "page".' )) @@ -247,7 +247,7 @@ def resolve_guid(guid, suffix=None): guid_object = Guid.load(guid) except KeyError as e: if e.message == 'osfstorageguidfile': # Used when an old detached OsfStorageGuidFile object is accessed - raise HTTPError(http.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) else: raise e if guid_object: @@ -260,13 +260,13 @@ def resolve_guid(guid, suffix=None): sentry.log_message( 'Guid resolved to an object with no deep_url', dict(guid=guid) ) - raise HTTPError(http.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) referent = guid_object.referent if referent is None: logger.error('Referent of GUID {0} not found'.format(guid)) - raise HTTPError(http.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) if not referent.deep_url: - raise HTTPError(http.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) # Handle file `/download` shortcut with supported types. if suffix and suffix.rstrip('/').lower() == 'download': @@ -286,13 +286,13 @@ def resolve_guid(guid, suffix=None): # Check if user isn't a nonetype or that the user has admin/moderator/superuser permissions if auth.user is None or not (auth.user.has_perm('view_submissions', file_referent.target.provider) or file_referent.target.has_permission(auth.user, permissions.ADMIN)): - raise HTTPError(http.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) # Extend `request.args` adding `action=download`. request.args = request.args.copy() request.args.update({'action': 'download'}) # Do not include the `download` suffix in the url rebuild. - url = _build_guid_url(urllib.unquote(file_referent.deep_url)) + url = _build_guid_url(unquote(file_referent.deep_url)) return proxy_url(url) # Handle Ember Applications @@ -300,7 +300,7 @@ def resolve_guid(guid, suffix=None): if referent.provider.domain_redirect_enabled: # This route should always be intercepted by nginx for the branded domain, # w/ the exception of `/download` handled above. - return redirect(referent.absolute_url, http.MOVED_PERMANENTLY) + return redirect(referent.absolute_url, http_status.HTTP_301_MOVED_PERMANENTLY) if PROXY_EMBER_APPS: resp = requests.get(EXTERNAL_EMBER_APPS['preprints']['server'], stream=True, timeout=EXTERNAL_EMBER_SERVER_TIMEOUT) @@ -310,7 +310,7 @@ def resolve_guid(guid, suffix=None): if isinstance(referent, BaseFileNode) and referent.is_file and (getattr(referent.target, 'is_quickfiles', False)): if referent.is_deleted: - raise HTTPError(http.GONE) + raise HTTPError(http_status.HTTP_410_GONE) if PROXY_EMBER_APPS: resp = requests.get(EXTERNAL_EMBER_APPS['ember_osf_web']['server'], stream=True, timeout=EXTERNAL_EMBER_SERVER_TIMEOUT) return Response(stream_with_context(resp.iter_content()), resp.status_code) @@ -328,7 +328,7 @@ def resolve_guid(guid, suffix=None): return send_from_directory(registries_dir, 'index.html') - url = _build_guid_url(urllib.unquote(referent.deep_url), suffix) + url = _build_guid_url(unquote(referent.deep_url), suffix) return proxy_url(url) # GUID not found; try lower-cased and redirect if exists @@ -339,7 +339,7 @@ def resolve_guid(guid, suffix=None): ) # GUID not found - raise HTTPError(http.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) # Redirects # @@ -377,7 +377,7 @@ def redirect_to_cos_news(**kwargs): # Return error for legacy SHARE v1 search route def legacy_share_v1_search(**kwargs): return HTTPError( - http.BAD_REQUEST, + http_status.HTTP_400_BAD_REQUEST, data=dict( message_long='Please use v2 of the SHARE search API available at {}api/v2/share/search/creativeworks/_search.'.format(settings.SHARE_URL) ) diff --git a/yarn.lock b/yarn.lock index 51fe593d458..5f6e1359292 100644 --- a/yarn.lock +++ b/yarn.lock @@ -638,10 +638,10 @@ bootstrap@3.3.7, bootstrap@~3.3.2: resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-3.3.7.tgz#5a389394549f23330875a3b150656574f8a9eb71" integrity sha1-WjiTlFSfIzMIdaOxUGVldPip63E= -bower@^1.3.12: - version "1.8.2" - resolved "https://registry.yarnpkg.com/bower/-/bower-1.8.2.tgz#adf53529c8d4af02ef24fb8d5341c1419d33e2f7" - integrity sha1-rfU1KcjUrwLvJPuNU0HBQZ0z4vc= +bower@^1.8.8: + version "1.8.8" + resolved "https://registry.yarnpkg.com/bower/-/bower-1.8.8.tgz#82544be34a33aeae7efb8bdf9905247b2cffa985" + integrity sha512-1SrJnXnkP9soITHptSO+ahx3QKp3cVzn8poI6ujqc5SeOkg5iqM1pK9H+DSc2OQ8SnO0jC/NG4Ur/UIwy7574A== boxen@^1.2.1: version "1.3.0"