Skip to content

Commit

Permalink
divolte#461 - added support to divolte.js - cookie - samesite
Browse files Browse the repository at this point in the history
  • Loading branch information
krunchakravarthy committed Mar 8, 2021
1 parent 5895532 commit 6b02889
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 47 deletions.
15 changes: 15 additions & 0 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,21 @@ Browser source property: ``cookie_domain``
cookie_domain = ".example.com"
}
Browser source property: ``cookie_same_site``
""""""""""""""""""""""""""""""""""""""""""
:Description:
The cookie SameSite that is assigned to the cookies. When left empty, the cookies will be set with browser default. Available options to set - Strict, Lax & None; Secure
:Default:
*Empty*
:Example:

.. code-block:: none
divolte.sources.a_source {
type = browser
cookie_same_site = "Lax"
}
Browser source property: ``javascript.name``
""""""""""""""""""""""""""""""""""""""""""""
:Description:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,21 @@

package io.divolte.server.config;

import java.time.Duration;
import java.util.Objects;
import java.util.Optional;

import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import javax.annotation.ParametersAreNullableByDefault;
import javax.validation.Valid;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.MoreObjects;
import io.divolte.server.BrowserSource;
import io.divolte.server.HttpSource;
import io.divolte.server.IncomingRequestProcessingPool;

import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import javax.annotation.ParametersAreNullableByDefault;
import javax.validation.Valid;
import java.time.Duration;
import java.util.Objects;
import java.util.Optional;

