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

TOR-2210 Lisää tokenin tiedot vastauksiin #3241

Merged
merged 2 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import org.scalatra.ContentEncodingSupport
import org.scalatra.forms._
import org.scalatra.i18n.I18nSupport

import java.time.Instant
import java.time.temporal.ChronoUnit

class OmaDataOAuth2AuthorizationServerServlet(implicit val application: KoskiApplication)
extends KoskiSpecificApiServlet
with Logging with ContentEncodingSupport with NoCache with FormSupport with I18nSupport with RequiresOmaDataOAuth2 with OmaDataOAuth2Support {
Expand Down Expand Up @@ -40,7 +43,11 @@ class OmaDataOAuth2AuthorizationServerServlet(implicit val application: KoskiApp
allowedScopes = koskiSession.omaDataOAuth2Scopes
) match {
case Left(error) => error.getAccessTokenErrorResponse
case Right(successResponse) => successResponse
case Right(accessTokenInfo: AccessTokenInfo) => AccessTokenSuccessResponse(
access_token = accessTokenInfo.accessToken,
token_type = "Bearer",
expires_in = Instant.now().until(accessTokenInfo.expirationTime, ChronoUnit.SECONDS)
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ object OmaDataOAuth2KaikkiOpiskeluoikeudet {

case class OmaDataOAuth2KaikkiOpiskeluoikeudet(
henkilö: OmaDataOAuth2Henkilötiedot,
opiskeluoikeudet: List[Opiskeluoikeus]
opiskeluoikeudet: List[Opiskeluoikeus],
tokenInfo: OmaDataOAuth2TokenInfo
)

object OmaDataOAuth2SuoritetutTutkinnot {
Expand All @@ -28,7 +29,8 @@ object OmaDataOAuth2SuoritetutTutkinnot {

case class OmaDataOAuth2SuoritetutTutkinnot(
henkilö: OmaDataOAuth2Henkilötiedot,
opiskeluoikeudet: List[SuoritetutTutkinnotOpiskeluoikeus]
opiskeluoikeudet: List[SuoritetutTutkinnotOpiskeluoikeus],
tokenInfo: OmaDataOAuth2TokenInfo
)

object OmaDataOAuth2AktiivisetJaPäättyneetOpiskeluoikeudet {
Expand All @@ -38,7 +40,8 @@ object OmaDataOAuth2AktiivisetJaPäättyneetOpiskeluoikeudet {

case class OmaDataOAuth2AktiivisetJaPäättyneetOpiskeluoikeudet(
henkilö: OmaDataOAuth2Henkilötiedot,
opiskeluoikeudet: List[AktiivisetJaPäättyneetOpinnotOpiskeluoikeus]
opiskeluoikeudet: List[AktiivisetJaPäättyneetOpinnotOpiskeluoikeus],
tokenInfo: OmaDataOAuth2TokenInfo
)

object OmaDataOAuth2Henkilötiedot {
Expand Down Expand Up @@ -155,3 +158,8 @@ case class OmaDataOAuth2Henkilötiedot(
hetu: Option[String] = None,
syntymäaika: Option[LocalDate] = None
)

case class OmaDataOAuth2TokenInfo(
scope: String,
expirationTime: String
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ package fi.oph.koski.omadataoauth2

import fi.oph.koski.db.KoskiTables.{OAuth2Jako, OAuth2JakoKaikki}
import fi.oph.koski.db.PostgresDriverWithJsonSupport.api._
import fi.oph.koski.db.{DB, DatabaseExecutionContext, KoskiTables, OAuth2JakoRow, QueryMethods}
import fi.oph.koski.db._
import fi.oph.koski.log.Logging
import fi.oph.koski.omadataoauth2.OmaDataOAuth2Security.{generateSecret, sha256}

import java.sql.Timestamp
import java.time.LocalDateTime
import java.time.temporal.ChronoUnit
import java.time.{Instant, LocalDateTime}

class OmaDataOAuth2Repository(val db: DB) extends DatabaseExecutionContext with Logging with QueryMethods {
def create(
Expand Down Expand Up @@ -89,20 +88,17 @@ class OmaDataOAuth2Repository(val db: DB) extends DatabaseExecutionContext with
val warning = OmaDataOAuth2Error(OmaDataOAuth2ErrorType.invalid_scope, s"scope=${tooWideScopes.mkString(" ")} exceeds the rights granted to the client ${row.clientId}")
logger.warn(warning.getLoggedErrorMessage)
DBIO.successful(Left(warning))
case Some(row) =>
case Some(row) =>
updateRow(
rows = rows,
accessTokenSHA256 = accessTokenAttemptSHA256.get
).flatMap {
case 1 => DBIO.successful(Right(
AccessTokenInfo(
AccessTokenSuccessResponse(
accessTokenAttempt,
"Bearer",
LocalDateTime.now.until(row.voimassaAsti.toLocalDateTime, ChronoUnit.SECONDS).max(0)
),
row.oppijaOid,
row.scope
accessToken = accessTokenAttempt,
expirationTime = row.voimassaAsti.toInstant,
oppijaOid = row.oppijaOid,
scope = row.scope
)
))
case _ => {
Expand Down Expand Up @@ -141,16 +137,13 @@ class OmaDataOAuth2Repository(val db: DB) extends DatabaseExecutionContext with
val warning = OmaDataOAuth2Error(OmaDataOAuth2ErrorType.invalid_scope, s"scope=${tooWideScopes.mkString(" ")} exceeds the rights granted to the client ${row.clientId}")
logger.warn(warning.getLoggedErrorMessage)
Left(warning)
case Some(row) =>
case Some(row) =>
Right(
AccessTokenInfo(
AccessTokenSuccessResponse(
accessToken,
"Bearer",
LocalDateTime.now.until(row.voimassaAsti.toLocalDateTime, ChronoUnit.SECONDS).max(0)
),
row.oppijaOid,
row.scope
accessToken = accessToken,
expirationTime = row.voimassaAsti.toInstant,
oppijaOid = row.oppijaOid,
scope = row.scope
)
)
}
Expand Down Expand Up @@ -208,7 +201,8 @@ class OmaDataOAuth2Repository(val db: DB) extends DatabaseExecutionContext with
}

case class AccessTokenInfo(
successResponse: AccessTokenSuccessResponse,
accessToken: String,
expirationTime: Instant,
oppijaOid: String,
scope: String,
)
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import fi.oph.koski.log.KoskiOperation.{OAUTH2_KATSOMINEN_AKTIIVISET_JA_PAATTYNE
import fi.oph.koski.log.{AuditLog, KoskiAuditLogMessage, KoskiOperation, Logging}
import fi.oph.koski.servlet.{KoskiSpecificApiServlet, NoCache}
import org.scalatra.ContentEncodingSupport

import java.time.ZoneId
import java.time.format.DateTimeFormatter
import scala.reflect.runtime.universe.TypeTag

class OmaDataOAuth2ResourceServerServlet(implicit val application: KoskiApplication) extends KoskiSpecificApiServlet
Expand All @@ -30,28 +33,28 @@ class OmaDataOAuth2ResourceServerServlet(implicit val application: KoskiApplicat
expectedClientId = koskiSession.user.username,
allowedScopes = koskiSession.omaDataOAuth2Scopes
) match {
case Right(AccessTokenInfo(_, oppijaOid, scope)) =>
renderOpinnot(oppijaOid, scope)
case Right(AccessTokenInfo(_, tokenExpirationTime, oppijaOid, scope)) =>
renderOpinnot(oppijaOid, scope, tokenExpirationTime.atZone(ZoneId.systemDefault()).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME))
case Left(error) =>
val errorResult = error.getAccessTokenErrorResponse
renderErrorWithStatus(errorResult, errorResult.httpStatus)
}
}

private def renderOpinnot(oppijaOid: String, scope: String): Unit = {
private def renderOpinnot(oppijaOid: String, scope: String, tokenExpirationTime: String): Unit = {
val overrideSession = KoskiSpecificSession.oauth2KatsominenUser(request)

scope.split(" ").filter(_.startsWith("OPISKELUOIKEUDET_")).toSeq match {
case Seq("OPISKELUOIKEUDET_SUORITETUT_TUTKINNOT") =>
val oppija = application.omaDataOAuth2Service.findSuoritetutTutkinnot(oppijaOid, scope, overrideSession)
val oppija = application.omaDataOAuth2Service.findSuoritetutTutkinnot(oppijaOid, scope, overrideSession, tokenExpirationTime)
auditLogKatsominen(OAUTH2_KATSOMINEN_SUORITETUT_TUTKINNOT, koskiSession.user.username, koskiSession, oppijaOid, scope)
renderOppijaData(oppija)
case Seq("OPISKELUOIKEUDET_AKTIIVISET_JA_PAATTYNEET_OPINNOT") =>
val oppija = application.omaDataOAuth2Service.findAktiivisetJaPäättyneetOpinnot(oppijaOid, scope, overrideSession)
val oppija = application.omaDataOAuth2Service.findAktiivisetJaPäättyneetOpinnot(oppijaOid, scope, overrideSession, tokenExpirationTime)
auditLogKatsominen(OAUTH2_KATSOMINEN_AKTIIVISET_JA_PAATTYNEET_OPINNOT, koskiSession.user.username, koskiSession, oppijaOid, scope)
renderOppijaData(oppija)
case Seq("OPISKELUOIKEUDET_KAIKKI_TIEDOT") =>
val oppija = application.omaDataOAuth2Service.findKaikkiTiedot(oppijaOid, scope, overrideSession)
val oppija = application.omaDataOAuth2Service.findKaikkiTiedot(oppijaOid, scope, overrideSession, tokenExpirationTime)
auditLogKatsominen(OAUTH2_KATSOMINEN_KAIKKI_TIEDOT, koskiSession.user.username, koskiSession, oppijaOid, scope)
renderOppijaData(oppija)
case _ =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class OmaDataOAuth2Service(oauth2Repository: OmaDataOAuth2Repository, val applic
expectedRedirectUri: Option[String],
koskiSession: KoskiSpecificSession,
allowedScopes: Set[String]
): Either[OmaDataOAuth2Error, AccessTokenSuccessResponse] = {
): Either[OmaDataOAuth2Error, AccessTokenInfo] = {
oauth2Repository.createAccessTokenForCode(code, expectedClientId, expectedCodeChallenge, expectedRedirectUri, allowedScopes)
.tap(response =>
AuditLog.log(KoskiAuditLogMessage(OAUTH2_ACCESS_TOKEN_LUONTI, koskiSession, Map(
Expand All @@ -64,7 +64,6 @@ class OmaDataOAuth2Service(oauth2Repository: OmaDataOAuth2Repository, val applic
omaDataOAuth2Scope -> response.scope
)))
)
.map(_.successResponse)
}

def getByAccessToken(
Expand All @@ -75,14 +74,15 @@ class OmaDataOAuth2Service(oauth2Repository: OmaDataOAuth2Repository, val applic
oauth2Repository.getByAccessToken(accessToken, expectedClientId, allowedScopes)
}

def findSuoritetutTutkinnot(oppijaOid: String, scope: String, overrideSession: KoskiSpecificSession): Either[HttpStatus, OmaDataOAuth2SuoritetutTutkinnot] = {
def findSuoritetutTutkinnot(oppijaOid: String, scope: String, overrideSession: KoskiSpecificSession, tokenExpirationTime: String): Either[HttpStatus, OmaDataOAuth2SuoritetutTutkinnot] = {
application.suoritetutTutkinnotService.findSuoritetutTutkinnotOppija(
oppijaOid,
merkitseSuoritusjakoTehdyksi = false
)(overrideSession).map(oppija => {
OmaDataOAuth2SuoritetutTutkinnot(
henkilö = OmaDataOAuth2Henkilötiedot(oppija.henkilö, scope),
opiskeluoikeudet = oppija.opiskeluoikeudet
opiskeluoikeudet = oppija.opiskeluoikeudet,
tokenInfo = OmaDataOAuth2TokenInfo(scope, tokenExpirationTime)
)
})
}
Expand Down Expand Up @@ -125,24 +125,26 @@ class OmaDataOAuth2Service(oauth2Repository: OmaDataOAuth2Repository, val applic
.getOrElse(LocalizedString.unlocalized(clientId))
}

def findAktiivisetJaPäättyneetOpinnot(oppijaOid: String, scope: String, overrideSession: KoskiSpecificSession): Either[HttpStatus, OmaDataOAuth2AktiivisetJaPäättyneetOpiskeluoikeudet] = {
def findAktiivisetJaPäättyneetOpinnot(oppijaOid: String, scope: String, overrideSession: KoskiSpecificSession, tokenExpirationTime: String): Either[HttpStatus, OmaDataOAuth2AktiivisetJaPäättyneetOpiskeluoikeudet] = {
application.aktiivisetJaPäättyneetOpinnotService.findAktiivisetJaPäättyneetOpinnotOppija(
oppijaOid,
merkitseSuoritusjakoTehdyksi = false
)(overrideSession).map(oppija => {
OmaDataOAuth2AktiivisetJaPäättyneetOpiskeluoikeudet(
henkilö = OmaDataOAuth2Henkilötiedot(oppija.henkilö, scope),
opiskeluoikeudet = oppija.opiskeluoikeudet
opiskeluoikeudet = oppija.opiskeluoikeudet,
tokenInfo = OmaDataOAuth2TokenInfo(scope, tokenExpirationTime)
)
})
}

def findKaikkiTiedot(oppijaOid: String, scope: String, overrideSession: KoskiSpecificSession): Either[HttpStatus, OmaDataOAuth2KaikkiOpiskeluoikeudet] = {
def findKaikkiTiedot(oppijaOid: String, scope: String, overrideSession: KoskiSpecificSession, tokenExpirationTime: String): Either[HttpStatus, OmaDataOAuth2KaikkiOpiskeluoikeudet] = {
application.oppijaFacade.findOppija(oppijaOid)(overrideSession).flatMap(_.warningsToLeft) match {
case Right(Oppija(henkilö: TäydellisetHenkilötiedot, opiskeluoikeudet: Seq[Opiskeluoikeus])) =>
Right(OmaDataOAuth2KaikkiOpiskeluoikeudet(
henkilö = OmaDataOAuth2Henkilötiedot(henkilö, scope),
opiskeluoikeudet = opiskeluoikeudet.toList
opiskeluoikeudet = opiskeluoikeudet.toList,
tokenInfo = OmaDataOAuth2TokenInfo(scope, tokenExpirationTime)
))
case Right(_) =>
Left(KoskiErrorCategory.internalError("Datatype not recognized"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -974,7 +974,7 @@ class OmaDataOAuth2BackendSpec
Some(validRedirectUri),
scope.split(" ").toSet
).getOrElse(throw new Error("Internal error"))
.successResponse.access_token
.accessToken

postResourceServer(token, palveluKäyttäjä) {
verifyResponseStatus(404)
Expand Down
Loading