feature/Tweak OBP Consent Flow; Request Header

This commit is contained in:
Marko Milić 2022-12-15 12:31:41 +01:00
parent ef77829695
commit a6cd0090f9
3 changed files with 43 additions and 14 deletions

View File

@ -2759,7 +2759,13 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
if (APIUtil.`hasConsent-ID`(reqHeaders)) { // Berlin Group's Consent
Consent.applyBerlinGroupRules(APIUtil.`getConsent-ID`(reqHeaders), cc)
} else if (APIUtil.hasConsentJWT(reqHeaders)) { // Open Bank Project's Consent
Consent.applyRules(APIUtil.getConsentJWT(reqHeaders), cc)
val consentValue = APIUtil.getConsentJWT(reqHeaders)
Consent.getConsentsJwtValueByConsentId(consentValue.getOrElse("")) match {
case Some(jwt) => // JWT value obtained via "Consent-Id" request header
Consent.applyRules(Some(jwt), cc)
case _ => // Assume it's JWT obtained via "Consent-JWT" request header
Consent.applyRules(APIUtil.getConsentJWT(reqHeaders), cc)
}
} else if (hasAnOAuthHeader(cc.authReqHeaderField)) { // OAuth 1
getUserFromOAuthHeaderFuture(cc)
} else if (hasAnOAuth2Header(cc.authReqHeaderField)) { // OAuth 2

View File

@ -1,10 +1,10 @@
package code.api.util
import java.text.SimpleDateFormat
import java.util.Date
import java.util.{Date, UUID}
import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3.{ConsentAccessJson, PostConsentJson}
import code.api.v3_1_0.{PostConsentEntitlementJsonV310, PostConsentBodyCommonJson, PostConsentViewJsonV310}
import code.api.v3_1_0.{PostConsentBodyCommonJson, PostConsentEntitlementJsonV310, PostConsentViewJsonV310}
import code.api.{Constant, RequestHeader}
import code.bankconnectors.Connector
import code.consent
@ -19,11 +19,12 @@ import code.views.Views
import com.nimbusds.jwt.JWTClaimsSet
import com.openbankproject.commons.ExecutionContext.Implicits.global
import com.openbankproject.commons.model._
import net.liftweb.common.{Box, Failure, Full}
import net.liftweb.common.{Box, Empty, Failure, Full}
import net.liftweb.http.provider.HTTPParam
import net.liftweb.json.JsonParser.ParseException
import net.liftweb.json.{Extraction, MappingException, compactRender, parse}
import net.liftweb.mapper.By
import net.liftweb.util.ControlHelpers
import sh.ory.hydra.model.OAuth2TokenIntrospection
import scala.collection.immutable.{List, Nil}
@ -312,7 +313,7 @@ object Consent {
}
}
private def hasConsentInternal(consentIdAsJwt: String, callContext: CallContext): Future[(Box[User], Option[CallContext])] = {
private def hasConsentInternal(consentAsJwt: String, callContext: CallContext): Future[(Box[User], Option[CallContext])] = {
implicit val dateFormats = CustomJsonFormats.formats
def applyConsentRules(consent: ConsentJWT): Future[(Box[User], Option[CallContext])] = {
@ -335,15 +336,15 @@ object Consent {
}
}
case _ =>
(Failure("Cannot create or get the user based on: " + consentIdAsJwt), Some(cc))
(Failure("Cannot create or get the user based on: " + consentAsJwt), Some(cc))
}
}
JwtUtil.getSignedPayloadAsJson(consentIdAsJwt) match {
JwtUtil.getSignedPayloadAsJson(consentAsJwt) match {
case Full(jsonAsString) =>
try {
val consent = net.liftweb.json.parse(jsonAsString).extract[ConsentJWT]
checkConsent(consent, consentIdAsJwt, callContext) match { // Check is it Consent-JWT expired
checkConsent(consent, consentAsJwt, callContext) match { // Check is it Consent-JWT expired
case (Full(true)) => // OK
applyConsentRules(consent)
case failure@Failure(_, _, _) => // Handled errors
@ -359,25 +360,36 @@ object Consent {
case failure@Failure(_, _, _) =>
Future(failure, Some(callContext))
case _ =>
Future(Failure("Cannot extract data from: " + consentIdAsJwt), Some(callContext))
Future(Failure("Cannot extract data from: " + consentAsJwt), Some(callContext))
}
}
private def hasConsentOldStyle(consentIdAsJwt: String, callContext: CallContext): (Box[User], CallContext) = {
(hasConsentInternalOldStyle(consentIdAsJwt, callContext), callContext)
}
private def hasConsent(consentIdAsJwt: String, callContext: CallContext): Future[(Box[User], Option[CallContext])] = {
hasConsentInternal(consentIdAsJwt, callContext)
private def hasConsent(consentAsJwt: String, callContext: CallContext): Future[(Box[User], Option[CallContext])] = {
hasConsentInternal(consentAsJwt, callContext)
}
def applyRules(consentId: Option[String], callContext: CallContext): Future[(Box[User], Option[CallContext])] = {
def applyRules(consentJwt: Option[String], callContext: CallContext): Future[(Box[User], Option[CallContext])] = {
val allowed = APIUtil.getPropsAsBoolValue(nameOfProperty="consents.allowed", defaultValue=false)
(consentId, allowed) match {
(consentJwt, allowed) match {
case (Some(consentId), true) => hasConsent(consentId, callContext)
case (_, false) => Future((Failure(ErrorMessages.ConsentDisabled), Some(callContext)))
case (None, _) => Future((Failure(ErrorMessages.ConsentHeaderNotFound), Some(callContext)))
}
}
def getConsentsJwtValueByConsentId(consentId: String): Option[String] = {
ControlHelpers.tryo(UUID.fromString(consentId)).isDefined match {
case true => // String is a UUID
Consents.consentProvider.vend.getConsentByConsentId(consentId) match {
case Full(consent) => Some(consent.jsonWebToken)
case _ => None // It's not valid UUID value
}
case false => None // It's not UUID at all
}
}
private def copyAuthContextOfConsentToUser(consentId: String, userId: String, newUser: Boolean): Box[List[UserAuthContext]] = {
if(newUser) {

View File

@ -131,7 +131,8 @@ class ConsentRequestTest extends V500ServerSetupAsync with PropsReset{
val getConsentByRequestResponseJson = getConsentByRequestResponse.body.extract[ConsentJsonV500]
getConsentByRequestResponseJson.consent_request_id.head should be(consentRequestId)
getConsentByRequestResponseJson.status should be(ConsentStatus.ACCEPTED.toString)
// Test Request Header "Consent-JWT:SOME_VALUE"
val consentRequestHeader = (s"Consent-JWT", getConsentByRequestResponseJson.jwt)
val requestGetUsers = (v5_0_0_Request / "users").GET
val responseGetUsers = makeGetRequest(requestGetUsers, List(consentRequestHeader))
@ -139,6 +140,16 @@ class ConsentRequestTest extends V500ServerSetupAsync with PropsReset{
responseGetUsers.code should equal(200)
val users = responseGetUsers.body.extract[UsersJsonV400].users
users.size should be > 0
// Test Request Header "Consent-Id:SOME_VALUE"
val consentIdRequestHeader = (s"Consent-Id", getConsentByRequestResponseJson.consent_id)
val responseGetUsersSecond = makeGetRequest(requestGetUsers, List(consentRequestHeader))
Then("We get successful response")
responseGetUsersSecond.code should equal(200)
val usersSecond = responseGetUsersSecond.body.extract[UsersJsonV400].users
usersSecond.size should be > 0
users.size should equal(usersSecond.size)
}
}