Skip to content

Commit

Permalink
tuunausta oikeustarkistuksiin, korjattu vastaanottajien sivutusta ja …
Browse files Browse the repository at this point in the history
…huomioidaan vastaanottajien puuttuminen
  • Loading branch information
marjakari committed Feb 13, 2024
1 parent d75e4c2 commit 111b9e6
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 96 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,10 @@ katoavat S3-bucketista (kannassa ne säilyvät).

1. Asenna aws vault: https://github.com/99designs/aws-vault
2. Asenna cdk cli (esim. homebrew:lla)
3. Aja juuressa ./deploy.sh hahtuva deploy
4. Kirjaudu sisään sovellukseen osoitteessa: https://viestinvalitys.hahtuvaopintopolku.fi/lahetys/login
5. Swagger on osoitteessa: https://viestinvalitys.hahtuvaopintopolku.fi/swagger
3. Aja cdk-hakemistossa `npm ci`
4. Aja juuressa ./deploy.sh hahtuva deploy
5. Kirjaudu sisään sovellukseen osoitteessa: https://viestinvalitys.hahtuvaopintopolku.fi/lahetys/login
6. Swagger on osoitteessa: https://viestinvalitys.hahtuvaopintopolku.fi/swagger

### Tietokannan luonti uuteen ympäristöön

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package fi.oph.viestinvalitys.raportointi.model

import com.fasterxml.jackson.annotation.JsonInclude

import java.util
import java.util.Optional
import scala.beans.BeanProperty


class PalautaLahetyksetResponse() {}

@JsonInclude(JsonInclude.Include.NON_ABSENT)
case class PalautaLahetyksetSuccessResponse(
@BeanProperty lahetykset: java.util.List[PalautaLahetysSuccessResponse],
@BeanProperty seuraavatAlkaen: Optional[String]
) extends PalautaLahetyksetResponse

case class PalautaLahetyksetFailureResponse(
@BeanProperty virhe: util.List[String],
) extends PalautaLahetyksetResponse


class PalautaLahetysResponse() {}

@JsonInclude(JsonInclude.Include.NON_ABSENT)
case class PalautaLahetysSuccessResponse(
@BeanProperty lahetysTunniste: String,
@BeanProperty otsikko: String,
@BeanProperty omistaja: String,
@BeanProperty lahettavaPalvelu: String,
@BeanProperty lahettavanVirkailijanOID: String,
@BeanProperty lahettajanNimi: String,
@BeanProperty lahettajanSahkoposti: String,
@BeanProperty replyTo: String,
@BeanProperty luotu: String,
@BeanProperty tilat: java.util.List[VastaanottajatTilassa]
) extends PalautaLahetysResponse

case class PalautaLahetysFailureResponse(
@BeanProperty virhe: String,
) extends PalautaLahetysResponse

case class VastaanottajatTilassa(
@BeanProperty vastaanottotila: String,
@BeanProperty vastaanottajaLkm: Int
)

class VastaanottajatResponse() {}

@JsonInclude(JsonInclude.Include.NON_ABSENT)
case class VastaanottajaResponse(
@BeanProperty tunniste: String,
@BeanProperty nimi: Optional[String],
@BeanProperty sahkoposti: String,
@BeanProperty viestiTunniste: String,
@BeanProperty tila: String
)

@JsonInclude(JsonInclude.Include.NON_ABSENT)
case class VastaanottajatSuccessResponse(
@BeanProperty vastaanottajat: java.util.List[VastaanottajaResponse],
@BeanProperty seuraavatAlkaen: Optional[String],
@BeanProperty viimeisenTila: Optional[String],
) extends VastaanottajatResponse

case class VastaanottajatFailureResponse(
@BeanProperty virheet: util.List[String],
) extends VastaanottajatResponse

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
import fi.oph.viestinvalitys.business.{KantaOperaatiot, Kayttooikeus, RaportointiTila, Vastaanottaja, raportointiTilat}
import fi.oph.viestinvalitys.raportointi.resource.RaportointiAPIConstants.*
import fi.oph.viestinvalitys.raportointi.security.{SecurityConstants, SecurityOperaatiot}
import fi.oph.viestinvalitys.raportointi.model.*
import fi.oph.viestinvalitys.util.{DbUtil, LogContext}
import io.swagger.v3.oas.annotations.links.{Link, LinkParameter}
import io.swagger.v3.oas.annotations.media.{Content, Schema}
Expand Down Expand Up @@ -40,63 +41,6 @@ import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.DurationInt


