Skip to content

Commit d6e1479

Browse files
committed
Lagt inn enkel validering av PID (fnr, dnr, hnr).
NB: Denne sjekker ikke skuddår eller om en måned faktisk har 31 dager - derav "enkel validering".
1 parent 8022394 commit d6e1479

File tree

8 files changed

+211
-1
lines changed

8 files changed

+211
-1
lines changed

ebms-send-in/build.gradle.kts

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ tasks {
4040

4141
dependencies {
4242
implementation(project(":felles"))
43-
implementation("no.nav.emottak:emottak-utils:0.0.4")
43+
implementation("no.nav.emottak:emottak-utils:0.0.7")
4444
implementation("com.sun.xml.messaging.saaj:saaj-impl:1.5.1")
4545
implementation(libs.hoplite.core)
4646
implementation(libs.hoplite.hocon)

ebms-send-in/src/main/kotlin/no/nav/emottak/pasientliste/PasientlisteService.kt

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package no.nav.emottak.pasientliste
22

33
import no.nav.emottak.pasientliste.validator.PasientlisteValidator.validateLegeIsAlsoSigner
4+
import no.nav.emottak.pasientliste.validator.PasientlisteValidator.validateSignerIsValidPid
45
import no.nav.emottak.util.LogLevel
56
import no.nav.emottak.util.asXml
67
import no.nav.emottak.util.marker
@@ -16,6 +17,7 @@ object PasientlisteService {
1617
when (fellesformatRequest.mottakenhetBlokk.ebAction) {
1718
"HentPasientliste", "StartAbonnement", "StoppAbonnement", "HentAbonnementStatus" -> {
1819
fellesformatRequest.validateLegeIsAlsoSigner()
20+
fellesformatRequest.validateSignerIsValidPid()
1921
forwardRequest(fellesformatRequest)
2022
}
2123
else -> throw NotImplementedError(

ebms-send-in/src/main/kotlin/no/nav/emottak/pasientliste/validator/PasientlisteValidator.kt

+13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package no.nav.emottak.pasientliste.validator
22

33
import no.kith.xmlstds.nav.pasientliste._2010_02_01.PasientlisteForesporsel
4+
import no.nav.emottak.util.PidValidator
45
import no.nav.emottak.util.marker
56
import no.trygdeetaten.xml.eiff._1.EIFellesformat
67
import org.slf4j.Logger
@@ -11,6 +12,7 @@ object PasientlisteValidator {
1112
private val log: Logger = LoggerFactory.getLogger(PasientlisteValidator::class.java)
1213

1314
const val CONFLICT_SIGNING_SSN = "Sender FNR og legen som har signert meldingen matcher ikke."
15+
const val CONFLICT_INVALID_FNR = "Sender FNR validerte ikke."
1416

1517
fun EIFellesformat.validateLegeIsAlsoSigner() {
1618
when (this.getLegeFnr() == this.mottakenhetBlokk.avsenderFnrFraDigSignatur) {
@@ -22,6 +24,16 @@ object PasientlisteValidator {
2224
}
2325
}
2426

27+
fun EIFellesformat.validateSignerIsValidPid() {
28+
when (PidValidator.isValidFodselsnummer(this.mottakenhetBlokk.avsenderFnrFraDigSignatur)) {
29+
true -> log.info(this.marker(), "Successfully validated that signer (lege) is a valid PID (FNR)")
30+
false -> {
31+
log.error(this.marker(), "Signer (lege) was not a valid PID (FNR)")
32+
throw InvalidPidException()
33+
}
34+
}
35+
}
36+
2537
private fun EIFellesformat.getLegeFnr(): String {
2638
try {
2739
val foresporsel = this.msgHead.document.first().refDoc.content.any.first() as PasientlisteForesporsel
@@ -33,4 +45,5 @@ object PasientlisteValidator {
3345
}
3446

3547
class SigningConflictException : IllegalStateException(CONFLICT_SIGNING_SSN)
48+
class InvalidPidException : IllegalStateException(CONFLICT_INVALID_FNR)
3649
}

ebms-send-in/src/test/kotlin/no/nav/emottak/Mocks.kt

+5
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ val validSendInPasientlisteRequest = lazy {
1313
mockSendInRequest("PasientisteForesporsel", "HentPasientliste", fagmelding.readAllBytes(), "123456789")
1414
}
1515

16+
val invalidPidSendInPasientlisteRequest = lazy {
17+
val fagmelding = ClassLoader.getSystemResourceAsStream("hentpasientliste/hentpasientliste-payload-invalidPid.xml")
18+
mockSendInRequest("PasientisteForesporsel", "HentPasientliste", fagmelding.readAllBytes(), "11223312345")
19+
}
20+
1621
val validSendInHarBorgerFrikortRequest = lazy {
1722
val fagmelding = ClassLoader.getSystemResourceAsStream("frikort/EgenandelForesporsel_HarBorgerFrikortRequest.xml")
1823
mockSendInRequest("HarBorgerFrikort", "EgenandelForesporsel", fagmelding.readAllBytes(), "123456789")

ebms-send-in/src/test/kotlin/no/nav/emottak/PasientlisteServiceTest.kt

+9
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,15 @@ class PasientlisteServiceTest {
3434
}
3535
}
3636

37+
@Test
38+
fun `Should throw exception when senderFnr (SSN) matches request's signedOf, but signedOf is not a valid PID`() {
39+
try {
40+
PasientlisteService.pasientlisteForesporsel(invalidPidSendInPasientlisteRequest.value.asEIFellesFormat())
41+
} catch (exception: RuntimeException) {
42+
Assertions.assertEquals(exception.message, PasientlisteValidator.CONFLICT_INVALID_FNR)
43+
}
44+
}
45+
3746
@Test
3847
fun `Should not throw exception when senderFnr (SSN) matches request's signedOf`() {
3948
val fellesformat = validSendInPasientlisteRequest.value.copy(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package no.nav.emottak.util
2+
3+
import org.junit.jupiter.api.Test
4+
5+
class PidValidatorTest {
6+
7+
@Test
8+
fun `Should accept FNR`() = assert(PidValidator.isValidFodselsnummer("13097248022"))
9+
10+
@Test
11+
fun `Should accept DNR`() {
12+
// dnr is identical to fnr except for the first digit
13+
assert(PidValidator.isValidFodselsnummer("53097248016"))
14+
}
15+
16+
@Test
17+
fun `Should accept HNR`() {
18+
// hnr is identical to fnr except for the third digit
19+
assert(PidValidator.isValidFodselsnummer("13527248013"))
20+
}
21+
22+
@Test
23+
fun `Should reject if less than 11 digits`() = assert(!PidValidator.isValidFodselsnummer("1234567890"))
24+
25+
@Test
26+
fun `Should reject if more than 11 digits`() = assert(!PidValidator.isValidFodselsnummer("123456789012"))
27+
28+
@Test
29+
fun `Should reject if checksum 1 is invalid`() = assert(!PidValidator.isValidFodselsnummer("13097248032"))
30+
31+
@Test
32+
fun `Should reject if checksum 2 is invalid`() = assert(!PidValidator.isValidFodselsnummer("13097248023"))
33+
34+
@Test
35+
fun `Should reject if day is invalid`() = assert(!PidValidator.isValidFodselsnummer("32049648742"))
36+
37+
// @Test
38+
fun `Should reject if day of month is invalid`() {
39+
// NB: Does not validate leap years or if the month actually have 31 days
40+
assert(!PidValidator.isValidFodselsnummer("31049648742"))
41+
}
42+
43+
@Test
44+
fun `Should reject if month is invalid`() = assert(!PidValidator.isValidFodselsnummer("13137248022"))
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<MsgHead xmlns="http://www.kith.no/xmlstds/msghead/2006-05-24"
3+
xsi:schemaLocation="http://www.kith.no/xmlstds/msghead/2006-05-24 MsgHead-v1_2.xsd"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
5+
<MsgInfo>
6+
<Type DN="Hent pasientliste" V="HentPasientliste"/>
7+
<MIGversion>v1.2 2006-05-24</MIGversion>
8+
<GenDate>2021-01-18T14:58:14</GenDate>
9+
<MsgId>1ed4dd1b-ec4f-4fc3-b152-1737555b47b9</MsgId>
10+
<ProcessingStatus DN="Opplæring" V="T"/>
11+
<Sender>
12+
<Organisation>
13+
<OrganisationName>Tveita Legesenter</OrganisationName>
14+
<Ident>
15+
<Id>1382</Id>
16+
<TypeId V="HER" DN="HER-id" S="2.16.578.1.12.4.1.1.9051"/>
17+
</Ident>
18+
<HealthcareProfessional>
19+
<FamilyName>Toska</FamilyName>
20+
<GivenName>Emil</GivenName>
21+
<Ident>
22+
<Id>11223312345</Id>
23+
<TypeId DN="Fødselsnummer" S="2.16.578.1.12.4.1.1.8116" V="FNR"/>
24+
</Ident>
25+
</HealthcareProfessional>
26+
</Organisation>
27+
</Sender>
28+
<Receiver>
29+
<Organisation>
30+
<OrganisationName>NAV Arbeidsog velferdsdirektoratet</OrganisationName>
31+
<Ident>
32+
<Id>90128</Id>
33+
<TypeId V="HER" DN="HER-id" S="2.16.578.1.12.4.1.1.9051"/>
34+
</Ident>
35+
<Organisation>
36+
<OrganisationName>Samhandling Arbeids- og velferdsetaten</OrganisationName>
37+
<Ident>
38+
<Id>79768</Id>
39+
<TypeId V="HER" DN="HER-id" S="2.16.578.1.12.4.1.1.9051"/>
40+
</Ident>
41+
</Organisation>
42+
</Organisation>
43+
</Receiver>
44+
</MsgInfo>
45+
<Document>
46+
<RefDoc>
47+
<MsgType DN="Hent pasientliste" V="HentPasientliste"/>
48+
<Content>
49+
<ep2:PasientlisteForesporsel xmlns:ep2="http://www.kith.no/xmlstds/nav/pasientliste/2010-02-01">
50+
<ep2:HentPasientliste>
51+
<ep2:FnrLege>11223312345</ep2:FnrLege>
52+
<ep2:KommuneNr>0301</ep2:KommuneNr>
53+
<ep2:Format DN="PasientInformasjon" V="PI"/>
54+
</ep2:HentPasientliste>
55+
</ep2:PasientlisteForesporsel>
56+
</Content>
57+
</RefDoc>
58+
</Document>
59+
<dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
60+
<dsig:SignedInfo>
61+
<dsig:CanonicalizationMethod
62+
Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></dsig:CanonicalizationMethod>
63+
<dsig:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"></dsig:SignatureMethod>
64+
<dsig:Reference URI="">
65+
<dsig:Transforms>
66+
<dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></dsig:Transform>
67+
<dsig:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></dsig:Transform>
68+
</dsig:Transforms>
69+
<dsig:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></dsig:DigestMethod>
70+
<dsig:DigestValue>YcNcAnTeHNLKvU38/aurCUppyv9bLhatfroV4OodtP4=</dsig:DigestValue>
71+
</dsig:Reference>
72+
</dsig:SignedInfo>
73+
<dsig:SignatureValue>RucQnGbNf2pRTDnDBhRN+S46oCj1nUedDkVgQsWRbX/7pjwS+B/RprcGqIeAMqEDOJW9CJJF
74+
M58exQ/s/Ke1qP+3Dhrw5HZtLc4spBblfKk4hENCphICcFsRTv5Aw7hVqk/VsMBR2YfQahwf
75+
JjTZ75SIrmYbG3dzy+x60FOqN71nPFF9+Gi10Dn4Oymh1W0ttttBegkumhnx5MQ2dD+MkXGe
76+
+yIqfbZk4ADyRxBno+QfOjgpmPhYRiHCE2y2eWgLoS3yxZKpun1+OK4/aZymtBxfTLlG4dlK
77+
wnL+QbkjwQHqsyiyMtMS/FvEADb64mcWi7GXRe2sxekBEhZoNh8HLQ==
78+
</dsig:SignatureValue>
79+
<dsig:KeyInfo>
80+
<dsig:X509Data>
81+
<dsig:X509Certificate>
82+
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
83+
</dsig:X509Certificate>
84+
</dsig:X509Data>
85+
</dsig:KeyInfo>
86+
</dsig:Signature>
87+
</MsgHead><!-- Version: 2.80.240903 User: ML-DEV-GSE - Machine: ML-UTV-GSE-001 - IP: 10.70.69.220 -->
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package no.nav.emottak.util
2+
3+
// Based on fnrValideringUtil.ts from https://github.com/navikt/finnfastlege
4+
abstract class PidValidator {
5+
6+
companion object {
7+
8+
fun isValidFodselsnummer(fodselsnummer: String): Boolean {
9+
if (!fodselsnummer.matches(Regex("^[0-9]{11}\$"))) {
10+
return false
11+
}
12+
if (!isValidFodselsdato(fodselsnummer.substring(0, 6))) {
13+
return false
14+
}
15+
val fodselsnummerListe = fodselsnummer.map { it.toString().toInt(decimalRadix) }
16+
val kontrollSiffer1 = hentKontrollSiffer(fodselsnummerListe.take(9), kontrollRekke1)
17+
val kontrollSiffer2 = hentKontrollSiffer(fodselsnummerListe.take(10), kontrollRekke2)
18+
return fodselsnummerListe[9] == kontrollSiffer1 && fodselsnummerListe[10] == kontrollSiffer2
19+
}
20+
21+
private val kontrollRekke1 = listOf(3, 7, 6, 1, 8, 9, 4, 5, 2)
22+
private val kontrollRekke2 = listOf(5, 4, 3, 2, 7, 6, 5, 4, 3, 2)
23+
private const val decimalRadix = 10
24+
25+
private fun isValidPNumber(dag: Int, maned: Int) = dag > 0 && dag <= 32 && maned > 0 && maned <= 12
26+
27+
private fun isValidDNumber(dag: Int, maned: Int) = dag > 40 && dag <= 72 && maned > 0 && maned <= 12
28+
29+
private fun isValidHNumber(dag: Int, maned: Int) = dag > 0 && dag <= 32 && maned > 40 && maned <= 52
30+
31+
private fun isValidDate(dag: Int, maned: Int) =
32+
isValidPNumber(dag, maned) || isValidDNumber(dag, maned) || isValidHNumber(dag, maned)
33+
34+
private fun isValidFodselsdato(fodselsnummer: String): Boolean {
35+
val dag = fodselsnummer.substring(0, 2).toInt(decimalRadix)
36+
val maned = fodselsnummer.substring(2, 4).toInt(decimalRadix)
37+
return isValidDate(dag, maned)
38+
}
39+
40+
private fun hentKontrollSiffer(fodselsnummer: List<Int>, kontrollrekke: List<Int>): Int {
41+
var sum = 0
42+
for (i in fodselsnummer.indices) {
43+
sum += fodselsnummer[i] * kontrollrekke[i]
44+
}
45+
val kontrollSiffer = sum % 11
46+
return if (kontrollSiffer != 0) 11 - kontrollSiffer else 0
47+
}
48+
}
49+
}

0 commit comments

Comments
 (0)