diff options
author | Thomas Steur <tsteur@users.noreply.github.com> | 2020-07-06 00:25:04 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-07-06 00:25:04 +0300 |
commit | 3a636705df0aa58d5a0264091294eb5d50e67b6f (patch) | |
tree | e17332fd6ce740fbf998594ab8c880aa19294ca2 | |
parent | e9455ccfa5f4780b65685ad15636bdab0010cc28 (diff) |
Don't set any cookies when no consent is given (#16173)
* Better detection for cookies for browser plugins report
* rebuilt piwik.js
* improve comment
* Add method to enable cookies
* rebuilt piwik.js
* fix test
* no longer include cookie in fingerprint
* only ignore cookies in fingerprint for IE
* fix tests
* fix test
* tweak enablecookies
* rebuilt piwik.js
* send tracking request if needed when enable cookies
* rebuilt piwik.js
* tweak code
* update docs
* rebuilt piwik.js
* Update Visit.php
* fix tests
* Don't set cookies unless consent given when consent is required
* fix test
* rebuilt piwik.js
* add tests
* add missing function
* rebuilt piwik.js
* fix jslint test
-rw-r--r-- | js/piwik.js | 67 | ||||
-rw-r--r-- | tests/javascript/index.php | 21 |
2 files changed, 78 insertions, 10 deletions
diff --git a/js/piwik.js b/js/piwik.js index 6751051893..2ab5f9b9a8 100644 --- a/js/piwik.js +++ b/js/piwik.js @@ -960,7 +960,7 @@ if (typeof window.JSON === 'object' && typeof window.JSON.stringify === 'functio /*members Piwik, Matomo, encodeURIComponent, decodeURIComponent, getElementsByTagName, shift, unshift, piwikAsyncInit, piwikPluginAsyncInit, frameElement, self, hasFocus, createElement, appendChild, characterSet, charset, all, - addEventListener, attachEvent, removeEventListener, detachEvent, disableCookies, enableCookies, + addEventListener, attachEvent, removeEventListener, detachEvent, disableCookies, enableCookies, areCookiesEnabled, cookie, domain, readyState, documentElement, doScroll, title, text, contentWindow, postMessage, location, top, onerror, document, referrer, parent, links, href, protocol, name, GearsFactory, performance, mozPerformance, msPerformance, webkitPerformance, timing, requestStart, @@ -4372,7 +4372,6 @@ if (typeof window.Piwik !== 'object') { function setSiteId(siteId) { configTrackerSiteId = siteId; - setVisitorIdCookie(); } function sortObjectByKeys(value) { @@ -6803,6 +6802,31 @@ if (typeof window.Piwik !== 'object') { } }; + this.areCookiesEnabled = function () { + return !configCookiesDisabled; + }; + + /** + * Enables cookies if they were disabled previously + */ + this.enableCookies = function () { + if (configCookiesDisabled && !configDoNotTrack) { + configCookiesDisabled = false; + if (configTrackerSiteId && hasSentTrackingRequestYet) { + setVisitorIdCookie(); + + // sets attribution cookie, and updates visitorId in the backend + // because hasSentTrackingRequestYet=true we assume there might not be another tracking + // request within this page view so we trigger one ourselves. + // if no tracking request has been sent yet, we don't set the attribution cookie cause Matomo + // sets the cookie only when there is a tracking request. It'll be set if the user sends + // a tracking request afterwards + var request = getRequest('ping=1', null, 'ping'); + sendRequest(request, configTrackerPause); + } + } + }; + /** * Enables cookies if they were disabled previously */ @@ -7631,6 +7655,11 @@ if (typeof window.Piwik !== 'object') { this.requireConsent = function () { configConsentRequired = true; configHasConsent = this.hasRememberedConsent(); + if (!configHasConsent) { + // we won't call this.disableCookies() since we don't want to delete any cookies just yet + // user might call `setConsentGiven` next + configCookiesDisabled = true; + } // Piwik.addPlugin might not be defined at this point, we add the plugin directly also to make JSLint happy // We also want to make sure to define an unload listener for each tracker, not only one tracker. coreConsentCounter++; @@ -7648,10 +7677,16 @@ if (typeof window.Piwik !== 'object') { * Call this method once the user has given consent. This will cause all tracking requests from this * page view to be sent. Please note that the given consent won't be remembered across page views. If you * want to remember consent across page views, call {@link rememberConsentGiven()} instead. + * + * It will also automatically enable cookies if they were disabled previously. + * + * @param bool [enableCookies=true] Internal parameter. Defines whether cookies should be enabled or not. */ - this.setConsentGiven = function () { + this.setConsentGiven = function (enableCookies) { configHasConsent = true; + deleteCookie(CONSENT_REMOVED_COOKIE_NAME, configCookiePath, configCookieDomain); + var i, requestType; for (i = 0; i < consentRequestsQueue.length; i++) { requestType = typeof consentRequestsQueue[i]; @@ -7662,6 +7697,18 @@ if (typeof window.Piwik !== 'object') { } } consentRequestsQueue = []; + + // we need to enable cookies after sending the previous requests as it will make sure that we send + // a ping request if needed. Cookies are only set once we call `getRequest`. Above only calls sendRequest + // meaning no cookies will be created unless we called enableCookies after at least one request has been sent. + // this will cause a ping request to be sent that sets the cookies and also updates the newly generated visitorId + // on the server. + // If the user calls setConsentGiven before sending any tracking request (which usually is the case) then + // nothing will need to be done as it only enables cookies and the next tracking request will set the cookies + // etc. + if (!isDefined(enableCookies) || enableCookies) { + this.enableCookies(); + } }; /** @@ -7678,6 +7725,9 @@ if (typeof window.Piwik !== 'object') { * for all sites that match the configured cookieDomain and cookiePath. Depending on your website structure, * you may need to restrict or widen the scope of the cookie domain/path to ensure the consent is applied * to the sites you want. + * + * @param int hoursToExpire After how many hours the consent should expire. By default the consent is valid + * for 30 years unless cookies are deleted by the user or the browser prior to this */ this.rememberConsentGiven = function (hoursToExpire) { if (hoursToExpire) { @@ -7685,7 +7735,10 @@ if (typeof window.Piwik !== 'object') { } else { hoursToExpire = 30 * 365 * 24 * 60 * 60 * 1000; } - this.setConsentGiven(); + var enableCookies = true; + // we currently always enable cookies if we remember consent cause we don't store across requests whether + // cookies should be automatically enabled or not. + this.setConsentGiven(enableCookies); var now = new Date().getTime(); setCookie(CONSENT_COOKIE_NAME, now, hoursToExpire, configCookiePath, configCookieDomain, configCookieIsSecure); }; @@ -7722,7 +7775,11 @@ if (typeof window.Piwik !== 'object') { /** * Alias for rememberConsentGiven(). After calling this function, the current user will be tracked. */ - this.forgetUserOptOut = this.rememberConsentGiven; + this.forgetUserOptOut = function () { + // we can't automatically enable cookies here as we don't know if user actually gave consent for cookies + var enableCookies = false; + this.rememberConsentGiven(0, enableCookies); + }; Piwik.trigger('TrackerSetup', [this]); } diff --git a/tests/javascript/index.php b/tests/javascript/index.php index ddd0e61d30..150b8da14c 100644 --- a/tests/javascript/index.php +++ b/tests/javascript/index.php @@ -530,7 +530,9 @@ function PiwikTest() { // we fix the line numbers so they match to the line numbers in ../../js/piwik.js JSLINT.errors.forEach( function (item, index) { - item.line += countOfLinesRemoved; + if (item) { + item.line += countOfLinesRemoved; + } console.log(item); }); @@ -2149,7 +2151,7 @@ function PiwikTest() { }); test("API methods", function() { - expect(112); + expect(113); equal( typeof Piwik.addPlugin, 'function', 'addPlugin' ); equal( typeof Piwik.addPlugin, 'function', 'addTracker' ); @@ -2245,6 +2247,7 @@ function PiwikTest() { equal( typeof tracker.setRequestQueueInterval, 'function', 'setRequestQueueInterval' ); equal( typeof tracker.disableCookies, 'function', 'disableCookies' ); equal( typeof tracker.enableCookies, 'function', 'enableCookies' ); + equal( typeof tracker.areCookiesEnabled, 'function', 'areCookiesEnabled' ); equal( typeof tracker.deleteCookies, 'function', 'deleteCookies' ); // content equal( typeof tracker.trackAllContentImpressions, 'function', 'trackAllContentImpressions' ); @@ -4945,7 +4948,7 @@ if ($mysql) { }); test("Test API - consent", function() { - expect(29); + expect(34); var queue; var tracker = Piwik.getTracker(); @@ -4955,8 +4958,12 @@ if ($mysql) { strictEqual(tracker.getRememberedConsent(), null, "getConsentRequestsQueue, does not return consent cookie content as no consent given" ); strictEqual(tracker.hasConsent(), true, "hasConsent, assumes consent by default" ); - ok(!tracker.isConsentRequired(), 'by default consent is not required') + ok(!tracker.isConsentRequired(), 'by default consent is not required'); + ok(tracker.areCookiesEnabled(), 'by default cookies are enabled'); tracker.requireConsent(); + ok(!tracker.areCookiesEnabled(), 'require consent disables cookies'); + + ok(tracker.isConsentRequired(), 'consent is required after requiring it') deepEqual(tracker.getConsentRequestsQueue(), [], "getConsentRequestsQueue, still empty after requiring consent" ); @@ -4976,7 +4983,10 @@ if ($mysql) { strictEqual(tracker.hasRememberedConsent(), false, "getConsentRequestsQueue, has not remembered consent" ); strictEqual(tracker.getRememberedConsent(), null, "getConsentRequestsQueue, does not return consent cookie content as no consent given" ); + tracker.requireConsent(); + ok(!tracker.areCookiesEnabled(), 'after requiring consent, cookies are disabled'); tracker.rememberConsentGiven(); + ok(tracker.areCookiesEnabled(), 'remember cookie consent enables cookies'); strictEqual(tracker.hasRememberedConsent(), true, "rememberConsentGiven, sets cookie to remember consent" ); var rememberedConsent = tracker.getRememberedConsent(); @@ -5004,7 +5014,8 @@ if ($mysql) { var results = fetchTrackedRequests(getConsentToken() + '1'); strictEqual(true, results.indexOf('myFoo=bar&baz=1') > 0, "setConsentGiven does replay all queued requests" ); strictEqual(true, results.indexOf('myFoo=bar&baz=2') > 0, "setConsentGiven does replay all queued requests" ); - strictEqual(2, (results.match(/consent=1/g) || []).length, "consent=1 parameter appears in URL when explicit consent given"); + strictEqual(true, results.indexOf('ping=1') > 0, "setConsentGiven does replay all queued requests" );// sent when enabling cookies as part of setConsentGiven. Called twice in total + strictEqual(4, (results.match(/consent=1/g) || []).length, "consent=1 parameter appears in URL when explicit consent given"); var results2 = fetchTrackedRequests(getConsentToken() + '2'); strictEqual(true, results2.indexOf('myFoo=bar&baz=3') > 0, "normal request" ); |