Skip to content

Commit

Permalink
Remove DEV_MODE and USE_FAKE_FXA_AUTH, replace with PROD_MODE and FXA…
Browse files Browse the repository at this point in the history
…_CONFIG

This commit introduces several changes to simplify and improve the authentication and environment configuration:

- Replaced `DEV_MODE` with `PROD_MODE` derived from version JSON instead of environment variable
- Removed `USE_FAKE_FXA_AUTH` setting replaced with `FXA_CONFIG` to control fake authentication
- Simplified conditional logic around development and production environments to rely on stable build time variables
  • Loading branch information
KevinMind committed Feb 3, 2025
1 parent 31aeb04 commit 0928b7f
Show file tree
Hide file tree
Showing 30 changed files with 429 additions and 301 deletions.
5 changes: 1 addition & 4 deletions docs/topics/development/static-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,10 @@ To better visualize the impact of the various settings, here is a reference:

Given a static file 'js/devhub/my-file.js':

In `DEV_MODE` the url will look like `/static/js/devhub/my-file.js` no matter what.
On development images, the url will look like `/static/js/devhub/my-file.js` no matter what.
However, in production, if `DEBUG` is `False`, the url will append the content hash like this,
`/static/js/devhub/my-file.1234567890.js`. Finally, if `DEBUG` is true, this file will be minified and concatenated with other files and probably look something like this `/static/js/devhub-all.min.1234567890.js`.

The true `production` mode is then when `DEBUG` is `False` and `DEV_MODE` is `False`. But it makes sense
to make these individually toggleable so you can better "debug" js files from a production image.

### Project Static Files

Static files specific to the addons-server project are stored in the `./static` directory. These include CSS, JavaScript, images, and other assets used by the application.
Expand Down
16 changes: 0 additions & 16 deletions docs/topics/development/troubleshooting_and_debugging.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,6 @@

Effective troubleshooting and debugging practices are essential for maintaining and improving the **addons-server** project. This section covers common issues, their solutions, and tools for effective debugging.

## DEV_MODE vs DEBUG

In our project, `DEV_MODE` and `DEBUG` serve distinct but complementary purposes.
`DEV_MODE` is directly tied to the `DOCKER_TARGET` environment variable and is used to enable or disable behaviors
based on whether we are running a production image or not.

For instance, production images always disables certain features like using fake fxa authentication. Additionally,
certain dependencies are only installed in [dev.txt](../../../requirements/dev.txt) and so must be disabled in production.

Conversely, `DEBUG` controls the activation of debugging tools and utilities, such as the debug_toolbar,
which are useful for troubleshooting. Unlike DEV_MODE, DEBUG is independent
and can be toggled in both development and production environments as needed.

This separation ensures that essential behaviors are managed according to the deployment target (DEV_MODE),
while allowing flexibility to enable or disable debugging features (DEBUG) in production or development images.

## Common Issues and Solutions

1. **Containers Not Starting**:
Expand Down
24 changes: 6 additions & 18 deletions settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,9 @@
import os
from urllib.parse import urlparse

from olympia.core.utils import get_version_json
from olympia.lib.settings_base import * # noqa


# "production" is a named docker stage corresponding to the production image.
# when we build the production image, the stage to use is determined
# via the "DOCKER_TARGET" variable which is also passed into the image.
# So if the value is anything other than "production" we are in development mode.
DEV_MODE = DOCKER_TARGET != 'production'

HOST_UID = os.environ.get('HOST_UID')

WSGI_APPLICATION = 'olympia.wsgi.application'
Expand Down Expand Up @@ -63,7 +56,7 @@ def insert_debug_toolbar_middleware(middlewares):