class PalautaLahetyksetResponse() {}

case class VastaanottajatTilassa(
@BeanProperty vastaanottotila: String,
@BeanProperty vastaanottajaLkm: Int
)

@JsonInclude(JsonInclude.Include.NON_ABSENT)
case class PalautaLahetyksetSuccessResponse(
@BeanProperty lahetykset: java.util.List[PalautaLahetysSuccessResponse],
@BeanProperty seuraavatAlkaen: Optional[String]
) extends PalautaLahetyksetResponse

case class PalautaLahetyksetFailureResponse(
@BeanProperty virhe: util.List[String],
) extends PalautaLahetyksetResponse
class PalautaLahetysResponse() {}
@JsonInclude(JsonInclude.Include.NON_ABSENT)
case class PalautaLahetysSuccessResponse(
@BeanProperty lahetysTunniste: String,
@BeanProperty otsikko: String,
@BeanProperty omistaja: String,
@BeanProperty lahettavaPalvelu: String,
@BeanProperty lahettavanVirkailijanOID: String,
@BeanProperty lahettajanNimi: String,
@BeanProperty lahettajanSahkoposti: String,
@BeanProperty replyTo: String,
@BeanProperty luotu: String,
@BeanProperty tilat: java.util.List[VastaanottajatTilassa]
) extends PalautaLahetysResponse

case class PalautaLahetysFailureResponse(
@BeanProperty virhe: String,
) extends PalautaLahetysResponse

class VastaanottajatResponse() {}

@JsonInclude(JsonInclude.Include.NON_ABSENT)
case class VastaanottajaResponse(
@BeanProperty tunniste: String,
@BeanProperty nimi: Optional[String],
@BeanProperty sahkoposti: String,
@BeanProperty viestiTunniste: String,
@BeanProperty tila: String
)

@JsonInclude(JsonInclude.Include.NON_ABSENT)
case class VastaanottajatSuccessResponse(
@BeanProperty vastaanottajat: java.util.List[VastaanottajaResponse],
@BeanProperty seuraavatAlkaen: Optional[String],
@BeanProperty viimeisenTila: Optional[String],
) extends VastaanottajatResponse

case class VastaanottajatFailureResponse(
@BeanProperty virheet: util.List[String],
) extends VastaanottajatResponse

