Skip to content

Commit

Permalink
organisaatiohierarkia käyttöoikeuksiin ja cachetus
Browse files Browse the repository at this point in the history
  • Loading branch information
marjakari committed Feb 23, 2024
1 parent fe27acd commit a41012d
Show file tree
Hide file tree
Showing 14 changed files with 252 additions and 87 deletions.
36 changes: 35 additions & 1 deletion integraatio/src/test/scala/fi/oph/viestinvalitys/LocalUtil.scala
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ object LocalUtil {
new LambdaHandler().handleRequest(null, new TestAwsContext("migraatio"))

// alustetaan data
val kayttooikeus = Kayttooikeus(Option.apply("1.2.3"), "OIKEUS")
val kayttooikeus = Kayttooikeus("OIKEUS", Option.apply("1.2.246.562.10.240484683010"))
val kantaOperaatiot = new KantaOperaatiot(DbUtil.database)
val lahetyksia = kantaOperaatiot.getLahetykset(Option.empty, Option.apply(20), Set(kayttooikeus))
if(lahetyksia.isEmpty || lahetyksia.length < 3) {
Expand Down Expand Up @@ -270,6 +270,40 @@ object LocalUtil {
Set(kayttooikeus),
Map("avain" -> Seq("arvo")),
"omistaja")
kantaOperaatiot.tallennaViesti("Kuopio yhteiskunta- ja kauppatieteet viesti",
"Tämä on viesti käyttöoikeushierarkian todentamiseen",
SisallonTyyppi.TEXT,
Set(Kieli.FI),
Map.empty,
Option.apply("0.1.2.3"),
Option.apply(Kontakti(Option.apply("Testi Virkailija"), "[email protected]")),
Option.apply("[email protected]"),
Range(0, 3).map(suffix => Kontakti(Option.apply("Testi Vastaanottaja" + suffix), "testi.vastaanottaja" + suffix + "@example.com")),
Seq.empty,
Option.apply("hakemuspalvelu"),
Option.empty,
Option.apply(Prioriteetti.NORMAALI),
Option.apply(365),
Set(Kayttooikeus("APP_HAKEMUS_CRUD", Some("1.2.246.562.10.2014041814455745619200"))),
Map("avain" -> Seq("arvo")),
"omistaja")
kantaOperaatiot.tallennaViesti("Viesti ilman organisaatiota",
"Tämä on viesti käyttöoikeustarkistuksen todentamiseen ilman organisaatiorajausta",
SisallonTyyppi.TEXT,
Set(Kieli.FI),
Map.empty,
Option.apply("0.1.2.3"),
Option.apply(Kontakti(Option.apply("Testi Virkailija"), "[email protected]")),
Option.apply("[email protected]"),
Range(0, 3).map(suffix => Kontakti(Option.apply("Testi Vastaanottaja" + suffix), "testi.vastaanottaja" + suffix + "@example.com")),
Seq.empty,
Option.apply("hakemuspalvelu"),
Option.empty,
Option.apply(Prioriteetti.NORMAALI),
Option.apply(365),
Set(Kayttooikeus("OIKEUS", None)),
Map("avain" -> Seq("arvo")),
"omistaja")
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,17 @@ class SecurityConfiguration {
val user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.authorities(SecurityConstants.SECURITY_ROOLI_LAHETYS_FULL, SecurityConstants.SECURITY_ROOLI_LAHETYS_FULL+"_1.2.3", SecurityConstants.SECURITY_ROOLI_KATSELU_FULL, SecurityConstants.SECURITY_ROOLI_KATSELU_FULL+"_1.2.3", "OIKEUS_1.2.3")
.authorities(SecurityConstants.SECURITY_ROOLI_LAHETYS_FULL, SecurityConstants.SECURITY_ROOLI_LAHETYS_FULL+"_1.2.246.562.10.240484683010", SecurityConstants.SECURITY_ROOLI_KATSELU_FULL, SecurityConstants.SECURITY_ROOLI_KATSELU_FULL+"_1.2.246.562.10.240484683010", "OIKEUS", "OIKEUS_1.2.246.562.10.240484683010", "APP_HAKEMUS_CRUD", "APP_HAKEMUS_CRUD_1.2.246.562.10.240484683010")
.build()
val lahetys = User.withDefaultPasswordEncoder()
.username("lahetys")
.password("password")
.authorities(SecurityConstants.SECURITY_ROOLI_LAHETYS_FULL, SecurityConstants.SECURITY_ROOLI_LAHETYS_FULL+"_1.2.3")
.authorities(SecurityConstants.SECURITY_ROOLI_LAHETYS_FULL, SecurityConstants.SECURITY_ROOLI_LAHETYS_FULL+"_1.2.246.562.10.240484683010")
.build()
val katselu = User.withDefaultPasswordEncoder()
.username("katselu")
.password("password")
.authorities(SecurityConstants.SECURITY_ROOLI_KATSELU_FULL, SecurityConstants.SECURITY_ROOLI_KATSELU_FULL+"_1.2.3")
.authorities(SecurityConstants.SECURITY_ROOLI_KATSELU_FULL, SecurityConstants.SECURITY_ROOLI_KATSELU_FULL+"_1.2.246.562.10.240484683010")
.build()
new InMemoryUserDetailsManager(admin, user, lahetys, katselu)
}
Expand All @@ -78,7 +78,7 @@ class SecurityConfiguration {
.csrf(c => c.disable())
.formLogin(c => {
// ohjataan lokaaliympäristön raportointikäliin
c.defaultSuccessUrl("http://localhost:3000/lahetykset")
c.defaultSuccessUrl("http://localhost:3000")
})
.build()
}
Expand Down
17 changes: 17 additions & 0 deletions lambdat/raportointi/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,23 @@
<groupId>fi.vm.sade.java-utils</groupId>
<artifactId>opintopolku-user-details-service</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.0.0-jre</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.scala-lang/toolkit -->
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>toolkit_3</artifactId>
<version>0.2.1</version>
<exclusions>
<exclusion>
<groupId>org.scala-lang</groupId>
<artifactId>scala3-library_3</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>fi.vm.sade.java-utils</groupId>
<artifactId>opintopolku-cas-servlet-filter</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package fi.oph.viestinvalitys.raportointi.integration

import com.google.common.cache.{CacheBuilder, CacheLoader, LoadingCache}
import org.slf4j.LoggerFactory
import sttp.client4.quick.*
import sttp.client4.Response
import sttp.model.Uri

import java.util.concurrent.TimeUnit

object OrganisaatioCache extends OrganisaatioCache
class OrganisaatioCache {
val callerId: String = "1.2.246.562.10.00000000001.viestinvalitys-raportointi"
val headers: Map[String, String] = Map("Caller-Id" -> callerId, "CSRF" -> callerId)
val queryParams =
Map("rekursiivisesti" -> "true", "aktiiviset" -> "true", "suunnitellut" -> "false", "lakkautetut" -> "false")

val LOG = LoggerFactory.getLogger(classOf[OrganisaatioCache])

val childOidsLoader = new CacheLoader[String, Response[String]] {
def load(oid: String): Response[String] =
// TODO url konfiguraatioihin
val uri: Uri = uri"https://virkailija.testiopintopolku.fi/organisaatio-service/api/$oid/childoids?$queryParams"
quickRequest
.headers(headers)
.cookie("CSRF", callerId)
.get(uri)
.send()
}

// TODO asetukset konffeihin?
val childOidsCache: LoadingCache[String, Response[String]] = CacheBuilder.newBuilder()
.maximumSize(1500)
.expireAfterAccess(60, TimeUnit.MINUTES)
.build(childOidsLoader)
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package fi.oph.viestinvalitys.raportointi.integration

import com.google.common.cache.*
import org.slf4j.LoggerFactory
import sttp.client4.Response
import upickle.default.*

import java.util.concurrent.TimeUnit

object OrganisaatioClient extends OrganisaatioClient
class OrganisaatioClient {

val LOG = LoggerFactory.getLogger(classOf[OrganisaatioClient])

def getAllChildOidsFlat(oid: String): Set[String] =
if (!OrganisaatioOid.isValid(oid))
LOG.error(s"Organisaation oid $oid on virheellinen")
throw new RuntimeException(s"Organisaation oid $oid on virheellinen")
val response: Response[String] = OrganisaatioCache.childOidsCache.get(oid)
response.code.code match
case 200 => read[List[String]](response.body).toSet
case _ =>
LOG.error(s"organisaatioiden haku epäonnistui, status ${response.code.code} error ${response.statusText}")
throw new RuntimeException(s"Organisaatioiden haku epäonnistui: ${response.statusText}")

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package fi.oph.viestinvalitys.raportointi.integration

import scala.util.matching.Regex

object OrganisaatioOid {

val organisaatioOidPattern: Regex = "^1\\.2\\.246\\.562\\.(10|99)\\.\\d+$".r
def isValid(oid: String): Boolean = organisaatioOidPattern.matches(oid)
}

case class Organisaatio(oid: String,
parentOidPath: String,
oppilaitostyyppi: Option[String] = None,
nimi: Map[String, String],
status: String,
kotipaikkaUri: Option[String] = None,
children: List[Organisaatio] = List(),
organisaatiotyypit: List[String] = List(),
tyypit: List[String] = List()) {

}

case class OrganisaatioHierarkia(organisaatiot: List[Organisaatio])
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
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.OrganisaatioClient
import fi.oph.viestinvalitys.vastaanotto.model.ViestiValidator
import org.slf4j.LoggerFactory
import org.springframework.security.core.context.SecurityContextHolder

import scala.jdk.CollectionConverters.*
Expand All @@ -11,9 +13,9 @@ object SecurityConstants {
final val SECURITY_ROOLI_LAHETYS = "VIESTINVALITYS_LAHETYS"
final val SECURITY_ROOLI_KATSELU = "VIESTINVALITYS_KATSELU"
final val SECURITY_ROOLI_PAAKAYTTAJA = "VIESTINVALITYS_OPH_PAAKAYTTAJA"
final val SECURITY_ROOLI_LAHETYS_OIKEUS = Kayttooikeus(Option.empty, SECURITY_ROOLI_LAHETYS)
final val SECURITY_ROOLI_KATSELU_OIKEUS = Kayttooikeus(Option.empty, SECURITY_ROOLI_KATSELU)
final val SECURITY_ROOLI_PAAKAYTTAJA_OIKEUS = Kayttooikeus(Option.empty, SECURITY_ROOLI_PAAKAYTTAJA)
final val SECURITY_ROOLI_LAHETYS_OIKEUS = Kayttooikeus(SECURITY_ROOLI_LAHETYS, Option.empty)
final val SECURITY_ROOLI_KATSELU_OIKEUS = Kayttooikeus(SECURITY_ROOLI_KATSELU, Option.empty)
final val SECURITY_ROOLI_PAAKAYTTAJA_OIKEUS = Kayttooikeus(SECURITY_ROOLI_PAAKAYTTAJA, Option.empty)

final val LAHETYS_ROLES = Set(SECURITY_ROOLI_LAHETYS_OIKEUS, SECURITY_ROOLI_PAAKAYTTAJA_OIKEUS)
final val KATSELU_ROLES = Set(SECURITY_ROOLI_KATSELU_OIKEUS, SECURITY_ROOLI_PAAKAYTTAJA_OIKEUS)
Expand All @@ -23,18 +25,26 @@ class SecurityOperaatiot(
getOikeudet: () => Seq[String] = () => SecurityContextHolder.getContext.getAuthentication.getAuthorities.asScala.map(a => a.getAuthority).toSeq,
getUsername: () => String = () => SecurityContextHolder.getContext.getAuthentication.getName()) {

val LOG = LoggerFactory.getLogger(classOf[SecurityOperaatiot])
final val SECURITY_ROOLI_PREFIX_PATTERN = "^ROLE_APP_"
private lazy val kayttajanOikeudet = {
getOikeudet()
private lazy val kayttajanOikeudet: Set[Kayttooikeus] = {
val casoikeudet = getOikeudet()
.map(a => a.replaceFirst(SECURITY_ROOLI_PREFIX_PATTERN, ""))
.map(a => {
val organisaatioOikeus = ViestiValidator.KAYTTOOIKEUSPATTERN.findFirstMatchIn(a)
if(organisaatioOikeus.isDefined)
Kayttooikeus(Option.apply(organisaatioOikeus.get.group(2)), organisaatioOikeus.get.group(1))
Kayttooikeus(organisaatioOikeus.get.group(1), Option.apply(organisaatioOikeus.get.group(2)))
else
Kayttooikeus(Option.empty, a)
Kayttooikeus(a, Option.empty)
})
.toSet
val lapsioikeudet = casoikeudet
.filter(kayttajanOikeus => kayttajanOikeus.organisaatio.isDefined)
.map(kayttajanOikeus =>
OrganisaatioClient.getAllChildOidsFlat(kayttajanOikeus.organisaatio.get)
.map(o => Kayttooikeus(kayttajanOikeus.oikeus, Some(o)))
).flatten
casoikeudet ++ lapsioikeudet
}
val identiteetti = getUsername()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,27 @@ class SecurityOperaatiotTest {
val OIKEUS = "OIKEUS1"

val securityOperaatiot = SecurityOperaatiot(() => Seq("ROLE_APP_" + OIKEUS + "_" + ORGANISAATIO), () => "")
Assertions.assertEquals(Set(Kayttooikeus(Option.apply(ORGANISAATIO), OIKEUS)), securityOperaatiot.getKayttajanOikeudet())
Assertions.assertEquals(Set(Kayttooikeus(OIKEUS, Option.apply(ORGANISAATIO))), securityOperaatiot.getKayttajanOikeudet())

@Test def testKatseluoikeudet(): Unit =

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), KATSELUOIKEUS))))
Assertions.assertEquals(true, securityOperaatiot.onOikeusKatsellaEntiteetti("omistaja", Set(Kayttooikeus(Option.empty, KATSELUOIKEUS))))
Assertions.assertEquals(true, securityOperaatiot.onOikeusKatsellaEntiteetti("omistaja", Set(Kayttooikeus(KATSELUOIKEUS, Option.apply(ORGANISAATIO)))))
Assertions.assertEquals(true, securityOperaatiot.onOikeusKatsellaEntiteetti("omistaja", Set(Kayttooikeus(KATSELUOIKEUS, Option.empty))))
Assertions.assertEquals(false, securityOperaatiot.onOikeusLahettaa())
Assertions.assertEquals(false, securityOperaatiot.onOikeusLahettaaEntiteetti("omistaja", Set(Kayttooikeus(Option.apply(ORGANISAATIO), KATSELUOIKEUS))))
Assertions.assertEquals(false, securityOperaatiot.onOikeusLahettaaEntiteetti("omistaja", Set(Kayttooikeus(KATSELUOIKEUS, Option.apply(ORGANISAATIO)))))

@Test def testLahetysoikeudet(): Unit =

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), LAHETYSOIKEUS))))
Assertions.assertEquals(true, securityOperaatiot.onOikeusLahettaaEntiteetti("omistaja", Set(Kayttooikeus(Option.empty, LAHETYSOIKEUS))))
Assertions.assertEquals(false, securityOperaatiot.onOikeusLahettaaEntiteetti("omistaja", Set(Kayttooikeus(Option.apply(ORGANISAATIO2), LAHETYSOIKEUS))))
Assertions.assertEquals(true, securityOperaatiot.onOikeusLahettaaEntiteetti("omistaja", Set(Kayttooikeus(LAHETYSOIKEUS, Option.apply(ORGANISAATIO)))))
Assertions.assertEquals(true, securityOperaatiot.onOikeusLahettaaEntiteetti("omistaja", Set(Kayttooikeus(LAHETYSOIKEUS, Option.empty))))
Assertions.assertEquals(false, securityOperaatiot.onOikeusLahettaaEntiteetti("omistaja", Set(Kayttooikeus(LAHETYSOIKEUS, Option.apply(ORGANISAATIO2)))))
// lähetysoikeuksiin ei automaattisesti sisälly katseluikeus!
Assertions.assertEquals(false, securityOperaatiot.onOikeusKatsella())
Assertions.assertEquals(false, securityOperaatiot.onOikeusKatsellaEntiteetti("omistaja", Set(Kayttooikeus(Option.apply(ORGANISAATIO), LAHETYSOIKEUS))))
Assertions.assertEquals(false, securityOperaatiot.onOikeusKatsellaEntiteetti("omistaja", Set(Kayttooikeus(LAHETYSOIKEUS, Option.apply(ORGANISAATIO)))))


