diff --git a/Examples/Apache/ApacheHandlerUsingConfigFromFile.lua b/Examples/Apache/ApacheHandlerUsingConfigFromFile.lua index dcdb0fe..7a8046d 100644 --- a/Examples/Apache/ApacheHandlerUsingConfigFromFile.lua +++ b/Examples/Apache/ApacheHandlerUsingConfigFromFile.lua @@ -14,11 +14,6 @@ --... * QUEUEIT_INT_CONF_FILE: The local JSON file containing the integration configuration -- * QUEUEIT_ERROR_CODE: (optional) The response code to use instead of declining to act -- if request handling fails --- * QUEUEIT_COOKIE_OPTIONS_HTTPONLY: (optional) Set to "true" if you want cookies with httponly --- flag set. Only enable if this you use pure server-side integration --- e.g. not JS Hybrid. --- * QUEUEIT_COOKIE_OPTIONS_SECURE: (optional) Set to "true" if you want cookies with secure --- flag set. Only enable if your website runs purely on https. -- Note that the integration configuration is read on every request. The JSON file containing -- The integration configuration should, for performance reasons, be available locally. -- @@ -29,6 +24,7 @@ -- SetEnv QUEUEIT_CUSTOMER_ID "{CUSTOMER_ID}" -- SetEnv QUEUEIT_SECRET_KEY "{SECRET_KEY}" -- SetEnv QUEUEIT_INT_CONF_FILE "{APP_FOLDER}/integration_config.json" +-- SetEnv QUEUEIT_ERROR_CODE "400" -- LuaMapHandler "{URI_PATTERN}" "{APP_FOLDER}/Handlers/ApacheHandlerUsingConfigFromFile.lua" -- LuaPackagePath "{APP_FOLDER}/SDK/?.lua" -- LuaPackagePath "{APP_FOLDER}/Helpers/?/?.lua" @@ -45,7 +41,7 @@ local DEBUG_TAG = "ApacheHandlerUsingConfigFromFile.lua" local kuHandler = require("KnownUserApacheHandler") local file = require("file") -local function initRequiredHelpers(r, cookieOptions) +local function initRequiredHelpers(r) local iHelpers = require("KnownUserImplementationHelpers") iHelpers.request.getAbsoluteUri = function() @@ -56,8 +52,6 @@ local function initRequiredHelpers(r, cookieOptions) r:debug(string.format("[%s] Rebuilt request URL as: %s", DEBUG_TAG, fullUrl)) return fullUrl end - - iHelpers.response.cookieOptions = cookieOptions end function handle(r) @@ -73,8 +67,6 @@ function handle(r) local secretKey = r.subprocess_env["QUEUEIT_SECRET_KEY"] local intConfFile = r.subprocess_env["QUEUEIT_INT_CONF_FILE"] local errorCode = r.subprocess_env["QUEUEIT_ERROR_CODE"] - local co_httpOnly = r.subprocess_env["QUEUEIT_COOKIE_OPTIONS_HTTPONLY"] - local co_secure = r.subprocess_env["QUEUEIT_COOKIE_OPTIONS_SECURE"] if customerId ~= nil then r:debug(string.format("[%s] Environment variable QUEUEIT_CUSTOMER_ID: %s", DEBUG_TAG, customerId)) @@ -88,12 +80,6 @@ function handle(r) if errorCode ~= nil then r:debug(string.format("[%s] Environment variable QUEUEIT_ERROR_CODE: %s", DEBUG_TAG, errorCode)) end - if co_httpOnly ~= nil then - r:debug(string.format("[%s] Environment variable QUEUEIT_COOKIE_OPTIONS_HTTPONLY: %s", DEBUG_TAG, co_httpOnly)) - end - if co_secure ~= nil then - r:debug(string.format("[%s] Environment variable QUEUEIT_COOKIE_OPTIONS_SECURE: %s", DEBUG_TAG, co_secure)) - end assert(customerId ~= nil, "customerId invalid") assert(secretKey ~= nil, "secretKey invalid") @@ -112,18 +98,8 @@ function handle(r) end r:debug(string.format("[%s] Value of variable errorCode: %s", DEBUG_TAG, errorCode)) - -- configure cookie options - local cookieOptions = - { - httpOnly = false, - secure = false - } - - if (co_httpOnly ~= nil and co_httpOnly == 'true') then cookieOptions.httpOnly = true end - if (co_secure ~= nil and co_secure == 'true') then cookieOptions.secure = true end - -- initialize helper functions - initRequiredHelpers(r, cookieOptions) + initRequiredHelpers(r) -- read integration configuration from file local intConfJson = file.readAll(intConfFile) diff --git a/Handlers/KnownUserApacheHandler.lua b/Handlers/KnownUserApacheHandler.lua index 0f48a42..999f7fe 100644 --- a/Handlers/KnownUserApacheHandler.lua +++ b/Handlers/KnownUserApacheHandler.lua @@ -103,7 +103,7 @@ local function handle(customerId, secretKey, config, isIntegrationConfig, reques -- Implementation is not using built in r:setcookie method -- because we want to support Apache version < 2.4.12 -- where there is bug in that specific method - iHelpers.response.setCookie = function(name, value, expire, domain) + iHelpers.response.setCookie = function(name, value, expire, domain, isHttpOnly, isSecure) -- lua_mod only supports 1 Set-Cookie header (because 'err_headers_out' is a table). -- So calling this method (setCookie) multiple times will not work as expected. -- In this case final call will apply. @@ -126,8 +126,8 @@ local function handle(customerId, secretKey, config, isIntegrationConfig, reques request_rec.err_headers_out["Set-Cookie"] = name .. '=' .. value .. expire_text .. (domain ~= "" and '; Domain=' .. domain or '') - .. (iHelpers.response.cookieOptions.httpOnly and '; HttpOnly' or '') - .. (iHelpers.response.cookieOptions.secure and '; Secure' or '') + .. (isHttpOnly and '; HttpOnly' or '') + .. (isSecure and '; Secure' or '') .. '; Path=/;' end @@ -153,7 +153,9 @@ local function handle(customerId, secretKey, config, isIntegrationConfig, reques -- end if (validationResult.isAjaxResult) then - request_rec.err_headers_out[validationResult.getAjaxQueueRedirectHeaderKey()] = validationResult:getAjaxRedirectUrl() + local headerName = validationResult.getAjaxQueueRedirectHeaderKey() + request_rec.err_headers_out[headerName] = validationResult:getAjaxRedirectUrl() + request_rec.err_headers_out['Access-Control-Expose-Headers'] = headerName else request_rec.err_headers_out["Location"] = validationResult.redirectUrl return apache2.HTTP_MOVED_TEMPORARILY diff --git a/Handlers/KnownUserNginxHandler.lua b/Handlers/KnownUserNginxHandler.lua index dd82245..e108419 100644 --- a/Handlers/KnownUserNginxHandler.lua +++ b/Handlers/KnownUserNginxHandler.lua @@ -39,7 +39,7 @@ iHelpers.request.getUserHostAddress = function() return ngx.var.remote_addr end -iHelpers.response.setCookie = function(name, value, expire, domain) +iHelpers.response.setCookie = function(name, value, expire, domain, isHttpOnly, isSecure) -- lua_mod only supports 1 Set-Cookie header (because 'header' is a table). -- So calling this method (setCookie) multiple times will not work as expected. -- In this case final call will apply. @@ -62,8 +62,8 @@ iHelpers.response.setCookie = function(name, value, expire, domain) ngx.header["Set-Cookie"] = name .. '=' .. value .. expire_text .. (domain ~= "" and '; Domain=' .. domain or '') - .. (iHelpers.response.cookieOptions.httpOnly and '; HttpOnly' or '') - .. (iHelpers.response.cookieOptions.secure and '; Secure' or '') + .. (isHttpOnly and '; HttpOnly' or '') + .. (isSecure and '; Secure' or '') .. '; Path=/;' end @@ -73,24 +73,6 @@ end local aHandler = {} -aHandler.setOptions = function(options) - if (options == nil) then - error('invalid options') - end - - if (options.secure) then - iHelpers.response.cookieOptions.secure = true - else - iHelpers.response.cookieOptions.secure = false - end - - if (options.httpOnly) then - iHelpers.response.cookieOptions.httpOnly = true - else - iHelpers.response.cookieOptions.httpOnly = false - end -end - aHandler.handleByIntegrationConfig = function(customerId, secretKey, integrationConfigJson) local queueitToken = '' if (ngx.var.arg_queueittoken ~= nil) then @@ -111,7 +93,9 @@ aHandler.handleByIntegrationConfig = function(customerId, secretKey, integration -- end if (validationResult.isAjaxResult) then - ngx.header[validationResult.getAjaxQueueRedirectHeaderKey()] = validationResult:getAjaxRedirectUrl() + local headerName = validationResult.getAjaxQueueRedirectHeaderKey() + ngx.header[headerName] = validationResult:getAjaxRedirectUrl() + ngx.header['Access-Control-Expose-Headers'] = headerName else ngx.redirect(validationResult.redirectUrl) ngx.exit(ngx.HTTP_MOVED_TEMPORARILY) diff --git a/SDK/KnownUser.lua b/SDK/KnownUser.lua index 4fcb55d..85bd870 100644 --- a/SDK/KnownUser.lua +++ b/SDK/KnownUser.lua @@ -37,7 +37,7 @@ local function setDebugCookie(debugEntries) cookieValue = cookieValue .. (key .. "=" .. value .. "|") end cookieValue = cookieValue:sub(0, cookieValue:len()-1) -- remove trailing | - iHelpers.response.setCookie(QUEUEIT_DEBUG_KEY, cookieValue, 0, nil) + iHelpers.response.setCookie(QUEUEIT_DEBUG_KEY, cookieValue, 0, nil, false, false) end local function generateTargetUrl(originalTargetUrl) @@ -124,7 +124,8 @@ local function cancelRequestByLocalConfig( end -- END Private functions -ku.extendQueueCookie = function(eventId, cookieValidityMinute, cookieDomain, secretKey) +ku.extendQueueCookie = function( + eventId, cookieValidityMinute, cookieDomain, isCookieHttpOnly, isCookieSecure, secretKey) assert(utils.toString(eventId) ~= "", "eventId can not be nil or empty.") assert(utils.toString(secretKey) ~= "", "secretKey can not be nil or empty.") @@ -133,7 +134,8 @@ ku.extendQueueCookie = function(eventId, cookieValidityMinute, cookieDomain, sec error("cookieValidityMinute should be a number greater than 0.") end - userInQueueService.extendQueueCookie(eventId, cookieValidityMinute, cookieDomain, secretKey) + userInQueueService.extendQueueCookie( + eventId, cookieValidityMinute, cookieDomain, isCookieHttpOnly, isCookieSecure, secretKey) end ku.cancelRequestByLocalConfig = function(targetUrl, queueitToken, cancelConfig, customerId, secretKey) @@ -165,19 +167,27 @@ ku.validateRequestByIntegrationConfig = function( currentUrlWithoutQueueITToken, queueitToken, integrationConfigJson, customerId, secretKey) -- Private functions local function handleQueueAction( - _currentUrlWithoutQueueITToken, _queueitToken, _customerIntegration, - _customerId, _secretKey, _matchedConfig, _debugEntries, _isDebug) + _currentUrlWithoutQueueITToken, + _queueitToken, + _customerIntegration, + _customerId, + _secretKey, + _matchedConfig, + _debugEntries, + _isDebug) local eventConfig = models.QueueEventConfig.create() local targetUrl eventConfig.eventId = _matchedConfig["EventId"] + eventConfig.version = _customerIntegration["Version"] eventConfig.queueDomain = _matchedConfig["QueueDomain"] - eventConfig.layoutName = _matchedConfig["LayoutName"] - eventConfig.culture = _matchedConfig["Culture"] eventConfig.cookieDomain = _matchedConfig["CookieDomain"] + eventConfig.isCookieHttpOnly = _matchedConfig["IsCookieHttpOnly"] or false + eventConfig.isCookieSecure = _matchedConfig["IsCookieSecure"] or false eventConfig.extendCookieValidity = _matchedConfig["ExtendCookieValidity"] eventConfig.cookieValidityMinute = _matchedConfig["CookieValidityMinute"] - eventConfig.version = _customerIntegration["Version"] + eventConfig.layoutName = _matchedConfig["LayoutName"] + eventConfig.culture = _matchedConfig["Culture"] eventConfig.actionName = _matchedConfig["Name"] if (_matchedConfig["RedirectLogic"] == "ForcedTargetUrl" @@ -201,9 +211,11 @@ ku.validateRequestByIntegrationConfig = function( local cancelEventConfig = models.CancelEventConfig.create() cancelEventConfig.eventId = _matchedConfig["EventId"] + cancelEventConfig.version = _customerIntegration["Version"] cancelEventConfig.queueDomain = _matchedConfig["QueueDomain"] cancelEventConfig.cookieDomain = _matchedConfig["CookieDomain"] - cancelEventConfig.version = _customerIntegration["Version"] + cancelEventConfig.isCookieHttpOnly = _matchedConfig["IsCookieHttpOnly"] or false + cancelEventConfig.isCookieSecure = _matchedConfig["IsCookieSecure"] or false cancelEventConfig.actionName = _matchedConfig["Name"] return cancelRequestByLocalConfig( diff --git a/SDK/KnownUserImplementationHelpers.lua b/SDK/KnownUserImplementationHelpers.lua index 89362ed..523d5b4 100644 --- a/SDK/KnownUserImplementationHelpers.lua +++ b/SDK/KnownUserImplementationHelpers.lua @@ -35,20 +35,14 @@ local iHelpers = { cookieOptions = { - -- true if response cookies should have httponly flag set - -- only enable if you use pure server-side integration e.g. not JS Hybrid - httpOnly = false, - -- true if response cookies should have secure flag set - -- only enable if your website runs on https - secure = false, -- set to any string value (none, strict, lax) if response cookies should have samesite flag set -- only use 'strict' if your queue protected site stays on same domain (no navigation to subdomains) sameSite = nil }, - -- arguments: name, value, expire, domain + -- arguments: name, value, expire, domain, isHttpOnly, isSecure -- returns: void - setCookie = function(_, _, _, _) - error("Not implemented : response.setCookie(name, value, expire, domain)") + setCookie = function(_, _, _, _, _, _) + error("Not implemented : response.setCookie(name, value, expire, domain, isHttpOnly, isSecure)") end }, hash = diff --git a/SDK/Models.lua b/SDK/Models.lua index 4d3bfbe..b4c2778 100644 --- a/SDK/Models.lua +++ b/SDK/Models.lua @@ -11,6 +11,8 @@ local models = { extendCookieValidity = nil, cookieValidityMinute = nil, cookieDomain = nil, + isCookieHttpOnly = nil, + isCookieSecure = nil, version = nil, actionName = "unspecified", getString = function(self) @@ -19,6 +21,8 @@ local models = { "&Version:" .. utils.toString(self.version) .. "&QueueDomain:" .. utils.toString(self.queueDomain) .. "&CookieDomain:" .. utils.toString(self.cookieDomain) .. + "&IsCookieHttpOnly:" .. utils.toString(self.isCookieHttpOnly) .. + "&IsCookieSecure:" .. utils.toString(self.isCookieSecure) .. "&ExtendCookieValidity:" .. utils.toString(self.extendCookieValidity) .. "&CookieValidityMinute:" .. utils.toString(self.cookieValidityMinute) .. "&LayoutName:" .. utils.toString(self.layoutName) .. @@ -36,6 +40,8 @@ local models = { eventId = nil, queueDomain = nil, cookieDomain = nil, + isCookieHttpOnly = nil, + isCookieSecure = nil, version = nil, actionName = "unspecified", getString = function(self) @@ -44,6 +50,8 @@ local models = { "&Version:" .. utils.toString(self.version) .. "&QueueDomain:" .. utils.toString(self.queueDomain) .. "&CookieDomain:" .. utils.toString(self.cookieDomain) .. + "&IsCookieHttpOnly:" .. utils.toString(self.isCookieHttpOnly) .. + "&IsCookieSecure:" .. utils.toString(self.isCookieSecure) .. "&ActionName:" .. utils.toString(self.actionName) end } diff --git a/SDK/Tests/KnownUserTest.lua b/SDK/Tests/KnownUserTest.lua index d6226bd..736b400 100644 --- a/SDK/Tests/KnownUserTest.lua +++ b/SDK/Tests/KnownUserTest.lua @@ -17,7 +17,7 @@ iHelpers.reset = function() iHelpers.request.getUserHostAddress = function() return nil end - iHelpers.response.setCookie = function(name, value, _, _) + iHelpers.response.setCookie = function(name, value, _, _, _, _) if(name=="queueitdebug") then iHelpers.response.debugCookieSet = value end @@ -53,10 +53,16 @@ userInQueueServiceMock.validateCancelRequest = function(targetUrl, cancelConfig, end end userInQueueServiceMock.extendQueueCookieResult = { } -userInQueueServiceMock.extendQueueCookie = function(eventId, cookieValidityMinute, cookieDomain, secretKey) +userInQueueServiceMock.extendQueueCookie = function( + eventId, cookieValidityMinute, cookieDomain, isCookieHttpOnly, isCookieSecure, secretKey) userInQueueServiceMock.methodInvokations = { - method="extendQueueCookie", eventId=eventId, cookieValidityMinute=cookieValidityMinute, - cookieDomain=cookieDomain, secretKey=secretKey + method="extendQueueCookie", + eventId=eventId, + cookieValidityMinute=cookieValidityMinute, + cookieDomain=cookieDomain, + isCookieHttpOnly=isCookieHttpOnly, + isCookieSecure=isCookieSecure, + secretKey=secretKey } return userInQueueServiceMock.validateQueueRequestResult end @@ -286,7 +292,7 @@ local function KnownUserTest() local errorMsg local status = xpcall( function() - knownUser.extendQueueCookie(nil, 10, "cookieDomain", "secretkey") + knownUser.extendQueueCookie(nil, 10, "cookieDomain", false, false, "secretkey") end, function(err) errorMsg = err @@ -304,7 +310,7 @@ local function KnownUserTest() local errorMsg local status = xpcall( function() - knownUser.extendQueueCookie("event1", 10, "cookieDomain", nil) + knownUser.extendQueueCookie("event1", 10, "cookieDomain", false, false, nil) end, function(err) errorMsg = err @@ -322,7 +328,7 @@ local function KnownUserTest() local errorMsg local status = xpcall( function() - knownUser.extendQueueCookie("event1", "notnumber", "cookieDomain", "secretKey") + knownUser.extendQueueCookie("event1", "notnumber", "cookieDomain", false, false, "secretKey") end, function(err) errorMsg = err @@ -340,7 +346,7 @@ local function KnownUserTest() local errorMsg local status = xpcall( function() - knownUser.extendQueueCookie("event1", -1, "cookieDomain", "secretKey") + knownUser.extendQueueCookie("event1", -1, "cookieDomain", false, false, "secretKey") end, function(err) errorMsg = err @@ -355,12 +361,14 @@ local function KnownUserTest() local function test_extendQueueCookie() resetAllMocks() - knownUser.extendQueueCookie("eventid", 10, "cookieDomain", "secretkey") + knownUser.extendQueueCookie("eventid", 10, "cookieDomain", true, true, "secretkey") assert( userInQueueServiceMock.methodInvokations.method == "extendQueueCookie" ) assert( userInQueueServiceMock.methodInvokations.eventId == "eventid" ) assert( userInQueueServiceMock.methodInvokations.cookieValidityMinute == 10 ) assert( userInQueueServiceMock.methodInvokations.cookieDomain == "cookieDomain" ) + assert( userInQueueServiceMock.methodInvokations.isCookieHttpOnly ) + assert( userInQueueServiceMock.methodInvokations.isCookieSecure ) assert( userInQueueServiceMock.methodInvokations.secretKey == "secretkey" ) end test_extendQueueCookie() @@ -1286,8 +1294,14 @@ local function KnownUserTest() "|RequestHttpHeader_XForwardedProto=xfp" .. "|ServerUtcTime=" .. timestamp .. "|QueueitToken=" .. token .. - "|CancelConfig=EventId:event1&Version:3" .. - "&QueueDomain:knownusertest.queue-it.net&CookieDomain:.test.com&ActionName:event1action" + "|CancelConfig=" + .. "EventId:event1" + .. "&Version:3" + .. "&QueueDomain:knownusertest.queue-it.net" + .. "&CookieDomain:.test.com" + .. "&IsCookieHttpOnly:false" + .. "&IsCookieSecure:false" + .. "&ActionName:event1action" local cookieArray = utils.explode("|", iHelpers.response.debugCookieSet ) for _, value in pairs(cookieArray) do @@ -1560,6 +1574,8 @@ local function KnownUserTest() eventconfig.culture = "culture" eventconfig.eventId = "eventId" eventconfig.queueDomain = "queueDomain" + eventconfig.isCookieHttpOnly = true + eventconfig.isCookieSecure = true eventconfig.extendCookieValidity = true eventconfig.cookieValidityMinute = 10 eventconfig.version = 12 @@ -1583,9 +1599,18 @@ local function KnownUserTest() "|RequestHttpHeader_XForwardedFor=xff" .. "|QueueitToken=" .. token .. "|RequestIP=userIP" .. - "|QueueConfig=EventId:eventId&Version:12" .. - "&QueueDomain:queueDomain&CookieDomain:cookieDomain&ExtendCookieValidity" .. - ":true&CookieValidityMinute:10&LayoutName:layoutName&Culture:culture&ActionName:event1action" + "|QueueConfig=" + .. "EventId:eventId" + .. "&Version:12" + .. "&QueueDomain:queueDomain" + .. "&CookieDomain:cookieDomain" + .. "&IsCookieHttpOnly:true" + .. "&IsCookieSecure:true" + .. "&ExtendCookieValidity:true" + .. "&CookieValidityMinute:10" + .. "&LayoutName:layoutName" + .. "&Culture:culture" + .. "&ActionName:event1action" local cookieArray = utils.explode("|", iHelpers.response.debugCookieSet ) for _, value in pairs(cookieArray) do @@ -1744,6 +1769,8 @@ local function KnownUserTest() local cancelEventconfig = models.CancelEventConfig.create() cancelEventconfig.cookieDomain = "cookieDomain" + cancelEventconfig.isCookieHttpOnly = true + cancelEventconfig.isCookieSecure = true cancelEventconfig.eventId = "eventId" cancelEventconfig.queueDomain = "queueDomain" cancelEventconfig.version = 1 @@ -1762,8 +1789,14 @@ local function KnownUserTest() "|RequestHttpHeader_XForwardedProto=xfp" .. "|RequestHttpHeader_Via=v" .. "|TargetUrl=targeturl" .. - "|CancelConfig=EventId:eventId&Version:1&QueueDomain:queueDomain&" .. - "CookieDomain:cookieDomain&ActionName:event1action" .. + "|CancelConfig=" + .. "EventId:eventId" + .. "&Version:1" + .. "&QueueDomain:queueDomain" + .. "&CookieDomain:cookieDomain" + .. "&IsCookieHttpOnly:true" + .. "&IsCookieSecure:true" + .. "&ActionName:event1action" .. "|OriginalUrl=OriginalURL" .. "|RequestHttpHeader_XForwardedHost=xfh" .. "|RequestHttpHeader_XForwardedFor=xff" .. diff --git a/SDK/Tests/UserInQueueServiceTest.lua b/SDK/Tests/UserInQueueServiceTest.lua index 3def269..952bf02 100644 --- a/SDK/Tests/UserInQueueServiceTest.lua +++ b/SDK/Tests/UserInQueueServiceTest.lua @@ -16,16 +16,29 @@ userInQueueStateCookieRepositoryMock.getState = function(eventId, cookieValidity end userInQueueStateCookieRepositoryMock.store = function( - eventId, queueId, fixedCookieValidityMinutes, cookieDomain, redirectType, secretKey) + eventId, queueId, fixedCookieValidityMinutes, cookieDomain, isCookieHttpOnly, + isCookieSecure, redirectType, secretKey) userInQueueStateCookieRepositoryMock.storeCall = { - eventId=eventId, queueId=queueId, fixedCookieValidityMinutes=fixedCookieValidityMinutes, - cookieDomain=cookieDomain, redirectType=redirectType, secretKey=secretKey + eventId=eventId, + queueId=queueId, + fixedCookieValidityMinutes=fixedCookieValidityMinutes, + cookieDomain=cookieDomain, + isCookieHttpOnly=isCookieHttpOnly, + isCookieSecure=isCookieSecure, + redirectType=redirectType, + secretKey=secretKey } end -userInQueueStateCookieRepositoryMock.cancelQueueCookie = function(eventId, cookieDomain) - userInQueueStateCookieRepositoryMock.cancelQueueCookieCall = { eventId=eventId, cookieDomain=cookieDomain} +userInQueueStateCookieRepositoryMock.cancelQueueCookie = function( + eventId, cookieDomain, isCookieHttpOnly, isCookieSecure) + userInQueueStateCookieRepositoryMock.cancelQueueCookieCall = { + eventId=eventId, + cookieDomain=cookieDomain, + isCookieHttpOnly=isCookieHttpOnly, + isCookieSecure=isCookieSecure + } end userInQueueStateCookieRepositoryMock.reset = function() @@ -34,7 +47,7 @@ userInQueueStateCookieRepositoryMock.reset = function() userInQueueStateCookieRepositoryMock.cancelQueueCookieCall = {} end -iHelpers.response.setCookie = function(_, _, _, _) +iHelpers.response.setCookie = function(_, _, _, _, _, _) end iHelpers.system.getConnectorName = function() return "mock-connector" @@ -482,10 +495,13 @@ local function UserInQueueServiceTest() eventConfig.actionName = "CancelAction" local url = "http://test.test.com?b=h" - local expectedUrl = "https://testDomain.com/cancel/testCustomer/e1/?c=testCustomer&e=e1" + local expectedUrl = "https://testDomain.com/cancel/testCustomer/e1/queueid?" + .. "c=testCustomer" + .. "&e=e1" .. "&ver=" .. userInQueueService.SDK_VERSION .. "&kupver=mock-connector" - .. "&cver=10&man=" .. eventConfig.actionName + .. "&cver=10" + .. "&man=" .. eventConfig.actionName .. "&r=" .. utils.urlEncode(url) local result = userInQueueService.validateCancelRequest(url, eventConfig, "testCustomer", key) diff --git a/SDK/Tests/UserInQueueStateCookieRepositoryTest.lua b/SDK/Tests/UserInQueueStateCookieRepositoryTest.lua index 7a80a84..ebad304 100644 --- a/SDK/Tests/UserInQueueStateCookieRepositoryTest.lua +++ b/SDK/Tests/UserInQueueStateCookieRepositoryTest.lua @@ -12,8 +12,8 @@ iHelpers.request.getUnescapedCookieValue = function(name) end end -iHelpers.response.setCookie = function(name, value, expire, domain) - mockCookies[name] = { name=name, value=value, expire=expire, domain=domain } +iHelpers.response.setCookie = function(name, value, expire, domain, isHttpOnly, isSecure) + mockCookies[name] = { name=name, value=value, expire=expire, domain=domain, isHttpOnly=isHttpOnly, isSecure=isSecure } end -- END Mocks @@ -29,10 +29,13 @@ local function UserInQueueStateCookieRepositoryTest() local eventId = "event1" local secretKey = "4e1deweb821-a82ew5-49da-acdqq0-5d3476f2068db" local cookieDomain = ".test.com" + local isCookieHttpOnly = true + local isCookieSecure = true local queueId = "queueId" local cookieValidity = 10 - userInQueueStateCookieRepository.store(eventId, queueId, nil, cookieDomain, "Queue", secretKey) + userInQueueStateCookieRepository.store( + eventId, queueId, nil, cookieDomain, isCookieHttpOnly, isCookieSecure, "Queue", secretKey) local state = userInQueueStateCookieRepository.getState(eventId, cookieValidity, secretKey, true) assert( state.isValid ) @@ -44,6 +47,8 @@ local function UserInQueueStateCookieRepositoryTest() assert( mockCookies[cookieKey] ~= nil ) assert( tonumber(mockCookies[cookieKey].expire) - os.time() - 24 * 60 * 60 < 100 ) assert( mockCookies[cookieKey].domain == cookieDomain ) + assert( mockCookies[cookieKey].isHttpOnly == isCookieHttpOnly ) + assert( mockCookies[cookieKey].isSecure == isCookieSecure ) end test_store_hasValidState_ExtendableCookie_CookieIsSaved() @@ -53,10 +58,13 @@ local function UserInQueueStateCookieRepositoryTest() local eventId = "event1" local secretKey = "4e1deweb821-a82ew5-49da-acdqq0-5d3476f2068db" local cookieDomain = ".test.com" + local isCookieHttpOnly = true + local isCookieSecure = true local queueId = "queueId" local cookieValidity = 3 - userInQueueStateCookieRepository.store(eventId, queueId, cookieValidity, cookieDomain, "Idle", secretKey) + userInQueueStateCookieRepository.store( + eventId, queueId, cookieValidity, cookieDomain, isCookieHttpOnly, isCookieSecure, "Idle", secretKey) local state = userInQueueStateCookieRepository.getState(eventId, cookieValidity, secretKey, true) assert( state.isValid ) @@ -69,6 +77,8 @@ local function UserInQueueStateCookieRepositoryTest() assert( mockCookies[cookieKey] ~= nil ) assert( tonumber(mockCookies[cookieKey].expire) - os.time() - 24 * 60 * 60 < 100 ) assert( mockCookies[cookieKey].domain == cookieDomain ) + assert( mockCookies[cookieKey].isHttpOnly == isCookieHttpOnly ) + assert( mockCookies[cookieKey].isSecure == isCookieSecure ) end test_store_hasValidState_nonExtendableCookie_CookieIsSaved() @@ -78,10 +88,13 @@ local function UserInQueueStateCookieRepositoryTest() local eventId = "event1" local secretKey = "4e1deweb821-a82ew5-49da-acdqq0-5d3476f2068db" local cookieDomain = ".test.com" + local isCookieHttpOnly = false + local isCookieSecure = false local queueId = "queueId" local cookieValidity = 10 - userInQueueStateCookieRepository.store(eventId, queueId, 3, cookieDomain, "Idle", secretKey) + userInQueueStateCookieRepository.store( + eventId, queueId, 3, cookieDomain, isCookieHttpOnly, isCookieSecure, "Idle", secretKey) local state = userInQueueStateCookieRepository.getState(eventId, cookieValidity, secretKey, true) assert( state.isValid ) @@ -100,10 +113,13 @@ local function UserInQueueStateCookieRepositoryTest() local eventId = "event1" local secretKey = "4e1deweb821-a82ew5-49da-acdqq0-5d3476f2068db" local cookieDomain = ".test.com" + local isCookieHttpOnly = false + local isCookieSecure = false local queueId = "queueId" local cookieValidity = 10 - userInQueueStateCookieRepository.store(eventId, queueId, 3, cookieDomain, "Idle", secretKey) + userInQueueStateCookieRepository.store( + eventId, queueId, 3, cookieDomain, isCookieHttpOnly, isCookieSecure, "Idle", secretKey) local state = userInQueueStateCookieRepository.getState(eventId, cookieValidity, secretKey, true) assert( state.isValid ) @@ -123,10 +139,13 @@ local function UserInQueueStateCookieRepositoryTest() local eventId = "event1" local secretKey = "4e1deweb821-a82ew5-49da-acdqq0-5d3476f2068db" local cookieDomain = ".test.com" + local isCookieHttpOnly = false + local isCookieSecure = false local queueId = "queueId" local cookieValidity = -1 - userInQueueStateCookieRepository.store(eventId, queueId, nil, cookieDomain, "Idle", secretKey) + userInQueueStateCookieRepository.store( + eventId, queueId, nil, cookieDomain, isCookieHttpOnly, isCookieSecure, "Idle", secretKey) local state = userInQueueStateCookieRepository.getState(eventId, cookieValidity, secretKey, true) assert( state.isValid == false ) end @@ -138,10 +157,13 @@ local function UserInQueueStateCookieRepositoryTest() local eventId = "event1" local secretKey = "4e1deweb821-a82ew5-49da-acdqq0-5d3476f2068db" local cookieDomain = ".test.com" + local isCookieHttpOnly = false + local isCookieSecure = false local queueId = "queueId" local cookieValidity = 10 - userInQueueStateCookieRepository.store(eventId, queueId, nil, cookieDomain, "Queue", secretKey) + userInQueueStateCookieRepository.store( + eventId, queueId, nil, cookieDomain, isCookieHttpOnly, isCookieSecure, "Queue", secretKey) local state = userInQueueStateCookieRepository.getState(eventId, cookieValidity, secretKey, true) assert( state.isValid ) @@ -168,10 +190,13 @@ local function UserInQueueStateCookieRepositoryTest() local eventId = "event1" local secretKey = "4e1deweb821-a82ew5-49da-acdqq0-5d3476f2068db" local cookieDomain = ".test.com" + local isCookieHttpOnly = false + local isCookieSecure = false local queueId = "queueId" local cookieValidity = 10 - userInQueueStateCookieRepository.store(eventId, queueId, 20, cookieDomain, "Queue", secretKey) + userInQueueStateCookieRepository.store( + eventId, queueId, 20, cookieDomain, isCookieHttpOnly, isCookieSecure, "Queue", secretKey) local state = userInQueueStateCookieRepository.getState(eventId, cookieValidity, secretKey, true) assert( state.isValid ) @@ -188,10 +213,13 @@ local function UserInQueueStateCookieRepositoryTest() local eventId = "event1" local secretKey = "4e1deweb821-a82ew5-49da-acdqq0-5d3476f2068db" local cookieDomain = ".test.com" + local isCookieHttpOnly = false + local isCookieSecure = false local queueId = "queueId" local cookieValidity = 20 - userInQueueStateCookieRepository.store(eventId, queueId, 20, cookieDomain, "Queue", secretKey) + userInQueueStateCookieRepository.store( + eventId, queueId, 20, cookieDomain, isCookieHttpOnly, isCookieSecure, "Queue", secretKey) local state = userInQueueStateCookieRepository.getState(eventId, cookieValidity, secretKey, true) assert( state.isValid ) @@ -213,11 +241,15 @@ local function UserInQueueStateCookieRepositoryTest() local eventId = "event1" local secretKey = "4e1deweb821-a82ew5-49da-acdqq0-5d3476f2068db" local cookieDomain = ".test.com" + local isCookieHttpOnly = true + local isCookieSecure = true local queueId = "queueId" local cookieKey = userInQueueStateCookieRepository.getCookieKey(eventId) - userInQueueStateCookieRepository.store(eventId, queueId, nil, cookieDomain, "Queue", secretKey) - userInQueueStateCookieRepository.reissueQueueCookie(eventId, 12, cookieDomain, secretKey) + userInQueueStateCookieRepository.store( + eventId, queueId, nil, cookieDomain, isCookieHttpOnly, isCookieSecure, "Queue", secretKey) + userInQueueStateCookieRepository.reissueQueueCookie( + eventId, 12, cookieDomain, isCookieHttpOnly, isCookieSecure, secretKey) local state = userInQueueStateCookieRepository.getState(eventId, 5, secretKey, true) assert( state.isValid ) @@ -225,6 +257,8 @@ local function UserInQueueStateCookieRepositoryTest() assert( state:isStateExtendable() ) assert( tonumber(mockCookies[cookieKey].expire) - os.time() - 24 * 60 * 60 < 100 ) assert( mockCookies[cookieKey].domain == cookieDomain ) + assert( mockCookies[cookieKey].isHttpOnly == isCookieHttpOnly ) + assert( mockCookies[cookieKey].isSecure == isCookieSecure ) end test_extendQueueCookie_cookieExist() @@ -234,13 +268,16 @@ local function UserInQueueStateCookieRepositoryTest() local eventId = "event1" local secretKey = "4e1deweb821-a82ew5-49da-acdqq0-5d3476f2068db" local cookieDomain = ".test.com" - local queueId = "queueId" + local isCookieHttpOnly = false + local isCookieSecure = false - userInQueueStateCookieRepository.store("event2", queueId, 20, cookieDomain, "Queue", secretKey) - userInQueueStateCookieRepository.reissueQueueCookie(eventId, 12, cookieDomain, secretKey) + userInQueueStateCookieRepository.reissueQueueCookie( + eventId, 12, cookieDomain, isCookieHttpOnly, isCookieSecure, secretKey) - local cookieKey = userInQueueStateCookieRepository.getCookieKey("event2") - assert( mockCookies[cookieKey] ~= nil ) + local state = userInQueueStateCookieRepository.getState(eventId, 12, secretKey) + assert(state.isValid == false) + assert(state:isStateExtendable() == false) + assert(state.queueId == nil) end test_extendQueueCookie_cookieDoesNotExist() diff --git a/SDK/UserInQueueService.lua b/SDK/UserInQueueService.lua index 0ed80e9..769fe82 100644 --- a/SDK/UserInQueueService.lua +++ b/SDK/UserInQueueService.lua @@ -5,7 +5,7 @@ local utils = require("Utils") local userInQueueStateCookieRepository = require("UserInQueueStateCookieRepository") local svc = { - SDK_VERSION = "v3-lua-" .. "3.6.5", + SDK_VERSION = "v3-lua-" .. "3.7.0", TokenValidationResult = { create = function(isValid, errorCode) local model = { @@ -90,8 +90,11 @@ local function getValidTokenResult(config, queueParams, secretKey) queueParams.queueId, queueParams.cookieValidityMinutes, utils.toString(config.cookieDomain), + config.isCookieHttpOnly, + config.isCoookieSecure, queueParams.redirectType, secretKey) + return models.RequestValidationResult.create( models.ActionTypes.QueueAction, config.eventId, queueParams.queueId, nil, queueParams.redirectType, config.actionName) @@ -126,6 +129,8 @@ svc.validateQueueRequest = function(targetUrl, queueitToken, config, customerId, state.queueId, nil, utils.toString(config.cookieDomain), + config.isCookieHttpOnly, + config.isCookieSecure, state.redirectType, secretKey) end @@ -154,7 +159,8 @@ svc.validateQueueRequest = function(targetUrl, queueitToken, config, customerId, end if (state.isFound and not isTokenValid) then - userInQueueStateCookieRepository.cancelQueueCookie(config.eventId, config.cookieDomain); + userInQueueStateCookieRepository.cancelQueueCookie( + config.eventId, config.cookieDomain, config.isCookieHttpOnly, config.isCookieSecure); end return requestValidationResult; @@ -163,8 +169,8 @@ end svc.validateCancelRequest = function(targetUrl, cancelConfig, customerId, secretKey) -- we do not care how long cookie is valid while canceling cookie local state = userInQueueStateCookieRepository.getState(cancelConfig.eventId, -1, secretKey, false) + if (state.isValid) then - local uriPath = "cancel/" .. customerId .. "/" .. cancelConfig.eventId .. "/" userInQueueStateCookieRepository.cancelQueueCookie(cancelConfig.eventId, cancelConfig.cookieDomain) local rParam = "" @@ -173,6 +179,12 @@ svc.validateCancelRequest = function(targetUrl, cancelConfig, customerId, secret end local query = getQueryString( customerId, cancelConfig.eventId, cancelConfig.version, cancelConfig.actionName, nil, nil) .. rParam + + local uriPath = "cancel/" .. customerId .. "/" .. cancelConfig.eventId + if (state.queueId and state.queueId ~= "") then + uriPath = uriPath .. "/" .. state.queueId + end + local redirectUrl = generateRedirectUrl(cancelConfig.queueDomain, uriPath, query) return models.RequestValidationResult.create( @@ -184,8 +196,10 @@ svc.validateCancelRequest = function(targetUrl, cancelConfig, customerId, secret end end -svc.extendQueueCookie = function(eventId, cookieValidityMinutes, cookieDomain, secretKey) - userInQueueStateCookieRepository.reissueQueueCookie(eventId, cookieValidityMinutes, cookieDomain, secretKey) +svc.extendQueueCookie = function( + eventId, cookieValidityMinutes, cookieDomain, isCookieHttpOnly, isCookieSecure, secretKey) + userInQueueStateCookieRepository.reissueQueueCookie( + eventId, cookieValidityMinutes, cookieDomain, isCookieHttpOnly, isCookieSecure, secretKey) end svc.getIgnoreActionResult = function(actionName) diff --git a/SDK/UserInQueueStateCookieRepository.lua b/SDK/UserInQueueStateCookieRepository.lua index 4dcbe91..cb37df7 100644 --- a/SDK/UserInQueueStateCookieRepository.lua +++ b/SDK/UserInQueueStateCookieRepository.lua @@ -113,9 +113,9 @@ repo.getCookieKey = function(eventId) return "QueueITAccepted-SDFrts345E-V3_" .. eventId end -repo.cancelQueueCookie = function(eventId, cookieDomain) +repo.cancelQueueCookie = function(eventId, cookieDomain, isCookieHttpOnly, isCookieSecure) local cookieKey = repo.getCookieKey(eventId) - iHelpers.response.setCookie(cookieKey, "deleted", 1, cookieDomain) + iHelpers.response.setCookie(cookieKey, "deleted", 1, cookieDomain, isCookieHttpOnly, isCookieSecure) end repo.getState = function(eventId, cookieValidityMinutes, secretKey, validateTime) @@ -151,7 +151,8 @@ repo.getState = function(eventId, cookieValidityMinutes, secretKey, validateTime return repo.StateInfo.create(true, false, nil, nil, nil) end -repo.reissueQueueCookie = function(eventId, cookieValidityMinutes, cookieDomain, secretKey) +repo.reissueQueueCookie = function( + eventId, cookieValidityMinutes, cookieDomain, isCookieHttpOnly, isCookieSecure, secretKey) local cookieKey = repo.getCookieKey(eventId) if (iHelpers.request.getUnescapedCookieValue(cookieKey) == nil) then return @@ -173,14 +174,17 @@ repo.reissueQueueCookie = function(eventId, cookieValidityMinutes, cookieDomain, cookieNameValueMap["RedirectType"], secretKey) - iHelpers.response.setCookie(cookieKey, cookieValue, os.time() + (24 * 60 * 60), cookieDomain) + iHelpers.response.setCookie( + cookieKey, cookieValue, os.time() + (24 * 60 * 60), cookieDomain, isCookieHttpOnly, isCookieSecure) end -repo.store = function(eventId, queueId, fixedCookieValidityMinutes, cookieDomain, redirectType, secretKey) +repo.store = function( + eventId, queueId, fixedCookieValidityMinutes, cookieDomain, isCookieHttpOnly, isCookieSecure, redirectType, secretKey) local cookieKey = repo.getCookieKey(eventId) local cookieValue = createCookieValue( eventId, queueId, utils.toString(fixedCookieValidityMinutes), redirectType, secretKey) - iHelpers.response.setCookie(cookieKey, cookieValue, os.time() + (24 * 60 * 60), cookieDomain) + iHelpers.response.setCookie( + cookieKey, cookieValue, os.time() + (24 * 60 * 60), cookieDomain, isCookieHttpOnly, isCookieSecure) end return repo