Skip to content

Commit

Permalink
header padding, CA TCF support
Browse files Browse the repository at this point in the history
  • Loading branch information
sylvialiu404 committed Jun 16, 2023
1 parent ee8418c commit 6aab8be
Show file tree
Hide file tree
Showing 8 changed files with 340 additions and 43 deletions.
8 changes: 7 additions & 1 deletion gpp_parsed_consent.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,13 @@ func ParseGppHeader(s string) (*GppHeader, error) {
// IAB's base64 conversion means a 6 bit grouped value can be converted to 8 bit bytes.
// Any leftover bits <8 would be skipped in normal base64 decoding.
// Therefore, pad with 6 '0's w/ `A` to ensure that all bits are decoded into bytes.
var b, err = base64.RawURLEncoding.DecodeString(s + "A")

gap := 3 - len(s)%4
if gap != 0 {
s += strings.Repeat("A", gap)
}

var b, err = base64.RawURLEncoding.DecodeString(s)
if err != nil {
return nil, errors.Wrap(err, "parse gpp header consent string")
}
Expand Down
24 changes: 24 additions & 0 deletions gpp_parsed_consent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,30 @@ func (s *GppParseSuite) TestParseGppHeader(c *check.C) {
Version: 1,
Sections: []int{8}},
},
{
header: "DBABBgAA",
expected: &iabconsent.GppHeader{
Type: 3,
Version: 1,
Sections: []int{8},
},
},
{
header: "DBABzYA",
expected: &iabconsent.GppHeader{
Type: 3,
Version: 1,
Sections: []int{6, 7, 8},
},
},
{
header: "DBACOaw",
expected: &iabconsent.GppHeader{
Type: 3,
Version: 1,
Sections: []int{2, 5, 6, 7, 8, 9, 10, 11, 12},
},
},
}