@Test def testPaakayttajanOikeudet(): Unit =
Expand All @@ -49,8 +49,8 @@ class SecurityOperaatiotTest {
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), KATSELUOIKEUS))))
Assertions.assertEquals(true, securityOperaatiot.onOikeusLahettaaEntiteetti("omistaja", Set(Kayttooikeus(Option.apply(ORGANISAATIO), KATSELUOIKEUS))))
Assertions.assertEquals(true, securityOperaatiot.onOikeusKatsellaEntiteetti("omistaja", Set(Kayttooikeus(KATSELUOIKEUS, Option.apply(ORGANISAATIO)))))
Assertions.assertEquals(true, securityOperaatiot.onOikeusLahettaaEntiteetti("omistaja", Set(Kayttooikeus(KATSELUOIKEUS, Option.apply(ORGANISAATIO)))))


@Test def testLahetyksellaEiOikeusrajauksia(): Unit =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ class ViestiResource {
prioriteetti = viesti.prioriteetti.map(p => Prioriteetti.valueOf(p.toUpperCase)).toScala,
sailytysAika = viesti.sailytysaika.map(s => s.asInstanceOf[Int]).toScala,
kayttooikeusRajoitukset = viesti.kayttooikeusRajoitukset.toScala.map(r => r.asScala.toSet)
.map(kayttooikeudet => kayttooikeudet.map(kayttooikeus => Kayttooikeus(kayttooikeus.getOrganisaatio.toScala, kayttooikeus.getOikeus.get))).getOrElse(Set.empty),
.map(kayttooikeudet => kayttooikeudet.map(kayttooikeus => Kayttooikeus(kayttooikeus.getOikeus.get, kayttooikeus.getOrganisaatio.toScala))).getOrElse(Set.empty),
metadata = viesti.metadata.toScala.map(m => m.asScala.map(entry => entry._1 -> entry._2.asScala.toSeq).toMap).getOrElse(Map.empty),
omistaja = securityOperaatiot.getIdentiteetti()
)
Expand Down
6 changes: 5 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,11 @@
<requireJavaVersion>
<version>17</version>
</requireJavaVersion>
<dependencyConvergence />
<dependencyConvergence>
<excludes>
<exclude>com.softwaremill.sttp.model:core_3</exclude>
</excludes>
</dependencyConvergence>
</rules>
</configuration>
</execution>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ case object raportointiTilat {

case class Kontakti(nimi: Option[String], sahkoposti: String)

case class Kayttooikeus(organisaatio: Option[String], oikeus: String)
case class Kayttooikeus(oikeus: String, organisaatio: Option[String])

case class Viesti(
tunniste: UUID,
Expand Down
Loading

0 comments on commit a41012d

Please sign in to comment.