From 2bc85c4ca526ad2ff4e2adc814b5cdd1220154de Mon Sep 17 00:00:00 2001 From: Marja Kari Date: Wed, 27 Nov 2024 13:31:25 +0200 Subject: [PATCH] =?UTF-8?q?OK-751=20k=C3=A4ytt=C3=A4j=C3=A4n=20oikeudet=20?= =?UTF-8?q?sessioon=20WIP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../configuration/SecurityConfiguration.scala | 1 - .../raportointi/integration/OnrService.scala | 1 - .../integration/OrganisaatioCache.scala | 1 + .../integration/OrganisaatioService.scala | 1 + .../resource/HenkiloResource.scala | 7 +- .../resource/LahetysResource.scala | 76 +++++++++++------- .../security/SecurityOperaatiot.scala | 78 ++++++++++++++++--- .../raportointi/SecurityOperaatiotTest.scala | 12 ++- .../src/app/lib/data.ts | 6 ++ 9 files changed, 138 insertions(+), 45 deletions(-) diff --git a/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/configuration/SecurityConfiguration.scala b/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/configuration/SecurityConfiguration.scala index 08565eea..d708b322 100644 --- a/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/configuration/SecurityConfiguration.scala +++ b/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/configuration/SecurityConfiguration.scala @@ -1,7 +1,6 @@ package fi.oph.viestinvalitys.raportointi.configuration import com.zaxxer.hikari.HikariDataSource -import fi.oph.viestinvalitys.raportointi.App import fi.oph.viestinvalitys.raportointi.resource.RaportointiAPIConstants import fi.oph.viestinvalitys.util.DbUtil import fi.vm.sade.javautils.kayttooikeusclient.OphUserDetailsServiceImpl diff --git a/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/integration/OnrService.scala b/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/integration/OnrService.scala index 5a29dd94..7049f161 100644 --- a/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/integration/OnrService.scala +++ b/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/integration/OnrService.scala @@ -4,7 +4,6 @@ import fi.oph.viestinvalitys.util.ConfigurationUtil import fi.vm.sade.javautils.nio.cas.{CasClient, CasClientBuilder} import org.asynchttpclient.RequestBuilder import org.slf4j.LoggerFactory -import upickle.default.* import java.util.concurrent.TimeUnit import scala.concurrent.Await diff --git a/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/integration/OrganisaatioCache.scala b/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/integration/OrganisaatioCache.scala index 5ab464eb..b1fcb831 100644 --- a/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/integration/OrganisaatioCache.scala +++ b/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/integration/OrganisaatioCache.scala @@ -22,6 +22,7 @@ class OrganisaatioCache { val childOidsLoader = new CacheLoader[String, Response[String]] { def load(oid: String): Response[String] = + LOG.info(s"Ladataan lapsiorganisaatio-cache oidille $oid") val uri: Uri = uri"https://virkailija.$opintopolkuDomain/organisaatio-service/api/$oid/childoids?$queryParams" quickRequest .headers(headers) diff --git a/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/integration/OrganisaatioService.scala b/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/integration/OrganisaatioService.scala index a0469f84..c447b7c8 100644 --- a/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/integration/OrganisaatioService.scala +++ b/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/integration/OrganisaatioService.scala @@ -11,6 +11,7 @@ class OrganisaatioService { val LOG = LoggerFactory.getLogger(classOf[OrganisaatioService]) def getAllChildOidsFlat(oid: String): Set[String] = + LOG.info(s"Haetaan lapsiorganisaatiot cachesta oidille $oid") if (!OrganisaatioOid.isValid(oid)) LOG.error(s"Organisaation oid $oid on virheellinen") throw new RuntimeException(s"Organisaation oid $oid on virheellinen") diff --git a/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/resource/HenkiloResource.scala b/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/resource/HenkiloResource.scala index b8e16dd8..8d43de2e 100644 --- a/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/resource/HenkiloResource.scala +++ b/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/resource/HenkiloResource.scala @@ -1,10 +1,11 @@ package fi.oph.viestinvalitys.raportointi.resource -import fi.oph.viestinvalitys.raportointi.integration.{ONRService} +import fi.oph.viestinvalitys.raportointi.integration.ONRService import fi.oph.viestinvalitys.raportointi.security.SecurityOperaatiot import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.responses.ApiResponse import io.swagger.v3.oas.annotations.tags.Tag +import jakarta.servlet.http.{HttpServletRequest, HttpSession} import org.slf4j.LoggerFactory import org.springframework.http.{HttpStatus, MediaType, ResponseEntity} import org.springframework.web.bind.annotation.{GetMapping, RequestMapping, RestController} @@ -27,8 +28,10 @@ class HenkiloResource { responses = Array( new ApiResponse(responseCode = "200", description = "Palauttaa asiointikielen"), )) - def getAsiointikieli() = { + def getAsiointikieli(request: HttpServletRequest) = { LOG.info("Haetaan asiointikieli") + val session: HttpSession = request.getSession(false) + LOG.warn(s"sessio: ${session}") val securityOperaatiot = new SecurityOperaatiot val result = OnrService.haeAsiointikieli(securityOperaatiot.getIdentiteetti()) result match diff --git a/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/resource/LahetysResource.scala b/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/resource/LahetysResource.scala index dd7d79f4..06a3c02b 100644 --- a/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/resource/LahetysResource.scala +++ b/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/resource/LahetysResource.scala @@ -11,7 +11,7 @@ import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.media.{Content, Schema} import io.swagger.v3.oas.annotations.responses.ApiResponse import io.swagger.v3.oas.annotations.tags.Tag -import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.{HttpServletRequest, HttpSession} import org.slf4j.LoggerFactory import org.springframework.http.{HttpStatus, MediaType, ResponseEntity} import org.springframework.web.bind.annotation.* @@ -58,7 +58,9 @@ class LahetysResource { @RequestParam(name = HAKU_ALKAEN_PARAM_NAME, required = false) hakuAlkaen: Optional[String], @RequestParam(name = HAKU_PAATTYEN_PARAM_NAME, required = false) hakuPaattyen: Optional[String], request: HttpServletRequest): ResponseEntity[PalautaLahetyksetResponse] = - val securityOperaatiot = new SecurityOperaatiot + LOG.info("Haetaan lähetyksiä") + val session: HttpSession = request.getSession(false) + val securityOperaatiot = new SecurityOperaatiot(optionalHttpSession=Some(session)) val kantaOperaatiot = new KantaOperaatiot(DbUtil.database) // jostain syystä parametri tulee enkoodattuna AWS-ympäristössä val viestiDecoded:Optional[String] = @@ -81,9 +83,9 @@ class LahetysResource { else Right(None)) .flatMap(_ => - val kayttooikeusTunnisteet = - if (securityOperaatiot.onPaakayttaja()) Option.empty - else Option.apply(kantaOperaatiot.getKayttooikeusTunnisteet(securityOperaatiot.getKayttajanOikeudet().toSeq)) + LOG.info("haetaan käyttäjän käyttöoikeustunnisteet") + val kayttooikeusTunnisteet = securityOperaatiot.getKayttajanKayttooikeustunnisteet() + LOG.info("haetaan lähetykset kannasta") val (lahetykset, hasSeuraavat, lkm) = kantaOperaatiot.searchLahetykset( kayttooikeusTunnisteet = kayttooikeusTunnisteet, organisaatiot = organisaatio.toScala.map(o => Set(o).union(OrganisaatioService.getAllChildOidsFlat(o))), @@ -95,10 +97,12 @@ class LahetysResource { lahettajaHakuLauseke = lahettaja.toScala, hakuAlkaen = ParametriUtil.asInstant(hakuAlkaen), hakuPaattyen = ParametriUtil.asInstant(hakuPaattyen)) + LOG.info("saatiin lähetykset kannasta") if (lahetykset.isEmpty) // on ok tilanne että haku ei palauta tuloksia Left(ResponseEntity.status(HttpStatus.OK).body(PalautaLahetyksetSuccessResponse(Seq.empty.asJava, Optional.empty, 0))) else + LOG.info("haetaan lähetyksen vastaanottajastatus") val lahetysStatukset = kantaOperaatiot.getLahetystenVastaanottotilat(lahetykset.map(_.tunniste), kayttooikeusTunnisteet) val seuraavatAlkaen = { if (hasSeuraavat) @@ -106,11 +110,11 @@ class LahetysResource { else Optional.empty } - + LOG.info("haetaan lähetyksen maskit") val maskit = kantaOperaatiot.getLahetystenMaskit(lahetykset.map(_.tunniste), kayttooikeusTunnisteet) AuditLog.logRead("lahetys", lahetykset.map(lahetys => lahetys.tunniste.toString).toList.toString(), AuditOperation.ReadLahetys, RequestContextHolder.getRequestAttributes.asInstanceOf[ServletRequestAttributes].getRequest) - + LOG.info(s"Palautetaan lähetykset") Right(ResponseEntity.status(HttpStatus.OK).body(PalautaLahetyksetSuccessResponse( lahetykset.map(lahetys => PalautaLahetysSuccessResponse( lahetys.tunniste.toString, lahetysotsikonMaskaus(lahetys.otsikko, lahetys.tunniste, maskit), lahetys.omistaja, lahetys.lahettavaPalvelu, lahetys.lahettavanVirkailijanOID.getOrElse(""), @@ -135,12 +139,13 @@ class LahetysResource { new ApiResponse(responseCode = "403", description = KATSELU_RESPONSE_403_DESCRIPTION, content = Array(new Content(schema = new Schema(implementation = classOf[Void])))), new ApiResponse(responseCode = "410", description = KATSELU_RESPONSE_410_DESCRIPTION, content = Array(new Content(schema = new Schema(implementation = classOf[Void])))) )) - def lueLahetys(@PathVariable(LAHETYSTUNNISTE_PARAM_NAME) lahetysTunniste: String): ResponseEntity[PalautaLahetysResponse] = - val securityOperaatiot = new SecurityOperaatiot + def lueLahetys(@PathVariable(LAHETYSTUNNISTE_PARAM_NAME) lahetysTunniste: String, request: HttpServletRequest): ResponseEntity[PalautaLahetysResponse] = + LOG.info(s"Haetaan lähetyksen $lahetysTunniste tiedot") + val session: HttpSession = request.getSession(false) + val securityOperaatiot = new SecurityOperaatiot(optionalHttpSession = Some(session)) val kantaOperaatiot = new KantaOperaatiot(DbUtil.database) - val kayttooikeusTunnisteet = - if (securityOperaatiot.onPaakayttaja()) Option.empty - else Option.apply(kantaOperaatiot.getKayttooikeusTunnisteet(securityOperaatiot.getKayttajanOikeudet().toSeq)) + LOG.info("haetaan käyttäjän käyttöoikeustunnisteet") + val kayttooikeusTunnisteet = securityOperaatiot.getKayttajanKayttooikeustunnisteet() LogContext(lahetysTunniste = lahetysTunniste)(() => try Right(None) @@ -159,7 +164,8 @@ class LahetysResource { else Right(uuid.get)) .flatMap(tunniste => - // haetaan lähetykset + // haetaan lähetys + LOG.info("haetaan lähetys kannasta") val lahetys = kantaOperaatiot.getLahetysKayttooikeusrajauksilla(tunniste, kayttooikeusTunnisteet) if (lahetys.isEmpty) LOG.warn(s"Tunnuksella $lahetysTunniste ei löytynyt lähetystä käyttäjälle ${securityOperaatiot.getIdentiteetti()}") @@ -167,6 +173,7 @@ class LahetysResource { else Right(lahetys.get)) .flatMap(lahetys => + LOG.info("haetaan lähetyksen käyttöoikeudet") val lahetyksenOikeudet: Set[Kayttooikeus] = kantaOperaatiot.getLahetystenKayttooikeudet(Seq(lahetys.tunniste)).getOrElse(lahetys.tunniste, Set.empty) if (!securityOperaatiot.onOikeusKatsellaEntiteetti(lahetys.omistaja, lahetyksenOikeudet)) LOG.warn(s"Käyttäjällä ${securityOperaatiot.getIdentiteetti()} ei ole katseluooikeuksia lähetykseen ${lahetysTunniste}") @@ -174,13 +181,17 @@ class LahetysResource { else Right(lahetys)) .map(lahetys => + LOG.info("haetaan lähetyksen vastaanottajastatus") val lahetysStatukset: Seq[VastaanottajatTilassa] = kantaOperaatiot.getLahetystenVastaanottotilat(Seq.apply(lahetys.tunniste), kayttooikeusTunnisteet) .getOrElse(lahetys.tunniste, Seq.empty) .map(status => VastaanottajatTilassa(status._1, status._2)) + LOG.info("haetaan lähetyksen viestilukumäärä") val viestiLkm: Int = kantaOperaatiot.getLahetyksenViestiLkm(lahetys.tunniste) + LOG.info("haetaan lähetyksen maskit") val maskit = kantaOperaatiot.getLahetystenMaskit(Seq.apply(lahetys.tunniste), kayttooikeusTunnisteet) AuditLog.logRead("lahetys", lahetys.tunniste.toString, AuditOperation.ReadLahetys, RequestContextHolder.getRequestAttributes.asInstanceOf[ServletRequestAttributes].getRequest) + LOG.info(s"Palautetaan lähetys") ResponseEntity.status(HttpStatus.OK).body(PalautaLahetysSuccessResponse( lahetys.tunniste.toString, lahetysotsikonMaskaus(lahetys.otsikko, lahetys.tunniste, maskit), lahetys.omistaja, lahetys.lahettavaPalvelu, lahetys.lahettavanVirkailijanOID.getOrElse(""), lahetys.lahettaja.nimi.getOrElse(""), lahetys.lahettaja.sahkoposti, lahetys.replyTo.getOrElse(""), lahetys.luotu.toString, lahetysStatukset.asJava, viestiLkm))) @@ -203,8 +214,9 @@ class LahetysResource { new ApiResponse(responseCode = "403", description = KATSELU_RESPONSE_403_DESCRIPTION, content = Array(new Content(schema = new Schema(implementation = classOf[Void])))), new ApiResponse(responseCode = "410", description = KATSELU_RESPONSE_410_DESCRIPTION, content = Array(new Content(schema = new Schema(implementation = classOf[Void])))) )) - def lueMassaviesti(@PathVariable(LAHETYSTUNNISTE_PARAM_NAME) lahetysTunniste: String): ResponseEntity[ViestiResponse] = - val securityOperaatiot = new SecurityOperaatiot + def lueMassaviesti(@PathVariable(LAHETYSTUNNISTE_PARAM_NAME) lahetysTunniste: String, request: HttpServletRequest): ResponseEntity[ViestiResponse] = + LOG.info(s"Haetaan massaviestin tiedot lähetystunnisteella $lahetysTunniste") + val securityOperaatiot = new SecurityOperaatiot(optionalHttpSession = Some(request.getSession())) val kantaOperaatiot = new KantaOperaatiot(DbUtil.database) LogContext(lahetysTunniste = lahetysTunniste)(() => @@ -225,9 +237,9 @@ class LahetysResource { Right(uuid.get)) .flatMap(tunniste => // haetaan viesti - val kayttooikeusTunnisteet = - if (securityOperaatiot.onPaakayttaja()) Option.empty - else Option.apply(kantaOperaatiot.getKayttooikeusTunnisteet(securityOperaatiot.getKayttajanOikeudet().toSeq)) + LOG.info("haetaan käyttäjän käyttöoikeustunnisteet") + val kayttooikeusTunnisteet = securityOperaatiot.getKayttajanKayttooikeustunnisteet() + LOG.info("haetaan viesti kannasta") val viesti = kantaOperaatiot.getMassaViestiLahetystunnisteella(tunniste, kayttooikeusTunnisteet) if (viesti.isEmpty) LOG.info(s"Tunnuksella $lahetysTunniste ei löytynyt viestejä käyttäjälle ${securityOperaatiot.getIdentiteetti()}") @@ -236,6 +248,7 @@ class LahetysResource { Right(viesti.get)) .flatMap(viesti => // tarkistetaan oikeudet viestiin + LOG.info("haetaan viestin käyttöoikeudet") val viestinOikeudet: Set[Kayttooikeus] = kantaOperaatiot.getViestinKayttooikeudet(Seq(viesti.tunniste)).getOrElse(viesti.tunniste, Set.empty) val onLukuOikeudet = securityOperaatiot.onOikeusKatsellaEntiteetti(viesti.omistaja, viestinOikeudet) if (!onLukuOikeudet) @@ -247,6 +260,7 @@ class LahetysResource { val maskattuSisalto = if (!viesti.maskit.isEmpty) MaskiUtil.maskaaSalaisuudet(viesti.sisalto, viesti.maskit) else viesti.sisalto AuditLog.logRead("viesti", viesti.tunniste.toString, AuditOperation.ReadViesti, RequestContextHolder.getRequestAttributes.asInstanceOf[ServletRequestAttributes].getRequest) + LOG.info(s"Palautetaan viesti") ResponseEntity.status(HttpStatus.OK).body(ViestiSuccessResponse( viesti.lahetysTunniste.toString, viesti.tunniste.toString, maskattuOtsikko, maskattuSisalto, viesti.sisallonTyyppi.toString, viesti.kielet.map(kieli => kieli.toString).toSeq.asJava @@ -271,9 +285,9 @@ class LahetysResource { new ApiResponse(responseCode = "403", description = KATSELU_RESPONSE_403_DESCRIPTION, content = Array(new Content(schema = new Schema(implementation = classOf[Void])))), new ApiResponse(responseCode = "410", description = KATSELU_RESPONSE_410_DESCRIPTION, content = Array(new Content(schema = new Schema(implementation = classOf[Void])))) )) - def lueViesti(@PathVariable(VIESTITUNNISTE_PARAM_NAME) viestiTunniste: String): ResponseEntity[ViestiResponse] = - - val securityOperaatiot = new SecurityOperaatiot + def lueViesti(@PathVariable(VIESTITUNNISTE_PARAM_NAME) viestiTunniste: String, request: HttpServletRequest): ResponseEntity[ViestiResponse] = + LOG.info(s"Haetaan viestin tiedot tunnisteella $viestiTunniste") + val securityOperaatiot = new SecurityOperaatiot(optionalHttpSession = Some(request.getSession())) val kantaOperaatiot = new KantaOperaatiot(DbUtil.database) LogContext(lahetysTunniste = viestiTunniste)(() => @@ -294,9 +308,9 @@ class LahetysResource { Right(uuid.get)) .flatMap(tunniste => // haetaan viesti - val kayttooikeusTunnisteet = - if (securityOperaatiot.onPaakayttaja()) Option.empty - else Option.apply(kantaOperaatiot.getKayttooikeusTunnisteet(securityOperaatiot.getKayttajanOikeudet().toSeq)) + LOG.info("haetaan käyttäjän käyttöoikeustunnisteet") + val kayttooikeusTunnisteet = securityOperaatiot.getKayttajanKayttooikeustunnisteet() + LOG.info("haetaan viesti kannasta") val viesti = kantaOperaatiot.getRaportointiViestiTunnisteella(tunniste, kayttooikeusTunnisteet) if (viesti.isEmpty) LOG.info(s"Tunnuksella $viestiTunniste ei löytynyt viestiä käyttäjälle ${securityOperaatiot.getIdentiteetti()}") @@ -305,6 +319,7 @@ class LahetysResource { Right(viesti.get)) .flatMap(viesti => // tarkistetaan oikeudet viestiin + LOG.info("haetaan viestin käyttöoikeudet") val viestinOikeudet: Set[Kayttooikeus] = kantaOperaatiot.getViestinKayttooikeudet(Seq(viesti.tunniste)).getOrElse(viesti.tunniste, Set.empty) val onLukuOikeudet = securityOperaatiot.onOikeusKatsellaEntiteetti(viesti.omistaja, viestinOikeudet) if (!onLukuOikeudet) @@ -316,6 +331,7 @@ class LahetysResource { val maskattuSisalto = if (!viesti.maskit.isEmpty) MaskiUtil.maskaaSalaisuudet(viesti.sisalto, viesti.maskit) else viesti.sisalto AuditLog.logRead("viesti", viesti.tunniste.toString, AuditOperation.ReadViesti, RequestContextHolder.getRequestAttributes.asInstanceOf[ServletRequestAttributes].getRequest) + LOG.info(s"Palautetaan viestif") ResponseEntity.status(HttpStatus.OK).body(ViestiSuccessResponse( viesti.lahetysTunniste.toString, viesti.tunniste.toString, maskattuOtsikko, maskattuSisalto, viesti.sisallonTyyppi.toString, viesti.kielet.map(kieli => kieli.toString).toSeq.asJava @@ -362,7 +378,8 @@ class LahetysResource { @RequestParam(name = ORGANISAATIO_PARAM_NAME, required = false) organisaatio: Optional[String], request: HttpServletRequest ): ResponseEntity[VastaanottajatResponse] = - val securityOperaatiot = new SecurityOperaatiot + LOG.info(s"Haetaan lähetyksen $lahetysTunniste vastaanottajia") + val securityOperaatiot = new SecurityOperaatiot(optionalHttpSession = Some(request.getSession())) val kantaOperaatiot = new KantaOperaatiot(DbUtil.database) LogContext(lahetysTunniste = lahetysTunniste)(() => try @@ -380,6 +397,7 @@ class LahetysResource { else Right(ParametriUtil.asUUID(lahetysTunniste).get)) .flatMap(tunniste => + LOG.info("haetaan lähetys kannasta") val lahetys = kantaOperaatiot.getLahetys(tunniste) if (lahetys.isEmpty) LOG.info(s"Tunnuksella $lahetysTunniste ei löytynyt lähetystä käyttäjälle ${securityOperaatiot.getIdentiteetti()}") @@ -387,6 +405,7 @@ class LahetysResource { else Right(lahetys.get)) .flatMap(lahetys => + LOG.info("haetaan lähetyksen käyttöoikeudet") val lahetyksenOikeudet: Set[Kayttooikeus] = kantaOperaatiot.getLahetystenKayttooikeudet(Seq(lahetys.tunniste)).getOrElse(lahetys.tunniste, Set.empty) if (!securityOperaatiot.onOikeusKatsellaEntiteetti(lahetys.omistaja, lahetyksenOikeudet)) LOG.warn(s"Käyttäjällä ${securityOperaatiot.getIdentiteetti()} ei ole katseluooikeuksia lähetykseen $lahetysTunniste") @@ -395,11 +414,10 @@ class LahetysResource { Right(lahetys)) .flatMap(lahetys => // sivutusta varten haetaan myös jonon seuraava + LOG.info("haetaan vastaanottajat kannasta") val (vastaanottajat, hasSeuraavat) = kantaOperaatiot.searchVastaanottajat( lahetysTunniste = lahetys.tunniste, - kayttooikeusTunnisteet = - if (securityOperaatiot.onPaakayttaja()) Option.empty - else Option.apply(kantaOperaatiot.getKayttooikeusTunnisteet(securityOperaatiot.getKayttajanOikeudet().toSeq)), + kayttooikeusTunnisteet = securityOperaatiot.getKayttajanKayttooikeustunnisteet(), organisaatiot = organisaatio.toScala.map(o => Set(o).union(OrganisaatioService.getAllChildOidsFlat(o))), alkaen = ParametriUtil.asUUID(alkaen), enintaan = ParametriUtil.asInt(enintaan).getOrElse(VASTAANOTTAJAT_ENINTAAN_DEFAULT), @@ -414,6 +432,7 @@ class LahetysResource { case v if !hasSeuraavat => Optional.empty // lista jatkuu seuraavalle sivulle case _ => Optional.of(vastaanottajat.last.tunniste.toString) } + LOG.info(s"Palautetaan vastaanottajat") AuditLog.logRead("vastaanottajat", vastaanottajat.map(v => v.tunniste.toString).toList.toString(), AuditOperation.ReadVastaanottajat, RequestContextHolder.getRequestAttributes.asInstanceOf[ServletRequestAttributes].getRequest) Right(ResponseEntity.status(HttpStatus.OK).body(VastaanottajatSuccessResponse( @@ -448,6 +467,7 @@ class LahetysResource { def organisaatiorajaus(organisaatio: Optional[String], kayttajanOikeudet: Set[Kayttooikeus], organisaatioClient: OrganisaatioService): Set[Kayttooikeus] = + LOG.info(s"Haetaan organisaatiorajaukset lähetykselle") if (organisaatio.isEmpty) kayttajanOikeudet else diff --git a/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/security/SecurityOperaatiot.scala b/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/security/SecurityOperaatiot.scala index 2a931f1b..679ec776 100644 --- a/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/security/SecurityOperaatiot.scala +++ b/lambdat/raportointi/src/main/scala/fi/oph/viestinvalitys/raportointi/security/SecurityOperaatiot.scala @@ -1,8 +1,10 @@ package fi.oph.viestinvalitys.raportointi.security -import fi.oph.viestinvalitys.business.Kayttooikeus +import fi.oph.viestinvalitys.business.{KantaOperaatiot, Kayttooikeus} import fi.oph.viestinvalitys.raportointi.integration.OrganisaatioService import fi.oph.viestinvalitys.raportointi.security.SecurityConstants.OPH_ORGANISAATIO_OID +import fi.oph.viestinvalitys.util.DbUtil +import jakarta.servlet.http.HttpSession import org.slf4j.LoggerFactory import org.springframework.security.core.context.SecurityContextHolder @@ -27,11 +29,13 @@ object SecurityConstants { } class SecurityOperaatiot( - getOikeudet: () => Seq[String] = () => SecurityContextHolder.getContext.getAuthentication.getAuthorities.asScala.map(a => a.getAuthority).toSeq, - getUsername: () => String = () => SecurityContextHolder.getContext.getAuthentication.getName(), - organisaatioClient: OrganisaatioService = OrganisaatioService) { + getOikeudet: () => Seq[String] = () => SecurityContextHolder.getContext.getAuthentication.getAuthorities.asScala.map(a => a.getAuthority).toSeq, + getUsername: () => String = () => SecurityContextHolder.getContext.getAuthentication.getName(), + organisaatioClient: OrganisaatioService = OrganisaatioService, + optionalHttpSession: Option[HttpSession] = None) { val LOG = LoggerFactory.getLogger(classOf[SecurityOperaatiot]) + val kantaOperaatiot = new KantaOperaatiot(DbUtil.database) final val SECURITY_ROOLI_PREFIX_PATTERN = "^ROLE_" private lazy val kayttajanCasOikeudet: Set[Kayttooikeus] = { getOikeudet() @@ -50,14 +54,44 @@ class SecurityOperaatiot( if(onPaakayttaja()) kayttajanCasOikeudet // ei tarvitse mapata kaikkia lapsiorganisaatioita else - val lapsioikeudet = kayttajanCasOikeudet - .filter(kayttajanOikeus => kayttajanOikeus.organisaatio.isDefined) - .map(kayttajanOikeus => - organisaatioClient.getAllChildOidsFlat(kayttajanOikeus.organisaatio.get) - .map(o => Kayttooikeus(kayttajanOikeus.oikeus, Some(o))) - ).flatten - kayttajanCasOikeudet ++ lapsioikeudet + optionalHttpSession match { + case Some(n) if optionalHttpSession.get.getAttribute("kayttooikeudet") != null => + LOG.warn("oikat sessiosta!") + getSetOfKayttooikeusFromSession(optionalHttpSession.get, "kayttooikeudet").getOrElse(Set.empty) + case _ => + LOG.warn("parsitaan oikat!") + LOG.info("Haetaan käyttöoikeuksien organisaatioiden aliorganisaatiot") + val lapsioikeudet = kayttajanCasOikeudet + .filter(kayttajanOikeus => kayttajanOikeus.organisaatio.isDefined) + .map(kayttajanOikeus => + organisaatioClient.getAllChildOidsFlat(kayttajanOikeus.organisaatio.get) + .map(o => Kayttooikeus(kayttajanOikeus.oikeus, Some(o)))).flatten + if(optionalHttpSession.isDefined) + optionalHttpSession.get.setAttribute("kayttooikeudet", kayttajanCasOikeudet ++ lapsioikeudet) + kayttajanCasOikeudet ++ lapsioikeudet + } } + + private lazy val kayttajanKayttooikeustunnisteet: Option[Set[Int]] = { + val pk = onPaakayttaja() + if (onPaakayttaja()) + Option.empty + else + optionalHttpSession match { + case None => + LOG.warn("ei sessiota, oikkatunnisteet kannasta!") + Option.apply(kantaOperaatiot.getKayttooikeusTunnisteet(kayttajanOikeudet.toSeq)) + case Some(n) if optionalHttpSession.get.getAttribute("kayttooikeustunnisteet") != null => + LOG.warn("oikkatunnisteet sessiosta!") + getSetOfIntsFromSession(optionalHttpSession.get, "kayttooikeustunnisteet") + case _ => + LOG.warn("oikkatunnisteet kannasta ja laitetaan sessioon!") + val tunnisteet = kantaOperaatiot.getKayttooikeusTunnisteet(kayttajanOikeudet.toSeq) + optionalHttpSession.get.setAttribute("kayttooikeustunnisteet", tunnisteet) + Some(tunnisteet) + } + } + val identiteetti = getUsername() def getIdentiteetti(): String = @@ -93,9 +127,31 @@ class SecurityOperaatiot( def getKayttajanOikeudet(): Set[Kayttooikeus] = kayttajanOikeudet + def getKayttajanKayttooikeustunnisteet(): Option[Set[Int]] = kayttajanKayttooikeustunnisteet + /** * Palauttaa käyttäjän käyttöoikeuksien organisaatiot ilman lapsihierarkiaa */ def getCasOrganisaatiot(): Set[String] = kayttajanCasOikeudet.filter(kayttooikeus => kayttooikeus.organisaatio.isDefined).map(ko => ko.organisaatio.get) + + def getSetOfIntsFromSession(session: HttpSession, attributeName: String): Option[Set[Int]] = { + Option(session.getAttribute(attributeName)) match { + case Some(value: Set[_]) => + // Safely cast elements to Int, filter out invalid ones + Some(value.collect { case i: Int => i }) + case _ => + None + } + } + + def getSetOfKayttooikeusFromSession(session: HttpSession, attributeName: String): Option[Set[Kayttooikeus]] = { + Option(session.getAttribute(attributeName)) match { + case Some(value: Set[_]) => + // Safely cast elements to Kayttooikeus, filter out invalid ones + Some(value.collect { case i: Kayttooikeus => i }) + case _ => + None + } + } } diff --git a/lambdat/raportointi/src/test/scala/fi/oph/viestinvalitys/raportointi/SecurityOperaatiotTest.scala b/lambdat/raportointi/src/test/scala/fi/oph/viestinvalitys/raportointi/SecurityOperaatiotTest.scala index 77b6fd98..df0f0e13 100644 --- a/lambdat/raportointi/src/test/scala/fi/oph/viestinvalitys/raportointi/SecurityOperaatiotTest.scala +++ b/lambdat/raportointi/src/test/scala/fi/oph/viestinvalitys/raportointi/SecurityOperaatiotTest.scala @@ -1,15 +1,18 @@ package fi.oph.viestinvalitys.raportointi -import fi.oph.viestinvalitys.business.Kayttooikeus +import fi.oph.viestinvalitys.business.{KantaOperaatiot, Kayttooikeus} import fi.oph.viestinvalitys.raportointi.integration.OrganisaatioService import fi.oph.viestinvalitys.raportointi.security.SecurityConstants.OPH_ORGANISAATIO_OID import fi.oph.viestinvalitys.raportointi.security.{SecurityConstants, SecurityOperaatiot} +import fi.oph.viestinvalitys.util.DbUtil import fi.oph.viestinvalitys.vastaanotto.security.SecurityConstants.SECURITY_ROOLI_PAAKAYTTAJA_FULL +import jakarta.servlet.http.HttpSession import org.junit.jupiter.api.* import org.junit.jupiter.api.TestInstance.Lifecycle import org.mockito.ArgumentMatchers.any import org.mockito.Mockito.{reset, times, verify, when} import org.scalatestplus.mockito.MockitoSugar.mock +import slick.jdbc.JdbcBackend.Database @TestInstance(Lifecycle.PER_CLASS) class SecurityOperaatiotTest { @@ -20,10 +23,15 @@ class SecurityOperaatiotTest { val ORGANISAATIO2 = "1.2.246.562.10.79559059674" val mockOrganisaatioService = mock[OrganisaatioService] + val mockKantaoperaatiot = mock[KantaOperaatiot] + val mockHttpSession = mock[HttpSession] + + @BeforeAll def setup(): Unit = { + System.setProperty("MODE", "LOCAL") // kantaa ei saanut mockattua joten oikaistaan tällä + } @Test def testKayttajanOikeudet(): Unit = val OIKEUS = "APP_OIKEUS1" - when(mockOrganisaatioService.getAllChildOidsFlat(ORGANISAATIO)).thenReturn(Set("1.2.246.562.10.2014041814455745619200")) val securityOperaatiot = SecurityOperaatiot(() => Seq("ROLE_" + OIKEUS + "_" + ORGANISAATIO), () => "", mockOrganisaatioService) Assertions.assertEquals(Set(Kayttooikeus(OIKEUS, Some(ORGANISAATIO)), Kayttooikeus(OIKEUS, Some("1.2.246.562.10.2014041814455745619200"))), securityOperaatiot.getKayttajanOikeudet()) diff --git a/viestinvalitys-raportointi/src/app/lib/data.ts b/viestinvalitys-raportointi/src/app/lib/data.ts index 12ed334a..6d976c52 100644 --- a/viestinvalitys-raportointi/src/app/lib/data.ts +++ b/viestinvalitys-raportointi/src/app/lib/data.ts @@ -10,6 +10,7 @@ const REVALIDATE_TIME_SECONDS = 60 * 60 * 2; const REVALIDATE_ASIOINTIKIELI = 60; export async function fetchLahetykset(hakuParams: LahetysHakuParams) { + console.info('Haetaan lähetykset') const fetchUrlBase = `${apiUrl}/lahetykset/lista?enintaan=${LAHETYKSET_SIVUTUS_KOKO}`; let fetchParams = hakuParams.seuraavatAlkaen ? `&alkaen=${hakuParams.seuraavatAlkaen}` @@ -33,14 +34,17 @@ export async function fetchLahetykset(hakuParams: LahetysHakuParams) { const res = await makeRequest(fetchUrlBase.concat(fetchParams), { cache: 'no-store', }); + console.info('saatiin lähetykset') return res.data; } export async function fetchLahetys(lahetysTunnus: string) { + console.info('haetaan lähetys') const url = `${apiUrl}/lahetykset/${lahetysTunnus}`; const res = await makeRequest(url, { cache: 'no-store', }); + console.info('saatiin lähetys') return res.data; } @@ -48,6 +52,7 @@ export async function fetchLahetyksenVastaanottajat( lahetysTunnus: string, hakuParams: VastaanottajatHakuParams, ) { + console.info('haetaan vastaanottajat') const url = `${apiUrl}/lahetykset/${lahetysTunnus}/vastaanottajat?enintaan=${VASTAANOTTAJAT_SIVUTUS_KOKO}`; let fetchParams = hakuParams.alkaen ? `&alkaen=${hakuParams.alkaen}` : ''; if (hakuParams.hakusana) { @@ -63,6 +68,7 @@ export async function fetchLahetyksenVastaanottajat( const res = await makeRequest(url.concat(fetchParams), { cache: 'no-store', }); + console.info('saatiin vastaanottajat') return res.data; }