for _, tc := range tcs {
Expand Down
93 changes: 60 additions & 33 deletions mspa_parsed_consent_fixture_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,11 +172,67 @@ var usVAConsentFixtures = map[string]*iabconsent.MspaParsedConsent{
}

var usCAConsentFixtures = map[string]*iabconsent.MspaParsedConsent{
"BVoAAACQ.QA": {
//"BVoAAACQ.QA": {
// Version: 1,
// SaleOptOutNotice: iabconsent.NoticeProvided,
// SharingOptOutNotice: iabconsent.NoticeProvided,
// SensitiveDataLimitUseNotice: iabconsent.NoticeProvided,
// SaleOptOut: iabconsent.NotOptedOut,
// SharingOptOut: iabconsent.NotOptedOut,
// SensitiveDataProcessing: map[int]iabconsent.MspaConsent{
// 0: iabconsent.ConsentNotApplicable,
// 1: iabconsent.ConsentNotApplicable,
// 2: iabconsent.ConsentNotApplicable,
// 3: iabconsent.ConsentNotApplicable,
// 4: iabconsent.ConsentNotApplicable,
// 5: iabconsent.ConsentNotApplicable,
// 6: iabconsent.ConsentNotApplicable,
// 7: iabconsent.ConsentNotApplicable,
// 8: iabconsent.ConsentNotApplicable,
// },
// KnownChildSensitiveDataConsents: map[int]iabconsent.MspaConsent{
// 0: iabconsent.ConsentNotApplicable,
// 1: iabconsent.ConsentNotApplicable,
// },
// PersonalDataConsents: iabconsent.ConsentNotApplicable,
// MspaCoveredTransaction: iabconsent.MspaNo,
// MspaOptOutOptionMode: iabconsent.MspaYes,
// MspaServiceProviderMode: iabconsent.MspaNotApplicable,
//
// Gpc: false,
//},
//"BVoBAARQ.QA": {
// Version: 1,
// SaleOptOutNotice: iabconsent.NoticeProvided,
// SharingOptOutNotice: iabconsent.NoticeProvided,
// SensitiveDataLimitUseNotice: iabconsent.NoticeProvided,
// SaleOptOut: iabconsent.NotOptedOut,
// SharingOptOut: iabconsent.NotOptedOut,
// SensitiveDataProcessing: map[int]iabconsent.MspaConsent{
// 0: iabconsent.ConsentNotApplicable,
// 1: iabconsent.ConsentNotApplicable,
// 2: iabconsent.ConsentNotApplicable,
// 3: iabconsent.NoConsent,
// 4: iabconsent.ConsentNotApplicable,
// 5: iabconsent.ConsentNotApplicable,
// 6: iabconsent.ConsentNotApplicable,
// 7: iabconsent.ConsentNotApplicable,
// 8: iabconsent.ConsentNotApplicable,
// },
// KnownChildSensitiveDataConsents: map[int]iabconsent.MspaConsent{
// 0: iabconsent.ConsentNotApplicable,
// 1: iabconsent.NoConsent,
// },
// PersonalDataConsents: iabconsent.ConsentNotApplicable,
// MspaCoveredTransaction: iabconsent.MspaYes,
// MspaOptOutOptionMode: iabconsent.MspaYes,
// MspaServiceProviderMode: iabconsent.MspaNotApplicable,
//},
"BUoAAAAA": {
Version: 1,
SaleOptOutNotice: iabconsent.NoticeProvided,
SharingOptOutNotice: iabconsent.NoticeProvided,
SensitiveDataLimitUseNotice: iabconsent.NoticeProvided,
SensitiveDataLimitUseNotice: iabconsent.NoticeNotApplicable,
SaleOptOut: iabconsent.NotOptedOut,
SharingOptOut: iabconsent.NotOptedOut,
SensitiveDataProcessing: map[int]iabconsent.MspaConsent{
Expand All @@ -195,37 +251,8 @@ var usCAConsentFixtures = map[string]*iabconsent.MspaParsedConsent{
1: iabconsent.ConsentNotApplicable,
},
PersonalDataConsents: iabconsent.ConsentNotApplicable,
MspaCoveredTransaction: iabconsent.MspaNo,
MspaOptOutOptionMode: iabconsent.MspaYes,
MspaServiceProviderMode: iabconsent.MspaNotApplicable,

Gpc: false,
},
"BVoBAARQ.QA": {
Version: 1,
SaleOptOutNotice: iabconsent.NoticeProvided,
SharingOptOutNotice: iabconsent.NoticeProvided,
SensitiveDataLimitUseNotice: iabconsent.NoticeProvided,
SaleOptOut: 2,
SharingOptOut: 2,
SensitiveDataProcessing: map[int]iabconsent.MspaConsent{
0: iabconsent.ConsentNotApplicable,
1: iabconsent.ConsentNotApplicable,
2: iabconsent.ConsentNotApplicable,
3: iabconsent.NoConsent,
4: iabconsent.ConsentNotApplicable,
5: iabconsent.ConsentNotApplicable,
6: iabconsent.ConsentNotApplicable,
7: iabconsent.ConsentNotApplicable,
8: iabconsent.ConsentNotApplicable,
},
KnownChildSensitiveDataConsents: map[int]iabconsent.MspaConsent{
0: iabconsent.ConsentNotApplicable,
1: iabconsent.NoConsent,
},
PersonalDataConsents: iabconsent.ConsentNotApplicable,
MspaCoveredTransaction: iabconsent.MspaYes,
MspaOptOutOptionMode: iabconsent.MspaYes,
MspaCoveredTransaction: iabconsent.MspaNotApplicable,
MspaOptOutOptionMode: iabconsent.MspaNotApplicable,
MspaServiceProviderMode: iabconsent.MspaNotApplicable,
},
}
Expand Down
12 changes: 6 additions & 6 deletions mspa_sections.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,11 @@ func (t *TCFEU) ParseConsent() (GppParsedConsent, error) {
}

func (t *TCFCA) ParseConsent() (GppParsedConsent, error) {
return ParseV2(t.sectionValue)
return ParseCAV2(t.sectionValue)
}

func (u *USPV) ParseConsent() (GppParsedConsent, error) {
return ParseCCPA(u.sectionValue)

}

func (m *MspaUsNational) ParseConsent() (GppParsedConsent, error) {
Expand Down Expand Up @@ -201,7 +200,7 @@ func (m *MspaUsCO) ParseConsent() (GppParsedConsent, error) {

var b, err = base64.RawURLEncoding.DecodeString(segments[0])
if err != nil {
return nil, errors.Wrap(err, "parse usva consent string")
return nil, errors.Wrap(err, "parse usco consent string")
}

var r = NewConsentReader(b)
Expand Down Expand Up @@ -241,7 +240,7 @@ func (m *MspaUsUT) ParseConsent() (GppParsedConsent, error) {

var b, err = base64.RawURLEncoding.DecodeString(segments[0])
if err != nil {
return nil, errors.Wrap(err, "parse usva consent string")
return nil, errors.Wrap(err, "parse usut consent string")
}

var r = NewConsentReader(b)
Expand Down Expand Up @@ -281,7 +280,7 @@ func (m *MspaUsCT) ParseConsent() (GppParsedConsent, error) {

var b, err = base64.RawURLEncoding.DecodeString(segments[0])
if err != nil {
return nil, errors.Wrap(err, "parse usva consent string")
return nil, errors.Wrap(err, "parse usct consent string")
}

var r = NewConsentReader(b)
Expand Down Expand Up @@ -321,8 +320,9 @@ func (m *MspaUsCA) ParseConsent() (GppParsedConsent, error) {
var segments = strings.Split(m.sectionValue, ".")

var b, err = base64.RawURLEncoding.DecodeString(segments[0])

if err != nil {
return nil, errors.Wrap(err, "parse usva consent string")
return nil, errors.Wrap(err, "parse usca consent string")
}

var r = NewConsentReader(b)
Expand Down
58 changes: 55 additions & 3 deletions parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ func (r *ConsentReader) ReadPublisherTCEntry() (*PublisherTCEntry, error) {
//
// Example Usage:
//
// var pc, err = iabconsent.Parse("BONJ5bvONJ5bvAMAPyFRAL7AAAAMhuqKklS-gAAAAAAAAAAAAAAAAAAAAAAAAAA")
// var pc, err = iabconsent.Parse("BONJ5bvONJ5bvAMAPyFRAL7AAAAMhuqKklS-gAAAAAAAAAAAAAAAAAAAAAAAAAA")
//
// Deprecated: Use ParseV1 to parse V1 consent strings.
func Parse(s string) (*ParsedConsent, error) {
Expand All @@ -352,7 +352,7 @@ func Parse(s string) (*ParsedConsent, error) {
//
// Example Usage:
//
// var pc, err = iabconsent.ParseV1("BONJ5bvONJ5bvAMAPyFRAL7AAAAMhuqKklS-gAAAAAAAAAAAAAAAAAAAAAAAAAA")
// var pc, err = iabconsent.ParseV1("BONJ5bvONJ5bvAMAPyFRAL7AAAAMhuqKklS-gAAAAAAAAAAAAAAAAAAAAAAAAAA")
func ParseV1(s string) (*ParsedConsent, error) {
var b, err = base64.RawURLEncoding.DecodeString(s)
if err != nil {
Expand Down Expand Up @@ -395,7 +395,7 @@ func ParseV1(s string) (*ParsedConsent, error) {
//
// Example Usage:
//
// var pc, err = iabconsent.ParseV2("COvzTO5OvzTO5BRAAAENAPCoALIAADgAAAAAAewAwABAAlAB6ABBFAAA")
// var pc, err = iabconsent.ParseV2("COvzTO5OvzTO5BRAAAENAPCoALIAADgAAAAAAewAwABAAlAB6ABBFAAA")
func ParseV2(s string) (*V2ParsedConsent, error) {
var segments = strings.Split(s, ".")

Expand Down Expand Up @@ -484,6 +484,58 @@ func ParseV2(s string) (*V2ParsedConsent, error) {
return p, r.Err
}

func ParseCAV2(s string) (*V2CAParsedConsent, error) {
var segments = strings.Split(s, ".")

var b, err = base64.RawURLEncoding.DecodeString(segments[0])
if err != nil {
return nil, errors.Wrap(err, "parse v2 consent string")
}

var r = NewConsentReader(b)

// This block of code directly describes the format of the payload.
// The spec for the consent string can be found here:
// https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/47b45ab362515310183bb3572a367b8391ef4613/TCFv2/IAB%20Tech%20Lab%20-%20Consent%20string%20and%20vendor%20list%20formats%20v2.md#about-the-transparency--consent-string-tc-string
var p = &V2CAParsedConsent{}
p.Version, _ = r.ReadInt(6)
if p.Version != int(V2) {
return nil, errors.New("non-v2 string passed to v2 parse method")
}
p.Created, _ = r.ReadTime()
p.LastUpdated, _ = r.ReadTime()
p.CMPID, _ = r.ReadInt(12)
p.CMPVersion, _ = r.ReadInt(12)
p.ConsentScreen, _ = r.ReadInt(6)
p.ConsentLanguage, _ = r.ReadString(2)
p.VendorListVersion, _ = r.ReadInt(12)
p.TCFPolicyVersion, _ = r.ReadInt(6)
p.UseNonStandardStacks, _ = r.ReadBool()
p.SpecialFeatureExpressConsent, _ = r.ReadBitField(12)
p.PurposesExpressConsent, _ = r.ReadBitField(24)
p.PurposesImpliedConsent, _ = r.ReadBitField(24)

p.MaxExpressConsentVendorID, _ = r.ReadInt(16)
p.IsExpressConsentRangeEncoding, _ = r.ReadBool()
if p.IsExpressConsentRangeEncoding {
p.NumExpressConsentEntries, _ = r.ReadInt(12)
p.VendorExpressConsent, _ = r.ReadRangeEntries(uint(p.NumExpressConsentEntries))
} else {
p.ExpressConsentedVendors, _ = r.ReadBitField(uint(p.MaxExpressConsentVendorID))
}

p.MaxImpliedConsentVendorID, _ = r.ReadInt(16)
p.IsImpliedConsentRangeEncoding, _ = r.ReadBool()
if p.IsImpliedConsentRangeEncoding {
p.NumImpliedConsentEntries, _ = r.ReadInt(12)
p.VendorImpliedConsent, _ = r.ReadRangeEntries(uint(p.NumImpliedConsentEntries))
} else {
p.ImpliedConsentedVendors, _ = r.ReadBitField(uint(p.MaxImpliedConsentVendorID))
}

return p, r.Err
}

// TCFVersion is an enum type used for easily identifying which version
// a consent string is.
type TCFVersion int
Expand Down
74 changes: 74 additions & 0 deletions v2_parsed_consent.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,80 @@ type V2ParsedConsent struct {
*PublisherTCEntry
}

// V2CAParsedConsent represents data extracted from an v2 CA TCF Consent String.
type V2CAParsedConsent struct {
// Version number of the encoding format.
Version int
// Epoch deciseconds when this TC String was first created (should not be changed
// unless a new TCString is created from scratch).
Created time.Time
// Epoch deciseconds when TC String was last updated (Must be updated any time a
// value is changed).
LastUpdated time.Time
// Consent Management Platform ID that last updated the TC String.
// A unique ID will be assigned to each Consent Management Platform.
CMPID int
// Consent Management Platform version of the CMP that last updated this TC String.
// Each change to a CMP should increment their internally assigned version number as
// a record of which version the user gave consent and transparency was established.
CMPVersion int
// CMP Screen number at which consent was given for a user with the CMP that last
// updated this TC String. The number is a CMP internal designation and is CMPVersion
// specific. The number is used for identifying on which screen a user gave consent
// as a record.
ConsentScreen int
// Two-letter ISO 639-1 language code in which the CMP UI was presented.
ConsentLanguage string
// Number corresponds to the Global Vendor List (GVL) vendorListVersion.
VendorListVersion int
// Version of policy used within GVL.
TCFPolicyVersion int
// Setting this to 1 means that a publisher-run CMP – that is still IAB Europe
// registered – is using customized Stack descriptions and not the standard stack
// descriptions defined in the Policies. A CMP that services multiple publishers sets
// this value to 0.
UseNonStandardStacks bool
// The TCF Policies designates certain Features as “special” which means a CMP must
// afford the user a means to opt in to their use. These “Special Features” are
// published and numerically identified in the Global Vendor List separately from
// normal Features.
SpecialFeatureExpressConsent map[int]bool
// The user’s consent value for each Purpose established on the legal basis of consent.
// The Purposes are numerically identified and published in the Global Vendor List.
// From left to right, Purpose 1 maps to the 0th bit, purpose 24 maps to the bit at
// index 23. Special Purposes are a different ID space and not included in this field.
PurposesExpressConsent map[int]bool
// The Purpose’s transparency requirements are met for each Purpose on the legal basis
// of legitimate interest and the user has not exercised their “Right to Object” to that
// Purpose. By default or if the user has exercised their “Right to Object” to a Purpose,
// the corresponding bit for that Purpose is set to 0. From left to right, Purpose 1 maps
// to the 0th bit, purpose 24 maps to the bit at index 23. Special Purposes are a
// different ID space and not included in this field.
PurposesImpliedConsent map[int]bool

MaxExpressConsentVendorID int
IsExpressConsentRangeEncoding bool
// The consent value for each Vendor ID.
ExpressConsentedVendors map[int]bool
// Number of RangeEntry sections to follow.
NumExpressConsentEntries int
// A single or range of Vendor ID(s) who have received consent. If a Vendor ID is not within
// the bounds of the ranges then the vendor is assumed to have “No Consent”.
VendorExpressConsent []*RangeEntry

MaxImpliedConsentVendorID int
IsImpliedConsentRangeEncoding bool
// The consent value for each Vendor ID.
ImpliedConsentedVendors map[int]bool
// Number of RangeEntry sections to follow.
NumImpliedConsentEntries int
// A single or range of Vendor ID(s) who have received consent. If a Vendor ID is not within
// the bounds of the ranges then the vendor is assumed to have “No Consent”.
VendorImpliedConsent []*RangeEntry

*PublisherTCEntry
}

// RestrictionType is an enum type of publisher restriction types.
type RestrictionType int

Expand Down
Loading

0 comments on commit 6aab8be

Please sign in to comment.