Skip to content

Commit

Permalink
Merge pull request #13 from prebid/tfc20-1
Browse files Browse the repository at this point in the history
First pass at adding TCF 2.0 handling
  • Loading branch information
hhhjort authored Mar 24, 2020
2 parents 92f359d + 315f57e commit b329781
Show file tree
Hide file tree
Showing 33 changed files with 1,351 additions and 122 deletions.
61 changes: 61 additions & 0 deletions api/consent.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package api

import (
"time"

"github.com/prebid/go-gdpr/consentconstants"
)

// VendorConsents is a GDPR Vendor Consent string, as defined by IAB Europe. For technical details,
// see https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/Consent%20string%20and%20vendor%20list%20formats%20v1.1%20Final.md#vendor-consent-string-format-
type VendorConsents interface {
// The version of the Consent string.
Version() uint8

// The time that the consent string was first created
Created() time.Time

// The time that the consent string was last updated
LastUpdated() time.Time

// The ID of the CMP used to update the consent string.
CmpID() uint16

// The version of the CMP used to update the consent string
CmpVersion() uint16

// The number of the CMP screen where consent was given
ConsentScreen() uint8

// The two-letter ISO639-1 language code used by the CMP to ask for consent, in uppercase.
ConsentLanguage() string

// The VendorListVersion which is needed to interpret this consent string.
//
// The IAB is hosting these on their webpage. For example, version 2 of the
// Vendor List can be found at https://vendorlist.consensu.org/v-2/vendorlist.json
//
// For other versions, just replace the "v-*" path with the value returned here.
// The latest version can always be found at https://vendorlist.consensu.org/vendorlist.json
VendorListVersion() uint16

// MaxVendorID describes how many vendors are encoded into the string.
// This is the upper bound (inclusive) on valid inputs for HasConsent(id).
MaxVendorID() uint16

// Determine if the user has consented to use data for the given Purpose.
//
// If the purpose is converted from an int > 24, the return value is undefined because
// the consent string doesn't have room for more purposes than that.
PurposeAllowed(id consentconstants.Purpose) bool

// Determine if a given vendor has consent to collect or receive user info.
//
// This function's behavior is undefined for "invalid" IDs.
// IDs with value < 1 or value > MaxVendorID() are definitely invalid, but IDs within that range
// may still be invalid, depending on the Vendor List.
//
// It is the caller's responsibility to get the right Vendor List version for the semantics of the ID.
// For more information, see VendorListVersion().
VendorConsent(id uint16) bool
}
31 changes: 31 additions & 0 deletions api/vendorlist.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package api

import "github.com/prebid/go-gdpr/consentconstants"

// VendorList is an interface used to fetch information about an IAB Global Vendor list.
// For the latest version, see: https://vendorlist.consensu.org/vendorlist.json
type VendorList interface {
// Version returns the version of the vendor list which this is.
//
// If the input was malformed, this will return 0.
Version() uint16

// Vendor returns info about the vendor with the given ID.
// This returns nil if that vendor isn't in this list, or the input was malformed somehow.
//
// If callers need to query multiple Purpose or LegitimateInterest statuses from the same vendor,
// they should call this function once and then reuse the object it returns for future queries.
Vendor(vendorID uint16) Vendor
}

// Vendor describes which purposes a given vendor claims to use data for, in this vendor list.
type Vendor interface {
// Purpose returns true if this vendor claims to use data for the given purpose, or false otherwise
Purpose(purposeID consentconstants.Purpose) bool

// LegitimateInterest retursn true if this vendor claims a "Legitimate Interest" to
// use data for the given purpose.
//
// For an explanation of legitimate interest, see https://www.gdpreu.org/the-regulation/key-concepts/legitimate-interest/
LegitimateInterest(purposeID consentconstants.Purpose) bool
}
1 change: 1 addition & 0 deletions consentconstants/purposes.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package consentconstants
// 2. PurposesAllowed of the Consent string: https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/Consent%20string%20and%20vendor%20list%20formats%20v1.1%20Final.md#vendor-consent-string-format-
type Purpose uint8

