Skip to content

Commit

Permalink
OY-5034 Hyväksytään myös ei validit sähköpostiosoitteet
Browse files Browse the repository at this point in the history
  - siirretään lähetyksen yhteydessä suoraan virhetilaan
  • Loading branch information
jkorri committed Jan 14, 2025
1 parent b8abd83 commit 2881ff5
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import com.fasterxml.jackson.datatype.jdk8.Jdk8Module
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.nimbusds.jose.util.StandardCharset
import fi.oph.viestinvalitys.business.{Kieli, Kontakti, Lahetys, Liite, LiitteenTila, Prioriteetti, SisallonTyyppi, VastaanottajanTila, Viesti}
import fi.oph.viestinvalitys.business.{Kieli, Kontakti, Lahetys, Liite, LiitteenTila, Prioriteetti, SisallonTyyppi, VastaanottajanSiirtyma, VastaanottajanTila, Viesti}
import fi.oph.viestinvalitys.lahetys.LambdaHandler.SAHKOPOSTIOSOITE_EI_VALIDI_ERROR
import fi.oph.viestinvalitys.security.{AuditLog, AuditOperation}
import fi.oph.viestinvalitys.util.AwsUtil
import fi.oph.viestinvalitys.vastaanotto.model.Lahetys.Lahettaja
Expand Down Expand Up @@ -767,4 +768,29 @@ class IntegraatioTest extends BaseIntegraatioTesti {
}, 60.seconds)
catch
case e: Exception => Assertions.fail("Vastaanottaja ei muuttunut delivery-tilaan sallitussa ajassa")

/**
* Testataan virheellisen vastaanottajan (sähköposti ei validi) lähetys. Pitää mennä virhetilaan.
*/
@Test def testLuoViestiVirheellinenVastaanottaja(): Unit =
// luodaan viesti
val luoViestiResult = mvc.perform(jsonPost(LahetysAPIConstants.LUO_VIESTI_PATH, getViesti(vastaanottajat =
java.util.List.of(VastaanottajaImpl(Optional.empty(), Optional.of("täysin väärä osoite")))))
.`with`(user("kayttaja").roles(SecurityConstants.SECURITY_ROOLI_LAHETYS_FULL.replace("ROLE_", ""))))
.andExpect(status().isOk()).andReturn()
val response = objectMapper.readValue(luoViestiResult.getResponse.getContentAsString, classOf[LuoViestiSuccessResponseImpl])
val vastaanottaja = kantaOperaatiot.getLahetyksenVastaanottajat(response.lahetysTunniste, Option.empty, Option.empty).head

// haetaan viestin ainoan vastaanottajan tila sekunnin välein kunnes tilassa VIRHE
var siirtyma: VastaanottajanSiirtyma = null
try
Await.ready(Future {
while (siirtyma==null || !VastaanottajanTila.VIRHE.equals(siirtyma.tila))
Thread.sleep(1000)
siirtyma = kantaOperaatiot.getVastaanottajanSiirtymat(vastaanottaja.tunniste).head
}, 60.seconds)
catch
case e: Exception => Assertions.fail("Vastaanottaja ei muuttunut delivery-tilaan sallitussa ajassa")

