Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: oppijanumerorekisteri - integraatio #125

Merged
merged 8 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions infra/lib/service-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ export class ServiceStack extends Stack {
"kielitesti-token",
),
),
OPPIJANUMERO_PASSWORD: aws_ecs.Secret.fromSecretsManager(
aws_secretsmanager.Secret.fromSecretNameV2(
this,
"OppijanumeroPassword",
"oppijanumero-password",
),
),
},
},
cpu: 1024,
Expand Down
6 changes: 6 additions & 0 deletions scripts/test_oppijanumerorekisteri.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env bash

curl -s -i \
-X POST "https://virkailija.testiopintopolku.fi/cas/v1/tickets" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=$1&password=$2"
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) (7.8.0).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
package fi.oph.kitu.generated.api

import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*

@RestController
interface OppijanumeroControllerApi {
@RequestMapping(
method = [RequestMethod.GET],
value = ["/api/oppijanumero"],
produces = ["text/plain"],
)
fun getOppijanumero(): ResponseEntity<kotlin.String>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package fi.oph.kitu.oppijanumero

import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse

@Service
class CasAuthenticatedService(
private val httpClient: HttpClient,
private val casService: CasService,
) {
@Value("\${kitu.oppijanumero.callerid}")
private lateinit var callerId: String

fun authenticateToCas() {
val grantingTicket = casService.getGrantingTicket()
val serviceTicket = casService.getServiceTicket(grantingTicket)

casService.sendAuthenticationRequest(serviceTicket)
}

fun sendRequest(requestBuilder: HttpRequest.Builder): HttpResponse<String> {
println("Sending CAS authenticated request")
requestBuilder
.header("Caller-Id", callerId)
.header("CSRF", "CSRF")
.header("Cookie", "CSRF=CSRF")
val response = httpClient.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofString())

if (isLoginToCas(response)) {
// Oppijanumerorekisteri ohjaa CAS kirjautumissivulle, jos autentikaatiota
// ei ole tehty. Luodaan uusi CAS ticket ja yritetään uudelleen.
println("Was redirected to CAS login")
authenticateToCas() // gets JSESSIONID Cookie and it will be used in the next request below
return httpClient.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofString())
} else if (response.statusCode() == 401) {
// Oppijanumerorekisteri vastaa HTTP 401 kun sessio on vanhentunut.
// HUOM! Oppijanumerorekisteri vastaa HTTP 401 myös jos käyttöoikeudet eivät riitä.
println("Received HTTP 401 response")
authenticateToCas() // gets JSESSIONID Cookie and it will be used in the next request below
return httpClient.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofString())
} else {
return response
}
}

private fun isLoginToCas(response: HttpResponse<*>): Boolean {
if (response.statusCode() == 302) {
val header = response.headers().firstValue("Location")
return header.map { location: String -> location.contains("/cas/login") }.orElse(false)
}
return false
}
}
85 changes: 85 additions & 0 deletions server/src/main/kotlin/fi/oph/kitu/oppijanumero/CasService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package fi.oph.kitu.oppijanumero

import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service
import java.net.URI
import java.net.URLEncoder
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse

