queueit_knownuserv3 3.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZTZmYWVkMDlhZmY4NzIwYzBiYjkyMmY0Njg4YTlmYzMyYzczNzI4OQ==
5
+ data.tar.gz: !binary |-
6
+ NDE4OTVjYzEyZWJmOGIwMzkwMTFiYWU2Yzg5ZjRiOGMxNGE0ZmMwOA==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ YWMzOGU1YTU2NDJjYTQwODJkMzdlNjBjNTU2Y2U2MTA3NWRkNmJmZTFhOGYy
10
+ MWIzYjY4Y2Q0ZTE1OTBjOTFkODM0NGJiMzIzZDRjNDc2ZTc3MDk2ODIwZmVi
11
+ YjJkN2QwZWIwODg5NzE0MDg5YmE5MDFlZTZhZWY1NWY3M2FkYTM=
12
+ data.tar.gz: !binary |-
13
+ NDQ1MjI5YmZiMDEwNGU3MTFkNWUxYjhhYTIyNjBhZmE5MGRmMmExODAwNWYw
14
+ NGZiMzM4NzYzN2YyYmEyYjc1YzAwZTc2OTNkZDdiYjg4Njc2YzlkNWI2OGE4
15
+ MjUwMWUyOTE2NGY2NjYyNWQxMzg1YTA0YjM5OTlmZTY0MDcxMWI=
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in queueit_knownuserv3.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "queueit_knownuserv3"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,9 @@
1
+ require_relative "queueit_knownuserv3/known_user"
2
+ require_relative "queueit_knownuserv3/models"
3
+ require_relative "queueit_knownuserv3/queue_url_params"
4
+ require_relative "queueit_knownuserv3/user_in_queue_state_cookie_repository"
5
+ require_relative "queueit_knownuserv3/user_in_queue_service"
6
+ require_relative "queueit_knownuserv3/integration_config_helpers"
7
+
8
+ module QueueIt
9
+ end
@@ -0,0 +1,265 @@
1
+ require 'uri'
2
+
3
+ module QueueIt
4
+ class IntegrationEvaluator
5
+ def getMatchedIntegrationConfig(customerIntegration, currentPageUrl, cookieList, userAgent)
6
+ if (!customerIntegration.kind_of?(Hash) || !customerIntegration.key?("Integrations") ||
7
+ !customerIntegration["Integrations"].kind_of?(Array))
8
+ return nil;
9
+ end
10
+ customerIntegration["Integrations"].each do |integrationConfig|
11
+ next if !integrationConfig.kind_of?(Hash) || !integrationConfig.key?("Triggers") || !integrationConfig["Triggers"].kind_of?(Array)
12
+
13
+ integrationConfig["Triggers"].each do |trigger|
14
+ if(!trigger.kind_of?(Hash))
15
+ return false
16
+ end
17
+ if(evaluateTrigger(trigger, currentPageUrl, cookieList, userAgent))
18
+ return integrationConfig
19
+ end
20
+ end
21
+ end
22
+
23
+ return nil
24
+ end
25
+
26
+ def evaluateTrigger(trigger, currentPageUrl, cookieList, userAgent)
27
+ if (!trigger.key?("LogicalOperator") ||
28
+ !trigger.key?("TriggerParts") ||
29
+ !trigger["TriggerParts"].kind_of?(Array))
30
+ return false;
31
+ end
32
+
33
+ if(trigger["LogicalOperator"].eql? "Or")
34
+ trigger["TriggerParts"].each do |triggerPart|
35
+ if(!triggerPart.kind_of?(Hash))
36
+ return false
37
+ end
38
+ if(evaluateTriggerPart(triggerPart, currentPageUrl, cookieList, userAgent))
39
+ return true
40
+ end
41
+ end
42
+ return false
43
+ else
44
+ trigger["TriggerParts"].each do |triggerPart|
45
+ if(!triggerPart.kind_of?(Hash))
46
+ return false
47
+ end
48
+ if(!evaluateTriggerPart(triggerPart, currentPageUrl, cookieList, userAgent))
49
+ return false
50
+ end
51
+ end
52
+ return true
53
+ end
54
+ end
55
+
56
+ def evaluateTriggerPart(triggerPart, currentPageUrl, cookieList, userAgent)
57
+ if (!triggerPart.key?("ValidatorType"))
58
+ return false
59
+ end
60
+
61
+ case triggerPart["ValidatorType"]
62
+ when "UrlValidator"
63
+ return UrlValidatorHelper.evaluate(triggerPart, currentPageUrl)
64
+ when "CookieValidator"
65
+ return CookieValidatorHelper.evaluate(triggerPart, cookieList)
66
+ when "UserAgentValidator"
67
+ return UserAgentValidatorHelper.evaluate(triggerPart, userAgent)
68
+ else
69
+ return false
70
+ end
71
+ end
72
+ end
73
+
74
+ class UrlValidatorHelper
75
+ def self.evaluate(triggerPart, url)
76
+ if (!triggerPart.key?("Operator") ||
77
+ !triggerPart.key?("IsNegative") ||
78
+ !triggerPart.key?("IsIgnoreCase") ||
79
+ !triggerPart.key?("ValueToCompare") ||
80
+ !triggerPart.key?("UrlPart"))
81
+ return false;
82
+ end
83
+
84
+ urlPart = UrlValidatorHelper.getUrlPart(triggerPart["UrlPart"], url)
85
+
86
+ return ComparisonOperatorHelper.evaluate(
87
+ triggerPart["Operator"],
88
+ triggerPart["IsNegative"],
89
+ triggerPart["IsIgnoreCase"],
90
+ urlPart,
91
+ triggerPart["ValueToCompare"])
92
+ end
93
+
94
+ def self.getUrlPart(urlPart, url)
95
+ begin
96
+ urlParts = URI.parse(url)
97
+ case urlPart
98
+ when "PagePath"
99
+ return urlParts.path
100
+ when "PageUrl"
101
+ return url
102
+ when "HostName"
103
+ return urlParts.host
104
+ else
105
+ return ''
106
+ end
107
+ rescue
108
+ return ''
109
+ end
110
+ end
111
+ end
112
+
113
+ class CookieValidatorHelper
114
+ def self.evaluate(triggerPart, cookieList)
115
+ begin
116
+ if (!triggerPart.key?("Operator") ||
117
+ !triggerPart.key?("IsNegative") ||
118
+ !triggerPart.key?("IsIgnoreCase") ||
119
+ !triggerPart.key?("ValueToCompare") ||
120
+ !triggerPart.key?("CookieName"))
121
+ return false;
122
+ end
123
+
124
+ if(cookieList.nil?)
125
+ return false
126
+ end
127
+
128
+ cookieName = triggerPart["CookieName"]
129
+ cookieValue = ''
130
+ if(!cookieName.nil? && !cookieList[cookieName.to_sym].nil?)
131
+ cookieValue = cookieList[cookieName.to_sym]
132
+ end
133
+ return ComparisonOperatorHelper.evaluate(
134
+ triggerPart["Operator"],
135
+ triggerPart["IsNegative"],
136
+ triggerPart["IsIgnoreCase"],
137
+ cookieValue,
138
+ triggerPart["ValueToCompare"])
139
+ rescue
140
+ return false
141
+ end
142
+ end
143
+ end
144
+
145
+ class UserAgentValidatorHelper
146
+ def self.evaluate(triggerPart, userAgent)
147
+ begin
148
+ if (!triggerPart.key?("Operator") ||
149
+ !triggerPart.key?("IsNegative") ||
150
+ !triggerPart.key?("IsIgnoreCase") ||
151
+ !triggerPart.key?("ValueToCompare"))
152
+ return false;
153
+ end
154
+
155
+ return ComparisonOperatorHelper.evaluate(
156
+ triggerPart["Operator"],
157
+ triggerPart["IsNegative"],
158
+ triggerPart["IsIgnoreCase"],
159
+ userAgent,
160
+ triggerPart["ValueToCompare"])
161
+ end
162
+ end
163
+ end
164
+
165
+ class ComparisonOperatorHelper
166
+ def self.evaluate(opt, isNegative, ignoreCase, left, right)
167
+ if(left.nil?)
168
+ left = ''
169
+ end
170
+ if(right.nil?)
171
+ right = ''
172
+ end
173
+
174
+ case opt
175
+ when "Equals"
176
+ return ComparisonOperatorHelper.equals(left, right, isNegative, ignoreCase)
177
+ when "Contains"
178
+ return ComparisonOperatorHelper.contains(left, right, isNegative, ignoreCase)
179
+ when "StartsWith"
180
+ return ComparisonOperatorHelper.startsWith(left, right, isNegative, ignoreCase)
181
+ when "EndsWith"
182
+ return ComparisonOperatorHelper.endsWith(left, right, isNegative, ignoreCase)
183
+ when "MatchesWith"
184
+ return ComparisonOperatorHelper.matchesWith(left, right, isNegative, ignoreCase)
185
+ else
186
+ return false
187
+ end
188
+ end
189
+
190
+ def self.equals(left, right, isNegative, ignoreCase)
191
+ if(ignoreCase)
192
+ evaluation = left.upcase.eql? right.upcase
193
+ else
194
+ evaluation = left.eql? right
195
+ end
196
+
197
+ if(isNegative)
198
+ return !evaluation
199
+ else
200
+ return evaluation
201
+ end
202
+ end
203
+
204
+ def self.contains(left, right, isNegative, ignoreCase)
205
+ if(right.eql? "*")
206
+ return true
207
+ end
208
+
209
+ if(ignoreCase)
210
+ left = left.upcase
211
+ right = right.upcase
212
+ end
213
+
214
+ evaluation = left.include? right
215
+ if(isNegative)
216
+ return !evaluation
217
+ else
218
+ return evaluation
219
+ end
220
+ end
221
+
222
+ def self.startsWith(left, right, isNegative, ignoreCase)
223
+ if(ignoreCase)
224
+ evaluation = left.upcase.start_with? right.upcase
225
+ else
226
+ evaluation = left.start_with? right
227
+ end
228
+
229
+ if(isNegative)
230
+ return !evaluation
231
+ else
232
+ return evaluation
233
+ end
234
+ end
235
+
236
+ def self.endsWith(left, right, isNegative, ignoreCase)
237
+ if(ignoreCase)
238
+ evaluation = left.upcase.end_with? right.upcase
239
+ else
240
+ evaluation = left.end_with? right
241
+ end
242
+
243
+ if(isNegative)
244
+ return !evaluation
245
+ else
246
+ return evaluation
247
+ end
248
+ end
249
+
250
+ def self.matchesWith(left, right, isNegative, ignoreCase)
251
+ if(ignoreCase)
252
+ pattern = Regexp.new(right, Regexp::IGNORECASE)
253
+ else
254
+ pattern = Regexp.new(right)
255
+ end
256
+
257
+ evaluation = pattern.match(left) != nil
258
+ if(isNegative)
259
+ return !evaluation
260
+ else
261
+ return evaluation
262
+ end
263
+ end
264
+ end
265
+ end
@@ -0,0 +1,310 @@
1
+ require 'json'
2
+
3
+ module QueueIt
4
+ class KnownUser
5
+ QUEUEIT_TOKEN_KEY = "queueittoken"
6
+ QUEUEIT_DEBUG_KEY = "queueitdebug"
7
+
8
+ @@userInQueueService = nil
9
+ def self.getUserInQueueService(cookieJar)
10
+ if (@@userInQueueService == nil)
11
+ return UserInQueueService.new(UserInQueueStateCookieRepository.new(CookieManager.new(cookieJar)))
12
+ end
13
+
14
+ return @@userInQueueService
15
+ end
16
+ private_class_method :getUserInQueueService
17
+
18
+ def self.convertToInt(value)
19
+ begin
20
+ converted = Integer(value)
21
+ rescue
22
+ converted = 0
23
+ end
24
+ return converted
25
+ end
26
+ private_class_method :convertToInt
27
+
28
+ def self.getIsDebug(queueitToken, secretKey)
29
+ qParams = QueueUrlParams.extractQueueParams(queueitToken)
30
+ if(qParams == nil)
31
+ return false
32
+ end
33
+
34
+ redirectType = qParams.redirectType
35
+ if(redirectType == nil)
36
+ return false
37
+ end
38
+
39
+ if (redirectType.upcase.eql?("DEBUG"))
40
+ calculatedHash = OpenSSL::HMAC.hexdigest('sha256', secretKey, qParams.queueITTokenWithoutHash)
41
+ valid = qParams.hashCode.eql?(calculatedHash)
42
+ return valid
43
+ end
44
+ return false
45
+ end
46
+ private_class_method :getIsDebug
47
+
48
+ def self.setDebugCookie(debugEntries, cookieJar)
49
+ if(debugEntries == nil || debugEntries.length == 0)
50
+ return
51
+ end
52
+
53
+ cookieManager = CookieManager.new(cookieJar)
54
+ cookieValue = ''
55
+ debugEntries.each do |entry|
56
+ cookieValue << (entry[0].to_s + '=' + entry[1].to_s + '|')
57
+ end
58
+ cookieValue = cookieValue.chop # remove trailing char
59
+ cookieManager.setCookie(QUEUEIT_DEBUG_KEY, cookieValue, nil, nil)
60
+ end
61
+ private_class_method :setDebugCookie
62
+
63
+ def self._resolveQueueRequestByLocalConfig(targetUrl, queueitToken, queueConfig, customerId, secretKey, cookieJar, request, debugEntries)
64
+ isDebug = getIsDebug(queueitToken, secretKey)
65
+ if(isDebug)
66
+ debugEntries["TargetUrl"] = targetUrl
67
+ debugEntries["QueueitToken"] = queueitToken
68
+ debugEntries["OriginalUrl"] = request.original_url
69
+ if(queueConfig == nil)
70
+ debugEntries["QueueConfig"] = "NULL"
71
+ else
72
+ debugEntries["QueueConfig"] = queueConfig.toString()
73
+ end
74
+ end
75
+
76
+ if(Utils.isNilOrEmpty(customerId))
77
+ raise KnownUserError, "customerId can not be nil or empty."
78
+ end
79
+
80
+ if(Utils.isNilOrEmpty(secretKey))
81
+ raise KnownUserError, "secretKey can not be nil or empty."
82
+ end
83
+
84
+ if(queueConfig == nil)
85
+ raise KnownUserError, "queueConfig can not be nil."
86
+ end
87
+
88
+ if(Utils.isNilOrEmpty(queueConfig.eventId))
89
+ raise KnownUserError, "queueConfig.eventId can not be nil or empty."
90
+ end
91
+
92
+ if(Utils.isNilOrEmpty(queueConfig.queueDomain))
93
+ raise KnownUserError, "queueConfig.queueDomain can not be nil or empty."
94
+ end
95
+
96
+ minutes = convertToInt(queueConfig.cookieValidityMinute)
97
+ if(minutes <= 0)
98
+ raise KnownUserError, "queueConfig.cookieValidityMinute should be integer greater than 0."
99
+ end
100
+
101
+ if(![true, false].include? queueConfig.extendCookieValidity)
102
+ raise KnownUserError, "queueConfig.extendCookieValidity should be valid boolean."
103
+ end
104
+
105
+ userInQueueService = getUserInQueueService(cookieJar)
106
+ userInQueueService.validateQueueRequest(targetUrl, queueitToken, queueConfig, customerId, secretKey)
107
+ end
108
+ private_class_method :_resolveQueueRequestByLocalConfig
109
+
110
+ def self._cancelRequestByLocalConfig(targetUrl, queueitToken, cancelConfig, customerId, secretKey, cookieJar, request, debugEntries)
111
+ isDebug = getIsDebug(queueitToken, secretKey)
112
+ if(isDebug)
113
+ debugEntries["TargetUrl"] = targetUrl
114
+ debugEntries["QueueitToken"] = queueitToken
115
+ debugEntries["OriginalUrl"] = request.original_url
116
+ if(cancelConfig == nil)
117
+ debugEntries["CancelConfig"] = "NULL"
118
+ else
119
+ debugEntries["CancelConfig"] = cancelConfig.toString()
120
+ end
121
+ end
122
+
123
+ if(Utils.isNilOrEmpty(targetUrl))
124
+ raise KnownUserError, "targetUrl can not be nil or empty."
125
+ end
126
+
127
+ if(Utils.isNilOrEmpty(customerId))
128
+ raise KnownUserError, "customerId can not be nil or empty."
129
+ end
130
+
131
+ if(Utils.isNilOrEmpty(secretKey))
132
+ raise KnownUserError, "secretKey can not be nil or empty."
133
+ end
134
+
135
+ if(cancelConfig == nil)
136
+ raise KnownUserError, "cancelConfig can not be nil."
137
+ end
138
+
139
+ if(Utils.isNilOrEmpty(cancelConfig.eventId))
140
+ raise KnownUserError, "cancelConfig.eventId can not be nil or empty."
141
+ end
142
+
143
+ if(Utils.isNilOrEmpty(cancelConfig.queueDomain))
144
+ raise KnownUserError, "cancelConfig.queueDomain can not be nil or empty."
145
+ end
146
+
147
+ userInQueueService = getUserInQueueService(cookieJar)
148
+ userInQueueService.validateCancelRequest(targetUrl, cancelConfig, customerId, secretKey)
149
+ end
150
+ private_class_method :_cancelRequestByLocalConfig
151
+
152
+ def self.extendQueueCookie(eventId, cookieValidityMinute, cookieDomain, secretKey, cookieJar)
153
+ if(Utils.isNilOrEmpty(eventId))
154
+ raise KnownUserError, "eventId can not be nil or empty."
155
+ end
156
+
157
+ if(Utils.isNilOrEmpty(secretKey))
158
+ raise KnownUserError, "secretKey can not be nil or empty."
159
+ end
160
+
161
+ minutes = convertToInt(cookieValidityMinute)
162
+ if(minutes <= 0)
163
+ raise KnownUserError, "cookieValidityMinute should be integer greater than 0."
164
+ end
165
+
166
+ userInQueueService = getUserInQueueService(cookieJar)
167
+ userInQueueService.extendQueueCookie(eventId, cookieValidityMinute, cookieDomain, secretKey)
168
+ end
169
+
170
+ def self.resolveQueueRequestByLocalConfig(targetUrl, queueitToken, queueConfig, customerId, secretKey, cookieJar, request)
171
+ debugEntries = Hash.new
172
+ begin
173
+ return _resolveQueueRequestByLocalConfig(targetUrl, queueitToken, queueConfig, customerId, secretKey, cookieJar, request, debugEntries)
174
+ ensure
175
+ setDebugCookie(debugEntries, cookieJar)
176
+ end
177
+ end
178
+
179
+ def self.validateRequestByIntegrationConfig(currentUrlWithoutQueueITToken, queueitToken, integrationsConfigString, customerId, secretKey, cookieJar, request)
180
+ if(Utils.isNilOrEmpty(currentUrlWithoutQueueITToken))
181
+ raise KnownUserError, "currentUrlWithoutQueueITToken can not be nil or empty."
182
+ end
183
+
184
+ if(Utils.isNilOrEmpty(integrationsConfigString))
185
+ raise KnownUserError, "integrationsConfigString can not be nil or empty."
186
+ end
187
+
188
+ begin
189
+ customerIntegration = JSON.parse(integrationsConfigString)
190
+
191
+ debugEntries = Hash.new
192
+ isDebug = getIsDebug(queueitToken, secretKey)
193
+ if(isDebug)
194
+ debugEntries["ConfigVersion"] = customerIntegration["Version"]
195
+ debugEntries["PureUrl"] = currentUrlWithoutQueueITToken
196
+ debugEntries["QueueitToken"] = queueitToken
197
+ debugEntries["OriginalUrl"] = request.original_url
198
+ end
199
+
200
+ integrationEvaluator = IntegrationEvaluator.new
201
+ matchedConfig = integrationEvaluator.getMatchedIntegrationConfig(customerIntegration, currentUrlWithoutQueueITToken, cookieJar, request.user_agent)
202
+
203
+ if(isDebug)
204
+ if(matchedConfig == nil)
205
+ debugEntries["MatchedConfig"] = "NULL"
206
+ else
207
+ debugEntries["MatchedConfig"] = matchedConfig["Name"]
208
+ end
209
+ end
210
+
211
+ if(matchedConfig == nil)
212
+ return RequestValidationResult.new(nil, nil, nil, nil)
213
+ end
214
+
215
+ if(!matchedConfig.key?("ActionType") || Utils.isNilOrEmpty(matchedConfig["ActionType"]) || matchedConfig["ActionType"].eql?(ActionTypes::QUEUE))
216
+ queueConfig = QueueEventConfig.new
217
+ queueConfig.eventId = matchedConfig["EventId"]
218
+ queueConfig.queueDomain = matchedConfig["QueueDomain"]
219
+ queueConfig.layoutName = matchedConfig["LayoutName"]
220
+ queueConfig.culture = matchedConfig["Culture"]
221
+ queueConfig.cookieDomain = matchedConfig["CookieDomain"]
222
+ queueConfig.extendCookieValidity = matchedConfig["ExtendCookieValidity"]
223
+ queueConfig.cookieValidityMinute = matchedConfig["CookieValidityMinute"]
224
+ queueConfig.version = customerIntegration["Version"]
225
+
226
+ case matchedConfig["RedirectLogic"]
227
+ when "ForcedTargetUrl"
228
+ targetUrl = matchedConfig["ForcedTargetUrl"]
229
+ when "EventTargetUrl"
230
+ targetUrl = ''
231
+ else
232
+ targetUrl = currentUrlWithoutQueueITToken
233
+ end
234
+
235
+ return _resolveQueueRequestByLocalConfig(targetUrl, queueitToken, queueConfig, customerId, secretKey, cookieJar, request, debugEntries)
236
+
237
+ else # cancel action
238
+ cancelConfig = CancelEventConfig.new;
239
+ cancelConfig.eventId = matchedConfig["EventId"]
240
+ cancelConfig.queueDomain = matchedConfig["QueueDomain"]
241
+ cancelConfig.cookieDomain = matchedConfig["CookieDomain"]
242
+ cancelConfig.version = customerIntegration["Version"]
243
+
244
+ return _cancelRequestByLocalConfig(currentUrlWithoutQueueITToken, queueitToken, cancelConfig, customerId, secretKey, cookieJar, request, debugEntries);
245
+ end
246
+ rescue StandardError => stdErr
247
+ raise KnownUserError, "integrationConfiguration text was not valid: " + stdErr.message
248
+ ensure
249
+ setDebugCookie(debugEntries, cookieJar)
250
+ end
251
+ end
252
+
253
+ def self.cancelRequestByLocalConfig(targetUrl, queueitToken, cancelConfig, customerId, secretKey, cookieJar, request)
254
+ debugEntries = Hash.new
255
+ begin
256
+ return _cancelRequestByLocalConfig(targetUrl, queueitToken, cancelConfig, customerId, secretKey, cookieJar, request, debugEntries)
257
+ ensure
258
+ setDebugCookie(debugEntries, cookieJar)
259
+ end
260
+ end
261
+ end
262
+
263
+ class CookieManager
264
+ @cookies = {}
265
+
266
+ def initialize(cookieJar)
267
+ @cookies = cookieJar
268
+ end
269
+
270
+ def getCookie(name)
271
+ key = name.to_sym
272
+ if(!Utils.isNilOrEmpty(@cookies[key]))
273
+ return @cookies[key]
274
+ end
275
+ return nil
276
+ end
277
+
278
+ def setCookie(name, value, expire, domain)
279
+ key = name.to_sym
280
+ noDomain = Utils.isNilOrEmpty(domain)
281
+ deleteCookie = Utils.isNilOrEmpty(value)
282
+ noExpire = Utils.isNilOrEmpty(expire)
283
+
284
+ if(noDomain)
285
+ if(deleteCookie)
286
+ @cookies.delete(key)
287
+ else
288
+ if(noExpire)
289
+ @cookies[key] = { :value => value }
290
+ else
291
+ @cookies[key] = { :value => value, :expires => expire }
292
+ end
293
+ end
294
+ else
295
+ if(deleteCookie)
296
+ @cookies.delete(key, :domain => domain)
297
+ else
298
+ if(noExpire)
299
+ @cookies[key] = { :value => value, :domain => domain }
300
+ else
301
+ @cookies[key] = { :value => value, :expires => expire, :domain => domain }
302
+ end
303
+ end
304
+ end
305
+ end
306
+ end
307
+ end
308
+
309
+
310
+
@@ -0,0 +1,96 @@
1
+ module QueueIt
2
+ class Utils
3
+ def self.isNilOrEmpty(value)
4
+ return !value || value.to_s == ''
5
+ end
6
+ def self.toString(value)
7
+ if(value == nil)
8
+ return ''
9
+ end
10
+ return value.to_s
11
+ end
12
+ end
13
+
14
+ class CancelEventConfig
15
+ attr_accessor :eventId
16
+ attr_accessor :queueDomain
17
+ attr_accessor :cookieDomain
18
+ attr_accessor :version
19
+
20
+ def initialize
21
+ @eventId = nil
22
+ @queueDomain = nil
23
+ @cookieDomain = nil
24
+ @version = nil
25
+ end
26
+
27
+ def toString
28
+ return "EventId:" + Utils.toString(eventId) +
29
+ "&Version:" + Utils.toString(version) +
30
+ "&QueueDomain:" + Utils.toString(queueDomain) +
31
+ "&CookieDomain:" + Utils.toString(cookieDomain)
32
+ end
33
+ end
34
+
35
+ class QueueEventConfig
36
+ attr_accessor :eventId
37
+ attr_accessor :layoutName
38
+ attr_accessor :culture
39
+ attr_accessor :queueDomain
40
+ attr_accessor :extendCookieValidity
41
+ attr_accessor :cookieValidityMinute
42
+ attr_accessor :cookieDomain
43
+ attr_accessor :version
44
+
45
+ def initialize
46
+ @eventId = nil
47
+ @layoutName = nil
48
+ @culture = nil
49
+ @queueDomain = nil
50
+ @extendCookieValidity = nil
51
+ @cookieValidityMinute = nil
52
+ @cookieDomain = nil
53
+ @version = nil
54
+ end
55
+
56
+ def toString
57
+ return "EventId:" + Utils.toString(eventId) +
58
+ "&Version:" + Utils.toString(version) +
59
+ "&QueueDomain:" + Utils.toString(queueDomain) +
60
+ "&CookieDomain:" + Utils.toString(cookieDomain) +
61
+ "&ExtendCookieValidity:" + Utils.toString(extendCookieValidity) +
62
+ "&CookieValidityMinute:" + Utils.toString(cookieValidityMinute) +
63
+ "&LayoutName:" + Utils.toString(layoutName) +
64
+ "&Culture:" + Utils.toString(culture)
65
+ end
66
+ end
67
+
68
+ class RequestValidationResult
69
+ attr_reader :actionType
70
+ attr_reader :eventId
71
+ attr_reader :queueId
72
+ attr_reader :redirectUrl
73
+
74
+ def initialize(actionType, eventId, queueId, redirectUrl)
75
+ @actionType = actionType
76
+ @eventId = eventId
77
+ @queueId = queueId
78
+ @redirectUrl = redirectUrl
79
+ end
80
+
81
+ def doRedirect
82
+ return !Utils.isNilOrEmpty(@redirectUrl)
83
+ end
84
+ end
85
+
86
+ class KnownUserError < StandardError
87
+ def initialize(message)
88
+ super(message)
89
+ end
90
+ end
91
+
92
+ class ActionTypes
93
+ CANCEL = "Cancel"
94
+ QUEUE = "Queue"
95
+ end
96
+ end
@@ -0,0 +1,81 @@
1
+ module QueueIt
2
+ class QueueUrlParams
3
+ KEY_VALUE_SEPARATOR_GROUP_CHAR = '~';
4
+ KEY_VALUE_SEPARATOR_CHAR = '_';
5
+ TIMESTAMP_KEY = "ts"
6
+ COOKIE_VALIDITY_MINUTE_KEY = "cv";
7
+ EVENT_ID_KEY = "e";
8
+ EXTENDABLE_COOKIE_KEY = "ce";
9
+ HASH_KEY = "h";
10
+ QUEUE_ID_KEY = "q";
11
+ REDIRECT_TYPE_KEY = "rt"
12
+
13
+ attr_accessor :timeStamp
14
+ attr_accessor :eventId
15
+ attr_accessor :hashCode
16
+ attr_accessor :extendableCookie
17
+ attr_accessor :cookieValidityMinute
18
+ attr_accessor :queueITToken
19
+ attr_accessor :queueITTokenWithoutHash
20
+ attr_accessor :queueId
21
+ attr_accessor :redirectType
22
+
23
+ def initialize
24
+ @timeStamp = 0
25
+ @eventId = ""
26
+ @hashCode = ""
27
+ @extendableCookie = false
28
+ @cookieValidityMinute = nil
29
+ @queueITToken = ""
30
+ @queueITTokenWithoutHash = ""
31
+ @queueId = ""
32
+ @redirectType = nil
33
+ end
34
+
35
+ def self.extractQueueParams(queueitToken)
36
+ begin
37
+ if(Utils.isNilOrEmpty(queueitToken))
38
+ return nil
39
+ end
40
+ result = QueueUrlParams.new
41
+ result.queueITToken = queueitToken
42
+ paramsNameValueList = result.queueITToken.split(KEY_VALUE_SEPARATOR_GROUP_CHAR)
43
+
44
+ paramsNameValueList.each do |pNameValue|
45
+ paramNameValueArr = pNameValue.split(KEY_VALUE_SEPARATOR_CHAR)
46
+
47
+ case paramNameValueArr[0]
48
+ when TIMESTAMP_KEY
49
+ begin
50
+ result.timeStamp = Integer(paramNameValueArr[1])
51
+ rescue
52
+ result.timeStamp = 0
53
+ end
54
+ when COOKIE_VALIDITY_MINUTE_KEY
55
+ begin
56
+ result.cookieValidityMinute = Integer(paramNameValueArr[1])
57
+ rescue
58
+ result.cookieValidityMinute = nil
59
+ end
60
+ when EVENT_ID_KEY
61
+ result.eventId = paramNameValueArr[1]
62
+ when EXTENDABLE_COOKIE_KEY
63
+ if paramNameValueArr[1].upcase.eql? 'TRUE'
64
+ result.extendableCookie = true
65
+ end
66
+ when HASH_KEY
67
+ result.hashCode = paramNameValueArr[1]
68
+ when QUEUE_ID_KEY
69
+ result.queueId = paramNameValueArr[1]
70
+ when REDIRECT_TYPE_KEY
71
+ result.redirectType = paramNameValueArr[1]
72
+ end
73
+ end
74
+ result.queueITTokenWithoutHash = result.queueITToken.gsub((KEY_VALUE_SEPARATOR_GROUP_CHAR + HASH_KEY + KEY_VALUE_SEPARATOR_CHAR + result.hashCode), "")
75
+ return result
76
+ rescue
77
+ return nil
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,116 @@
1
+ require 'open-uri'
2
+ require 'cgi'
3
+
4
+ module QueueIt
5
+ class UserInQueueService
6
+ SDK_VERSION = "3.2.3"
7
+
8
+ def initialize(userInQueueStateRepository)
9
+ @userInQueueStateRepository = userInQueueStateRepository
10
+ end
11
+
12
+ def validateQueueRequest(targetUrl, queueitToken, config, customerId, secretKey)
13
+ state = @userInQueueStateRepository.getState(config.eventId, secretKey)
14
+ if (state.isValid)
15
+ if (state.isStateExtendable && config.extendCookieValidity)
16
+ @userInQueueStateRepository.store(
17
+ config.eventId,
18
+ state.queueId,
19
+ true,
20
+ config.cookieValidityMinute,
21
+ !Utils::isNilOrEmpty(config.cookieDomain) ? config.cookieDomain : '',
22
+ secretKey)
23
+ end
24
+ return RequestValidationResult.new(ActionTypes::QUEUE, config.eventId, state.queueId, nil)
25
+ end
26
+
27
+ queueParams = QueueUrlParams::extractQueueParams(queueitToken)
28
+ if(!queueParams.nil?)
29
+ return getQueueITTokenValidationResult(targetUrl, config.eventId, config, queueParams, customerId, secretKey)
30
+ else
31
+ return getInQueueRedirectResult(targetUrl, config, customerId)
32
+ end
33
+ end
34
+
35
+ def validateCancelRequest(targetUrl, config, customerId, secretKey)
36
+ state = @userInQueueStateRepository.getState(config.eventId, secretKey)
37
+ if (state.isValid)
38
+ @userInQueueStateRepository.cancelQueueCookie(config.eventId, config.cookieDomain)
39
+
40
+ query = getQueryString(customerId, config.eventId, config.version, nil, nil) + ( !Utils::isNilOrEmpty(targetUrl) ? ("&r=" + CGI.escape(targetUrl)) : "" )
41
+
42
+ domainAlias = config.queueDomain
43
+ if (!domainAlias.end_with?("/") )
44
+ domainAlias = domainAlias + "/"
45
+ end
46
+
47
+ redirectUrl = "https://" + domainAlias + "cancel/" + customerId + "/" + config.eventId + "/?" + query;
48
+ return RequestValidationResult.new(ActionTypes::CANCEL, config.eventId, state.queueId, redirectUrl)
49
+ else
50
+ return RequestValidationResult.new(ActionTypes::CANCEL, config.eventId, nil, nil)
51
+ end
52
+ end
53
+
54
+ def getQueueITTokenValidationResult(targetUrl, eventId, config, queueParams,customerId, secretKey)
55
+ calculatedHash = OpenSSL::HMAC.hexdigest('sha256', secretKey, queueParams.queueITTokenWithoutHash)
56
+ if (calculatedHash.upcase() != queueParams.hashCode.upcase())
57
+ return getVaidationErrorResult(customerId, targetUrl, config, queueParams, "hash")
58
+ end
59
+ if (queueParams.eventId.upcase() != eventId.upcase())
60
+ return getVaidationErrorResult(customerId, targetUrl, config, queueParams, "eventid")
61
+ end
62
+ if (queueParams.timeStamp < Time.now.getutc.tv_sec)
63
+ return getVaidationErrorResult(customerId, targetUrl, config, queueParams, "timestamp")
64
+ end
65
+
66
+ @userInQueueStateRepository.store(
67
+ config.eventId,
68
+ queueParams.queueId,
69
+ queueParams.extendableCookie,
70
+ !(queueParams.cookieValidityMinute.nil?) ? queueParams.cookieValidityMinute : config.cookieValidityMinute,
71
+ !Utils::isNilOrEmpty(config.cookieDomain) ? config.cookieDomain : '',
72
+ secretKey)
73
+ return RequestValidationResult.new(ActionTypes::QUEUE, config.eventId, queueParams.queueId, nil)
74
+ end
75
+
76
+ def getVaidationErrorResult(customerId, targetUrl, config, qParams, errorCode)
77
+ query = getQueryString(customerId, config.eventId, config.version, config.culture, config.layoutName) +
78
+ "&queueittoken=" + qParams.queueITToken +
79
+ "&ts=" + Time.now.getutc.tv_sec.to_s +
80
+ (!Utils::isNilOrEmpty(targetUrl) ? ("&t=" + CGI.escape(targetUrl)) : "")
81
+ domainAlias = config.queueDomain
82
+ if (!domainAlias.end_with?("/") )
83
+ domainAlias = domainAlias + "/"
84
+ end
85
+ redirectUrl = "https://" + domainAlias + "error/" + errorCode + "?" + query
86
+ return RequestValidationResult.new(ActionTypes::QUEUE, config.eventId, nil, redirectUrl)
87
+ end
88
+
89
+ def getInQueueRedirectResult(targetUrl, config, customerId)
90
+ redirectUrl = "https://" + config.queueDomain +
91
+ "?" + getQueryString(customerId, config.eventId, config.version, config.culture, config.layoutName) +
92
+ (!Utils::isNilOrEmpty(targetUrl) ? "&t=" +
93
+ CGI.escape( targetUrl) : "")
94
+ return RequestValidationResult.new(ActionTypes::QUEUE, config.eventId, nil, redirectUrl)
95
+ end
96
+
97
+ def getQueryString(customerId, eventId, configVersion, culture, layoutName)
98
+ queryStringList = Array.new
99
+ queryStringList.push("c=" + CGI.escape(customerId))
100
+ queryStringList.push("e=" + CGI.escape(eventId))
101
+ queryStringList.push("ver=v3-ruby-" + SDK_VERSION)
102
+ queryStringList.push("cver=" + (!configVersion.nil? ? configVersion.to_s : '-1'))
103
+ if (!Utils::isNilOrEmpty(culture))
104
+ queryStringList.push("cid=" + CGI.escape(culture))
105
+ end
106
+ if (!Utils::isNilOrEmpty(layoutName))
107
+ queryStringList.push("l=" + CGI.escape(layoutName))
108
+ end
109
+ return queryStringList.join("&")
110
+ end
111
+
112
+ def extendQueueCookie(eventId, cookieValidityMinute, cookieDomain, secretKey)
113
+ @userInQueueStateRepository.extendQueueCookie(eventId, cookieValidityMinute, cookieDomain, secretKey)
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,125 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+ require 'date'
4
+
5
+ module QueueIt
6
+ class UserInQueueStateCookieRepository
7
+ QUEUEIT_DATA_KEY = "QueueITAccepted-SDFrts345E-V3"
8
+
9
+ def initialize(cookieManager)
10
+ @cookieManager = cookieManager
11
+ end
12
+
13
+ def cancelQueueCookie(eventId, cookieDomain)
14
+ cookieKey = self.class.getCookieKey(eventId)
15
+ @cookieManager.setCookie(cookieKey, nil, -1, cookieDomain)
16
+ end
17
+
18
+ def store(eventId, queueId, isStateExtendable, cookieValidityMinute, cookieDomain, secretKey)
19
+ cookieKey = self.class.getCookieKey(eventId)
20
+ expirationTime = (Time.now.getutc.tv_sec + (cookieValidityMinute * 60)).to_s
21
+ isStateExtendableString = (isStateExtendable) ? 'true' : 'false'
22
+ cookieValue = createCookieValue(queueId, isStateExtendableString, expirationTime, secretKey)
23
+ @cookieManager.setCookie(cookieKey, cookieValue, Time.now + (24*60*60), cookieDomain)
24
+ end
25
+
26
+ def self.getCookieKey(eventId)
27
+ return QUEUEIT_DATA_KEY + '_' + eventId
28
+ end
29
+
30
+ def createCookieValue(queueId, isStateExtendable, expirationTime, secretKey)
31
+ hashValue = OpenSSL::HMAC.hexdigest('sha256', secretKey, queueId + isStateExtendable + expirationTime)
32
+ cookieValue = "QueueId=" + queueId + "&IsCookieExtendable=" + isStateExtendable + "&Expires=" + expirationTime + "&Hash=" + hashValue
33
+ return cookieValue
34
+ end
35
+
36
+ def getCookieNameValueMap(cookieValue)
37
+ result = Hash.new
38
+ cookieNameValues = cookieValue.split("&")
39
+ if (cookieNameValues.length != 4)
40
+ return result
41
+ end
42
+
43
+ cookieNameValues.each do |item|
44
+ arr = item.split("=")
45
+ if(arr.length == 2)
46
+ result[arr[0]] = arr[1]
47
+ end
48
+ end
49
+ return result
50
+ end
51
+
52
+ def isCookieValid(cookieNameValueMap, secretKey)
53
+ begin
54
+ if (!cookieNameValueMap.key?("IsCookieExtendable"))
55
+ return false
56
+ end
57
+ if (!cookieNameValueMap.key?("Expires"))
58
+ return false
59
+ end
60
+ if (!cookieNameValueMap.key?("Hash"))
61
+ return false
62
+ end
63
+ if (!cookieNameValueMap.key?("QueueId"))
64
+ return false
65
+ end
66
+ hashValue = OpenSSL::HMAC.hexdigest('sha256', secretKey, cookieNameValueMap["QueueId"] + cookieNameValueMap["IsCookieExtendable"] + cookieNameValueMap["Expires"])
67
+ if (hashValue != cookieNameValueMap["Hash"])
68
+ return false
69
+ end
70
+ if(Integer(cookieNameValueMap["Expires"]) < Time.now.getutc.tv_sec)
71
+ return false
72
+ end
73
+ return true
74
+ rescue
75
+ return false
76
+ end
77
+ end
78
+
79
+ def extendQueueCookie(eventId, cookieValidityMinute, cookieDomain, secretKey)
80
+ cookieKey = self.class.getCookieKey(eventId)
81
+ cookieValue = @cookieManager.getCookie(cookieKey)
82
+ if (cookieValue.nil?)
83
+ return
84
+ end
85
+
86
+ cookieNameValueMap = getCookieNameValueMap(cookieValue)
87
+ if (!isCookieValid(cookieNameValueMap, secretKey))
88
+ return
89
+ end
90
+ expirationTime = (Time.now.getutc.tv_sec + (cookieValidityMinute * 60)).to_s
91
+ cookieValue = createCookieValue(cookieNameValueMap["QueueId"], cookieNameValueMap["IsCookieExtendable"], expirationTime, secretKey)
92
+ @cookieManager.setCookie(cookieKey, cookieValue, Time.now + (24*60*60), cookieDomain)
93
+ end
94
+
95
+ def getState(eventId, secretKey)
96
+ cookieKey = cookieKey = self.class.getCookieKey(eventId)
97
+ if (@cookieManager.getCookie(cookieKey).nil?)
98
+ return StateInfo.new(false, nil, false, 0)
99
+ end
100
+ cookieNameValueMap = getCookieNameValueMap(@cookieManager.getCookie(cookieKey))
101
+ if (!isCookieValid(cookieNameValueMap, secretKey))
102
+ return StateInfo.new(false, nil, false,0)
103
+ end
104
+ return StateInfo.new(
105
+ true,
106
+ cookieNameValueMap["QueueId"],
107
+ cookieNameValueMap["IsCookieExtendable"] == 'true',
108
+ Integer(cookieNameValueMap["Expires"]))
109
+ end
110
+ end
111
+
112
+ class StateInfo
113
+ attr_reader :isValid
114
+ attr_reader :queueId
115
+ attr_reader :isStateExtendable
116
+ attr_reader :expires # used just for unit tests
117
+
118
+ def initialize(isValid, queueId, isStateExtendable, expires)
119
+ @isValid = isValid
120
+ @queueId = queueId
121
+ @isStateExtendable = isStateExtendable
122
+ @expires = expires
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'queueit_knownuserv3/user_in_queue_service'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "queueit_knownuserv3"
8
+ spec.version = QueueIt::UserInQueueService::SDK_VERSION
9
+ spec.authors = ["Queue-it"]
10
+ spec.email = ["support@queue-it.com"]
11
+ spec.licenses = "LGPL-3.0"
12
+ spec.summary = %q{ Gem for implementing Queue-it KnownUser V3}
13
+ spec.homepage = "https://www.queue-it.com/"
14
+
15
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
16
+ # delete this section to allow pushing this gem to any host.
17
+
18
+ # if spec.respond_to?(:metadata)
19
+ # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
20
+ # else
21
+ # raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
22
+ # end
23
+
24
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
+ spec.bindir = "exe"
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ["lib"]
28
+
29
+ spec.add_development_dependency "bundler", "~> 1.11"
30
+ spec.add_development_dependency "rake", "~> 10.0"
31
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: queueit_knownuserv3
3
+ version: !ruby/object:Gem::Version
4
+ version: 3.2.3
5
+ platform: ruby
6
+ authors:
7
+ - Queue-it
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-10-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.11'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.11'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description:
42
+ email:
43
+ - support@queue-it.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - Gemfile
49
+ - Rakefile
50
+ - bin/console
51
+ - bin/setup
52
+ - lib/queueit_knownuserv3.rb
53
+ - lib/queueit_knownuserv3/integration_config_helpers.rb
54
+ - lib/queueit_knownuserv3/known_user.rb
55
+ - lib/queueit_knownuserv3/models.rb
56
+ - lib/queueit_knownuserv3/queue_url_params.rb
57
+ - lib/queueit_knownuserv3/user_in_queue_service.rb
58
+ - lib/queueit_knownuserv3/user_in_queue_state_cookie_repository.rb
59
+ - queueit_knownuserv3.gemspec
60
+ homepage: https://www.queue-it.com/
61
+ licenses:
62
+ - LGPL-3.0
63
+ metadata: {}
64
+ post_install_message:
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ! '>='
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubyforge_project:
80
+ rubygems_version: 2.6.13
81
+ signing_key:
82
+ specification_version: 4
83
+ summary: Gem for implementing Queue-it KnownUser V3
84
+ test_files: []