Skip to content

Commit

Permalink
Merge pull request #2459 from constantine2nd/develop
Browse files Browse the repository at this point in the history
PSU Authentication methods props psu_authentication_method
  • Loading branch information
simonredfern authored Dec 10, 2024
2 parents 99910c9 + 98a576e commit e0ef3fb
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 13 deletions.
22 changes: 22 additions & 0 deletions obp-api/src/main/resources/props/sample.props.template
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,28 @@ display_internal_errors=false
# oauth2.keycloak.source-of-truth = false
# ------------------------------------------------------------------------------ OAuth 2 ------

# -- PSU Authentication methods --------------------------------------------------------------
# The EBA notes that there would appear to currently be three main ways or methods
# of carrying out the authentication procedure of the PSU through a dedicated interface,
# and APIs in particular, namely:
# - redirection,
# - embedded approaches and
# - decoupled approaches (or a combination thereof).
# In the cases of redirection and decoupled approaches,
# PSU’s authentication data is exchanged directly between PSUs and ASPSPs,
# as opposed to embedded approaches, in which PSU’s authentication data
# is exchanged between TPPs and ASPSPs through the interface.
####
# psu_authentication_method = redirection_with_dedicated_start_of_authorization
# Possible values:
# - redirection
# - redirection_with_dedicated_start_of_authorization
# - embedded
# - decoupled
# In case that "psu_authentication_method = redirection" you must define
# psu_authentication_method_sca_redirect_url = redirect_url_value
# -------------------------------------------------------------- Authentication methods --

## This property is used for documenting at Resource Doc. It may include the port also (but not /obp)
## (this needs to be a URL)
documented_server_url=https://apisandbox.openbankproject.com
Expand Down
2 changes: 1 addition & 1 deletion obp-api/src/main/scala/code/api/OAuth2.scala
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ object OAuth2Login extends RestHelper with MdcLoggable {
}

