Skip to content

Commit

Permalink
Bug 1879792 - Add a cookie banner to BMO
Browse files Browse the repository at this point in the history
  • Loading branch information
dklawren authored Jan 3, 2025
1 parent 70544f7 commit 34c4020
Show file tree
Hide file tree
Showing 31 changed files with 919 additions and 220 deletions.
37 changes: 36 additions & 1 deletion Bugzilla/CGI.pm
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use Bugzilla::Util;
use Bugzilla::Search::Recent;

use File::Basename;
use List::Util qw(any none);
use URI;

BEGIN {
Expand Down Expand Up @@ -493,7 +494,7 @@ sub param {

sub _fix_utf8 {
my $input = shift;

# The is_utf8 is here in case CGI gets smart about UTF-8 someday.
utf8::decode($input) if defined $input && !ref $input && !utf8::is_utf8($input);
return $input;
Expand All @@ -514,6 +515,16 @@ sub should_set {
sub send_cookie {
my ($self, %paramhash) = @_;

# We check to see if the cookie be set is essential and if
# not we check to see if the user has given consent to set it
if (Bugzilla->params->{cookie_consent_enabled}
&& $self->cookie_consent_required
&& !$self->cookie_consented
)
{
return undef if none { $_ eq $paramhash{'-name'} } ESSENTIAL_COOKIES;
}

# Complain if -value is not given or empty (bug 268146).
if (!exists($paramhash{'-value'}) || !$paramhash{'-value'}) {
ThrowCodeError('cookies_need_value');
Expand Down Expand Up @@ -666,6 +677,30 @@ sub set_dated_content_disp {
$self->{'_content_disp'} = $disposition;
}

# Return true/false if a user has consent to non-essential cookies
# 1. If cookie is not present then no consent
# 2. If cookie is present and equal to 'yes' then we have consent
# 3. Any other value we do not have consent
sub cookie_consented {
my ($self) = @_;
if (defined $self->cookie(CONSENT_COOKIE)
&& $self->cookie(CONSENT_COOKIE) eq 'yes')
{
return 1;
}
return 0;
}

# Return true if client is accessing this site
# from within a required consent country
sub cookie_consent_required {
my ($self) = @_;
return 1 if $ENV{CI};
my $client_region = $self->http('X-Client-Region') || '';
return 1 if any { $client_region eq $_ } COOKIE_CONSENT_COUNTRIES;
return 0;
}

# If a cookie is requested that has been set but not yet stored in the browser,
# then we can return it here. 'X' means the cookie is being removed
sub cookie {
Expand Down
6 changes: 6 additions & 0 deletions Bugzilla/Config/Admin.pm
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ sub get_param_list {
type => 't',
default => 'https://product-details.mozilla.org/1.0',
},

{
name => 'cookie_consent_enabled',
type => 'b',
default => 0,
}
);
return @param_list;
}
Expand Down
34 changes: 26 additions & 8 deletions Bugzilla/Constants.pm
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,10 @@ use Memoize;
JOB_QUEUE_VIEW_MAX_JOBS
BOUNCE_COUNT_MAX
CONSENT_COOKIE
ESSENTIAL_COOKIES
COOKIE_CONSENT_COUNTRIES
);

@Bugzilla::Constants::EXPORT_OK = qw(contenttypes);
Expand Down Expand Up @@ -678,6 +682,27 @@ use constant JOB_QUEUE_VIEW_MAX_JOBS => 2500;
# before the account is completely disabled.
use constant BOUNCE_COUNT_MAX => 5;

# Consent cookie name
use constant CONSENT_COOKIE => 'moz-consent-pref';

# List of essential cookies that cannot be opted out
use constant ESSENTIAL_COOKIES => qw(
bugzilla
Bugzilla_login
Bugzilla_logincookie
Bugzilla_login_request_cookie
github_state
github_token
mfa_verification_token
moz-consent-pref
sudo
);

# List of countries that require cookie consent
use constant COOKIE_CONSENT_COUNTRIES => qw(
AT BE BG HR CY CZ DK EE FI FR DE GR HU IE IS IT LV
LI LT LU MT NL NO PL PT RO SK SI ES SE CH GB );

sub bz_locations {

# Force memoize() to re-compute data per project, to avoid
Expand Down Expand Up @@ -745,7 +770,7 @@ sub DEFAULT_CSP {
my %policy = (
default_src => ['self'],
script_src =>
['self', 'nonce', 'unsafe-inline', 'https://www.google-analytics.com'],
['self', 'nonce', 'unsafe-inline'],
frame_src => [
# This is for extensions/BMO/web/js/firefox-crash-table.js
'https://crash-stop-addon.herokuapp.com',
Expand All @@ -760,9 +785,6 @@ sub DEFAULT_CSP {
# This is for extensions/BMO/web/js/firefox-crash-table.js
'https://product-details.mozilla.org',

# This is for extensions/GoogleAnalytics using beacon or XHR
'https://www.google-analytics.com',

# This is from extensions/OrangeFactor/web/js/orange_factor.js
'https://treeherder.mozilla.org/api/failurecount/',

Expand Down Expand Up @@ -805,7 +827,6 @@ sub SHOW_BUG_MODAL_CSP {
script_src => [
'self', 'nonce',
'unsafe-inline', 'unsafe-eval',
'https://www.google-analytics.com'
],
img_src => ['self', 'data:', 'https://secure.gravatar.com'],
media_src => ['self'],
Expand All @@ -815,9 +836,6 @@ sub SHOW_BUG_MODAL_CSP {
# This is for extensions/BMO/web/js/firefox-crash-table.js
'https://product-details.mozilla.org',

# This is for extensions/GoogleAnalytics using beacon or XHR
'https://www.google-analytics.com',

# This is from extensions/OrangeFactor/web/js/orange_factor.js
'https://treeherder.mozilla.org/api/failurecount/',
],
Expand Down
1 change: 1 addition & 0 deletions docker-compose.test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ services:
- BZ_ANSWERS_FILE=/app/conf/checksetup_answers.txt
- BZ_QA_CONF_FILE=/app/qa/config/selenium_test.conf
- BZ_QA_CONFIG=1
- CI=1
- LOCALCONFIG_ENV=1
- LOG4PERL_CONFIG_FILE=log4perl-test.conf
- LOGGING_PORT=5880
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"js/bug.js", # Possible Duplicates table
"js/attachment.js",
"extensions/BugModal/web/create.js",
"js/util.js"
]
style_urls = [
"skins/standard/attachment.css",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@
"extensions/ComponentWatching/web/js/overlay.js",
"js/bugzilla-readable-status-min.js",
"js/field.js",
"js/comments.js"
"js/comments.js",
"js/util.js"
);
jquery.push(
"contextMenu",
Expand Down Expand Up @@ -117,7 +118,8 @@
remember_collapsed: [% user.settings.ui_remember_collapsed.value == "on" ? "true" : "false" %],
inline_attachments: [% user.settings.inline_attachments.value == "on" ? "true" : "false" %],
autosize_comments: [% user.settings.autosize_comments.value == "on" ? "true" : "false" %]
}
},
cookie_consent: [% Bugzilla.cgi.consent_cookie ? "true" : "false" %]
};
[% IF user.id %]
BUGZILLA.default_assignee = '[% bug.component_obj.default_assignee.login FILTER js %]';
Expand Down
22 changes: 11 additions & 11 deletions extensions/BugModal/web/bug_modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ function slide_module(module, action, fast) {
'aria-label': is_visible ? latch.data('label-expanded') : latch.data('label-collapsed'),
});
if (BUGZILLA.user.settings.remember_collapsed && module.is(':visible'))
localStorage.setItem(module.attr('id') + '.visibility', is_visible ? 'show' : 'hide');
Bugzilla.Storage.set(module.attr('id') + '.visibility', is_visible ? 'show' : 'hide');
}

if (action == 'show') {
Expand All @@ -43,7 +43,7 @@ function init_module_visibility() {
var id = that.attr('id');
if (!id) return;
if (that.data('non-stick')) return;
var stored = localStorage.getItem(id + '.visibility');
var stored = Bugzilla.Storage.get(id + '.visibility');
if (stored) {
slide_module(that, stored, true);
}
Expand Down Expand Up @@ -139,7 +139,7 @@ $(function() {
// restore edit mode after navigating back
function restoreEditMode() {
if (!$('#editing').val()) {
if (localStorage.getItem('modal-perm-edit-mode') === 'true') {
if (Bugzilla.Storage.get('modal-perm-edit-mode') === 'true') {
$('#mode-btn').click();
$('#action-enable-perm-edit').attr('aria-checked', 'true');
}
Expand Down Expand Up @@ -170,7 +170,7 @@ $(function() {
text: text,
savedAt: Date.now()
};
localStorage.setItem(bugCommentCacheKey, JSON.stringify(value));
Bugzilla.Storage.set(bugCommentCacheKey, JSON.stringify(value));
}

/**
Expand All @@ -180,7 +180,7 @@ $(function() {
* to take such special cases into account. Otherwise the current bug’s comment cache will be removed.
*/
const clearSavedBugComment = (bug_id = BUGZILLA.bug_id) => {
localStorage.removeItem(`bug-modal-saved-comment-${bug_id}`);
Bugzilla.Storage.delete(`bug-modal-saved-comment-${bug_id}`);
};

/**
Expand All @@ -200,7 +200,7 @@ $(function() {

function restoreSavedBugComment() {
expireSavedComments();
let value = JSON.parse(localStorage.getItem(bugCommentCacheKey));
let value = Bugzilla.Storage.get(bugCommentCacheKey);
if (value){
let commentBox = document.querySelector("textarea#comment");
if (commentBox.value === '')
Expand All @@ -213,10 +213,10 @@ $(function() {
function expireSavedComments() {
const AGE_THRESHOLD = 7 * 24 * 60 * 60 * 1000; // 7 days in milliseconds.
let expiredKeys = [];
for (let i = 0; i < localStorage.length; i++) {
let key = localStorage.key(i);
for (let i = 0; i < window.localStorage.length; i++) {
let key = window.localStorage.key(i);
if (key.match(/^bug-modal-saved-comment-/)) {
let value = JSON.parse(localStorage.getItem(key));
let value = Bugzilla.Storage.get(key);
let savedAt = value['savedAt'] || 0;
let age = Date.now() - savedAt;
if (age < 0 || age > AGE_THRESHOLD) {
Expand All @@ -225,7 +225,7 @@ $(function() {
}
}
expiredKeys.forEach((key) => {
localStorage.removeItem(key);
Bugzilla.Storage.delete(key);
});
}

Expand Down Expand Up @@ -543,7 +543,7 @@ $(function() {
event.preventDefault();
const enabled = $(this).attr('aria-checked') !== 'true';
$(this).attr('aria-checked', enabled);
localStorage.setItem('modal-perm-edit-mode', enabled);
Bugzilla.Storage.set('modal-perm-edit-mode', enabled);
});

// reset
Expand Down
4 changes: 2 additions & 2 deletions extensions/BugModal/web/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ window.addEventListener('DOMContentLoaded', () => {
$toggleAdvanced.textContent = $toggleAdvanced.dataset[advancedStateStr];

if (cache) {
window.localStorage.setItem('create-form.advanced', advancedStateStr);
Bugzilla.Storage.set('create-form.advanced', advancedStateStr);
}
};

Expand All @@ -73,7 +73,7 @@ window.addEventListener('DOMContentLoaded', () => {
// Check the local storage or the TUI cookie used on the legacy form to see if the user wants
// to show advanced fields on the bug form.
let showAdvanced =
window.localStorage.getItem('create-form.advanced') === 'show'
Bugzilla.Storage.get('create-form.advanced') === 'show'
|| /\bTUI=\S*?expert_fields=1\b/.test(document.cookie);

if (showAdvanced) {
Expand Down
16 changes: 0 additions & 16 deletions extensions/GoogleAnalytics/Config.pm

This file was deleted.

23 changes: 0 additions & 23 deletions extensions/GoogleAnalytics/Extension.pm

This file was deleted.

38 changes: 0 additions & 38 deletions extensions/GoogleAnalytics/lib/Config.pm

This file was deleted.

This file was deleted.

Loading

0 comments on commit 34c4020

Please sign in to comment.