@ParametersAreNonnullByDefault
public class BrowserSourceConfiguration extends SourceConfiguration {
private static final String DEFAULT_PARTY_COOKIE = "_dvp";
Expand All @@ -43,19 +42,21 @@ public class BrowserSourceConfiguration extends SourceConfiguration {
private static final String DEFAULT_HTTP_RESPONSE_DELAY = "0 seconds";

public static final BrowserSourceConfiguration DEFAULT_BROWSER_SOURCE_CONFIGURATION = new BrowserSourceConfiguration(
DEFAULT_PREFIX,
DEFAULT_EVENT_SUFFIX,
Optional.empty(),
DEFAULT_PARTY_COOKIE,
DurationDeserializer.parseDuration(DEFAULT_PARTY_TIMEOUT),
DEFAULT_SESSION_COOKIE,
DurationDeserializer.parseDuration(DEFAULT_SESSION_TIMEOUT),
DurationDeserializer.parseDuration(DEFAULT_HTTP_RESPONSE_DELAY),
JavascriptConfiguration.DEFAULT_JAVASCRIPT_CONFIGURATION);
DEFAULT_PREFIX,
DEFAULT_EVENT_SUFFIX,
Optional.empty(),
DEFAULT_PARTY_COOKIE,
DurationDeserializer.parseDuration(DEFAULT_PARTY_TIMEOUT),
DEFAULT_SESSION_COOKIE,
DurationDeserializer.parseDuration(DEFAULT_SESSION_TIMEOUT),
DurationDeserializer.parseDuration(DEFAULT_HTTP_RESPONSE_DELAY),
JavascriptConfiguration.DEFAULT_JAVASCRIPT_CONFIGURATION,
Optional.empty());

public final String prefix;
public final String eventSuffix;
public final Optional<String> cookieDomain;
public final Optional<String> cookieSameSite;
public final String partyCookie;
public final Duration partyTimeout;
public final String sessionCookie;
Expand All @@ -67,15 +68,16 @@ public class BrowserSourceConfiguration extends SourceConfiguration {

@JsonCreator
@ParametersAreNullableByDefault
BrowserSourceConfiguration(@JsonProperty(defaultValue=DEFAULT_PREFIX) final String prefix,
@JsonProperty(defaultValue=DEFAULT_EVENT_SUFFIX) final String eventSuffix,
BrowserSourceConfiguration(@JsonProperty(defaultValue = DEFAULT_PREFIX) final String prefix,
@JsonProperty(defaultValue = DEFAULT_EVENT_SUFFIX) final String eventSuffix,
@Nonnull final Optional<String> cookieDomain,
@JsonProperty(defaultValue=DEFAULT_PARTY_COOKIE) final String partyCookie,
@JsonProperty(defaultValue=DEFAULT_PARTY_TIMEOUT) final Duration partyTimeout,
@JsonProperty(defaultValue=DEFAULT_SESSION_COOKIE) final String sessionCookie,
@JsonProperty(defaultValue=DEFAULT_SESSION_TIMEOUT) final Duration sessionTimeout,
@JsonProperty(defaultValue=DEFAULT_HTTP_RESPONSE_DELAY) final Duration httpResponseDelay,
final JavascriptConfiguration javascript) {
@JsonProperty(defaultValue = DEFAULT_PARTY_COOKIE) final String partyCookie,
@JsonProperty(defaultValue = DEFAULT_PARTY_TIMEOUT) final Duration partyTimeout,
@JsonProperty(defaultValue = DEFAULT_SESSION_COOKIE) final String sessionCookie,
@JsonProperty(defaultValue = DEFAULT_SESSION_TIMEOUT) final Duration sessionTimeout,
@JsonProperty(defaultValue = DEFAULT_HTTP_RESPONSE_DELAY) final Duration httpResponseDelay,
final JavascriptConfiguration javascript,
@Nonnull final Optional<String> cookieSameSite) {
// TODO: register a custom deserializer with Jackson that uses the defaultValue property from the annotation to fix this
this.prefix = Optional.ofNullable(prefix).map(BrowserSourceConfiguration::ensureTrailingSlash).orElse(DEFAULT_PREFIX);
this.eventSuffix = Optional.ofNullable(eventSuffix).orElse(DEFAULT_EVENT_SUFFIX);
Expand All @@ -86,6 +88,7 @@ public class BrowserSourceConfiguration extends SourceConfiguration {
this.sessionTimeout = Optional.ofNullable(sessionTimeout).orElseGet(() -> DurationDeserializer.parseDuration(DEFAULT_SESSION_TIMEOUT));
this.httpResponseDelay = Optional.ofNullable(httpResponseDelay).orElseGet(() -> DurationDeserializer.parseDuration(DEFAULT_HTTP_RESPONSE_DELAY));
this.javascript = Optional.ofNullable(javascript).orElse(JavascriptConfiguration.DEFAULT_JAVASCRIPT_CONFIGURATION);
this.cookieSameSite = Objects.requireNonNull(cookieSameSite);
}

private static String ensureTrailingSlash(final String s) {
Expand All @@ -95,22 +98,23 @@ private static String ensureTrailingSlash(final String s) {
@Override
protected MoreObjects.ToStringHelper toStringHelper() {
return super.toStringHelper()
.add("prefix", prefix)
.add("eventSuffix", eventSuffix)
.add("cookieDomain", cookieDomain)
.add("partyCookie", partyCookie)
.add("partyTimeout", partyTimeout)
.add("sessionCookie", sessionCookie)
.add("sessionTimeout", sessionTimeout)
.add("httpResponseDelay", httpResponseDelay)
.add("javascript", javascript);
.add("prefix", prefix)
.add("eventSuffix", eventSuffix)
.add("cookieDomain", cookieDomain)
.add("partyCookie", partyCookie)
.add("partyTimeout", partyTimeout)
.add("sessionCookie", sessionCookie)
.add("sessionTimeout", sessionTimeout)
.add("httpResponseDelay", httpResponseDelay)
.add("javascript", javascript)
.add("SameSite", cookieSameSite);
}

@Override
public HttpSource createSource(
final ValidatedConfiguration vc,
final String sourceName,
final IncomingRequestProcessingPool processingPool) {
final ValidatedConfiguration vc,
final String sourceName,
final IncomingRequestProcessingPool processingPool) {
return new BrowserSource(vc, sourceName, processingPool);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,14 @@ private static ImmutableMap<String, Object> createScriptConstants(final BrowserS
builder.put("PARTY_ID_TIMEOUT_SECONDS", trimLongToMaxInt(browserSourceConfiguration.partyTimeout.get(ChronoUnit.SECONDS)));
builder.put("SESSION_COOKIE_NAME", browserSourceConfiguration.sessionCookie);
builder.put("SESSION_ID_TIMEOUT_SECONDS", trimLongToMaxInt(browserSourceConfiguration.sessionTimeout.get(ChronoUnit.SECONDS)));
browserSourceConfiguration.cookieDomain.ifPresent((v) -> builder.put("COOKIE_DOMAIN", v));
browserSourceConfiguration.cookieDomain.ifPresent(v -> builder.put("COOKIE_DOMAIN", v));
browserSourceConfiguration.cookieSameSite.ifPresent(v -> builder.put("COOKIE_SAME_SITE", v));
builder.put("LOGGING", browserSourceConfiguration.javascript.logging);
builder.put(SCRIPT_CONSTANT_NAME, browserSourceConfiguration.javascript.name);
builder.put("EVENT_SUFFIX", browserSourceConfiguration.eventSuffix);
builder.put("AUTO_PAGE_VIEW_EVENT", browserSourceConfiguration.javascript.autoPageViewEvent);
builder.put("EVENT_TIMEOUT_SECONDS", browserSourceConfiguration.javascript.eventTimeout.getSeconds() +
browserSourceConfiguration.javascript.eventTimeout.getNano() / (double)NANOS_PER_SECOND);
browserSourceConfiguration.javascript.eventTimeout.getNano() / (double) NANOS_PER_SECOND);
return builder.build();
}

Expand All @@ -68,17 +69,17 @@ private static int trimLongToMaxInt(long duration) {
} else {
result = Integer.MAX_VALUE;
logger.warn("Configured duration ({}) is too higher; capping at {}.",
duration, result);
duration, result);
}
return result;
}

public static TrackingJavaScriptResource create(final ValidatedConfiguration vc,
final String sourceName) throws IOException {
final BrowserSourceConfiguration browserSourceConfiguration =
vc.configuration().getSourceConfiguration(sourceName, BrowserSourceConfiguration.class);
vc.configuration().getSourceConfiguration(sourceName, BrowserSourceConfiguration.class);
return new TrackingJavaScriptResource("divolte.js",
createScriptConstants(browserSourceConfiguration),
browserSourceConfiguration.javascript.debug);
createScriptConstants(browserSourceConfiguration),
browserSourceConfiguration.javascript.debug);
}
}
14 changes: 11 additions & 3 deletions src/main/resources/divolte.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ var SCRIPT_NAME = 'divolte.js';
var EVENT_SUFFIX = 'csc-event';
/** @define {boolean} */
var AUTO_PAGE_VIEW_EVENT = true;
/** @define {string} */
var COOKIE_SAME_SITE = '';

(function (global, factory) {
factory(global);
Expand Down Expand Up @@ -179,14 +181,20 @@ var AUTO_PAGE_VIEW_EVENT = true;
* @param {number} nowMs The current time, in milliseconds since the Unix epoch.
* @param {string} domain The domain to set the cookies for, if non-zero in length.
*/
var setCookie = function(name, value, maxAgeSeconds, nowMs, domain) {
var setCookie = function(name, value, maxAgeSeconds, nowMs, domain, sameSite) {
var expiry = new Date(nowMs + 1000 * maxAgeSeconds);
// Assumes cookie name and value are sensible. (For our use they are.)
// Note: No domain means these are always first-party cookies.
var cookieString = name + '=' + value + "; path=/; expires=" + expiry.toUTCString() + "; max-age=" + maxAgeSeconds;
if (domain) {
cookieString += "; domain=" + domain;
}

// SameSite supports None; Secure, Strict & Lax
if (sameSite) {
cookieString += "; SameSite=" + sameSite;
}

document.cookie = cookieString;
};

Expand Down Expand Up @@ -1309,8 +1317,8 @@ var AUTO_PAGE_VIEW_EVENT = true;
isFirstInSession = false;

// Update the party and session cookies.
setCookie(SESSION_COOKIE_NAME, sessionId, SESSION_ID_TIMEOUT_SECONDS, eventTime, COOKIE_DOMAIN);
setCookie(PARTY_COOKIE_NAME, partyId, PARTY_ID_TIMEOUT_SECONDS, eventTime, COOKIE_DOMAIN);
setCookie(SESSION_COOKIE_NAME, sessionId, SESSION_ID_TIMEOUT_SECONDS, eventTime, COOKIE_DOMAIN, COOKIE_SAME_SITE);
setCookie(PARTY_COOKIE_NAME, partyId, PARTY_ID_TIMEOUT_SECONDS, eventTime, COOKIE_DOMAIN, COOKIE_SAME_SITE);

// Last thing we do: add a checksum to the queryString.
addParam('x', calculateChecksum(params).toString(36));
Expand Down

0 comments on commit 6b02889

Please sign in to comment.