# We can only add these dependencies if we have development dependencies
if os.environ.get('OLYMPIA_DEPS', '') == 'development':
if OLYMPIA_DEPS == 'development':
INSTALLED_APPS += (
'debug_toolbar',
'dbbackup',
Expand Down Expand Up @@ -118,14 +111,6 @@ def insert_debug_toolbar_middleware(middlewares):
FXA_OAUTH_HOST = 'https://oauth.stage.mozaws.net/v1'
FXA_PROFILE_HOST = 'https://profile.stage.mozaws.net/v1'

# When USE_FAKE_FXA_AUTH and settings.DEV_MODE are both True, we serve a fake
# authentication page, bypassing FxA. To disable this behavior, set
# USE_FAKE_FXA = False in your local settings.
# You will also need to specify `client_id` and `client_secret` in your
# local_settings.py or environment variables - you must contact the FxA team to get your
# own credentials for FxA stage.
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
Expand Down Expand Up @@ -199,8 +184,11 @@ def insert_debug_toolbar_middleware(middlewares):

ENABLE_ADMIN_MLBF_UPLOAD = True

# Use dev mode if we are on a non production imqage and debug is enabled.
if get_version_json().get('target') != 'production' and DEBUG:
# In non production images, we should enable dev mode.
# The 'bundle' prefix is used to control routing behavior in nginx,
# ensuring static files are not served by nginx
# but redirected to the vite dev server.
if not PROD_MODE:
DJANGO_VITE = {
'default': {
'dev_mode': True,
Expand Down
13 changes: 11 additions & 2 deletions settings_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
IN_TEST_SUITE = True

DEBUG = False
# We should default to production mode unless otherwise specified
DEV_MODE = False

# We won't actually send an email.
SEND_REAL_EMAIL = True
Expand Down Expand Up @@ -113,3 +111,14 @@

# This is a testing environment
TESTING_ENV = True

FXA_CONFIG = {
'default': {
'client_id': 'amodefault',
'client_secret': 'amodefault',
},
'skip': {
'client_id': '.',
'client_secret': '.',
},
}
9 changes: 6 additions & 3 deletions src/olympia/accounts/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
'client_id': 'foo',
'client_secret': 'bar',
},
'fake': {
'client_id': '.',
'client_secret': '.',
},
'other': {'client_id': 'foo_other', 'client_secret': 'bar_other'},
}

Expand Down Expand Up @@ -292,13 +296,12 @@ def test_redirect_for_login_with_2fa_enforced_and_config():
assert request.session['enforce_2fa'] is True


@override_settings(DEV_MODE=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'],
config=FXA_CONFIG['fake'],
state=request.session['fxa_state'],
next_path=path,
)
Expand All @@ -309,7 +312,7 @@ def test_fxa_login_url_when_faking_fxa_auth():
query = parse_qs(url.query)
next_path = urlsafe_b64encode(path.encode('utf-8')).rstrip(b'=')
assert query == {
'client_id': ['foo'],
'client_id': ['.'],
'scope': ['profile openid'],
'state': [f'myfxastate:{force_str(next_path)}'],
'access_type': ['offline'],
Expand Down
27 changes: 20 additions & 7 deletions src/olympia/accounts/tests/test_verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@
from olympia.amo.tests import TestCase


FXA_CONFIG = {
'default': {
'client_id': 'test-client-id',
'client_secret': "don't look",
},
'fake': {
'client_id': '.',
'client_secret': '.',
},
}


class TestProfile(TestCase):
def setUp(self):
patcher = mock.patch('olympia.accounts.verify.requests.get')
Expand Down Expand Up @@ -234,17 +246,18 @@ def test_with_id_token(self):
self.get_profile.assert_called_with('cafe')


@override_settings(USE_FAKE_FXA_AUTH=False, DEV_MODE=True, VERIFY_FXA_ACCESS_TOKEN=True)
@override_settings(FXA_CONFIG=FXA_CONFIG, VERIFY_FXA_ACCESS_TOKEN=True)
class TestCheckAndUpdateFxaAccessToken(TestCase):
def setUp(self):
super().setUp()
self.get_fxa_token_mock = self.patch('olympia.accounts.verify.get_fxa_token')

def get_request(self, expiry_timestamp=None):
def get_request(self, expiry_timestamp=None, config_name='default'):
expiry_timestamp = (
expiry_timestamp or (datetime.now() - timedelta(days=1)).timestamp()
)
request = mock.Mock()
request.GET = {'config': config_name}
request.session = {
SESSION_KEY: '1',
'fxa_access_token_expiry': expiry_timestamp,
Expand All @@ -253,12 +266,12 @@ def get_request(self, expiry_timestamp=None):
return request

def test_use_fake_fxa_auth(self):
request = self.get_request()
with override_settings(USE_FAKE_FXA_AUTH=True):
verify.check_and_update_fxa_access_token(request)
self.get_fxa_token_mock.assert_not_called()
verify.check_and_update_fxa_access_token(self.get_request(config_name='fake'))
self.get_fxa_token_mock.assert_not_called()

verify.check_and_update_fxa_access_token(request)
verify.check_and_update_fxa_access_token(
self.get_request(config_name='default')
)
self.get_fxa_token_mock.assert_called()

def test_verify_access_token_setting_false(self):
Expand Down
6 changes: 3 additions & 3 deletions src/olympia/accounts/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ def has_cors_headers(response, origin='https://addons-frontend'):


class TestLoginStartView(TestCase):
@override_settings(DEV_MODE=True, USE_FAKE_FXA_AUTH=True)
@override_settings(FXA_CONFIG={'default': {'client_id': '.'}})
def test_redirect_url_fake_fxa_auth(self):
response = self.client.get(reverse_ns('accounts.login_start'))
assert response.status_code == 302
Expand Down Expand Up @@ -689,7 +689,7 @@ def test_waffle_flag_off_enforced_2fa_should_have_no_effect(self):
self.request.session['enforce_2fa'] = True
self._test_should_continue_without_redirect_for_two_factor_auth()

@override_settings(DEV_MODE=True, USE_FAKE_FXA_AUTH=True)
@override_settings(FXA_CONFIG={'default': {'client_id': '.'}})
def test_fake_fxa_auth(self):
self.user = user_factory()
self.find_user.return_value = self.user
Expand All @@ -710,7 +710,7 @@ def test_fake_fxa_auth(self):
assert kwargs['next_path'] == '/a/path/?'
assert self.fxa_identify.call_count == 0

@override_settings(DEV_MODE=True, USE_FAKE_FXA_AUTH=True)
@override_settings(FXA_CONFIG={'default': {'client_id': '.'}})
def test_fake_fxa_auth_with_2fa(self):
self.user = user_factory()
self.find_user.return_value = self.user
Expand Down
2 changes: 1 addition & 1 deletion src/olympia/accounts/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def fxa_login_url(
elif login_hint:
query['prompt'] = 'none'
query['login_hint'] = login_hint
if use_fake_fxa():
if use_fake_fxa(config):
base_url = reverse('fake-fxa-authorization')
else:
base_url = f'{settings.FXA_OAUTH_HOST}/authorization'
Expand Down
3 changes: 2 additions & 1 deletion src/olympia/accounts/verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from django_statsd.clients import statsd

import olympia.core.logger
from olympia.accounts.utils import get_fxa_config
from olympia.amo.utils import use_fake_fxa


Expand Down Expand Up @@ -117,7 +118,7 @@ def check_and_update_fxa_access_token(request):
otherwise."""

if (
not use_fake_fxa()
not use_fake_fxa(get_fxa_config(request))
and settings.VERIFY_FXA_ACCESS_TOKEN
and (request.session.get('fxa_access_token_expiry') or 0) < time.time()
):
Expand Down
2 changes: 1 addition & 1 deletion src/olympia/accounts/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ def inner(self, request):
elif request.user.is_authenticated and not enforce_2fa_for_this_session:
return safe_redirect(request, next_path, ERROR_AUTHENTICATED)
try:
if use_fake_fxa() and 'fake_fxa_email' in data:
if use_fake_fxa(fxa_config) and 'fake_fxa_email' in data:
# Bypassing real authentication, we take the email provided
# and generate a random fxa id.
identity = {
Expand Down
7 changes: 6 additions & 1 deletion src/olympia/amo/templates/amo/fake_fxa_authorization.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@
<h2> {{ _('Register or Log in') }} </h2>
<form id="fake_fxa_authorization" method="get" action="{{ url('auth:accounts.authenticate') }}">
<h3> Don't panic! </h3>
<p> You're seeing this page instead of the regular authentication page because both <code>settings.DEBUG</code> and <code>settings.USE_FAKE_FXA_AUTH</code> are set to <code>True</code>.</p>
<p>
You're seeing this page instead of the regular authentication page because the
<code>FXA_CONFIG</code> being used has the client_id set to <code>.</code>.
This is used to trigger fake authentication without redirecting to an actual
authentication server.
</p>
<p> Because this is a fake authentication page, you don't need to enter a password, and you also don't need to control the email address. This can be be used to log in to the admin pages which normally require an account with <code>@mozilla.com</code> email address. </p>
{% for key, value in request.GET.items() %}
<input type="hidden" name="{{ key }}" value="{{ value }}" />
Expand Down
4 changes: 2 additions & 2 deletions src/olympia/amo/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@

from olympia import amo
from olympia.access.models import Group, GroupUser
from olympia.accounts.utils import fxa_login_url
from olympia.accounts.utils import fxa_login_url, get_fxa_config
from olympia.addons.indexers import AddonIndexer
from olympia.addons.models import (
Addon,
Expand Down Expand Up @@ -304,7 +304,7 @@ def create_session(self, user, **overrides):
# this is pretty much what django.contrib.auth.login does to initialize session
fxa_details = (
{'fxa_access_token_expiry': time.time() + 1000}
if not use_fake_fxa()
if not use_fake_fxa(get_fxa_config(request))
else {}
)
initialize_session(
Expand Down
38 changes: 15 additions & 23 deletions src/olympia/amo/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,35 +509,27 @@ def test_allow_mozilla_collections(self):


@pytest.mark.django_db
@override_settings(FXA_CONFIG={'default': {'client_id': '.'}})
def test_fake_fxa_authorization_correct_values_passed():
with override_settings(DEV_MODE=True): # USE_FAKE_FXA_AUTH is already True
url = reverse('fake-fxa-authorization')
response = test.Client().get(url, {'state': 'foobar'})
assert response.status_code == 200
doc = pq(response.content)
form = doc('#fake_fxa_authorization')[0]
assert form.attrib['action'] == reverse('auth:accounts.authenticate')
elm = doc('#fake_fxa_authorization input[name=code]')[0]
assert elm.attrib['value'] == 'fakecode'
elm = doc('#fake_fxa_authorization input[name=state]')[0]
assert elm.attrib['value'] == 'foobar'
elm = doc('#fake_fxa_authorization input[name=fake_fxa_email]')
assert elm # No value yet, should just be present.
url = reverse('fake-fxa-authorization')
response = test.Client().get(url, {'state': 'foobar'})
assert response.status_code == 200
doc = pq(response.content)
form = doc('#fake_fxa_authorization')[0]
assert form.attrib['action'] == reverse('auth:accounts.authenticate')
elm = doc('#fake_fxa_authorization input[name=code]')[0]
assert elm.attrib['value'] == 'fakecode'
elm = doc('#fake_fxa_authorization input[name=state]')[0]
assert elm.attrib['value'] == 'foobar'
elm = doc('#fake_fxa_authorization input[name=fake_fxa_email]')
assert elm # No value yet, should just be present.


@pytest.mark.django_db
@override_settings(FXA_CONFIG={'default': {'client_id': 'amodefault'}})
def test_fake_fxa_authorization_deactivated():
url = reverse('fake-fxa-authorization')
with override_settings(DEV_MODE=False, USE_FAKE_FXA_AUTH=False):
response = test.Client().get(url)
assert response.status_code == 404

with override_settings(DEV_MODE=False, USE_FAKE_FXA_AUTH=True):
response = test.Client().get(url)
assert response.status_code == 404

with override_settings(DEV_MODE=True, USE_FAKE_FXA_AUTH=False):
response = test.Client().get(url)
response = test.Client().get(url)
assert response.status_code == 404


Expand Down
4 changes: 2 additions & 2 deletions src/olympia/amo/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1161,10 +1161,10 @@ def extract_colors_from_image(path):
return colors


def use_fake_fxa():
def use_fake_fxa(config):
"""Return whether or not to use a fake FxA server for authentication.
Should always return False in production"""
return settings.DEV_MODE and settings.USE_FAKE_FXA_AUTH
return config.get('client_id') == '.'


class AMOJSONEncoder(JSONEncoder):
Expand Down
3 changes: 2 additions & 1 deletion src/olympia/amo/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from rest_framework.views import APIView

from olympia import amo
from olympia.accounts.utils import get_fxa_config
from olympia.amo.utils import HttpResponseXSendFile, use_fake_fxa
from olympia.api.exceptions import base_500_data
from olympia.api.serializers import SiteStatusSerializer
Expand Down Expand Up @@ -194,7 +195,7 @@ def frontend_view(*args, **kwargs):

def fake_fxa_authorization(request):
"""Fake authentication page to bypass FxA in local development envs."""
if not use_fake_fxa():
if not use_fake_fxa(get_fxa_config(request)):
raise Http404()
interesting_accounts = UserProfile.objects.exclude(groups=None).exclude(
deleted=True
Expand Down
2 changes: 1 addition & 1 deletion src/olympia/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def get_versioned_api_routes(version, url_patterns):
routes = url_patterns

# For now, this feature is only enabled in dev mode
if settings.DEV_MODE:
if not settings.PROD_MODE:
routes.extend(
[
re_path(
Expand Down
Loading

0 comments on commit 0928b7f

Please sign in to comment.