Support multiple FxA configurations (fixes #3496)

Fixes #3505
This commit is contained in:
Mark Striemer 2016-09-14 00:18:07 -05:00
Родитель 023d548587
Коммит c939ccbc67
8 изменённых файлов: 110 добавлений и 24 удалений

Просмотреть файл

@ -111,6 +111,7 @@ FXA_CONFIG = {
'scope': 'profile',
},
}
FXA_CONFIG['amo'] = FXA_CONFIG['internal']
# CSP report endpoint which returns a 204 from addons-nginx in local dev.
CSP_REPORT_URI = '/csp-report'

Просмотреть файл

@ -7,6 +7,7 @@ from django.conf import settings
from django.contrib.auth.models import AnonymousUser
from django.contrib.messages import get_messages
from django.core.urlresolvers import resolve, reverse
from django.test import RequestFactory
from django.test.utils import override_settings
import mock
from waffle.models import Switch
@ -66,7 +67,7 @@ class TestFxALoginWaffle(APITestCase):
class TestLoginStartBaseView(WithDynamicEndpoints, TestCase):
class LoginStartView(views.LoginStartBaseView):
FXA_CONFIG_NAME = 'current-config'
DEFAULT_FXA_CONFIG_NAME = 'current-config'
def setUp(self):
super(TestLoginStartBaseView, self).setUp()
@ -171,7 +172,8 @@ class TestLoginView(BaseAuthenticationView):
return self.client_class(HTTP_ORIGIN=origin).options(url)
def test_correct_config_is_used(self):
assert views.LoginView.FXA_CONFIG_NAME == 'default'
assert views.LoginView.DEFAULT_FXA_CONFIG_NAME == 'default'
assert views.LoginView.ALLOWED_FXA_CONFIGS == ['default', 'amo']
def test_cors_addons_frontend(self):
response = self.options(self.url, origin='https://addons-frontend')
@ -195,7 +197,8 @@ class TestLoginView(BaseAuthenticationView):
class TestLoginStartView(TestCase):
def test_default_config_is_used(self):
assert views.LoginStartView.FXA_CONFIG_NAME == 'default'
assert views.LoginStartView.DEFAULT_FXA_CONFIG_NAME == 'default'
assert views.LoginStartView.ALLOWED_FXA_CONFIGS == ['default', 'amo']
class TestLoginUser(TestCase):
@ -407,11 +410,6 @@ class TestWithUser(TestCase):
'next_path': None,
}
@override_settings(FXA_CONFIG={'default': {}})
def test_unknown_config_blows_up_early(self):
with self.assertRaises(AssertionError):
views.with_user(format='json', config='notconfigured')
def test_profile_exists_with_user_and_path(self):
identity = {'uid': '1234', 'email': 'hey@yo.it'}
self.fxa_identify.return_value = identity
@ -650,11 +648,56 @@ class TestRegisterUser(TestCase):
assert not user.has_usable_password()
@override_settings(FXA_CONFIG={
'foo': {'FOO': 123},
'bar': {'BAR': 456},
'baz': {'BAZ': 789},
})
class TestFxAConfigMixin(TestCase):
class DefaultConfig(views.FxAConfigMixin):
DEFAULT_FXA_CONFIG_NAME = 'bar'
class MultipleConfigs(views.FxAConfigMixin):
DEFAULT_FXA_CONFIG_NAME = 'baz'
ALLOWED_FXA_CONFIGS = ['foo', 'baz']
def test_default_only_no_config(self):
request = RequestFactory().get('/login')
config = self.DefaultConfig().get_fxa_config(request)
assert config == {'BAR': 456}
def test_default_only_not_allowed(self):
request = RequestFactory().get('/login?config=foo')
config = self.DefaultConfig().get_fxa_config(request)
assert config == {'BAR': 456}
def test_default_only_allowed(self):
request = RequestFactory().get('/login?config=bar')
config = self.DefaultConfig().get_fxa_config(request)
assert config == {'BAR': 456}
def test_config_is_allowed(self):
request = RequestFactory().get('/login?config=foo')
config = self.MultipleConfigs().get_fxa_config(request)
assert config == {'FOO': 123}
def test_config_is_default(self):
request = RequestFactory().get('/login?config=baz')
config = self.MultipleConfigs().get_fxa_config(request)
assert config == {'BAZ': 789}
def test_config_is_not_allowed(self):
request = RequestFactory().get('/login?config=bar')
config = self.MultipleConfigs().get_fxa_config(request)
assert config == {'BAZ': 789}
@override_settings(FXA_CONFIG={'current-config': FXA_CONFIG})
class TestLoginBaseView(WithDynamicEndpoints, TestCase):
class LoginView(views.LoginBaseView):
FXA_CONFIG_NAME = 'current-config'
DEFAULT_FXA_CONFIG_NAME = 'current-config'
def setUp(self):
super(TestLoginBaseView, self).setUp()

Просмотреть файл

