diff --git a/integration/src/test/scala/org/knora/sipi/SipiClientTestDelegator.scala b/integration/src/test/scala/org/knora/sipi/SipiClientTestDelegator.scala index 64f302b69b..32cd860eba 100644 --- a/integration/src/test/scala/org/knora/sipi/SipiClientTestDelegator.scala +++ b/integration/src/test/scala/org/knora/sipi/SipiClientTestDelegator.scala @@ -17,12 +17,12 @@ import org.knora.webapi.messages.store.sipimessages.MoveTemporaryFileToPermanent import org.knora.webapi.messages.store.sipimessages.SipiGetTextFileRequest import org.knora.webapi.messages.store.sipimessages.SipiGetTextFileResponse import org.knora.webapi.messages.v2.responder.SuccessResponseV2 -import org.knora.webapi.routing.JwtService import org.knora.webapi.slice.admin.api.model.MaintenanceRequests.AssetId import org.knora.webapi.slice.admin.domain.model.KnoraProject import org.knora.webapi.slice.admin.domain.model.User import org.knora.webapi.slice.admin.domain.service.Asset import org.knora.webapi.slice.admin.domain.service.DspIngestClient +import org.knora.webapi.slice.infrastructure.JwtService import org.knora.webapi.store.iiif.api.FileMetadataSipiResponse import org.knora.webapi.store.iiif.api.SipiService import org.knora.webapi.store.iiif.impl.SipiServiceLive diff --git a/integration/src/test/scala/org/knora/sipi/SipiIT.scala b/integration/src/test/scala/org/knora/sipi/SipiIT.scala index f5dcf815c9..d661fc90e6 100644 --- a/integration/src/test/scala/org/knora/sipi/SipiIT.scala +++ b/integration/src/test/scala/org/knora/sipi/SipiIT.scala @@ -30,14 +30,14 @@ import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectIdentif import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectIdentifierADM.ShortnameIdentifier import org.knora.webapi.messages.util.KnoraSystemInstances.Users.SystemUser import org.knora.webapi.routing.InvalidTokenCache -import org.knora.webapi.routing.JwtService -import org.knora.webapi.routing.JwtServiceLive import org.knora.webapi.slice.admin.domain.model.KnoraProject import org.knora.webapi.slice.admin.domain.model.KnoraProject.ProjectIri import org.knora.webapi.slice.admin.domain.service.KnoraProjectRepo import org.knora.webapi.slice.admin.domain.service.KnoraProjectService import org.knora.webapi.slice.common.repo.service.CrudRepository import org.knora.webapi.slice.infrastructure.CacheManager +import org.knora.webapi.slice.infrastructure.JwtService +import org.knora.webapi.slice.infrastructure.JwtServiceLive import org.knora.webapi.testcontainers.SharedVolumes import org.knora.webapi.testcontainers.SipiTestContainer diff --git a/integration/src/test/scala/org/knora/webapi/CoreSpec.scala b/integration/src/test/scala/org/knora/webapi/CoreSpec.scala index 3cc7e3715e..21cddab470 100644 --- a/integration/src/test/scala/org/knora/webapi/CoreSpec.scala +++ b/integration/src/test/scala/org/knora/webapi/CoreSpec.scala @@ -25,9 +25,9 @@ import org.knora.webapi.core.AppServer import org.knora.webapi.core.LayersTest.DefaultTestEnvironmentWithoutSipi import org.knora.webapi.core.TestStartupUtils import org.knora.webapi.messages.store.triplestoremessages.RdfDataObject -import org.knora.webapi.routing.JwtService import org.knora.webapi.routing.UnsafeZioRun import org.knora.webapi.slice.admin.domain.model.User +import org.knora.webapi.slice.infrastructure.JwtService import org.knora.webapi.util.LogAspect abstract class CoreSpec diff --git a/integration/src/test/scala/org/knora/webapi/core/LayersTest.scala b/integration/src/test/scala/org/knora/webapi/core/LayersTest.scala index 7ddd8e5b61..1d6292fdb8 100644 --- a/integration/src/test/scala/org/knora/webapi/core/LayersTest.scala +++ b/integration/src/test/scala/org/knora/webapi/core/LayersTest.scala @@ -43,6 +43,8 @@ import org.knora.webapi.slice.admin.domain.service._ import org.knora.webapi.slice.common.api._ import org.knora.webapi.slice.common.repo.service.PredicateObjectMapper import org.knora.webapi.slice.infrastructure.CacheManager +import org.knora.webapi.slice.infrastructure.JwtService +import org.knora.webapi.slice.infrastructure.JwtServiceLive import org.knora.webapi.slice.infrastructure.api.ManagementEndpoints import org.knora.webapi.slice.infrastructure.api.ManagementRoutes import org.knora.webapi.slice.ontology.api.service.RestCardinalityService diff --git a/webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala b/webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala index fd47509789..4345bc4aa7 100644 --- a/webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala +++ b/webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala @@ -41,6 +41,8 @@ import org.knora.webapi.slice.admin.domain.service._ import org.knora.webapi.slice.common.api._ import org.knora.webapi.slice.common.repo.service.PredicateObjectMapper import org.knora.webapi.slice.infrastructure.CacheManager +import org.knora.webapi.slice.infrastructure.JwtService +import org.knora.webapi.slice.infrastructure.JwtServiceLive import org.knora.webapi.slice.infrastructure.api.ManagementEndpoints import org.knora.webapi.slice.infrastructure.api.ManagementRoutes import org.knora.webapi.slice.ontology.api.service.RestCardinalityService diff --git a/webapi/src/main/scala/org/knora/webapi/routing/Authenticator.scala b/webapi/src/main/scala/org/knora/webapi/routing/Authenticator.scala index 5d5705b96f..818dc96a7c 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/Authenticator.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/Authenticator.scala @@ -35,6 +35,7 @@ import org.knora.webapi.slice.admin.domain.model.Username import org.knora.webapi.slice.admin.domain.service.KnoraUserRepo import org.knora.webapi.slice.admin.domain.service.PasswordService import org.knora.webapi.slice.admin.domain.service.UserService +import org.knora.webapi.slice.infrastructure.JwtService /** * This trait is used in routes that need authentication support. It provides methods that use the [[RequestContext]] diff --git a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/service/DspIngestClient.scala b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/service/DspIngestClient.scala index 64d988ef65..6be33f6845 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/service/DspIngestClient.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/service/DspIngestClient.scala @@ -33,9 +33,9 @@ import java.io.IOException import scala.concurrent.duration.DurationInt import org.knora.webapi.config.DspIngestConfig -import org.knora.webapi.routing.JwtService import org.knora.webapi.slice.admin.api.model.MaintenanceRequests.AssetId import org.knora.webapi.slice.admin.domain.model.KnoraProject.Shortcode +import org.knora.webapi.slice.infrastructure.JwtService trait DspIngestClient { def exportProject(shortcode: Shortcode): ZIO[Scope, Throwable, Path] diff --git a/webapi/src/main/scala/org/knora/webapi/slice/infrastructure/JwtService.scala b/webapi/src/main/scala/org/knora/webapi/slice/infrastructure/JwtService.scala index dd66ed0589..defcd3bde5 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/infrastructure/JwtService.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/infrastructure/JwtService.scala @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package org.knora.webapi.routing +package org.knora.webapi.slice.infrastructure import com.typesafe.scalalogging.Logger import org.slf4j.LoggerFactory @@ -31,6 +31,7 @@ import org.knora.webapi.IRI import org.knora.webapi.config.DspIngestConfig import org.knora.webapi.config.JwtConfig import org.knora.webapi.messages.admin.responder.permissionsmessages.PermissionADM +import org.knora.webapi.routing.InvalidTokenCache import org.knora.webapi.slice.admin.domain.model.KnoraProject.ProjectIri import org.knora.webapi.slice.admin.domain.model.KnoraProject.Shortcode import org.knora.webapi.slice.admin.domain.model.Permission.Administrative @@ -89,13 +90,11 @@ final case class JwtServiceLive( private val algorithm: JwtAlgorithm = JwtAlgorithm.HS256 private val header: String = """{"typ":"JWT","alg":"HS256"}""" private val logger = Logger(LoggerFactory.getLogger(this.getClass)) + private val audience = Set("Knora", "Sipi", dspIngestConfig.audience) - override def createJwt(user: User, content: Map[String, JsValue] = Map.empty): UIO[Jwt] = { - val audience = if (user.isSystemAdmin) { Set("Knora", "Sipi", dspIngestConfig.audience) } - else { Set("Knora", "Sipi") } + override def createJwt(user: User, content: Map[String, JsValue] = Map.empty): UIO[Jwt] = calculateScope(user) .flatMap(scope => createJwtToken(jwtConfig.issuerAsString(), user.id, audience, scope, Some(JsObject(content)))) - } private def calculateScope(user: User) = if (user.isSystemAdmin || user.isSystemUser) { ZIO.succeed(Scope.admin) } diff --git a/webapi/src/main/scala/org/knora/webapi/store/iiif/impl/SipiServiceLive.scala b/webapi/src/main/scala/org/knora/webapi/store/iiif/impl/SipiServiceLive.scala index 5fd8a45b64..158908286a 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/iiif/impl/SipiServiceLive.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/iiif/impl/SipiServiceLive.scala @@ -37,13 +37,13 @@ import org.knora.webapi.config.Sipi import org.knora.webapi.messages.store.sipimessages._ import org.knora.webapi.messages.util.KnoraSystemInstances import org.knora.webapi.messages.v2.responder.SuccessResponseV2 -import org.knora.webapi.routing.Jwt -import org.knora.webapi.routing.JwtService import org.knora.webapi.slice.admin.api.model.MaintenanceRequests.AssetId import org.knora.webapi.slice.admin.domain.model.KnoraProject.Shortcode import org.knora.webapi.slice.admin.domain.model.User import org.knora.webapi.slice.admin.domain.service.Asset import org.knora.webapi.slice.admin.domain.service.DspIngestClient +import org.knora.webapi.slice.infrastructure.Jwt +import org.knora.webapi.slice.infrastructure.JwtService import org.knora.webapi.store.iiif.api.FileMetadataSipiResponse import org.knora.webapi.store.iiif.api.SipiService import org.knora.webapi.store.iiif.errors.SipiException diff --git a/webapi/src/test/scala/org/knora/webapi/slice/admin/domain/service/DspIngestClientLiveSpec.scala b/webapi/src/test/scala/org/knora/webapi/slice/admin/domain/service/DspIngestClientLiveSpec.scala index 97467116a3..ecb4d268a4 100644 --- a/webapi/src/test/scala/org/knora/webapi/slice/admin/domain/service/DspIngestClientLiveSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/slice/admin/domain/service/DspIngestClientLiveSpec.scala @@ -42,14 +42,14 @@ import zio.test.assertTrue import org.knora.webapi.IRI import org.knora.webapi.config.DspIngestConfig -import org.knora.webapi.routing.Jwt -import org.knora.webapi.routing.JwtService import org.knora.webapi.slice.admin.api.model.MaintenanceRequests.AssetId import org.knora.webapi.slice.admin.domain.model.KnoraProject.Shortcode import org.knora.webapi.slice.admin.domain.model.User import org.knora.webapi.slice.admin.domain.service.DspIngestClientLiveSpecLayers.dspIngestConfigLayer import org.knora.webapi.slice.admin.domain.service.DspIngestClientLiveSpecLayers.jwtServiceMockLayer import org.knora.webapi.slice.admin.domain.service.HttpMockServer.TestPort +import org.knora.webapi.slice.infrastructure.Jwt +import org.knora.webapi.slice.infrastructure.JwtService object DspIngestClientLiveSpec extends ZIOSpecDefault { diff --git a/webapi/src/test/scala/org/knora/webapi/slice/infrastructure/JwtServiceLiveSpec.scala b/webapi/src/test/scala/org/knora/webapi/slice/infrastructure/JwtServiceLiveSpec.scala index d2864c9044..ffd21e3ab0 100644 --- a/webapi/src/test/scala/org/knora/webapi/slice/infrastructure/JwtServiceLiveSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/slice/infrastructure/JwtServiceLiveSpec.scala @@ -37,8 +37,6 @@ import org.knora.webapi.messages.admin.responder.permissionsmessages.PermissionA import org.knora.webapi.messages.admin.responder.permissionsmessages.PermissionsDataADM import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2 import org.knora.webapi.routing.InvalidTokenCache -import org.knora.webapi.routing.JwtService -import org.knora.webapi.routing.JwtServiceLive import org.knora.webapi.slice.admin.domain.model.KnoraProject import org.knora.webapi.slice.admin.domain.model.KnoraProject.Description import org.knora.webapi.slice.admin.domain.model.KnoraProject.ProjectIri @@ -104,13 +102,18 @@ object JwtServiceLiveSpec extends ZIOSpecDefault { Map(project1.id.value -> Set(PermissionADM.from(Administrative.ProjectAdminAll))), ) - private val issuerStr = "https://dsp-api" + private val dspIngestAudience = "https://dsp-ingest/audience" + private val dspApiIssuer = "https://dsp-api" private val dspIngestConfigLayer = - ZLayer.succeed(DspIngestConfig("https://dps-ingest", "https://dsp-ingest/audience")) + ZLayer.succeed(DspIngestConfig("https://dps-ingest", dspIngestAudience)) - private val jwtConfigLayer = - ZLayer.succeed(JwtConfig("n76lPIwWKNeTodFfZPPPYFn7V24R14aE63A+XgS8MMA=", Duration.ofSeconds(10), Some(issuerStr))) + private val jwtConfigLayer = ZLayer.succeed( + JwtConfig("n76lPIwWKNeTodFfZPPPYFn7V24R14aE63A+XgS8MMA=", Duration.ofSeconds(10), Some(dspApiIssuer)), + ) + + private val expectedAudience: Set[String] = + Set("Knora", "Sipi", dspIngestAudience) private def decodeToken(token: String): ZIO[JwtConfig, Nothing, (JwtHeader, JwtClaim, String)] = ZIO.serviceWithZIO[JwtConfig] { jwtConfig => @@ -135,7 +138,7 @@ object JwtServiceLiveSpec extends ZIOSpecDefault { scope <- getScopeClaimValue(token.jwtString) } yield assertTrue( userIri.contains(user.id), - audience == Set("Knora", "Sipi"), + audience == expectedAudience, scope == "", ) }, @@ -177,8 +180,8 @@ object JwtServiceLiveSpec extends ZIOSpecDefault { audience <- getClaim(token.jwtString, _.audience.getOrElse(Set.empty)) scope <- getScopeClaimValue(token.jwtString) } yield assertTrue( - userIri.contains(issuerStr), - audience.contains("https://dsp-ingest/audience"), + userIri.contains(dspApiIssuer), + audience == Set(dspIngestAudience), scope == "admin", ) }, @@ -190,9 +193,9 @@ object JwtServiceLiveSpec extends ZIOSpecDefault { }, test("fail to validate an invalid token") { def createClaim( - issuer: Option[String] = Some(issuerStr), + issuer: Option[String] = Some(dspApiIssuer), subject: Option[String] = Some(UserIri.makeNew.value), - audience: Option[Set[String]] = Some(Set("Knora", "Sipi")), + audience: Option[Set[String]] = Some(expectedAudience), issuedAt: Option[Long] = Some(Instant.now.getEpochSecond), expiration: Option[Long] = Some(Instant.now.plusSeconds(10).getEpochSecond), jwtId: Option[String] = Some(UuidUtil.base64Encode(UUID.randomUUID())),