Assertions.assertEquals(SAHKOPOSTIOSOITE_EI_VALIDI_ERROR, siirtyma.lisatiedot)
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ case class LahettajaImpl(
@(Schema @field)(example = "Opintopolku", maxLength = Viesti.VIESTI_NIMI_MAX_PITUUS)
@BeanProperty nimi: Optional[String],

@(Schema @field)(description="Domainin pitää olla opintopolku.fi", example = "[email protected]", requiredMode=RequiredMode.REQUIRED)
@(Schema @field)(description="Domainin pitää olla opintopolku.fi", example = "[email protected]", requiredMode=RequiredMode.REQUIRED, maxLength = Viesti.VIESTI_OSOITE_MAX_PITUUS)
@BeanProperty sahkopostiOsoite: Optional[String],
) extends Lahettaja {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public interface Viesti {
* Viestin lähettäjän ja yksittäisten vastaanottajien nimien maksimipituus.
*/
static final int VIESTI_NIMI_MAX_PITUUS = 64;
static final int VIESTI_OSOITE_MAX_PITUUS = 512;

static final int VIESTI_SALAISUUS_MIN_PITUUS = 8;
static final int VIESTI_SALAISUUS_MAX_PITUUS = 1024;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ case class VastaanottajaImpl(
@(Schema @field)(example = "Vallu Vastaanottaja", maxLength = Viesti.VIESTI_NIMI_MAX_PITUUS)
@BeanProperty nimi: Optional[String],

@(Schema @field)(example = "[email protected]", requiredMode=RequiredMode.REQUIRED)
@(Schema @field)(example = "[email protected]", requiredMode=RequiredMode.REQUIRED, maxLength = Viesti.VIESTI_OSOITE_MAX_PITUUS)
@BeanProperty sahkopostiOsoite: Optional[String],
) extends Vastaanottaja {

Expand Down
4 changes: 4 additions & 0 deletions lambdat/lahetys/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
</dependency>
<dependency>
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import software.amazon.awssdk.core.SdkBytes
import software.amazon.awssdk.services.cloudwatch.model.{Dimension, MetricDatum, PutMetricDataRequest, StandardUnit}
import software.amazon.awssdk.services.s3.model.GetObjectRequest
import software.amazon.awssdk.services.ses.model.{RawMessage, SendRawEmailRequest}
import org.apache.commons.validator.routines.EmailValidator

import java.io.ByteArrayOutputStream
import java.time.Instant
Expand All @@ -28,6 +29,8 @@ import java.util.UUID
import scala.jdk.CollectionConverters.{CollectionHasAsScala, SeqHasAsJava}

object LambdaHandler {
val SAHKOPOSTIOSOITE_EI_VALIDI_ERROR = "Sähköpostiosoite ei validi"

val LOG = LoggerFactory.getLogger(classOf[LambdaHandler]);
val queueUrl = ConfigurationUtil.getConfigurationItem(ConfigurationUtil.AJASTUS_QUEUE_URL_KEY).get;
val kantaOperaatiot = new KantaOperaatiot(DbUtil.database)
Expand Down Expand Up @@ -97,60 +100,70 @@ class LambdaHandler extends RequestHandler[SQSEvent, Void], Resource {
LogContext(vastaanottajaTunniste = vastaanottaja.tunniste.toString, viestiTunniste = vastaanottaja.viestiTunniste.toString)(() => {
try {
LOG.info(s"Lähetetään viestiä vastaanottajalle ${vastaanottaja.tunniste.toString}")
val viesti = viestit.getOrElseUpdate(vastaanottaja.viestiTunniste, kantaOperaatiot.getViestit(Seq(vastaanottaja.viestiTunniste)).find(v => true).get)

var builder = EmailBuilder.startingBlank()
.withContentTransferEncoding(ContentTransferEncoding.BASE_64)
.withSubject(viesti.otsikko)

if (viesti.replyTo.isDefined)
builder.withReplyTo(viesti.replyTo.get)

viesti.sisallonTyyppi match {
case SisallonTyyppi.TEXT => builder = builder.withPlainText(viesti.sisalto)
case SisallonTyyppi.HTML => builder = builder.withHTMLText(viesti.sisalto)
}

liitteet.getOrElseUpdate(viesti.tunniste, kantaOperaatiot.getViestinLiitteet(Seq(viesti.tunniste))
.find((viestiTunniste, liitteet) => true).map((viestiTunniste, liitteet) => liitteet.map(liite => {
val getObjectResponse = AwsUtil.s3Client.getObject(GetObjectRequest
.builder()
.bucket(bucketName)
.key(liite.tunniste.toString)

if(!EmailValidator.getInstance().isValid(vastaanottaja.kontakti.sahkoposti))
LOG.error(s"Vastaanottajan ${vastaanottaja.tunniste.toString} sähköposti ei ole validi, siirretään suoraan virhetilaan")
val changes: Changes = new Changes.Builder()
.added("lisatiedot", SAHKOPOSTIOSOITE_EI_VALIDI_ERROR)
.updated("vastaanottajanTila", vastaanottaja.tila.toString, VastaanottajanTila.VIRHE.toString)
.build()
AuditLog.logChanges(AuditLog.getAuditUserForLambda(), Map("vastaanottaja" -> vastaanottaja.tunniste.toString), AuditOperation.UpdateVastaanottajanTila, changes)
kantaOperaatiot.paivitaVastaanottajaVirhetilaan(vastaanottaja.tunniste, SAHKOPOSTIOSOITE_EI_VALIDI_ERROR)
else
val viesti = viestit.getOrElseUpdate(vastaanottaja.viestiTunniste, kantaOperaatiot.getViestit(Seq(vastaanottaja.viestiTunniste)).find(v => true).get)

var builder = EmailBuilder.startingBlank()
.withContentTransferEncoding(ContentTransferEncoding.BASE_64)
.withSubject(viesti.otsikko)

if (viesti.replyTo.isDefined)
builder.withReplyTo(viesti.replyTo.get)

viesti.sisallonTyyppi match {
case SisallonTyyppi.TEXT => builder = builder.withPlainText(viesti.sisalto)
case SisallonTyyppi.HTML => builder = builder.withHTMLText(viesti.sisalto)
}

liitteet.getOrElseUpdate(viesti.tunniste, kantaOperaatiot.getViestinLiitteet(Seq(viesti.tunniste))
.find((viestiTunniste, liitteet) => true).map((viestiTunniste, liitteet) => liitteet.map(liite => {
val getObjectResponse = AwsUtil.s3Client.getObject(GetObjectRequest
.builder()
.bucket(bucketName)
.key(liite.tunniste.toString)
.build())
(liite, getObjectResponse.readAllBytes)
})).getOrElse(Seq.empty)).foreach((liite, bytes) => {
builder = builder.withAttachment(liite.nimi, bytes, liite.contentType)
})

val sesTunniste = {
if (mode == Mode.PRODUCTION)
this.sendSesEmail(builder
.from(viesti.lahettaja.nimi.getOrElse(null), viesti.lahettaja.sahkoposti)
.to(vastaanottaja.kontakti.nimi.getOrElse(null), vastaanottaja.kontakti.sahkoposti)
.buildEmail())
else
sendTestEmail(vastaanottaja, builder.from(viesti.lahettaja.nimi.getOrElse(null), s"noreply@${ConfigurationUtil.opintopolkuDomain}"))
}
val changes: Changes = new Changes.Builder()
.added("sesTunniste", sesTunniste)
.updated("vastaanottajanTila",vastaanottaja.tila.toString, VastaanottajanTila.LAHETETTY.toString)
.build()
AuditLog.logChanges(AuditLog.getAuditUserForLambda(), Map("vastaanottaja" -> vastaanottaja.tunniste.toString), AuditOperation.SendEmail, changes)
LOG.info(s"Lähetetty viesti vastaanottajalle ${vastaanottaja.tunniste.toString}")
kantaOperaatiot.paivitaVastaanottajaLahetetyksi(vastaanottaja.tunniste, sesTunniste)

metricDatums.add(MetricDatum.builder()
.metricName("LahetyksienMaara")
.value(1)
.storageResolution(1)
.dimensions(Seq(Dimension.builder()
.name("Prioriteetti")
.value(viesti.prioriteetti.toString)
.build()).asJava)
.timestamp(Instant.now())
.unit(StandardUnit.COUNT)
.build())
(liite, getObjectResponse.readAllBytes)
})).getOrElse(Seq.empty)).foreach((liite, bytes) => {
builder = builder.withAttachment(liite.nimi, bytes, liite.contentType)
})

val sesTunniste = {
if (mode == Mode.PRODUCTION)
this.sendSesEmail(builder
.from(viesti.lahettaja.nimi.getOrElse(null), viesti.lahettaja.sahkoposti)
.to(vastaanottaja.kontakti.nimi.getOrElse(null), vastaanottaja.kontakti.sahkoposti)
.buildEmail())
else
sendTestEmail(vastaanottaja, builder.from(viesti.lahettaja.nimi.getOrElse(null), s"noreply@${ConfigurationUtil.opintopolkuDomain}"))
}
val changes: Changes = new Changes.Builder()
.added("sesTunniste", sesTunniste)
.updated("vastaanottajanTila",vastaanottaja.tila.toString, VastaanottajanTila.LAHETETTY.toString)
.build()
AuditLog.logChanges(AuditLog.getAuditUserForLambda(), Map("vastaanottaja" -> vastaanottaja.tunniste.toString), AuditOperation.SendEmail, changes)
LOG.info(s"Lähetetty viesti vastaanottajalle ${vastaanottaja.tunniste.toString}")
kantaOperaatiot.paivitaVastaanottajaLahetetyksi(vastaanottaja.tunniste, sesTunniste)

metricDatums.add(MetricDatum.builder()
.metricName("LahetyksienMaara")
.value(1)
.storageResolution(1)
.dimensions(Seq(Dimension.builder()
.name("Prioriteetti")
.value(viesti.prioriteetti.toString)
.build()).asJava)
.timestamp(Instant.now())
.unit(StandardUnit.COUNT)
.build())
} catch {
case e: Exception =>
LOG.error(s"Virhe lähetettäessä viestiä vastaanottajalle ${vastaanottaja.tunniste.toString}", e)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import fi.oph.viestinvalitys.vastaanotto.model.Lahetys.*
import fi.oph.viestinvalitys.vastaanotto.model.LahetysImpl.LAHETYS_PRIORITEETTI_KORKEA
import fi.oph.viestinvalitys.vastaanotto.model.Viesti.*
import fi.oph.viestinvalitys.vastaanotto.validation.LahetysValidator
import org.apache.commons.validator.routines.EmailValidator

import java.util.stream.Collectors
import java.util.{List, Optional, UUID}
Expand Down Expand Up @@ -52,7 +51,7 @@ object ViestiValidator:
final val VALIDATION_VASTAANOTTAJA_OSOITE_DUPLICATE = "vastaanottajat: Osoite-kentissä on duplikaatteja: "
final val VALIDATION_VASTAANOTTAJAN_NIMI_LIIAN_PITKA = "nimi-kenttä voi maksimissaan olla " + VIESTI_NIMI_MAX_PITUUS + " merkkiä pitkä"
final val VALIDATION_VASTAANOTTAJAN_OSOITE_TYHJA = "sähköpostiosoite-kenttä on pakollinen"
final val VALIDATION_VASTAANOTTAJAN_OSOITE_INVALID = "sähköpostiosoite ei ole validi sähköpostiosoite"
final val VALIDATION_VASTAANOTTAJAN_OSOITE_LIIAN_PITKA = "sähköpostiosoite void maksimissaan olla " + " merkkiä pitkä"

final val VALIDATION_LIITETUNNISTE_NULL = "liiteTunnisteet: Kenttä sisältää null-arvoja"
final val VALIDATION_LIITETUNNISTE_LIIKAA = "liiteTunnisteet: Viestillä voi maksimissaan olla " + VIESTI_LIITTEET_MAX_MAARA + " liitettä"
Expand Down Expand Up @@ -214,8 +213,8 @@ object ViestiValidator:
// validoidaan sahkopostiosoite
if (vastaanottaja.getSahkopostiOsoite.isEmpty || vastaanottaja.getSahkopostiOsoite.get.length == 0)
vastaanottajaVirheet.incl(VALIDATION_VASTAANOTTAJAN_OSOITE_TYHJA)
else if (!EmailValidator.getInstance().isValid(vastaanottaja.getSahkopostiOsoite.get))
vastaanottajaVirheet.incl(VALIDATION_VASTAANOTTAJAN_OSOITE_INVALID)
else if (vastaanottaja.getSahkopostiOsoite.get().length>Viesti.VIESTI_OSOITE_MAX_PITUUS)
vastaanottajaVirheet.incl(VALIDATION_VASTAANOTTAJAN_OSOITE_LIIAN_PITKA)
else vastaanottajaVirheet).get

if (!vastaanottajaVirheet.isEmpty)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,17 +176,17 @@ class ViestiValidatorTest {
Assertions.assertEquals(Set("Vastaanottaja (nimi: Vallu Vastaanottaja, sähköpostiosoite: Optional.empty): " + ViestiValidator.VALIDATION_VASTAANOTTAJAN_OSOITE_TYHJA),
ViestiValidator.validateVastaanottajat(Optional.of(util.List.of(getVastaanottaja("Vallu Vastaanottaja", null)))))

// ei validi sähköpostiosoite ei ole sallittu
Assertions.assertEquals(Set("Vastaanottaja (nimi: Vallu Vastaanottaja, sähköpostiosoite: Optional[ei validi osoite]): " + ViestiValidator.VALIDATION_VASTAANOTTAJAN_OSOITE_INVALID),
ViestiValidator.validateVastaanottajat(Optional.of(util.List.of(getVastaanottaja("Vallu Vastaanottaja", "ei validi osoite")))))
// liian pitkä sähköpostiosoite ei ole sallittu
Assertions.assertEquals(Set("Vastaanottaja (nimi: Vallu Vastaanottaja, sähköpostiosoite: Optional[" + "x".repeat(Viesti.VIESTI_OSOITE_MAX_PITUUS+1) + "]): " + ViestiValidator.VALIDATION_VASTAANOTTAJAN_OSOITE_LIIAN_PITKA),
ViestiValidator.validateVastaanottajat(Optional.of(util.List.of(getVastaanottaja("Vallu Vastaanottaja", "x".repeat(Viesti.VIESTI_OSOITE_MAX_PITUUS+1))))))

// kaikki virheet kerätään
Assertions.assertEquals(Set(
"Vastaanottaja (nimi: Vallu Vastaanottaja, sähköpostiosoite: Optional.empty): " + ViestiValidator.VALIDATION_VASTAANOTTAJAN_OSOITE_TYHJA,
"Vastaanottaja (nimi: Vallu Vastaanottaja, sähköpostiosoite: Optional[ei validi osoite]): " + ViestiValidator.VALIDATION_VASTAANOTTAJAN_OSOITE_INVALID),
"Vastaanottaja (nimi: Vallu Vastaanottaja, sähköpostiosoite: Optional[" + "x".repeat(Viesti.VIESTI_OSOITE_MAX_PITUUS+1) + "]): " + ViestiValidator.VALIDATION_VASTAANOTTAJAN_OSOITE_LIIAN_PITKA),
ViestiValidator.validateVastaanottajat(Optional.of(util.List.of(
getVastaanottaja("Vallu Vastaanottaja", null),
getVastaanottaja("Vallu Vastaanottaja", "ei validi osoite")
getVastaanottaja("Vallu Vastaanottaja", "x".repeat(Viesti.VIESTI_OSOITE_MAX_PITUUS+1))
))))

// duplikaattiosoitteet eivät ole sallittuja
Expand Down

0 comments on commit 2881ff5

Please sign in to comment.