Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Make Sipi handle multiple KnoraAuthentication* cookies correctly (DEV-2271) #2713

Merged
merged 3 commits into from
Jun 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 16 additions & 5 deletions sipi/scripts/authentication.lua
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,22 @@ end
-- @return jwt token or nil if the cookie is missing or invalid.
function _get_jwt_token_from_cookie()
log("authentication: checking for jwt token in cookie header", server.loglevel.LOG_DEBUG)
local cookie_header_value = _get_cookie_header()
if cookie_header_value == nil then
local cookie_name = env_knora_authentication_cookie_name()
local cookies = _get_cookie_header()
if cookies == nil then
log("authentication: no cookie header found", server.loglevel.LOG_DEBUG)
return nil
end
local cookie_name = env_knora_authentication_cookie_name()
local jwt_token = str_strip_prefix(cookie_header_value, cookie_name .. "=")
return jwt_token

cookie_name = cookie_name:lower()
for entry in cookies:gmatch("([^,]+)") do
local key, value = entry:match("([^=]+)=(.+)")
if key and value then
if key:lower() == cookie_name then
return value
end
end
end
log("authentication: cookie header does not contain " .. cookie_name, server.loglevel.LOG_DEBUG)
return nil
end
2 changes: 1 addition & 1 deletion sipi/scripts/env.lua
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ end
-- Returns the name of the cookie used for authentication.
function env_knora_authentication_cookie_name()
local host_port_base32 = basexx.to_base32Custom(env_dsp_api_host_port())
return "KnoraAuthentication" .. host_port_base32
return string.lower("KnoraAuthentication" .. host_port_base32)
end
28 changes: 24 additions & 4 deletions sipi/scripts/strings.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,26 @@

--- Utility functions for working with strings.

-- Break a string up at occurrences of the first single character.
-- In Lua, there is no built-in function for splitting a string at a specific character.
-- http://lua-users.org/wiki/SplitJoin
-- @param str the string to split.
-- @param separator the separator character.
-- @return a table containing:
--- * the split string or
--- * the original string if the separator was not found.
function str_splitString(str, separator)
local result = {}
local pattern = string.format("([^%s]+)", separator)

for match in str:gmatch(pattern) do
table.insert(result, match)
end

return result
end


--- Checks if a string starts with a specific prefix.
-- In Lua, there is no built-in function for checking if a string starts with a specific prefix.
-- This function implements this functionality.
Expand Down Expand Up @@ -33,18 +53,18 @@ end
-- @param tbl the table to transform.
-- @return a string representation of the table.
function tableToString(tbl)
local str = "{"
local str = "{\n"
local isFirst = true

for key, value in pairs(tbl) do
if not isFirst then
str = str .. ", "
str = str .. "\n"
end

if type(value) == "table" then
str = str .. key .. "=" .. tableToString(value)
str = str .. key .. " = " .. tableToString(value) .. "\n"
else
str = str .. key .. "=" .. tostring(value)
str = str .. key .. " = " .. tostring(value) .. "\n"
end

isFirst = false
Expand Down
53 changes: 53 additions & 0 deletions webapi/src/it/scala/org/knora/sipi/SipiIT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor
import com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo
import com.github.tomakehurst.wiremock.core.WireMockConfiguration.options
import com.github.tomakehurst.wiremock.matching.RequestPatternBuilder
import org.apache.commons.codec.binary.Base32
import spray.json.JsString
import zio._
import zio.http._
import zio.http.model.Status
Expand All @@ -26,9 +28,17 @@ import scala.util.Success
import scala.util.Try

import org.knora.sipi.MockDspApiServer.verify._
import org.knora.webapi.messages.StringFormatter
import org.knora.webapi.messages.admin.responder.KnoraResponseADM
import org.knora.webapi.messages.admin.responder.sipimessages._
import org.knora.webapi.routing.JwtService
import org.knora.webapi.routing.JwtServiceLive
import org.knora.webapi.sharedtestdata.SharedTestDataADM
import org.knora.webapi.testcontainers.SipiTestContainer
import com.github.tomakehurst.wiremock.client.WireMock.equalTo
import com.github.tomakehurst.wiremock.matching.RequestPatternBuilder.newRequestPattern

import org.knora.sipi.SipiIT.fileEndpointSuite

object SipiIT extends ZIOSpecDefault {

Expand All @@ -43,6 +53,48 @@ object SipiIT extends ZIOSpecDefault {
private def getWithoutAuthorization(path: String) =
SipiTestContainer.resolveUrl(path).map(Request.get).flatMap(Client.request(_))

private val jwt =
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiIwLjAuMC4wOjMzMzMiLCJzdWIiOiJodHRwOi8vcmRmaC5jaC91c2Vycy9yb290IiwiYXVkIjpbIktub3JhIiwiU2lwaSJdLCJleHAiOjE2ODk3NTY1MzksImlhdCI6MTY4NzE2NDUzOSwianRpIjoiSG9SSFg5V1lSZHV6VnVmTXZFT1c4USJ9.tlTqr1NGjsOqnMRxjDW1TokDjGAPO5nvG-pcbn09Hrw"

private val cookiesSuite =
suite("Given a Request contains multiple auth cookies")(
test(
"When getting an existing file, " +
"then Sipi should extract the correct cookie, send it to dsp-api " +
"and responds with Ok"
) {
for {
_ <- copyTestFilesToSipi
mockServer <- MockDspApiServer.resetAndStubGetResponse(
s"/admin/files/$prefix/$imageTestfile",
200,
SipiFileInfoGetResponseADM(permissionCode = 2, restrictedViewSettings = None)
)
response <-
SipiTestContainer
.resolveUrl(s"/$prefix/$imageTestfile/file")
.map { url =>
Request
.get(url)
.withCookie(s"KnoraAuthenticationGAXDALRQFYYDUMZTGMZQ9999aSecondCookie=anotherValueShouldBeIgnored")
.withCookie(s"KnoraAuthenticationGAXDALRQFYYDUMZTGMZQ9999=$jwt")
}
.flatMap(Client.request(_))
requestToDspApiContainsJwt <- ZIO
.attempt(
mockServer.verify(
// Number of times the request should be received (in this case, only once)
1,
// The expected request with header and value
newRequestPattern().withHeader("Authorization", equalTo(s"Bearer $jwt"))
)
)
.logError
.fold(err => false, succ => true)
} yield assertTrue(response.status == Status.Ok, requestToDspApiContainsJwt)
}
)

private val knoraJsonEndpointSuite =
suite("Endpoint /{prefix}/{identifier}/knora.json")(
suite("Given the user is unauthorized")(
Expand Down Expand Up @@ -208,6 +260,7 @@ object SipiIT extends ZIOSpecDefault {

override def spec: Spec[TestEnvironment with Scope, Any] =
suite("Sipi integration tests with mocked dsp-api")(
cookiesSuite,
knoraJsonEndpointSuite,
fileEndpointSuite,
iiifEndpoint,
Expand Down