@ -177,14 +177,17 @@ def parse_next_path(state_parts):
return next_path
def with_user(format, config='default'):
assert config in settings.FXA_CONFIG, \
'"{config}" not found in FXA_CONFIG'.format(config=config)
def with_user(format, config=None):
def outer(fn):
@functools.wraps(fn)
@write
def inner(self, request):
if config is None:
fxa_config = settings.FXA_CONFIG['default']
else:
fxa_config = config
if request.method == 'GET':
data = request.query_params
else:
@ -209,8 +212,7 @@ def with_user(format, config='default'):
format=format)
try:
identity = verify.fxa_identify(
data['code'], config=settings.FXA_CONFIG[config])
identity = verify.fxa_identify(data['code'], config=fxa_config)
except verify.IdentificationError:
log.info('Profile not found. Code: {}'.format(data['code']))
return render_error(
@ -263,26 +265,38 @@ def add_api_token_to_response(response, user, set_cookie=True):
return response
class LoginStartBaseView(APIView):
class FxAConfigMixin(object):
def get_fxa_config(self, request):
config_name = request.GET.get('config')
if config_name in getattr(self, 'ALLOWED_FXA_CONFIGS', []):
return settings.FXA_CONFIG[config_name]
return settings.FXA_CONFIG[self.DEFAULT_FXA_CONFIG_NAME]
class LoginStartBaseView(FxAConfigMixin, APIView):
def get(self, request):
request.session.setdefault('fxa_state', generate_fxa_state())
return HttpResponseRedirect(
fxa_login_url(
config=settings.FXA_CONFIG[self.FXA_CONFIG_NAME],
config=self.get_fxa_config(request),
state=request.session['fxa_state'],
next_path=request.GET.get('to'),
action=request.GET.get('action', 'signin')))
class LoginStartView(LoginStartBaseView):
FXA_CONFIG_NAME = 'default'
DEFAULT_FXA_CONFIG_NAME = 'default'
ALLOWED_FXA_CONFIGS = ['default', 'amo']
class LoginBaseView(APIView):
class LoginBaseView(FxAConfigMixin, APIView):
def post(self, request):
@with_user(format='json', config=self.FXA_CONFIG_NAME)
config = self.get_fxa_config(request)
@with_user(format='json', config=config)
def _post(self, request, user, identity, next_path):
if user is None:
return Response({'error': ERROR_NO_USER}, status=422)
@ -299,7 +313,8 @@ class LoginBaseView(APIView):
class LoginView(LoginBaseView):
FXA_CONFIG_NAME = 'default'
DEFAULT_FXA_CONFIG_NAME = 'default'
ALLOWED_FXA_CONFIGS = ['default', 'amo']
class RegisterView(APIView):

Просмотреть файл

@ -253,6 +253,15 @@ FXA_CONFIG = {
'https://addons-dev.allizom.org/api/v3/accounts/authorize/',
'scope': 'profile',
},
'amo': {
'client_id': env('AMO_FXA_CLIENT_ID'),
'client_secret': env('AMO_FXA_CLIENT_SECRET'),
'content_host': 'https://stable.dev.lcip.org',
'oauth_host': 'https://oauth-stable.dev.lcip.org/v1',
'profile_host': 'https://stable.dev.lcip.org/profile/v1',
'redirect_url': 'https://amo.dev.mozaws.net/fxa-authenticate',
'scope': 'profile',
},
}
INTERNAL_DOMAINS = [

Просмотреть файл

@ -215,6 +215,15 @@ FXA_CONFIG = {
'https://addons.mozilla.org/api/v3/accounts/authorize/',
'scope': 'profile',
},
'amo': {
'client_id': env('AMO_FXA_CLIENT_ID'),
'client_secret': env('AMO_FXA_CLIENT_SECRET'),
'content_host': 'https://accounts.firefox.com',
'oauth_host': 'https://oauth.accounts.firefox.com/v1',
'profile_host': 'https://profile.accounts.firefox.com/v1',
'redirect_url': 'https://amo.prod.mozaws.net/fxa-authenticate',
'scope': 'profile',
},
}
INTERNAL_DOMAINS = ['addons-admin.prod.mozaws.net']

Просмотреть файл

@ -247,6 +247,15 @@ FXA_CONFIG = {
'https://addons.allizom.org/api/v3/accounts/authorize/',
'scope': 'profile',
},
'amo': {
'client_id': env('AMO_FXA_CLIENT_ID'),
'client_secret': env('AMO_FXA_CLIENT_SECRET'),
'content_host': 'https://accounts.firefox.com',
'oauth_host': 'https://oauth.accounts.firefox.com/v1',
'profile_host': 'https://profile.accounts.firefox.com/v1',
'redirect_url': 'https://amo.stage.mozaws.net/fxa-authenticate',
'scope': 'profile',
},
}
INTERNAL_DOMAINS = ['addons-admin.stage.mozaws.net']

Просмотреть файл

@ -169,7 +169,7 @@ class TestInternalAddonSearchView(ESTestCase):
class TestLoginStartView(TestCase):
def test_internal_config_is_used(self):
assert views.LoginStartView.FXA_CONFIG_NAME == 'internal'
assert views.LoginStartView.DEFAULT_FXA_CONFIG_NAME == 'internal'
def has_cors_headers(response, origin='https://addons-frontend'):
@ -208,7 +208,7 @@ class TestLoginView(BaseAuthenticationView):
return self.client_class(HTTP_ORIGIN=origin).options(url)
def test_internal_config_is_used(self):
assert views.LoginView.FXA_CONFIG_NAME == 'internal'
assert views.LoginView.DEFAULT_FXA_CONFIG_NAME == 'internal'
def test_cors_addons_frontend(self):
response = self.options(self.url, origin='https://addons-frontend')

Просмотреть файл

@ -27,8 +27,8 @@ class InternalAddonSearchView(AddonSearchView):
class LoginStartView(LoginStartBaseView):
FXA_CONFIG_NAME = 'internal'
DEFAULT_FXA_CONFIG_NAME = 'internal'
class LoginView(LoginBaseView):
FXA_CONFIG_NAME = 'internal'
DEFAULT_FXA_CONFIG_NAME = 'internal'