diff --git a/settings.py b/settings.py index ebed365970..0450595166 100644 --- a/settings.py +++ b/settings.py @@ -127,6 +127,11 @@ FXA_OAUTH_HOST = 'https://oauth-stable.dev.lcip.org/v1' FXA_PROFILE_HOST = 'https://stable.dev.lcip.org/profile/v1' ALLOWED_FXA_CONFIGS = ['default', 'amo', 'local'] +# When USE_FAKE_FXA_AUTH and settings.DEBUG are both True, we serve a fake +# authentication page, bypassing FxA. To disable this behavior, set +# USE_FAKE_FXA = False in your local settings. +USE_FAKE_FXA_AUTH = True + # CSP report endpoint which returns a 204 from addons-nginx in local dev. CSP_REPORT_URI = '/csp-report' RESTRICTED_DOWNLOAD_CSP['REPORT_URI'] = CSP_REPORT_URI diff --git a/src/olympia/accounts/tests/test_utils.py b/src/olympia/accounts/tests/test_utils.py index 44d2ac40dd..c0b3ba722f 100644 --- a/src/olympia/accounts/tests/test_utils.py +++ b/src/olympia/accounts/tests/test_utils.py @@ -17,6 +17,7 @@ from waffle.testutils import override_switch from olympia.accounts import utils from olympia.accounts.utils import process_fxa_event from olympia.amo.tests import TestCase, user_factory +from olympia.amo.urlresolvers import reverse from olympia.users.models import UserProfile @@ -208,6 +209,29 @@ def test_redirect_for_login(default_fxa_login_url): assert response['location'] == login_url +@override_settings(DEBUG=True, USE_FAKE_FXA_AUTH=True) +def test_fxa_login_url_when_faking_fxa_auth(): + path = '/en-US/addons/abp/?source=ddg' + request = RequestFactory().get(path) + request.session = {'fxa_state': 'myfxastate'} + raw_url = utils.fxa_login_url( + config=FXA_CONFIG['default'], state=request.session['fxa_state'], + next_path=path, action='signin') + url = urlparse(raw_url) + assert url.scheme == '' + assert url.netloc == '' + assert url.path == reverse('fake-fxa-authorization') + query = parse_qs(url.query) + next_path = urlsafe_b64encode(path.encode('utf-8')).rstrip(b'=') + assert query == { + 'action': ['signin'], + 'client_id': ['foo'], + 'scope': ['profile openid'], + 'state': ['myfxastate:{next_path}'.format( + next_path=force_text(next_path))], + } + + class TestProcessSqsQueue(TestCase): @mock.patch('boto3._get_default_session') diff --git a/src/olympia/accounts/tests/test_views.py b/src/olympia/accounts/tests/test_views.py index 9faaefb7b9..a8f6f71fb5 100644 --- a/src/olympia/accounts/tests/test_views.py +++ b/src/olympia/accounts/tests/test_views.py @@ -159,6 +159,15 @@ class TestLoginStartView(TestCase): assert views.LoginStartView.ALLOWED_FXA_CONFIGS == ( ['default', 'amo', 'local']) + @override_settings(DEBUG=True, USE_FAKE_FXA_AUTH=True) + def test_redirect_url_fake_fxa_auth(self): + response = self.client.get(reverse_ns('accounts.login_start')) + assert response.status_code == 302 + url = urlparse(response['location']) + assert url.path == reverse('fake-fxa-authorization') + query = parse_qs(url.query) + assert query['state'] + class TestLoginUserAndRegisterUser(TestCase): @@ -723,6 +732,25 @@ class TestWithUser(TestCase): addon_factory(users=[self.user]) self._test_should_continue_without_redirect_for_two_factor_auth() + @override_settings(DEBUG=True, USE_FAKE_FXA_AUTH=True) + def test_fake_fxa_auth(self): + self.user = user_factory() + self.find_user.return_value = self.user + self.request.data = { + 'code': 'foo', + 'fake_fxa_email': self.user.email, + 'state': 'some-blob:{next_path}'.format( + next_path=force_text(base64.urlsafe_b64encode(b'/a/path/?'))), + } + args, kwargs = self.fn(self.request) + assert args == (self, self.request) + assert kwargs['user'] == self.user + assert kwargs['identity']['email'] == self.user.email + assert kwargs['identity']['uid'].startswith('fake_fxa_id-') + assert len(kwargs['identity']['uid']) == 44 # 32 random chars + prefix + assert kwargs['next_path'] == '/a/path/?' + assert self.fxa_identify.call_count == 0 + @override_settings(FXA_CONFIG={ 'foo': {'FOO': 123}, diff --git a/src/olympia/accounts/utils.py b/src/olympia/accounts/utils.py index f8dba99ba6..d9d9007af4 100644 --- a/src/olympia/accounts/utils.py +++ b/src/olympia/accounts/utils.py @@ -14,6 +14,8 @@ import boto3 from olympia.accounts.tasks import ( delete_user_event, primary_email_change_event) +from olympia.amo.urlresolvers import reverse +from olympia.amo.utils import use_fake_fxa from olympia.core.logger import getLogger @@ -71,8 +73,12 @@ def fxa_login_url(config, state, next_path=None, action=None, if id_token: query['prompt'] = 'none' query['id_token_hint'] = id_token - return '{host}/authorization?{query}'.format( - host=settings.FXA_OAUTH_HOST, query=urlencode(query)) + if use_fake_fxa(): + base_url = reverse('fake-fxa-authorization') + else: + base_url = '{host}/authorization'.format(host=settings.FXA_OAUTH_HOST) + return '{base_url}?{query}'.format( + base_url=base_url, query=urlencode(query)) def default_fxa_register_url(request): diff --git a/src/olympia/accounts/views.py b/src/olympia/accounts/views.py index 45fb1c15ca..bd028609a4 100644 --- a/src/olympia/accounts/views.py +++ b/src/olympia/accounts/views.py @@ -46,7 +46,7 @@ from olympia.access import acl from olympia.access.models import GroupUser from olympia.amo import messages from olympia.amo.decorators import use_primary_db -from olympia.amo.utils import fetch_subscribed_newsletters +from olympia.amo.utils import fetch_subscribed_newsletters, use_fake_fxa from olympia.api.authentication import ( JWTKeyAuthentication, UnsubscribeTokenAuthentication, WebTokenAuthentication) @@ -261,8 +261,19 @@ def with_user(format): response, request.user) return response try: - identity, id_token = verify.fxa_identify( - data['code'], config=fxa_config) + if use_fake_fxa() and 'fake_fxa_email' in data: + # Bypassing real authentication, we take the email provided + # and generate a random fxa id. + identity = { + 'email': data['fake_fxa_email'], + 'uid': 'fake_fxa_id-%s' % force_text( + binascii.b2a_hex(os.urandom(16)) + ) + } + id_token = identity['email'] + else: + identity, id_token = verify.fxa_identify( + data['code'], config=fxa_config) except verify.IdentificationError: log.info('Profile not found. Code: {}'.format(data['code'])) return render_error( diff --git a/src/olympia/amo/templates/amo/fake_fxa_authorization.html b/src/olympia/amo/templates/amo/fake_fxa_authorization.html new file mode 100644 index 0000000000..a9a0990788 --- /dev/null +++ b/src/olympia/amo/templates/amo/fake_fxa_authorization.html @@ -0,0 +1,13 @@ +{% extends "base.html" %} + +{% block content %} +