@Service
class CasService(
private val httpClient: HttpClient,
) {
@Value("\${kitu.oppijanumero.username}")
private lateinit var onrUsername: String

@Value("\${kitu.oppijanumero.password}")
private lateinit var onrPassword: String

@Value("\${kitu.oppijanumero.casUrl}")
private lateinit var casUrl: String

@Value("\${kitu.oppijanumero.serviceUrl}")
private lateinit var serviceUrl: String

fun sendAuthenticationRequest(serviceTicket: String) {
val authRequest =
HttpRequest
.newBuilder(URI.create("$serviceUrl/j_spring_cas_security_check?ticket=$serviceTicket"))
.method("GET", HttpRequest.BodyPublishers.noBody())
.build()
val authResponse = httpClient.send(authRequest, HttpResponse.BodyHandlers.ofString())
println("Auth reset response: $authResponse")
}

fun getServiceTicket(ticketGrantingTicket: String): String {
val request =
HttpRequest
.newBuilder(URI.create("$casUrl/v1/tickets/$ticketGrantingTicket"))
.POST(
HttpRequest.BodyPublishers.ofString(
"service=$serviceUrl/j_spring_cas_security_check",
),
).header("Content-Type", "application/x-www-form-urlencoded")
.build()

val response = httpClient.send(request, HttpResponse.BodyHandlers.ofString())

if (response.statusCode() != 200) {
throw RuntimeException("Unexpected status code: ${response.statusCode()} and message: ${response.body()}")
}

val ticket = response.body()
println("Successfully got service ticket $ticket")
return ticket
}

fun getGrantingTicket(): String {
// Step 2 - form a request
val username = URLEncoder.encode(onrUsername, "UTF-8")
val password = URLEncoder.encode(onrPassword, "UTF-8")
val request =
HttpRequest
.newBuilder(URI.create("$casUrl/v1/tickets"))
.POST(HttpRequest.BodyPublishers.ofString("username=$username&password=$password"))
.header("Content-Type", "application/x-www-form-urlencoded")
.build()

// Step 3 - Get the response
val response = httpClient.send(request, HttpResponse.BodyHandlers.ofString())
val statusCode = response.statusCode()
val body = response.body()

if (statusCode != 201) {
throw RuntimeException("Ticket granting service responded with status code $statusCode and message $body")
}

val location = response.headers().firstValue("Location").get()
val ticket = location.substring(location.lastIndexOf("/") + 1)
println("Successfully fetched TGT (Ticket Granting Ticket): $ticket")

return ticket
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package fi.oph.kitu.oppijanumero

import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.net.CookieManager
import java.net.http.HttpClient
import java.time.Duration

@Configuration
class HttpClientConfig {
@Bean
fun HttpClient(): HttpClient =
HttpClient
.newBuilder()
.cookieHandler(CookieManager()) // sends JSESSIONID Cookie between the requests
.connectTimeout(Duration.ofSeconds(10))
.build()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package fi.oph.kitu.oppijanumero

import fi.oph.kitu.generated.api.OppijanumeroControllerApi
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.RestController

@RestController
class OppijanumeroController(
private val oppijanumeroService: OppijanumeroService,
) : OppijanumeroControllerApi {
override fun getOppijanumero(): ResponseEntity<String> {
try {
val response =
oppijanumeroService.yleistunnisteHae(
YleistunnisteHaeRequest(
"Magdalena Testi",
"010866-9260",
"Magdalena",
"Sallinen-Testi",
),
)
return ResponseEntity(
response.body(),
if (response.statusCode() == 200) HttpStatus.OK else HttpStatus.INTERNAL_SERVER_ERROR,
)
} catch (e: Exception) {
println("ERROR: ${e.message}")

return ResponseEntity(
"An unexpected error has occurred:${e.localizedMessage}",
HttpStatus.INTERNAL_SERVER_ERROR,
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package fi.oph.kitu.oppijanumero

import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service
import java.net.URI
import java.net.http.HttpRequest
import java.net.http.HttpResponse

@Service
class OppijanumeroService(
private val casAuthenticatedService: CasAuthenticatedService,
) {
@Value("\${kitu.oppijanumero.serviceUrl}")
private lateinit var serviceUrl: String

fun yleistunnisteHae(request: YleistunnisteHaeRequest): HttpResponse<String> {
val httpRequest =
HttpRequest
.newBuilder(URI.create("$serviceUrl/yleistunniste/hae"))
.POST(toBodyPublisher(request))
.header("Content-Type", "application/json")

val response = casAuthenticatedService.sendRequest(httpRequest)
return response
}

private fun toBodyPublisher(request: YleistunnisteHaeRequest): HttpRequest.BodyPublisher =
HttpRequest.BodyPublishers.ofString(
"""{
"etunimet": "${request.etunimet}",
"hetu": "${request.hetu}",
"kutsumanimi": "${request.kutsumanimi}",
"sukunimi": "${request.sukunimi}"
}""".trim(),
)
}

data class YleistunnisteHaeRequest(
val etunimet: String,
val hetu: String,
val kutsumanimi: String,
val sukunimi: String,
)
6 changes: 6 additions & 0 deletions server/src/main/resources/application-e2e.properties
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,9 @@ kitu.opintopolkuHostname=virkailija.untuvaopintopolku.fi

kitu.kielitesti.wstoken=
kitu.kielitesti.baseurl=

kitu.oppijanumero.username=koto-rekisteri
kitu.oppijanumero.password=
kitu.oppijanumero.callerid=1.2.246.562.24.85478397072
kitu.oppijanumero.casUrl=https://virkailija.untuvaopintopolku.fi/cas
kitu.oppijanumero.serviceUrl=https://virkailija.untuvaopintopolku.fi/oppijanumerorekisteri-service
6 changes: 6 additions & 0 deletions server/src/main/resources/application-local.properties
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,9 @@ kitu.appUrl=http://localhost:8080
kitu.opintopolkuHostname=virkailija.untuvaopintopolku.fi

kitu.kielitesti.baseurl=https://kielitesti.mmg.fi

kitu.oppijanumero.username=koto-rekisteri
kitu.oppijanumero.password=${OPPIJANUMERO_PASSWORD}
kitu.oppijanumero.callerid=1.2.246.562.24.85478397072
kitu.oppijanumero.casUrl=https://virkailija.untuvaopintopolku.fi/cas
kitu.oppijanumero.serviceUrl=https://virkailija.untuvaopintopolku.fi/oppijanumerorekisteri-service
6 changes: 6 additions & 0 deletions server/src/main/resources/application-prod.properties
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,9 @@ spring.datasource.username=${DATABASE_USER}
spring.datasource.password=${DATABASE_PASSWORD}

kitu.kielitesti.baseurl=https://kielitesti.oph.fi

kitu.oppijanumero.username=koto-rekisteri
kitu.oppijanumero.password=${OPPIJANUMERO_PASSWORD}
kitu.oppijanumero.callerid=
kitu.oppijanumero.casUrl=https://virkailija.opintopolku.fi/cas
kitu.oppijanumero.serviceUrl=https://virkailija.opintopolku.fi/oppijanumerorekisteri-service
6 changes: 6 additions & 0 deletions server/src/main/resources/application-qa.properties
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,9 @@ spring.datasource.username=${DATABASE_USER}
spring.datasource.password=${DATABASE_PASSWORD}

kitu.kielitesti.baseurl=https://kielitesti.mmg.fi

kitu.oppijanumero.username=koto-rekisteri
kitu.oppijanumero.password=${OPPIJANUMERO_PASSWORD}
kitu.oppijanumero.callerid=1.2.246.562.24.77101904300
kitu.oppijanumero.casUrl=https://virkailija.testiopintopolku.fi/cas
kitu.oppijanumero.serviceUrl=https://virkailija.testiopintopolku.fi/oppijanumerorekisteri-service
6 changes: 6 additions & 0 deletions server/src/main/resources/application-untuva.properties
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,9 @@ spring.datasource.username=${DATABASE_USER}
spring.datasource.password=${DATABASE_PASSWORD}

kitu.kielitesti.baseurl=https://kielitesti.mmg.fi

kitu.oppijanumero.username=koto-rekisteri
kitu.oppijanumero.password=${OPPIJANUMERO_PASSWORD}
kitu.oppijanumero.callerid=1.2.246.562.24.85478397072
kitu.oppijanumero.casUrl=https://virkailija.untuvaopintopolku.fi/cas
kitu.oppijanumero.serviceUrl=https://virkailija.untuvaopintopolku.fi/oppijanumerorekisteri-service
6 changes: 6 additions & 0 deletions server/src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,9 @@ springdoc.swagger-ui.csrf.enabled=true
springdoc.api-docs.enabled=false

kitu.kielitesti.wstoken=${KIELITESTI_TOKEN}

kitu.oppijanumero.username=
kitu.oppijanumero.password=
kitu.oppijanumero.callerid=
kitu.oppijanumero.casUrl=
kitu.oppijanumero.serviceUrl=
6 changes: 6 additions & 0 deletions server/src/main/resources/example-local.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
# Kielitesti Moodle web service token
kitu.kielitesti.wstoken=

kitu.oppijanumero.username=koto-rekisteri
kitu.oppijanumero.password=
kitu.oppijanumero.callerid=
kitu.oppijanumero.casUrl=
kitu.oppijanumero.serviceUrl=
19 changes: 19 additions & 0 deletions server/src/main/resources/static/open-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,25 @@ paths:
type: array
items:
$ref: "#/components/schemas/Oppija"

/api/oppijanumero:
get:
tags:
- "oppijanumero-controller"
summary: "Hakee yhden käyttäjän oppijanumerorekisteristä."
operationId: "getOppijanumero"
responses:
200:
description: "Oppija löytyi"
content:
"text/plain":
schema:
type: string
example: "oid"
400:
description: "Virheellinen pyyntö"
500:
description: "Virheellinen pyyntö"
components:
schemas:
Oppija:
Expand Down
6 changes: 6 additions & 0 deletions server/src/test/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,9 @@ springdoc.api-docs.enabled=false

kitu.kielitesti.wstoken=
kitu.kielitesti.baseurl=

kitu.oppijanumero.username=koto-rekisteri
kitu.oppijanumero.password=
kitu.oppijanumero.callerid=
kitu.oppijanumero.casUrl=
kitu.oppijanumero.serviceUrl=
Loading