iab_consent_string 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 603e1f8a6c7839dd0206dac21322c64bf3199f8b
4
+ data.tar.gz: 28349c00d6545617eadd88b4cab1b3f815649129
5
+ SHA512:
6
+ metadata.gz: 60525c45d3349de4d745ff1f70427d6fc5e83fc641b0d752d08e8af43410126eba278446769dcc8178bdca906657f54ade477da19c0c42e4d591600ec7283b30
7
+ data.tar.gz: 1aa2736692465a5b053d876be1e7751713a3206111d3a3f96f7c1d49835b3bfefe83eb18d81950ae30354666521f7b25661833e528f0ac4cdfdeb27c3ca890b3
@@ -0,0 +1,14 @@
1
+ require 'iab_consent_string/consent/vendor_consent'
2
+ require 'iab_consent_string/consent/vendor_consent_encoder'
3
+ require 'iab_consent_string/consent/vendor_consent_decoder'
4
+ require 'iab_consent_string/consent/implementation/v1/byte_buffer_backed_vendor_consent'
5
+ require 'iab_consent_string/consent/implementation/v1/vendor_consent_builder'
6
+ require 'iab_consent_string/consent/range/range_entry.rb'
7
+ require 'iab_consent_string/consent/range/start_end_range_entry.rb'
8
+ require 'iab_consent_string/consent/range/single_range_entry.rb'
9
+ require 'iab_consent_string/gdpr_constants'
10
+ require 'iab_consent_string/purpose'
11
+ require 'iab_consent_string/util/utils'
12
+
13
+ module IABConsentString
14
+ end
@@ -0,0 +1,207 @@
1
+ require 'iab_consent_string/error/vendor_consent_create_error'
2
+ require 'iab_consent_string/error/vendor_consent_parse_error'
3
+
4
+ # @author Fidzup Coding Stars Team
5
+ module IABConsentString
6
+ class Bits
7
+ # big endian
8
+ BYTE_POWS = [ -128, 64, 32, 16, 8, 4, 2, 1 ]
9
+
10
+ def initialize(b)
11
+ @bytes = b
12
+ end
13
+
14
+ # Get the value of the specified index bit, under a boolean format (1 = true, 0 = false)
15
+ # @param index [Integer] the nth number bit to get from the bit string
16
+ # @return [Boolean], true if the bit is switched to 1, false otherwise
17
+ def getBit(index)
18
+ byteIndex = index / 8
19
+ bitExact = index % 8
20
+ b = @bytes[byteIndex]
21
+ return (b & BYTE_POWS[bitExact]) != 0
22
+ end
23
+
24
+ # Set the value of the specified index bit to 1
25
+ # @param index [Integer] set the nth number bit from the bit string
26
+ def setBit(index)
27
+ byteIndex = index / 8
28
+ shift = (byteIndex + 1) * 8 - index - 1
29
+ @bytes[byteIndex] |= 1 << shift
30
+ end
31
+
32
+ # Set the value of the specified index bit to 1
33
+ # @param index [Integer] set the nth number bit from the bit string
34
+ def unsetBit(index)
35
+ byteIndex = index / 8
36
+ shift = (byteIndex + 1) * 8 - index - 1
37
+ @bytes[byteIndex] &= ~(1 << shift)
38
+ end
39
+
40
+ # Interprets n number of bits as a big endiant int
41
+ # @param startInclusive [Integer] the nth to begin interpreting from
42
+ # @param size [Integer] the number of bits to interpret
43
+ # @return [void]
44
+ # @raise [VendorConsentParseError] when the bits cannot fit in an int sized field
45
+ def getInt(startInclusive, size)
46
+ # Integer Size limited to 32 bits
47
+ if (size > 32)
48
+ raise IABConsentString::Error::VendorConsentParseError, "can't fit bit range in int " + size, caller
49
+ end
50
+ val = 0
51
+ sigMask = 1
52
+ sigIndex = size - 1
53
+
54
+ for i in (0...size) do
55
+ if getBit(startInclusive + i)
56
+ val += (sigMask << sigIndex)
57
+ end
58
+ sigIndex -= 1
59
+ end
60
+ val
61
+ end
62
+
63
+ # Writes an integer value into a bit array of given size
64
+ # @param startInclusive [Integer] the nth to begin writing to
65
+ # @param size [Integer] the number of bits available to write
66
+ # @param to [Integer] the integer to write out
67
+ # @return [void]
68
+ # @raise [VendorConsentCreateError] when the bits cannot fit into the provided size
69
+ def setInt(startInclusive, size, to)
70
+ # Integer Size limited to 32 bits
71
+ if (size > 32 || to > maxOfSize(size) || to < 0)
72
+ raise IABConsentString::Error::VendorConsentCreateError, "can't fit integer into bit range of size" + size , caller
73
+ end
74
+
75
+ setNumber(startInclusive, size, to);
76
+ end
77
+
78
+ # Interprets n bits as a big endian long
79
+ # @param startInclusive [Integer] the nth to begin interpreting from
80
+ # @param size [Integer] the number of bits to interpret
81
+ # @return [Long] the long value create by interpretation of provided bits
82
+ # @raise [VendorConsentParseError] when the bits cannot fit in a long sized field
83
+ def getLong(startInclusive, size)
84
+ # Long Size limited to 64 bits
85
+ if (size > 64)
86
+ raise IABConsentString::Error::VendorConsentParseError, "can't fit bit range in long: " + size , caller
87
+ end
88
+ val = 0
89
+ sigMask = 1
90
+ sigIndex = size - 1
91
+
92
+ for i in (0...size) do
93
+ if (getBit(startInclusive + i))
94
+ val += (sigMask << sigIndex)
95
+ end
96
+ sigIndex -= 1
97
+ end
98
+ val
99
+ end
100
+
101
+ # Writes a long value into a bit array of given size
102
+ # @param startInclusive [Integer] the nth to begin writing to
103
+ # @param size [Integer] the number of bits available to write
104
+ # @param to [Integer] the long number to write out
105
+ # @return [void]
106
+ # @raise [VendorConsentCreateError] when the bits cannot fit into the provided size
107
+ def setLong(startInclusive, size, to)
108
+ # Long Size limited to 64 bits
109
+ if (size > 64 || to > maxOfSize(size) || to < 0)
110
+ raise IABConsentString::Error::VendorConsentCreateError, "can't fit long into bit range of size " + size , caller
111
+ end
112
+
113
+ setNumber(startInclusive, size, to)
114
+ end
115
+
116
+ # returns a time derived from interpreting the given interval on the bit string as long representing
117
+ # the number of demiseconds from the unix epoch
118
+ # @param startInclusive [Integer] the bit from which to begin interpreting
119
+ # @param size [Integer] the number of bits to interpret
120
+ # @return [void]
121
+ # @raise [VendorConsentParseError] when the number of bits requested cannot fit in a long
122
+ def getDateTimeFromEpochDeciseconds(startInclusive,size)
123
+ epochDemi = getLong(startInclusive, size)
124
+ epochDemi * 100
125
+ end
126
+
127
+ def setDateTimeToEpochDeciseconds(startInclusive, size, dateTime)
128
+ setLong(startInclusive, size, dateTime / 100)
129
+ end
130
+
131
+ # @return [Integer] the number of bits in the bit string
132
+ def length
133
+ @bytes.length * 8
134
+ end
135
+
136
+ # Interprets the given interval in the bit string as a series of six bit characters, where 0=A and 26=Z
137
+ # @param startInclusive [Integer] the nth bit in the bitstring from which to start the interpretation
138
+ # @param size [Integer] the number of bits to include in the string
139
+ # @return [String] the string given by the above interpretation
140
+ # @raise [VendorConsentParseError] when the requested interval is not a multiple of six
141
+ def getSixBitString(startInclusive, size)
142
+ if (size % 6 != 0)
143
+ raise IABConsentString::Error::VendorConsentParseError , "string bit length must be multiple of six: " + size, caller
144
+ end
145
+ charNum = size / 6
146
+ val = String.new()
147
+ for i in (0...charNum) do
148
+ charCode = getInt(startInclusive + (i * 6), 6) + 65
149
+ val << charCode.chr
150
+ end
151
+ val.upcase
152
+ end
153
+
154
+ # Interprets characters, as 0=A and 26=Z and writes to the given interval in the bit string as a series of six bits
155
+ # @param startInclusive [Integer] the nth bit in the bitstring from which to start writing
156
+ # @param size [Integer] the size of the bitstring
157
+ # @param to [Integer] the string given by the above interpretation
158
+ # @raise [VendorConsentCreateError] when the requested interval is not a multiple of six
159
+ def setSixBitString(startInclusive, size, to)
160
+ if (size % 6 != 0 || size / 6 != to.length())
161
+ raise IABConsentString::Error::VendorConsentCreateError , "bit array size must be multiple of six and equal to 6 times the size of string", caller
162
+ end
163
+ values = to.chars
164
+ for i in (0...values.length) do
165
+ charCode = values[i].ord - 65
166
+ setInt(startInclusive + (i * 6), 6, charCode)
167
+ end
168
+ end
169
+
170
+ # @return [String] a string representation of the byte array passed in the constructor. for example, a bit array of [4]
171
+ # yields a String of "0100"
172
+ def getBinaryString
173
+ s = String.new()
174
+ size = length()
175
+ for i in (0...size) do
176
+ if (getBit(i))
177
+ s << "1"
178
+ else
179
+ s << "0"
180
+ end
181
+ end
182
+ s
183
+ end
184
+
185
+ def toByteArray
186
+ @bytes
187
+ end
188
+
189
+ def setNumber(startInclusive,size,to)
190
+ (size - 1).downto(0) do |i|
191
+ index = startInclusive + i
192
+ byteIndex = index / 8
193
+ shift = (byteIndex + 1) * 8 - index - 1
194
+ @bytes[byteIndex] |= (to % 2) << shift
195
+ to /= 2
196
+ end
197
+ end
198
+
199
+ def maxOfSize(size)
200
+ max = 0
201
+ for i in (0...size) do
202
+ max += 2**i
203
+ end
204
+ max
205
+ end
206
+ end
207
+ end
@@ -0,0 +1,162 @@
1
+ require 'set'
2
+ require 'date'
3
+ require 'iab_consent_string/bits'
4
+ require 'iab_consent_string/gdpr_constants'
5
+ require 'iab_consent_string/consent/vendor_consent'
6
+
7
+ module IABConsentString
8
+ module Consent
9
+ module Implementation
10
+ module V1
11
+ class ByteBufferBackedVendorConsent < IABConsentString::Consent::VendorConsent
12
+ def initialize(bits)
13
+ @bits = bits
14
+ end
15
+
16
+ def getVersion
17
+ @bits.getInt(IABConsentString::GDPRConstants::VERSION_BIT_OFFSET,IABConsentString::GDPRConstants::VERSION_BIT_SIZE)
18
+ end
19
+
20
+ def getConsentRecordCreated
21
+ @bits.getDateTimeFromEpochDeciseconds(IABConsentString::GDPRConstants::CREATED_BIT_OFFSET, IABConsentString::GDPRConstants::CREATED_BIT_SIZE)
22
+ end
23
+
24
+ def getConsentRecordLastUpdated
25
+ @bits.getDateTimeFromEpochDeciseconds(IABConsentString::GDPRConstants::UPDATED_BIT_OFFSET,IABConsentString::GDPRConstants::UPDATED_BIT_SIZE)
26
+ end
27
+
28
+ def getCmpId
29
+ @bits.getInt(IABConsentString::GDPRConstants::CMP_ID_OFFSET,IABConsentString::GDPRConstants::CMP_ID_SIZE)
30
+ end
31
+
32
+ def getCmpVersion
33
+ @bits.getInt(IABConsentString::GDPRConstants::CMP_VERSION_OFFSET,IABConsentString::GDPRConstants::CMP_VERSION_SIZE)
34
+ end
35
+
36
+ def getConsentScreen
37
+ @bits.getInt(IABConsentString::GDPRConstants::CONSENT_SCREEN_SIZE_OFFSET,IABConsentString::GDPRConstants::CONSENT_SCREEN_SIZE)
38
+ end
39
+
40
+ def getConsentLanguage
41
+ @bits.getSixBitString(IABConsentString::GDPRConstants::CONSENT_LANGUAGE_OFFSET,IABConsentString::GDPRConstants::CONSENT_LANGUAGE_SIZE)
42
+ end
43
+
44
+ def getVendorListVersion
45
+ @bits.getInt(IABConsentString::GDPRConstants::VENDOR_LIST_VERSION_OFFSET,IABConsentString::GDPRConstants::VENDOR_LIST_VERSION_SIZE)
46
+ end
47
+
48
+ def getAllowedPurposeIds
49
+ allowedPurposes = Set[]
50
+ for i in (IABConsentString::GDPRConstants::PURPOSES_OFFSET...(IABConsentString::GDPRConstants::PURPOSES_OFFSET + IABConsentString::GDPRConstants::PURPOSES_SIZE)) do
51
+ if (@bits.getBit(i))
52
+ allowedPurposes.add(i - IABConsentString::GDPRConstants::PURPOSES_OFFSET + 1)
53
+ end
54
+ end
55
+ allowedPurposes
56
+ end
57
+
58
+ def getAllowedPurposes
59
+ allowedPurposes = getAllowedPurposeIds().map! {|id| IABConsentString::Purpose.new(id)}
60
+ allowedPurposes.to_a.uniq{|o| [o.getId]}.to_set
61
+ end
62
+
63
+ def getAllowedPurposesBits
64
+ @bits.getInt(IABConsentString::GDPRConstants::PURPOSES_OFFSET,IABConsentString::GDPRConstants::PURPOSES_SIZE)
65
+ end
66
+
67
+ def getMaxVendorId
68
+ @bits.getInt(IABConsentString::GDPRConstants::MAX_VENDOR_ID_OFFSET,IABConsentString::GDPRConstants::MAX_VENDOR_ID_SIZE)
69
+ end
70
+
71
+ def isPurposeIdAllowed(purposeId)
72
+ if ((purposeId < 1) || (purposeId > IABConsentString::GDPRConstants::PURPOSES_SIZE))
73
+ return false
74
+ end
75
+ @bits.getBit(IABConsentString::GDPRConstants::PURPOSES_OFFSET + purposeId - 1);
76
+ end
77
+
78
+ def isPurposeAllowed(purpose)
79
+ isPurposeIdAllowed(purpose.getId())
80
+ end
81
+
82
+ def isVendorAllowed(vendorId)
83
+ if ((vendorId < 1) || (vendorId > getMaxVendorId()))
84
+ return false
85
+ end
86
+ if (encodingType() == IABConsentString::GDPRConstants::VENDOR_ENCODING_RANGE)
87
+ defaultConsent = @bits.getBit(IABConsentString::GDPRConstants::DEFAULT_CONSENT_OFFSET)
88
+ present = isVendorPresentInRange(vendorId)
89
+ return (present != defaultConsent)
90
+ else
91
+ return @bits.getBit(IABConsentString::GDPRConstants::VENDOR_BITFIELD_OFFSET + vendorId - 1)
92
+ end
93
+ end
94
+
95
+ def toByteArray
96
+ @bits.toByteArray()
97
+ end
98
+
99
+ def hashCode
100
+ @bit.toByteArray().toString().hash
101
+ end
102
+
103
+ def toString
104
+ "ByteBufferVendorConsent{" +
105
+ "Version=" + getVersion().to_s +
106
+ ",Created=" + getConsentRecordCreated().to_s +
107
+ ",LastUpdated=" + getConsentRecordLastUpdated().to_s +
108
+ ",CmpId=" + getCmpId().to_s +
109
+ ",CmpVersion=" + getCmpVersion().to_s +
110
+ ",ConsentScreen=" + getConsentScreen().to_s +
111
+ ",ConsentLanguage=" + getConsentLanguage() +
112
+ ",VendorListVersion=" + getVendorListVersion().to_s +
113
+ ",PurposesAllowed=" + getAllowedPurposeIds().to_s +
114
+ ",MaxVendorId=" + getMaxVendorId().to_s +
115
+ ",EncodingType=" + encodingType().to_s +
116
+ "}"
117
+ end
118
+
119
+ private
120
+ def encodingType
121
+ @bits.getInt(IABConsentString::GDPRConstants::ENCODING_TYPE_OFFSET, IABConsentString::GDPRConstants::ENCODING_TYPE_SIZE)
122
+ end
123
+
124
+ def isVendorPresentInRange(vendorId)
125
+ numEntries = @bits.getInt(IABConsentString::GDPRConstants::NUM_ENTRIES_OFFSET, IABConsentString::GDPRConstants::NUM_ENTRIES_SIZE)
126
+ maxVendorId = getMaxVendorId()
127
+ currentOffset = IABConsentString::GDPRConstants::RANGE_ENTRY_OFFSET
128
+ for i in (0...numEntries) do
129
+ range = @bits.getBit(currentOffset)
130
+ currentOffset += 1
131
+ if range
132
+ startVendorId = @bits.getInt(currentOffset, IABConsentString::GDPRConstants::VENDOR_ID_SIZE)
133
+ currentOffset += IABConsentString::GDPRConstants::VENDOR_ID_SIZE
134
+ endVendorId = @bits.getInt(currentOffset, IABConsentString::GDPRConstants::VENDOR_ID_SIZE)
135
+ currentOffset += IABConsentString::GDPRConstants::VENDOR_ID_SIZE
136
+
137
+ if ((startVendorId > endVendorId) || (endVendorId > maxVendorId))
138
+ raise IABConsentString::Error::VendorConsentParseError.new("Start VendorId must not be greater than End VendorId and End VendorId must not be greater than Max Vendor Id")
139
+ end
140
+ if ((vendorId >= startVendorId) && (vendorId <= endVendorId))
141
+ return true
142
+ end
143
+ else
144
+ singleVendorId = @bits.getInt(currentOffset, IABConsentString::GDPRConstants::VENDOR_ID_SIZE)
145
+ currentOffset += IABConsentString::GDPRConstants::VENDOR_ID_SIZE
146
+
147
+ if (singleVendorId > maxVendorId)
148
+ raise IABConsentString::Error::VendorConsentParseError.new("VendorId in the range entries must not be greater than Max VendorId")
149
+ end
150
+ if (singleVendorId == vendorId)
151
+ return true
152
+ end
153
+ end
154
+ end
155
+
156
+ false
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,246 @@
1
+ require 'iab_consent_string/gdpr_constants'
2
+ require 'iab_consent_string/error/vendor_consent_create_error'
3
+ require 'iab_consent_string/consent/implementation/v1/byte_buffer_backed_vendor_consent'
4
+
5
+ module IABConsentString
6
+ module Consent
7
+ module Implementation
8
+ module V1
9
+ # Builder for version 1 of vendor consent
10
+ class VendorConsentBuilder
11
+ VERSION = 1
12
+
13
+ # With creation date
14
+ # @param consentRecordCreated [DateTime] Epoch deciseconds when record was created
15
+ # @return [VendorConsentBuilder] self
16
+ def withConsentRecordCreatedOn(consentRecordCreated)
17
+ @consentRecordCreated = consentRecordCreated
18
+ self
19
+ end
20
+
21
+ # With update date
22
+ # @param consentRecordLastUpdated [DateTime] Epoch deciseconds when consent string was last updated
23
+ # @return [VendorConsentBuilder] self
24
+ def withConsentRecordLastUpdatedOn(consentRecordLastUpdated)
25
+ @consentRecordLastUpdated = consentRecordLastUpdated;
26
+ self
27
+ end
28
+
29
+ # With CMP version
30
+ # @param cmpVersion [Integer] Consent Manager Provider version
31
+ # @return [VendorConsentBuilder] self
32
+ def withCmpVersion(cmpVersion)
33
+ @cmpVersion = cmpVersion
34
+ self
35
+ end
36
+
37
+ # With CMP Id
38
+ # @param cmpId [Integer] Consent Manager Provider Id
39
+ # @return [VendorConsentBuilder] self
40
+ def withCmpId(cmpId)
41
+ @cmpId = cmpId
42
+ self
43
+ end
44
+
45
+ # With Consent Screen Id
46
+ # @param consentScreenId [Integer] Consent Screen Id
47
+ # @return [VendorConsentBuilder] self
48
+ def withConsentScreenId(consentScreenId)
49
+ @consentScreenId = consentScreenId
50
+ self
51
+ end
52
+
53
+ # With consent language
54
+ # @param consentLanguage [Char(2)] Two-letter ISO639-1 language code that CMP asked for consent in
55
+ # @return [VendorConsentBuilder] self
56
+ def withConsentLanguage(consentLanguage)
57
+ @consentLanguage = consentLanguage
58
+ self
59
+ end
60
+
61
+ # With vendor list version
62
+ # @param vendorListVersion [Integer] Version of vendor list used in most recent consent string update
63
+ # @return [VendorConsentBuilder] self
64
+ def withVendorListVersion(vendorListVersion)
65
+ @vendorListVersion = vendorListVersion
66
+ self
67
+ end
68
+
69
+ # With allowed purpose IDs
70
+ # @param allowedPurposeIds [Set<Integer>] set of allowed purposes
71
+ # @return [VendorConsentBuilder] self
72
+ def withAllowedPurposeIds(allowedPurposeIds)
73
+ if allowedPurposeIds.nil?
74
+ raise "Argument allowedPurposeIds is null"
75
+ end
76
+ allowedPurposeIds.each do |purposeId|
77
+ if purposeId < 0 || purposeId > IABConsentString::GDPRConstants::PURPOSES_SIZE
78
+ raise "Invalid purpose ID found"
79
+ end
80
+ end
81
+ @allowedPurposes = allowedPurposeIds;
82
+ self
83
+ end
84
+
85
+ # With allowed purposes
86
+ # @param allowedPurposes [Set<Purpose>] set of allowed purposes
87
+ # @return [VendorConsentBuilder] self
88
+ def withAllowedPurposes(allowedPurposes)
89
+ if allowedPurposes.nil?
90
+ raise "Argument allowedPurposes is null"
91
+ end
92
+ @allowedPurposes = allowedPurposes.map! {|purpose| purpose.getId }
93
+ self
94
+ end
95
+
96
+ # With max vendor ID
97
+ # @param maxVendorId [Integer] The maximum VendorId for which consent values are given.
98
+ # @return [VendorConsentBuilder] self
99
+ def withMaxVendorId(maxVendorId)
100
+ @maxVendorId = maxVendorId
101
+ self
102
+ end
103
+
104
+ # With vendor encoding type
105
+ # @param vendorEncodingType [Integer] 0=BitField 1=Range
106
+ # @return [VendorConsentBuilder] self
107
+ def withVendorEncodingType(vendorEncodingType)
108
+ if (vendorEncodingType < 0 || vendorEncodingType > 1)
109
+ raise "Illegal value for argument vendorEncodingType:" + vendorEncodingType.to_s
110
+ end
111
+ @vendorEncodingType = vendorEncodingType
112
+ self
113
+ end
114
+
115
+ # With bit field entries
116
+ # @param bitFieldEntries [Set<Integer>] set of VendorIds for which the vendors have consent
117
+ # @return [VendorConsentBuilder] self
118
+ def withBitField(bitFieldEntries)
119
+ @vendorsBitField = bitFieldEntries
120
+ self
121
+ end
122
+
123
+ # With range entries
124
+ # @param rangeEntries [Set<RangeEntry>] List of VendorIds or a range of VendorIds for which the vendors have consent
125
+ # @return [VendorConsentBuilder] self
126
+ def withRangeEntries(rangeEntries)
127
+ @rangeEntries = rangeEntries
128
+ self
129
+ end
130
+
131
+ # With default consent
132
+ # @param defaultConsent [Boolean] Default consent for VendorIds not covered by a RangeEntry. 0=No Consent 1=Consent
133
+ # @return [VendorConsentBuilder] self
134
+ def withDefaultConsent(defaultConsent)
135
+ @defaultConsent = defaultConsent
136
+ self
137
+ end
138
+
139
+ # Validate supplied values and build VendorConsent object
140
+ # @return [IABConsentString::Consent::VendorConsent] vendor consent object
141
+ def build
142
+ if @consentRecordCreated.nil?
143
+ raise IABConsentString::Error::VendorConsentCreateError, "consentRecordCreated must be set", caller
144
+ end
145
+ if @consentRecordLastUpdated.nil?
146
+ raise IABConsentString::Error::VendorConsentCreateError, "consentRecordLastUpdated must be set", caller
147
+ end
148
+ if @consentLanguage.nil?
149
+ raise IABConsentString::Error::VendorConsentCreateError, "consentLanguage must be set", caller
150
+ end
151
+
152
+ if @vendorListVersion.nil? || @vendorListVersion <=0
153
+ raise IABConsentString::Error::VendorConsentCreateError, "Invalid value for vendorListVersion:" + @vendorListVersion.to_s, caller
154
+ end
155
+
156
+ if @maxVendorId.nil? || @maxVendorId<=0
157
+ raise IABConsentString::Error::VendorConsentCreateError, "Invalid value for maxVendorId:" + @maxVendorId.to_s, caller
158
+ end
159
+
160
+ # For range encoding, check if each range entry is valid
161
+ if @vendorEncodingType == IABConsentString::GDPRConstants::VENDOR_ENCODING_RANGE
162
+ if @rangeEntries.nil?
163
+ raise IABConsentString::Error::VendorConsentCreateError, "Range entries must be set", caller
164
+ end
165
+ @rangeEntries.each do |rangeEntry|
166
+ if !rangeEntry.valid(@maxVendorId)
167
+ raise IABConsentString::Error::VendorConsentCreateError, "Invalid range entries found", caller
168
+ end
169
+ end
170
+ end
171
+
172
+ # Calculate size of bit buffer in bits
173
+ bitBufferSizeInBits = 0
174
+ if (@vendorEncodingType == IABConsentString::GDPRConstants::VENDOR_ENCODING_RANGE)
175
+ rangeEntrySectionSize = 0
176
+ @rangeEntries.each do |rangeEntry|
177
+ rangeEntrySectionSize += rangeEntry.size
178
+ end
179
+ bitBufferSizeInBits = IABConsentString::GDPRConstants::RANGE_ENTRY_OFFSET + rangeEntrySectionSize
180
+ else
181
+ bitBufferSizeInBits = IABConsentString::GDPRConstants::VENDOR_BITFIELD_OFFSET + @maxVendorId
182
+ end
183
+
184
+ # Create new bit buffer
185
+ bitsFit = (bitBufferSizeInBits % 8) == 0
186
+ str = ""
187
+ for i in (0...(bitBufferSizeInBits / 8 + (bitsFit ? 0 : 1))) do
188
+ str << 0b00000000
189
+ end
190
+ bits = IABConsentString::Bits.new(str.bytes.to_a)
191
+
192
+ # Set fields in bit buffer
193
+ bits.setInt(IABConsentString::GDPRConstants::VERSION_BIT_OFFSET, IABConsentString::GDPRConstants::VERSION_BIT_SIZE, VERSION)
194
+ bits.setDateTimeToEpochDeciseconds(IABConsentString::GDPRConstants::CREATED_BIT_OFFSET, IABConsentString::GDPRConstants::CREATED_BIT_SIZE, @consentRecordCreated)
195
+ bits.setDateTimeToEpochDeciseconds(IABConsentString::GDPRConstants::UPDATED_BIT_OFFSET, IABConsentString::GDPRConstants::UPDATED_BIT_SIZE, @consentRecordLastUpdated)
196
+ bits.setInt(IABConsentString::GDPRConstants::CMP_ID_OFFSET, IABConsentString::GDPRConstants::CMP_ID_SIZE, @cmpId)
197
+ bits.setInt(IABConsentString::GDPRConstants::CMP_VERSION_OFFSET, IABConsentString::GDPRConstants::CMP_VERSION_SIZE, @cmpVersion)
198
+ bits.setInt(IABConsentString::GDPRConstants::CONSENT_SCREEN_SIZE_OFFSET, IABConsentString::GDPRConstants::CONSENT_SCREEN_SIZE, @consentScreenId)
199
+ bits.setSixBitString(IABConsentString::GDPRConstants::CONSENT_LANGUAGE_OFFSET, IABConsentString::GDPRConstants::CONSENT_LANGUAGE_SIZE, @consentLanguage)
200
+ bits.setInt(IABConsentString::GDPRConstants::VENDOR_LIST_VERSION_OFFSET, IABConsentString::GDPRConstants::VENDOR_LIST_VERSION_SIZE, @vendorListVersion)
201
+
202
+ # Set purposes bits
203
+ for i in (0...IABConsentString::GDPRConstants::PURPOSES_SIZE) do
204
+ if (@allowedPurposes.include?(i+1))
205
+ bits.setBit(IABConsentString::GDPRConstants::PURPOSES_OFFSET + i)
206
+ else
207
+ bits.unsetBit(IABConsentString::GDPRConstants::PURPOSES_OFFSET + i)
208
+ end
209
+ end
210
+
211
+ bits.setInt(IABConsentString::GDPRConstants::MAX_VENDOR_ID_OFFSET, IABConsentString::GDPRConstants::MAX_VENDOR_ID_SIZE, @maxVendorId)
212
+ bits.setInt(IABConsentString::GDPRConstants::ENCODING_TYPE_OFFSET, IABConsentString::GDPRConstants::ENCODING_TYPE_SIZE, @vendorEncodingType)
213
+
214
+ # Set the bit field or range sections
215
+ if (@vendorEncodingType == IABConsentString::GDPRConstants::VENDOR_ENCODING_RANGE)
216
+ # Range encoding
217
+ if (@defaultConsent)
218
+ bits.setBit(IABConsentString::GDPRConstants::DEFAULT_CONSENT_OFFSET)
219
+ else
220
+ bits.unsetBit(IABConsentString::GDPRConstants::DEFAULT_CONSENT_OFFSET)
221
+ end
222
+ bits.setInt(IABConsentString::GDPRConstants::NUM_ENTRIES_OFFSET, IABConsentString::GDPRConstants::NUM_ENTRIES_SIZE, @rangeEntries.size)
223
+
224
+ currentOffset = IABConsentString::GDPRConstants::RANGE_ENTRY_OFFSET
225
+
226
+ @rangeEntries.each do |rangeEntry|
227
+ currentOffset = rangeEntry.appendTo(bits, currentOffset)
228
+ end
229
+ else
230
+ # Bit field encoding
231
+ for i in (0...@maxVendorId) do
232
+ if @vendorsBitField.include?(i+1)
233
+ bits.setBit(IABConsentString::GDPRConstants::VENDOR_BITFIELD_OFFSET+i)
234
+ else
235
+ bits.unsetBit(IABConsentString::GDPRConstants::VENDOR_BITFIELD_OFFSET+i)
236
+ end
237
+ end
238
+ end
239
+
240
+ IABConsentString::Consent::Implementation::V1::ByteBufferBackedVendorConsent.new(bits)
241
+ end
242
+ end
243
+ end
244
+ end
245
+ end
246
+ end
@@ -0,0 +1,22 @@
1
+ module IABConsentString
2
+ module Consent
3
+ module Range
4
+ class RangeEntry
5
+ # Append this range entry to the bit buffer
6
+ # @param buffer [Bits] bit buffer
7
+ # @param currentOffset [Integer] current offset in the buffer
8
+ # @return [Integer] new offset
9
+ def appendTo(buffer, currentOffset)
10
+ raise NotImplementedError
11
+ end
12
+
13
+ # Check if range entry is valid for the specified max vendor id
14
+ # @param maxVendorId [Integer] max vendor id
15
+ # @return [Boolean] true if range entry is valid, false otherwise
16
+ def valid(maxVendorId)
17
+ raise NotImplementedError
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,33 @@
1
+ require 'iab_consent_string/consent/range/range_entry'
2
+ require 'iab_consent_string/gdpr_constants'
3
+
4
+ module IABConsentString
5
+ module Consent
6
+ module Range
7
+ class SingleRangeEntry < RangeEntry
8
+
9
+ def initialize(singleVendorId)
10
+ @singleVendorId = singleVendorId
11
+ end
12
+
13
+ def size()
14
+ # One bit for SingleOrRange flag, VENDOR_ID_SIZE for single vendor ID
15
+ 1 + IABConsentString::GDPRConstants::VENDOR_ID_SIZE
16
+ end
17
+
18
+ def appendTo(buffer, currentOffset)
19
+ newOffset = currentOffset
20
+ buffer.unsetBit(newOffset)
21
+ newOffset += 1
22
+ buffer.setInt(newOffset, IABConsentString::GDPRConstants::VENDOR_ID_SIZE, @singleVendorId)
23
+ newOffset += IABConsentString::GDPRConstants::VENDOR_ID_SIZE
24
+ newOffset
25
+ end
26
+
27
+ def valid(maxVendorId)
28
+ (@singleVendorId > 0) && (@singleVendorId <= maxVendorId)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,36 @@
1
+ require 'iab_consent_string/consent/range/range_entry'
2
+ require 'iab_consent_string/gdpr_constants'
3
+
4
+ module IABConsentString
5
+ module Consent
6
+ module Range
7
+ class StartEndRangeEntry < RangeEntry
8
+
9
+ def initialize(startVendorId,endVendorId)
10
+ @startVendorId = startVendorId
11
+ @endVendorId = endVendorId
12
+ end
13
+
14
+ def size()
15
+ # One bit for SingleOrRange flag, 2 * VENDOR_ID_SIZE for 2 vendor IDs
16
+ 1 + ( IABConsentString::GDPRConstants::VENDOR_ID_SIZE * 2 )
17
+ end
18
+
19
+ def appendTo(buffer, currentOffset)
20
+ newOffset = currentOffset
21
+ buffer.setBit(newOffset)
22
+ newOffset += 1
23
+ buffer.setInt(newOffset, IABConsentString::GDPRConstants::VENDOR_ID_SIZE, @startVendorId)
24
+ newOffset += IABConsentString::GDPRConstants::VENDOR_ID_SIZE
25
+ buffer.setInt(newOffset, IABConsentString::GDPRConstants::VENDOR_ID_SIZE, @endVendorId)
26
+ newOffset += IABConsentString::GDPRConstants::VENDOR_ID_SIZE
27
+ newOffset
28
+ end
29
+
30
+ def valid(maxVendorId)
31
+ (@startVendorId > 0) && (@endVendorId > 0) && (@startVendorId < @endVendorId) && (@endVendorId <= maxVendorId)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,91 @@
1
+ module IABConsentString
2
+ module Consent
3
+ class VendorConsent
4
+ # @return [Integer] the version of consent string format
5
+ def getVersion
6
+ raise NotImplementedError
7
+ end
8
+
9
+ # @return [Timestamp] the time (milli since epoch) at which the consent string was created
10
+ def getConsentRecordCreated
11
+ raise NotImplementedError
12
+ end
13
+
14
+ # @return [Timestamp] the time (milli since epoch) at which the consent string was last updated
15
+ def getConsentRecordLastUpdated
16
+ raise NotImplementedError
17
+ end
18
+
19
+ # @return [Integer] the Consent Manager Provider ID that last updated the consent string
20
+ def getCmpId
21
+ raise NotImplementedError
22
+ end
23
+
24
+ # @return [Integer] the Consent Manager Provider version
25
+ def getCmpVersion
26
+ raise NotImplementedError
27
+ end
28
+
29
+ # @return [Integer] the screen number in the CMP where consent was given
30
+ def getConsentScreen
31
+ raise NotImplementedError
32
+ end
33
+
34
+ # @return [Char(2)] the two-letter ISO639-1 language code that CMP asked for consent in
35
+ def getConsentLanguage
36
+ raise NotImplementedError
37
+ end
38
+
39
+ # @return [Integer] version of vendor list used in most recent consent string update.
40
+ def getVendorListVersion
41
+ raise NotImplementedError
42
+ end
43
+
44
+ # @return [Set<Integer>] the set of purpose id's which are permitted according to this consent string
45
+ def getAllowedPurposesIds
46
+ raise NotImplementedError
47
+ end
48
+
49
+ # @return [Set<Purpose>] the set of allowed purposes which are permitted according to this consent string
50
+ def getAllowedPurposes
51
+ raise NotImplementedError
52
+ end
53
+
54
+ # @return [Integer] an integer equivalent of allowed purpose id bits according to this consent string
55
+ def getAllowedPurposesBits
56
+ raise NotImplementedError
57
+ end
58
+
59
+ # @return [Integer] the maximum VendorId for which consent values are given.
60
+ def getMaxVendorId
61
+ raise NotImplementedError
62
+ end
63
+
64
+ # Check wether purpose with specified ID is allowed
65
+ # @param purposeId [Integer] purpose ID
66
+ # @return [Boolean] true if purpose is allowed in this consent, false otherwise
67
+ def isPurposeIdAllowed(purposeId)
68
+ raise NotImplementedError
69
+ end
70
+
71
+ # Check wether specified purpose is allowed
72
+ # @param purpose [Purpose] purpose to check
73
+ # @return [Boolean] true if purpose is allowed in this consent, false otherwise
74
+ def isPurposeAllowed(purpose)
75
+ raise NotImplementedError
76
+ end
77
+
78
+ # Check wether vendor with specified ID is allowed
79
+ # @param vendorId [Integer] vendor ID
80
+ # @return [Boolean] true if vendor is allowed in this consent, false otherwise
81
+ def isVendorAllowed(vendorId)
82
+ raise NotImplementedError
83
+ end
84
+
85
+ # @return [Array<Byte>] the value of this consent as byte array
86
+ def toByteArray
87
+ raise NotImplementedError
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,41 @@
1
+ require 'base64'
2
+ require 'iab_consent_string/bits'
3
+ require 'iab_consent_string/gdpr_constants'
4
+ require 'iab_consent_string/consent/implementation/v1/byte_buffer_backed_vendor_consent'
5
+
6
+ module IABConsentString
7
+ module Consent
8
+ # IABConsentString::Consent::VendorConsent decoder from Base64 string. Right now only version 1 is know, but eventually
9
+ # this can be extended to support new versions
10
+ class VendorConsentDecoder
11
+ # Build a IABConsentString::Consent::VendorConsent object from a base64 string
12
+ # @params consentString [String] a url safe base64 encoded consent string
13
+ # @return [IABConsentString::Consent::VendorConsent] a VendorConsent object
14
+ # @raise an error when there's a problem with the consentString passed
15
+ def self.fromBase64String(consentString)
16
+ if consentString.nil?
17
+ raise "Null or empty consent string passed as an argument"
18
+ end
19
+ fromByteArray(Base64.urlsafe_decode64(consentString).bytes.to_a)
20
+ end
21
+
22
+ def self.fromByteArray(bytes)
23
+ if ( bytes.nil? || bytes.length == 0)
24
+ raise "Null or empty consent string passed as an argument"
25
+ end
26
+ bits = Bits.new(bytes)
27
+ version = getVersion(bits)
28
+ case version
29
+ when 1
30
+ IABConsentString::Consent::Implementation::V1::ByteBufferBackedVendorConsent.new(bits)
31
+ else
32
+ raise "Unsupported version: " + version.to_s
33
+ end
34
+ end
35
+
36
+ def self.getVersion(bits)
37
+ bits.getInt(IABConsentString::GDPRConstants::VERSION_BIT_OFFSET, IABConsentString::GDPRConstants::VERSION_BIT_SIZE)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,15 @@
1
+ require 'base64'
2
+
3
+ module IABConsentString
4
+ module Consent
5
+ class VendorConsentEncoder
6
+ # Encode vendor consent to Base64 string
7
+ # @param vendorConsent [VendorConsent] vendor consent
8
+ # @return [String] Base64 encoded string
9
+ def self.toBase64String(vendorConsent)
10
+ # Encode Without Padding to respect IAB Consent String Spec
11
+ Base64.urlsafe_encode64(vendorConsent.toByteArray().pack("C*") , padding: false)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,8 @@
1
+ require 'iab_consent_string/error/vendor_consent_error'
2
+
3
+ module IABConsentString
4
+ module Error
5
+ class VendorConsentCreateError < IABConsentString::Error::VendorConsentError
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,6 @@
1
+ module IABConsentString
2
+ module Error
3
+ class VendorConsentError < StandardError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,8 @@
1
+ require 'iab_consent_string/error/vendor_consent_error'
2
+
3
+ module IABConsentString
4
+ module Error
5
+ class VendorConsentParseError < IABConsentString::Error::VendorConsentError
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,33 @@
1
+ module IABConsentString
2
+ class GDPRConstants
3
+ VENDOR_ENCODING_RANGE = 1
4
+ VERSION_BIT_OFFSET = 0
5
+ VERSION_BIT_SIZE = 6
6
+ CREATED_BIT_OFFSET = 6
7
+ CREATED_BIT_SIZE = 36
8
+ UPDATED_BIT_OFFSET = 42
9
+ UPDATED_BIT_SIZE = 36
10
+ CMP_ID_OFFSET = 78
11
+ CMP_ID_SIZE = 12
12
+ CMP_VERSION_OFFSET = 90
13
+ CMP_VERSION_SIZE = 12
14
+ CONSENT_SCREEN_SIZE_OFFSET = 102
15
+ CONSENT_SCREEN_SIZE = 6
16
+ CONSENT_LANGUAGE_OFFSET = 108
17
+ CONSENT_LANGUAGE_SIZE = 12
18
+ VENDOR_LIST_VERSION_OFFSET = 120
19
+ VENDOR_LIST_VERSION_SIZE = 12
20
+ PURPOSES_OFFSET = 132
21
+ PURPOSES_SIZE = 24
22
+ MAX_VENDOR_ID_OFFSET = 156
23
+ MAX_VENDOR_ID_SIZE = 16
24
+ ENCODING_TYPE_OFFSET = 172
25
+ ENCODING_TYPE_SIZE = 1
26
+ VENDOR_BITFIELD_OFFSET = 173
27
+ DEFAULT_CONSENT_OFFSET = 173
28
+ NUM_ENTRIES_OFFSET = 174
29
+ NUM_ENTRIES_SIZE = 12
30
+ RANGE_ENTRY_OFFSET = 186
31
+ VENDOR_ID_SIZE = 16
32
+ end
33
+ end
@@ -0,0 +1,69 @@
1
+ # Enumeration of currently defined purposes by IAB
2
+ # @see <a href="https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework">https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework</a>
3
+ module IABConsentString
4
+ class Purpose
5
+ # The storage of information, or access to information that is already stored, on user device such as accessing advertising identifiers
6
+ # and/or other device identifiers, and/or using cookies or similar technologies.
7
+ STORAGE_AND_ACCESS = 1
8
+ # The collection and processing of information about user of a site to subsequently personalize advertising for them in other contexts,
9
+ # i.e. on other sites or apps, over time. Typically, the content of the site or app is used to make inferences about user interests, which inform future selections.
10
+ PERSONALIZATION = 2
11
+ # The collection of information and combination with previously collected information, to select and deliver advertisements and to measure the delivery
12
+ # and effectiveness of such advertisements. This includes using previously collected information about user interests to select ads, processing data about
13
+ # what advertisements were shown, how often they were shown, when and where they were shown, and whether they took any action related to the advertisement,
14
+ # including for example clicking an ad or making a purchase.
15
+ AD_SELECTION = 3
16
+ # The collection of information, and combination with previously collected information, to select and deliver content and to measure the delivery and
17
+ # effectiveness of such content. This includes using previously collected information about user interests to select content, processing data about
18
+ # what content was shown, how often or how long it was shown, when and where it was shown, and whether they took any action related to the content,
19
+ # including for example clicking on content.
20
+ CONTENT_DELIVERY = 4
21
+ # The collection of information about user use of content, and combination with previously collected information, used to measure, understand,
22
+ # and report on user usage of content.
23
+ MEASUREMENT = 5
24
+ # Ads targeted on geolocalization
25
+ GEOLOCALIZED_ADS = 6
26
+ # Purpose ID that is currently not defined
27
+ UNDEFINED = -1
28
+
29
+ def initialize(id)
30
+ case id
31
+ when STORAGE_AND_ACCESS, PERSONALIZATION, AD_SELECTION, CONTENT_DELIVERY, MEASUREMENT, GEOLOCALIZED_ADS
32
+ @id = id
33
+ else
34
+ @id = -1
35
+ end
36
+ end
37
+
38
+ def getId
39
+ @id
40
+ end
41
+
42
+ def eql?(other)
43
+ @id == other.getId
44
+ end
45
+
46
+ def ==(other)
47
+ @id == other.getId
48
+ end
49
+
50
+ def valueOf()
51
+ case @id
52
+ when STORAGE_AND_ACCESS
53
+ return "STORAGE_AND_ACCESS"
54
+ when PERSONALIZATION
55
+ return "PERSONALIZATION"
56
+ when AD_SELECTION
57
+ return "AD_SELECTION"
58
+ when CONTENT_DELIVERY
59
+ return "CONTENT_DELIVERY"
60
+ when MEASUREMENT
61
+ return "MEASUREMENT"
62
+ when GEOLOCALIZED_ADS
63
+ return "GEOLOCALIZED_ADS"
64
+ else
65
+ return "UNDEFINED"
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,28 @@
1
+ require 'iab_consent_string/bits'
2
+
3
+ module IABConsentString
4
+ module Util
5
+ class Utils
6
+ # Create bit buffer from string representation
7
+ # @param binaryString [String] binary string
8
+ # @return [Bits] bit buffer
9
+ def self.fromBinaryString(binaryString)
10
+ length = binaryString.length
11
+ bitsFit = (length % 8) == 0
12
+ str = ""
13
+ for i in (0...length / 8 + (bitsFit ? 0 : 1)) do
14
+ str << 0b00000000
15
+ end
16
+ bits = IABConsentString::Bits.new(str.bytes.to_a)
17
+ for i in (0...length) do
18
+ if binaryString[i] == '1'
19
+ bits.setBit(i)
20
+ else
21
+ bits.unsetBit(i)
22
+ end
23
+ end
24
+ bits
25
+ end
26
+ end
27
+ end
28
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: iab_consent_string
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Fidzup Coding Star Team
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-10-23 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: |-
14
+ This library is a Ruby reference implementation for dealing with consent strings in the IAB EU's GDPR Transparency and Consent Framework.
15
+ It should be used by anyone who receives or sends consent information like vendors that receive consent data from a partner, or consent management platforms that need to encode/decode the global cookie.
16
+
17
+ The IAB specification for the consent string format is available on the IAB Github (section 'Vendor Consent Cookie Format').
18
+
19
+ This library supports the version v1.1 of the specification. It can encode and decode consent strings with version bit 1.
20
+ email: coding.stars@fidzup.com
21
+ executables: []
22
+ extensions: []
23
+ extra_rdoc_files: []
24
+ files:
25
+ - lib/iab_consent_string.rb
26
+ - lib/iab_consent_string/bits.rb
27
+ - lib/iab_consent_string/consent/implementation/v1/byte_buffer_backed_vendor_consent.rb
28
+ - lib/iab_consent_string/consent/implementation/v1/vendor_consent_builder.rb
29
+ - lib/iab_consent_string/consent/range/range_entry.rb
30
+ - lib/iab_consent_string/consent/range/single_range_entry.rb
31
+ - lib/iab_consent_string/consent/range/start_end_range_entry.rb
32
+ - lib/iab_consent_string/consent/vendor_consent.rb
33
+ - lib/iab_consent_string/consent/vendor_consent_decoder.rb
34
+ - lib/iab_consent_string/consent/vendor_consent_encoder.rb
35
+ - lib/iab_consent_string/error/vendor_consent_create_error.rb
36
+ - lib/iab_consent_string/error/vendor_consent_error.rb
37
+ - lib/iab_consent_string/error/vendor_consent_parse_error.rb
38
+ - lib/iab_consent_string/gdpr_constants.rb
39
+ - lib/iab_consent_string/purpose.rb
40
+ - lib/iab_consent_string/util/utils.rb
41
+ homepage: https://rubygems.org/gems/iab_consent_string
42
+ licenses:
43
+ - MIT
44
+ metadata:
45
+ source_code_uri: https://github.com/Fidzup/Consent-String-SDK-Ruby
46
+ post_install_message:
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ requirements: []
61
+ rubyforge_project:
62
+ rubygems_version: 2.5.2.3
63
+ signing_key:
64
+ specification_version: 4
65
+ summary: Encode and decode web-safe base64 consent information with the IAB EU's GDPR
66
+ Transparency and Consent Framework.
67
+ test_files: []