Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug 1879792 - Add a cookie banner to BMO #2306

Merged
merged 12 commits into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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',
},

dklawren marked this conversation as resolved.
Show resolved Hide resolved
{
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');
dklawren marked this conversation as resolved.
Show resolved Hide resolved
}

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);
dklawren marked this conversation as resolved.
Show resolved Hide resolved
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);
dklawren marked this conversation as resolved.
Show resolved Hide resolved
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
Loading