@RequestMapping(path = Array(""))
@RestController("RaportointiLahetys")
@Tag(
Expand Down Expand Up @@ -219,19 +163,17 @@ class LahetysResource {
else
Right(lahetys.get))
.flatMap(lahetys =>
// validoidaan lukuoikeudet lähetykseen, vähän turha tuplatsekkaus
val lahetyksenOikeudet: Set[Kayttooikeus] = kantaOperaatiot.getLahetystenKayttooikeudet(Seq(lahetys.tunniste))(lahetys.tunniste)
val lahetyksenOikeudet: Set[Kayttooikeus] = kantaOperaatiot.getLahetystenKayttooikeudet(Seq(lahetys.tunniste)).getOrElse(lahetys.tunniste, Set.empty)
if (!securityOperaatiot.onOikeusKatsellaEntiteetti(lahetys.omistaja, lahetyksenOikeudet))
LOG.info(s"Käyttäjällä ei ole katseluooikeuksia lähetykseen ${lahetysTunniste}")
Left(ResponseEntity.status(HttpStatus.FORBIDDEN).build())
else
Right(lahetys))
.map(lahetys =>
val lahetysStatukset: Seq[VastaanottajatTilassa] = kantaOperaatiot.getLahetystenVastaanottotilat(Seq.apply(lahetys.tunniste), securityOperaatiot.getKayttajanOikeudet())
val lahetysStatukset: Seq[VastaanottajatTilassa] = kantaOperaatiot.getLahetystenVastaanottotilat(Seq.apply(lahetys.tunniste), securityOperaatiot.getKayttajanOikeudet())
.getOrElse(lahetys.tunniste, Seq.empty)
.map(status => VastaanottajatTilassa(status._1, status._2))

ResponseEntity.status(HttpStatus.OK).body(PalautaLahetysSuccessResponse(
ResponseEntity.status(HttpStatus.OK).body(PalautaLahetysSuccessResponse(
lahetys.tunniste.toString, lahetys.otsikko, lahetys.omistaja, lahetys.lahettavaPalvelu, lahetys.lahettavanVirkailijanOID.getOrElse(""),
lahetys.lahettaja.nimi.getOrElse(""), lahetys.lahettaja.sahkoposti, lahetys.replyTo.getOrElse(""), lahetys.luotu.toString, lahetysStatukset.asJava)))
.fold(e => e, r => r).asInstanceOf[ResponseEntity[PalautaLahetysResponse]]
Expand Down Expand Up @@ -329,13 +271,13 @@ class LahetysResource {
else
Right(lahetys.get))
.flatMap(lahetys =>
val lahetyksenOikeudet: Set[Kayttooikeus] = kantaOperaatiot.getLahetystenKayttooikeudet(Seq(lahetys.tunniste))(lahetys.tunniste)
val lahetyksenOikeudet: Set[Kayttooikeus] = kantaOperaatiot.getLahetystenKayttooikeudet(Seq(lahetys.tunniste)).getOrElse(lahetys.tunniste, Set.empty)
if (!securityOperaatiot.onOikeusKatsellaEntiteetti(lahetys.omistaja, lahetyksenOikeudet))
LOG.info(s"Ei katseluooikeuksia lähetykseen ${lahetysTunniste}")
LOG.info(s"Ei katseluooikeuksia lähetykseen $lahetysTunniste")
Left(ResponseEntity.status(HttpStatus.FORBIDDEN).build())
else
Right(lahetys))
.map(lahetys =>
.flatMap(lahetys =>
val enintaanInt = ParametriUtil.asInt(enintaan).getOrElse(VASTAANOTTAJAT_ENINTAAN_DEFAULT)
// TODO tee tyylikkäämmin
// haetaan aina epäonnistuneet
Expand All @@ -352,17 +294,19 @@ class LahetysResource {
if (kesken.size < enintaanInt)
valmiit = kantaOperaatiot.haeLahetyksenVastaanottajia(lahetys.tunniste, Option.empty, Option.apply(enintaanInt), Option.apply("valmis"), securityOperaatiot.getKayttajanOikeudet())
val vastaanottajat = epaonnistuneet++kesken++valmiit
val viimeisenTila = ParametriUtil.getRaportointiTila(vastaanottajat.last.tila)
val seuraavatAlkaen = {
if (vastaanottajat.isEmpty || kantaOperaatiot.haeLahetyksenVastaanottajia(lahetys.tunniste, Option.apply(vastaanottajat.last.kontakti.sahkoposti), Option.apply(1), viimeisenTila,securityOperaatiot.getKayttajanOikeudet()).isEmpty)
Optional.empty
else
Optional.of(vastaanottajat.last.kontakti.sahkoposti)
}
ResponseEntity.status(HttpStatus.OK).body(VastaanottajatSuccessResponse(
if (vastaanottajat.isEmpty)
Left(ResponseEntity.status(HttpStatus.OK).body(VastaanottajatSuccessResponse(Seq.empty.asJava, Optional.empty, Optional.empty)))
else
val viimeisenTila = ParametriUtil.getRaportointiTila(vastaanottajat.last.tila)
val seuraavatAlkaen = {
vastaanottajat match
case v if kantaOperaatiot.haeLahetyksenVastaanottajia(lahetys.tunniste, Option.apply(v.last.kontakti.sahkoposti), Option.apply(1), viimeisenTila,securityOperaatiot.getKayttajanOikeudet()).isEmpty => Optional.empty
case _ => Optional.of(vastaanottajat.last.kontakti.sahkoposti)
}
Right(ResponseEntity.status(HttpStatus.OK).body(VastaanottajatSuccessResponse(
vastaanottajat.map(vastaanottaja => VastaanottajaResponse(vastaanottaja.tunniste.toString,
Optional.ofNullable(vastaanottaja.kontakti.nimi.getOrElse(null)), vastaanottaja.kontakti.sahkoposti,
vastaanottaja.viestiTunniste.toString, vastaanottaja.tila.toString)).asJava, seuraavatAlkaen, Optional.of(viimeisenTila.get))))
vastaanottaja.viestiTunniste.toString, vastaanottaja.tila.toString)).asJava, seuraavatAlkaen, Optional.of(viimeisenTila.get)))))
.fold(e => e, r => r).asInstanceOf[ResponseEntity[VastaanottajatResponse]]
catch
case e: Exception =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ class SecurityOperaatiot(
if (identiteetti.equals(omistaja) || onPaakayttaja())
true
else
entiteetinOikeudet.intersect(kayttajanOikeudet).size > 0
onOikeusLahettaa() && entiteetinOikeudet.intersect(kayttajanOikeudet).size > 0

def onOikeusKatsellaEntiteetti(omistaja: String, entiteetinOikeudet: Set[Kayttooikeus]): Boolean =
if (identiteetti.equals(omistaja) || onPaakayttaja())
true
else
entiteetinOikeudet.intersect(kayttajanOikeudet).size > 0
onOikeusKatsella() && entiteetinOikeudet.intersect(kayttajanOikeudet).size > 0

/**
* Tarkastelee pelkkää käyttöoikeusroolia, ei huomioi organisaatiorajoituksia
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import org.junit.jupiter.api.TestInstance.Lifecycle
@TestInstance(Lifecycle.PER_CLASS)
class SecurityOperaatiotTest {

val KATSELUOIKEUS = "VIESTINVALITYS_KATSELU"
val LAHETYSOIKEUS = "VIESTINVALITYS_LAHETYS"
val ORGANISAATIO = "1.2.3.4"
val ORGANISAATIO2 = "1.2.3.5"

@Test def testKayttajanOikeudet(): Unit =
val ORGANISAATIO = "1.2.3.4"
val OIKEUS = "OIKEUS1"
Expand All @@ -17,36 +22,42 @@ class SecurityOperaatiotTest {

@Test def testKatseluoikeudet(): Unit =

val ORGANISAATIO = "1.2.3.4"
val ORGANISAATIO2 = "1.2.3.5"
val OIKEUS = "VIESTINVALITYS_KATSELU"

val securityOperaatiot = SecurityOperaatiot(() => Seq("ROLE_APP_"+OIKEUS, "ROLE_APP_"+OIKEUS+"_"+ORGANISAATIO, "ROLE_APP_"+OIKEUS+"_"+ORGANISAATIO2), () => "")
val securityOperaatiot = SecurityOperaatiot(() => Seq("ROLE_APP_"+KATSELUOIKEUS, "ROLE_APP_"+KATSELUOIKEUS+"_"+ORGANISAATIO, "ROLE_APP_"+KATSELUOIKEUS+"_"+ORGANISAATIO2), () => "")
Assertions.assertEquals(true, securityOperaatiot.onOikeusKatsella())
Assertions.assertEquals(true, securityOperaatiot.onOikeusKatsellaEntiteetti("omistaja", Set(Kayttooikeus(Option.apply(ORGANISAATIO), OIKEUS))))
Assertions.assertEquals(true, securityOperaatiot.onOikeusKatsellaEntiteetti("omistaja", Set(Kayttooikeus(Option.apply(ORGANISAATIO), KATSELUOIKEUS))))
Assertions.assertEquals(true, securityOperaatiot.onOikeusKatsellaEntiteetti("omistaja", Set(Kayttooikeus(Option.empty, KATSELUOIKEUS))))
Assertions.assertEquals(false, securityOperaatiot.onOikeusLahettaa())
Assertions.assertEquals(false, securityOperaatiot.onOikeusLahettaaEntiteetti("omistaja", Set(Kayttooikeus(Option.apply(ORGANISAATIO), KATSELUOIKEUS))))

@Test def testLahetysoikeudet(): Unit =

val ORGANISAATIO = "1.2.3.4"
val ORGANISAATIO2 = "1.2.3.5"
val OIKEUS = "VIESTINVALITYS_LAHETYS"

val securityOperaatiot = SecurityOperaatiot(() => Seq("ROLE_APP_"+OIKEUS, "ROLE_APP_"+OIKEUS+"_"+ORGANISAATIO), () => "")
val securityOperaatiot = SecurityOperaatiot(() => Seq("ROLE_APP_"+LAHETYSOIKEUS, "ROLE_APP_"+LAHETYSOIKEUS+"_"+ORGANISAATIO), () => "")
Assertions.assertEquals(true, securityOperaatiot.onOikeusLahettaa())
Assertions.assertEquals(true, securityOperaatiot.onOikeusLahettaaEntiteetti("omistaja", Set(Kayttooikeus(Option.apply(ORGANISAATIO), OIKEUS))))
Assertions.assertEquals(false, securityOperaatiot.onOikeusLahettaaEntiteetti("omistaja", Set(Kayttooikeus(Option.apply(ORGANISAATIO2), OIKEUS))))
Assertions.assertEquals(true, securityOperaatiot.onOikeusLahettaaEntiteetti("omistaja", Set(Kayttooikeus(Option.apply(ORGANISAATIO), LAHETYSOIKEUS))))
Assertions.assertEquals(true, securityOperaatiot.onOikeusLahettaaEntiteetti("omistaja", Set(Kayttooikeus(Option.empty, LAHETYSOIKEUS))))
Assertions.assertEquals(false, securityOperaatiot.onOikeusLahettaaEntiteetti("omistaja", Set(Kayttooikeus(Option.apply(ORGANISAATIO2), LAHETYSOIKEUS))))
// lähetysoikeuksiin ei automaattisesti sisälly katseluikeus!
Assertions.assertEquals(false, securityOperaatiot.onOikeusKatsella())
Assertions.assertEquals(false, securityOperaatiot.onOikeusKatsellaEntiteetti("omistaja", Set(Kayttooikeus(Option.apply(ORGANISAATIO), LAHETYSOIKEUS))))


@Test def testPaakayttajanOikeudet(): Unit =

val ORGANISAATIO = "1.2.3.4"
val OPH_ORGANISAATIO = "1.2.246.562.10.48587687889"
val OIKEUS = "VIESTINVALITYS_KATSELU"
val PAAKAYTTAJA_OIKEUS = "VIESTINVALITYS_OPH_PAAKAYTTAJA"

val securityOperaatiot = SecurityOperaatiot(() => Seq("ROLE_APP_"+PAAKAYTTAJA_OIKEUS, "ROLE_APP_"+PAAKAYTTAJA_OIKEUS+"_"+OPH_ORGANISAATIO), () => "")
Assertions.assertEquals(true, securityOperaatiot.onOikeusKatsella())
Assertions.assertEquals(true, securityOperaatiot.onOikeusLahettaa())
Assertions.assertEquals(true, securityOperaatiot.onOikeusKatsellaEntiteetti("omistaja", Set(Kayttooikeus(Option.apply(ORGANISAATIO), OIKEUS))))
Assertions.assertEquals(true, securityOperaatiot.onOikeusLahettaaEntiteetti("omistaja", Set(Kayttooikeus(Option.apply(ORGANISAATIO), OIKEUS))))
Assertions.assertEquals(true, securityOperaatiot.onOikeusKatsellaEntiteetti("omistaja", Set(Kayttooikeus(Option.apply(ORGANISAATIO), KATSELUOIKEUS))))
Assertions.assertEquals(true, securityOperaatiot.onOikeusLahettaaEntiteetti("omistaja", Set(Kayttooikeus(Option.apply(ORGANISAATIO), KATSELUOIKEUS))))


@Test def testLahetyksellaEiOikeusrajauksia(): Unit =

val securityOperaatiotKatselu = SecurityOperaatiot(() => Seq("ROLE_APP_" + KATSELUOIKEUS, "ROLE_APP_" + KATSELUOIKEUS + "_" + ORGANISAATIO), () => "")
val securityOperaatiotLahetys = SecurityOperaatiot(() => Seq("ROLE_APP_" + LAHETYSOIKEUS, "ROLE_APP_" + LAHETYSOIKEUS + "_" + ORGANISAATIO), () => "")

Assertions.assertEquals(true, securityOperaatiotKatselu.onOikeusKatsellaEntiteetti("omistaja", Set.empty))
Assertions.assertEquals(true, securityOperaatiotKatselu.onOikeusLahettaaEntiteetti("omistaja", Set.empty))
}
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,8 @@ class KantaOperaatiotTest {
@Test def testGetLahetystenVastaanottotilat(): Unit =
// tallennetaan lähetys
val lahetys1 = this.tallennaLahetys()
// vastaanottotilat ilman vastaanottajia on tyhjä
Assertions.assertEquals(Map.empty, kantaOperaatiot.getLahetystenVastaanottotilat(Seq(lahetys1.tunniste), Set(Kayttooikeus(Option.apply(ORGANISAATIO1), "OIKEUS1"))))
// tallennetaan viesti
val (viesti, vastaanottajat) = tallennaViesti(5, lahetysTunniste = lahetys1.tunniste,
kayttooikeudet = Set(Kayttooikeus(Option.apply(ORGANISAATIO1), "OIKEUS1")))
Expand Down
14 changes: 14 additions & 0 deletions viestinvalitys-raportointi/src/app/components/HomeIconLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
'use client'
import { IconButton } from "@mui/material"
import HomeOutlinedIcon from '@mui/icons-material/HomeOutlined';
import Link from 'next/link';

const HomeIconLink = () => {
return (
<IconButton aria-label="home" href="/" size="large" component={Link} sx={{ border: "1px solid", borderRadius: "5px", width: 30, height: 30}}>
<HomeOutlinedIcon />
</IconButton>
)
}

export default HomeIconLink
Loading

0 comments on commit 111b9e6

Please sign in to comment.