def resolveProvider(idToken: String) = {
isIssuer(jwtToken = idToken, identityProvider = hydraPublicUrl) match {
HydraUtil.integrateWithHydra && isIssuer(jwtToken = idToken, identityProvider = hydraPublicUrl) match {
case true if HydraUtil.hydraUsesObpUserCredentials => // Case that source of the truth of Hydra user management is the OBP-API mapper DB
// In case that ORY Hydra login url is "hostname/user_mgt/login" we MUST override hydraPublicUrl as provider
// in order to avoid creation of a new user
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ As a last option, an ASPSP might in addition accept a command with access rights
PostConsentResponseJson(
consentId = "1234-wertiq-983",
consentStatus = "received",
_links = ConsentLinksV13("/v1.3/consents/1234-wertiq-983/authorisations")
_links = ConsentLinksV13(Some(Href("/v1.3/consents/1234-wertiq-983/authorisations")))
),
List(UserNotLoggedIn, UnknownError),
ApiTag("Account Information Service (AIS)") :: apiTagBerlinGroupM :: Nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package code.api.berlin.group.v1_3

import java.text.SimpleDateFormat
import java.util.Date

import code.api.berlin.group.v1_3.model._
import code.api.util.APIUtil._
import code.api.util.ErrorMessages.MissingPropsValueAtThisInstance
import code.api.util.{APIUtil, ConsentJWT, CustomJsonFormats, JwtUtil}
import code.bankconnectors.Connector
import code.consent.ConsentTrait
Expand All @@ -15,6 +15,7 @@ import net.liftweb.common.Box.tryo
import net.liftweb.common.{Box, Full}
import net.liftweb.json
import net.liftweb.json.{JValue, parse}

import scala.collection.immutable.List

case class JvalueCaseClass(jvalueToCaseclass: JValue)
Expand Down Expand Up @@ -231,21 +232,29 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats {
combinedServiceIndicator: Boolean
)
case class ConsentLinksV13(
startAuthorisation: String
startAuthorisation: Option[Href] = None,
scaRedirect: Option[Href] = None,
status: Option[Href] = None,
scaStatus: Option[Href] = None,
startAuthorisationWithPsuIdentification: Option[Href] = None,
startAuthorisationWithPsuAuthentication: Option[Href] = None,
)

case class PostConsentResponseJson(
consentId: String,
consentStatus: String,
_links: ConsentLinksV13
)
case class Href(href: String)

case class PutConsentResponseJson(
scaStatus: String,
_links: ConsentLinksV13
)




case class GetConsentResponseJson(
access: ConsentAccessJson,
recurringIndicator: Boolean,
Expand Down Expand Up @@ -508,11 +517,51 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats {
}

def createPostConsentResponseJson(consent: ConsentTrait) : PostConsentResponseJson = {
PostConsentResponseJson(
consentId = consent.consentId,
consentStatus = consent.status.toLowerCase(),
_links= ConsentLinksV13(s"/v1.3/consents/${consent.consentId}/authorisations")
)
def redirectionWithDedicatedStartOfAuthorization = {
PostConsentResponseJson(
consentId = consent.consentId,
consentStatus = consent.status.toLowerCase(),
_links = ConsentLinksV13(
startAuthorisation = Some(Href(s"/v1.3/consents/${consent.consentId}/authorisations"))
)
)
}

getPropsValue("psu_authentication_method") match {
case Full("redirection") =>
val scaRedirectUrl = getPropsValue("psu_authentication_method_sca_redirect_url")
.openOr(MissingPropsValueAtThisInstance + "psu_authentication_method_sca_redirect_url")
PostConsentResponseJson(
consentId = consent.consentId,
consentStatus = consent.status.toLowerCase(),
_links = ConsentLinksV13(
scaRedirect = Some(Href(s"$scaRedirectUrl/${consent.consentId}")),
status = Some(Href(s"/v1.3/consents/${consent.consentId}/status")),
scaStatus = Some(Href(s"/v1.3/consents/${consent.consentId}/authorisations/AUTHORISATIONID")),
)
)
case Full("redirection_with_dedicated_start_of_authorization") =>
redirectionWithDedicatedStartOfAuthorization
case Full("embedded") =>
PostConsentResponseJson(
consentId = consent.consentId,
consentStatus = consent.status.toLowerCase(),
_links = ConsentLinksV13(
startAuthorisationWithPsuAuthentication = Some(Href(s"/v1.3/consents/${consent.consentId}/authorisations"))
)
)
case Full("decoupled") =>
PostConsentResponseJson(
consentId = consent.consentId,
consentStatus = consent.status.toLowerCase(),
_links = ConsentLinksV13(
startAuthorisationWithPsuIdentification = Some(Href(s"/v1.3/consents/${consent.consentId}/authorisations"))
)
)
case _ =>
redirectionWithDedicatedStartOfAuthorization
}

}
def createPutConsentResponseJson(consent: ConsentTrait) : ScaStatusResponse = {
ScaStatusResponse(
Expand Down
7 changes: 5 additions & 2 deletions obp-api/src/main/scala/code/api/util/APIUtil.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1092,6 +1092,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
} yield deleted
case "status" => Full(OBPStatus(values.head))
case "consumer_id" => Full(OBPConsumerId(values.head))
case "consent_id" => Full(OBPConsentId(values.head))
case "user_id" => Full(OBPUserId(values.head))
case "bank_id" => Full(OBPBankId(values.head))
case "account_id" => Full(OBPAccountId(values.head))
Expand Down Expand Up @@ -1139,6 +1140,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
anon <- getHttpParamValuesByName(httpParams,"anon")
deletedStatus <- getHttpParamValuesByName(httpParams,"is_deleted")
consumerId <- getHttpParamValuesByName(httpParams,"consumer_id")
consentId <- getHttpParamValuesByName(httpParams,"consent_id")
userId <- getHttpParamValuesByName(httpParams, "user_id")
bankId <- getHttpParamValuesByName(httpParams, "bank_id")
accountId <- getHttpParamValuesByName(httpParams, "account_id")
Expand Down Expand Up @@ -1176,7 +1178,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
val ordering = OBPOrdering(sortBy, sortDirection)
//This guarantee the order
List(limit, offset, ordering, fromDate, toDate,
anon, status, consumerId, userId, url, appName, implementedByPartialFunction, implementedInVersion,
anon, status, consumerId, consentId, userId, url, appName, implementedByPartialFunction, implementedInVersion,
verb, correlationId, duration, excludeAppNames, excludeUrlPattern, excludeImplementedByPartialfunctions,
includeAppNames, includeUrlPattern, includeImplementedByPartialfunctions,
connectorName,functionName, bankId, accountId, customerId, lockedStatus, deletedStatus
Expand Down Expand Up @@ -1214,6 +1216,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
val status = getHttpRequestUrlParam(httpRequestUrl,"status")
val isDeleted = getHttpRequestUrlParam(httpRequestUrl, "is_deleted")
val consumerId = getHttpRequestUrlParam(httpRequestUrl,"consumer_id")
val consentId = getHttpRequestUrlParam(httpRequestUrl,"consent_id")
val userId = getHttpRequestUrlParam(httpRequestUrl, "user_id")
val bankId = getHttpRequestUrlParam(httpRequestUrl, "bank_id")
val accountId = getHttpRequestUrlParam(httpRequestUrl, "account_id")
Expand Down Expand Up @@ -1244,7 +1247,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{

Full(List(
HTTPParam("sort_direction",sortDirection), HTTPParam("from_date",fromDate), HTTPParam("to_date", toDate), HTTPParam("limit",limit), HTTPParam("offset",offset),
HTTPParam("anon", anon), HTTPParam("status", status), HTTPParam("consumer_id", consumerId), HTTPParam("user_id", userId), HTTPParam("url", url), HTTPParam("app_name", appName),
HTTPParam("anon", anon), HTTPParam("status", status), HTTPParam("consumer_id", consumerId), HTTPParam("consent_id", consentId), HTTPParam("user_id", userId), HTTPParam("url", url), HTTPParam("app_name", appName),
HTTPParam("implemented_by_partial_function",implementedByPartialFunction), HTTPParam("implemented_in_version",implementedInVersion), HTTPParam("verb", verb),
HTTPParam("correlation_id", correlationId), HTTPParam("duration", duration), HTTPParam("exclude_app_names", excludeAppNames),
HTTPParam("exclude_url_patterns", excludeUrlPattern),HTTPParam("exclude_implemented_by_partial_functions", excludeImplementedByPartialfunctions),
Expand Down
1 change: 1 addition & 0 deletions obp-api/src/main/scala/code/api/util/OBPParam.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ case class OBPFromDate(value: Date) extends OBPQueryParam
case class OBPToDate(value: Date) extends OBPQueryParam
case class OBPOrdering(field: Option[String], order: OBPOrder) extends OBPQueryParam
case class OBPConsumerId(value: String) extends OBPQueryParam
case class OBPConsentId(value: String) extends OBPQueryParam
case class OBPUserId(value: String) extends OBPQueryParam
case class OBPStatus(value: String) extends OBPQueryParam
case class OBPBankId(value: String) extends OBPQueryParam
Expand Down
6 changes: 4 additions & 2 deletions obp-api/src/main/scala/code/consent/MappedConsent.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package code.consent

import java.util.Date
import code.api.util.{APIUtil, Consent, ErrorMessages, OBPStatus, OBPOffset, OBPQueryParam, OBPUserId, OBPLimit, OBPConsumerId, SecureRandomUtil}
import code.api.util.{APIUtil, Consent, ErrorMessages, OBPConsentId, OBPConsumerId, OBPLimit, OBPOffset, OBPQueryParam, OBPStatus, OBPUserId, SecureRandomUtil}
import code.consent.ConsentStatus.ConsentStatus
import code.model.Consumer
import code.util.MappedUUID
Expand Down Expand Up @@ -67,8 +67,9 @@ object MappedConsentProvider extends ConsentProvider {
private def getQueryParams(queryParams: List[OBPQueryParam]) = {
val limit = queryParams.collect { case OBPLimit(value) => MaxRows[MappedConsent](value) }.headOption
val offset = queryParams.collect { case OBPOffset(value) => StartAt[MappedConsent](value) }.headOption
// he optional variables:
// The optional variables:
val consumerId = queryParams.collect { case OBPConsumerId(value) => By(MappedConsent.mConsumerId, value)}.headOption
val consentId = queryParams.collect { case OBPConsentId(value) => By(MappedConsent.mConsentId, value)}.headOption
val userId = queryParams.collect { case OBPUserId(value) => By(MappedConsent.mUserId, value)}.headOption
val status = queryParams.collect { case OBPStatus(value) => By(MappedConsent.mStatus, value.toUpperCase())}.headOption

Expand All @@ -77,6 +78,7 @@ object MappedConsentProvider extends ConsentProvider {
limit.toSeq,
status.toSeq,
userId.toSeq,
consentId.toSeq,
consumerId.toSeq
).flatten
}
Expand Down

0 comments on commit e0ef3fb

Please sign in to comment.