// TCF 1 Purposes:
const (
// InfoStorageAccess includes the storage of information, or access to information that is already stored,
// on your device such as advertising identifiers, device identifiers, cookies, and similar technologies.
Expand Down
91 changes: 91 additions & 0 deletions consentconstants/tcf2/purposes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package consentconstants

import base "github.com/prebid/go-gdpr/consentconstants"

// TCF 2.0 Purposes:
const (
// InfoStorageAccess includes the storage of information, or access to information that is already stored,
// on your device such as advertising identifiers, device identifiers, cookies, and similar technologies.
InfoStorageAccess base.Purpose = 1

// Cookies, device identifiers, or other information can be stored or accessed on your device for the purposes presented to you.
// Vendors can:
// * Store and access information on the device such as cookies and device identifiers presented to a user.
// Reuse InfoStorageAccess above

// Ads can be shown to you based on the content you are viewing, the app you are using, your approximate location, or your device type.
// To do basic ad selection vendors can:
// * Use real-time information about the context in which the ad will be shown, to show the ad, including information about the content and
// the device, such as: device type and capabilities, user agent, URL, IP address
// * Use a user's non-precise geolocation data
// * Control the frequency of ads shown to a user.\n* Sequence the order in which ads are shown to a user.
// * Prevent an ad from serving in an unsuitable editorial (brand-unsafe) context
// Vendors cannot:
// * Create a personalised ads profile using this information for the selection of future ads.
// * N.B. Non-precise means only an approximate location involving at least a radius of 500 meters is permitted.
BasicAdserving base.Purpose = 2

// A profile can be built about you and your interests to show you personalised ads that are relevant to you.
// To create a personalised ads profile vendors can:
// * Collect information about a user, including a user's activity, interests, demographic information, or location, to create or edit a user profile for use in personalised advertising.
// * Combine this information with other information previously collected, including from across websites and apps, to create or edit a user profile for use in personalised advertising.
PersonalizationProfile base.Purpose = 3

// Personalised ads can be shown to you based on a profile about you.
// To select personalised ads vendors can:
// * Select personalised ads based on a user profile or other historical user data, including a user's prior activity, interests, visits to sites or apps, location, or demographic information.
PersonalizationSelection base.Purpose = 4

// A profile can be built about you and your interests to show you personalised content that is relevant to you.
// To create a personalised content profile vendors can:
// * Collect information about a user, including a user's activity, interests, visits to sites or apps, demographic information, or location, to create or edit a user profile for personalising content.
// * Combine this information with other information previously collected, including from across websites and apps, to create or edit a user profile for use in personalising content.
ContentProfile base.Purpose = 5

// Personalised content can be shown to you based on a profile about you.
// To select personalised content vendors can:
// * Select personalised content based on a user profile or other historical user data, including a user\u2019s prior activity, interests, visits to sites or apps, location, or demographic information.
ContentSelection base.Purpose = 6

// The performance and effectiveness of ads that you see or interact with can be measured.
// To measure ad performance vendors can:
// * Measure whether and how ads were delivered to and interacted with by a user
// * Provide reporting about ads including their effectiveness and performance
// * Provide reporting about users who interacted with ads using data observed during the course of the user's interaction with that ad
// * Provide reporting to publishers about the ads displayed on their property
// * Measure whether an ad is serving in a suitable editorial environment (brand-safe) context
// * Determine the percentage of the ad that had the opportunity to be seen and the duration of that opportunity
// * Combine this information with other information previously collected, including from across websites and apps
// Vendors cannot:
// *Apply panel- or similarly-derived audience insights data to ad measurement data without a Legal Basis to apply market research to generate audience insights (Purpose 9)
AdPerformance base.Purpose = 7

// The performance and effectiveness of content that you see or interact with can be measured.
// To measure content performance vendors can:
// * Measure and report on how content was delivered to and interacted with by users.
// * Provide reporting, using directly measurable or known information, about users who interacted with the content
// * Combine this information with other information previously collected, including from across websites and apps.
// Vendors cannot:
// * Measure whether and how ads (including native ads) were delivered to and interacted with by a user.
// * Apply panel- or similarly derived audience insights data to ad measurement data without a Legal Basis to apply market research to generate audience insights (Purpose 9)
ContentPerformance base.Purpose = 8

// Market research can be used to learn more about the audiences who visit sites/apps and view ads.
// To apply market research to generate audience insights vendors can:
// * Provide aggregate reporting to advertisers or their representatives about the audiences reached by their ads, through panel-based and similarly derived insights.
// * Provide aggregate reporting to publishers about the audiences that were served or interacted with content and/or ads on their property by applying panel-based and similarly derived insights.
// * Associate offline data with an online user for the purposes of market research to generate audience insights if vendors have declared to match and combine offline data sources (Feature 1)
// * Combine this information with other information previously collected including from across websites and apps.
// Vendors cannot:
// * Measure the performance and effectiveness of ads that a specific user was served or interacted with, without a Legal Basis to measure ad performance.
// * Measure which content a specific user was served and how they interacted with it, without a Legal Basis to measure content performance.
MarketResearch base.Purpose = 9

// Your data can be used to improve existing systems and software, and to develop new products
// To develop new products and improve products vendors can:
// * Use information to improve their existing products with new features and to develop new products
// * Create new models and algorithms through machine learning
// Vendors cannot:
// * Conduct any other data processing operation allowed under a different purpose under this purpose
DevelopImprove base.Purpose = 10
)
94 changes: 17 additions & 77 deletions vendorconsent/consent.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,93 +2,33 @@ package vendorconsent

import (
"encoding/base64"
"time"
"strings"

"github.com/prebid/go-gdpr/consentconstants"
"github.com/prebid/go-gdpr/api"
tcf1 "github.com/prebid/go-gdpr/vendorconsent/tcf1"
tcf2 "github.com/prebid/go-gdpr/vendorconsent/tcf2"
)

// VendorConsents is a GDPR Vendor Consent string, as defined by IAB Europe. For technical details,
// see https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/Consent%20string%20and%20vendor%20list%20formats%20v1.1%20Final.md#vendor-consent-string-format-
type VendorConsents interface {
// The version of the Consent string.
Version() uint8

// The time that the consent string was first created
Created() time.Time

// The time that the consent string was last updated
LastUpdated() time.Time

// The ID of the CMP used to update the consent string.
CmpID() uint16

// The version of the CMP used to update the consent string
CmpVersion() uint16

// The number of the CMP screen where consent was given
ConsentScreen() uint8

// The two-letter ISO639-1 language code used by the CMP to ask for consent, in uppercase.
ConsentLanguage() string

// The VendorListVersion which is needed to interpret this consent string.
//
// The IAB is hosting these on their webpage. For example, version 2 of the
// Vendor List can be found at https://vendorlist.consensu.org/v-2/vendorlist.json
//
// For other versions, just replace the "v-*" path with the value returned here.
// The latest version can always be found at https://vendorlist.consensu.org/vendorlist.json
VendorListVersion() uint16

// MaxVendorID describes how many vendors are encoded into the string.
// This is the upper bound (inclusive) on valid inputs for HasConsent(id).
MaxVendorID() uint16

// Determine if the user has consented to use data for the given Purpose.
//
// If the purpose is converted from an int > 24, the return value is undefined because
// the consent string doesn't have room for more purposes than that.
PurposeAllowed(id consentconstants.Purpose) bool

// Determine if a given vendor has consent to collect or receive user info.
//
// This function's behavior is undefined for "invalid" IDs.
// IDs with value < 1 or value > MaxVendorID() are definitely invalid, but IDs within that range
// may still be invalid, depending on the Vendor List.
//
// It is the caller's responsibility to get the right Vendor List version for the semantics of the ID.
// For more information, see VendorListVersion().
VendorConsent(id uint16) bool
}

// ParseString parses a Raw (unpadded) base64 URL encoded string.
func ParseString(consent string) (VendorConsents, error) {
decoded, err := base64.RawURLEncoding.DecodeString(consent)
func ParseString(consent string) (api.VendorConsents, error) {
pieces := strings.Split(consent, ".")
decoded, err := base64.RawURLEncoding.DecodeString(pieces[0])
if err != nil {
return nil, err
}
return Parse(decoded)
}

// Parse the vendor consent data from the string. This string should *not* be encoded (by base64 or any other encoding).
// If the data is malformed and cannot be interpreted as a vendor consent string, this will return an error.
func Parse(data []byte) (VendorConsents, error) {
metadata, err := parseMetadata(data)
if err != nil {
return nil, err
version := uint8(decoded[0] >> 2)
if version == 2 {
return tcf2.Parse(decoded)
}
return tcf1.Parse(decoded)
}

// Bit 172 determines whether or not the consent string encodes Vendor data in a RangeSection or BitField.
if isSet(data, 172) {
return parseRangeSection(metadata)
}
// Backwards compatibility

return parseBitField(metadata)
type VendorConsents interface {
api.VendorConsents
}

// Returns true if the bitIndex'th bit in data is a 1, and false if it's a 0.
func isSet(data []byte, bitIndex uint) bool {
byteIndex := bitIndex / 8
bitOffset := bitIndex % 8
return byteToBool(data[byteIndex] & (0x80 >> bitOffset))
func Parse(data []byte) (api.VendorConsents, error) {
return tcf1.Parse(data)
}
Loading

0 comments on commit b329781

Please sign in to comment.