feature/Dynamic Registration of Consumer - WIP 4

This commit is contained in:
Marko Milić 2023-12-06 17:56:30 +01:00
parent e12fecd8ed
commit 34a8954edc
4 changed files with 37 additions and 11 deletions

View File

@ -520,6 +520,7 @@ object ErrorMessages {
val RegulatedEntityNotFound = "OBP-34100: Regulated Entity not found. Please specify a valid value for REGULATED_ENTITY_ID."
val RegulatedEntityNotDeleted = "OBP-34101: Regulated Entity cannot be deleted. Please specify a valid value for REGULATED_ENTITY_ID."
val RegulatedEntityNotFoundByCertificate = "OBP-34102: Regulated Entity cannot be found by provided certificate."
val PostJsonIsNotSigned = "OBP-34110: JWT at the post json cannot be verified."
// Consents
val ConsentNotFound = "OBP-35001: Consent not found by CONSENT_ID. "

View File

@ -269,6 +269,15 @@ object JwtUtil extends MdcLoggable {
jwk.toPublicJWK.toRSAKey
}
def verifyJwt(jwtString: String, pemEncodedRsaPublicKey: String): Boolean = {
// Parse PEM-encoded key to RSA public / private JWK
val jwk: JWK = JWK.parseFromPEMEncodedObjects(pemEncodedRsaPublicKey);
val rsaPublicKey: RSAKey = jwk.toPublicJWK.toRSAKey
val signedJWT = SignedJWT.parse(jwtString)
val verifier = new RSASSAVerifier(rsaPublicKey)
signedJWT.verify(verifier)
}
def main(args: Array[String]): Unit = {
val jwtToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjhhYWQ2NmJkZWZjMWI0M2Q4ZGIyN2U2NWUyZTJlZjMwMTg3OWQzZTgiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTM5NjY4NTQyNDU3ODA4OTI5NTkiLCJhdF9oYXNoIjoiWGlpckZ1cnJ2X0ZxN3RHd25rLWt1QSIsIm5hbWUiOiJNYXJrbyBNaWxpxIciLCJwaWN0dXJlIjoiaHR0cHM6Ly9saDUuZ29vZ2xldXNlcmNvbnRlbnQuY29tLy1YZDQ0aG5KNlREby9BQUFBQUFBQUFBSS9BQUFBQUFBQUFBQS9BS3hyd2NhZHd6aG00TjR0V2s1RThBdnhpLVpLNmtzNHFnL3M5Ni1jL3Bob3RvLmpwZyIsImdpdmVuX25hbWUiOiJNYXJrbyIsImZhbWlseV9uYW1lIjoiTWlsacSHIiwibG9jYWxlIjoiZW4iLCJpYXQiOjE1NDczMTE3NjAsImV4cCI6MTU0NzMxNTM2MH0.UyOmM0rsO0-G_ibDH3DFogS94GcsNd9GtYVw7j3vSMjO1rZdIraV-N2HUtQN3yHopwdf35A2FEJaag6X8dbvEkJC7_GAynyLIpodoaHNtaLbww6XQSYuQYyF27aPMpROoGZUYkMpB_82LF3PbD4ecDPC2IA5oSyDF4Eya4yn-MzxYmXS7usVWvanREg8iNQSxpu7zZqj4UwhvSIv7wH0vskr_M-PnefQzNTrdUx74i-v9lVqC4E_bF5jWeDGO8k5dqWqg55QuZdyJdSh89KNiIjJXGZDWUBzGfsbetWRnObIgX264fuOW4SpRglUc8fzv41Sc7SSqjqRAFm05t60kg"

View File

@ -8,6 +8,7 @@ import code.api.util.ApiRole._
import code.api.util.ApiTag._
import code.api.util.ErrorMessages.{$UserNotLoggedIn, BankNotFound, ConsentNotFound, InvalidJsonFormat, UnknownError, UserNotFoundByUserId, UserNotLoggedIn, _}
import code.api.util.FutureUtil.{EndpointContext, EndpointTimeout}
import code.api.util.JwtUtil.{getSignedPayloadAsJson, verifyJwt}
import code.api.util.NewStyle.HttpCode
import code.api.util.X509.{getCommonName, getEmailAddress, getOrganization}
import code.api.util._
@ -42,7 +43,7 @@ import com.openbankproject.commons.model._
import com.openbankproject.commons.util.{ApiVersion, ScannedApiVersion}
import net.liftweb.common.Full
import net.liftweb.http.rest.RestHelper
import net.liftweb.json.parse
import net.liftweb.json.{compactRender, parse}
import net.liftweb.mapper.By
import net.liftweb.util.Helpers
import net.liftweb.util.Helpers.tryo
@ -1781,21 +1782,28 @@ trait APIMethods510 {
"/dynamic-registration/consumers",
"Create a Consumer",
s"""Create a Consumer (mTLS access).
|
| JWT payload:
| - minimal
| { "description":"Description" }
| - full
| {
| "description": "Description",
| "app_name": "Tesobe GmbH",
| "app_type": "Sofit",
| "developer_email": "marko@tesobe.com",
| "redirect_url": "http://localhost:8082"
| }
| Please note that JWT must be signed with the counterpart private kew of the public key used to establish mTLS
|
|""",
ConsumerPostJsonV510(
None,
None,
"Description",
None,
None,
),
ConsumerJwtPostJsonV510("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkZXNjcmlwdGlvbiI6IkRlc2NyaXB0aW9uIn0.qDnzk1dGK8akdLFRl8fmJV_SeoDjRTDG_eMogCIzZ7M"),
consumerJsonV510,
List(
InvalidJsonFormat,
UnknownError
),
List(apiTagConsumer),
List(apiTagDirectory, apiTagConsumer),
Some(Nil))
@ -1804,10 +1812,16 @@ trait APIMethods510 {
cc =>
implicit val ec = EndpointContext(Some(cc))
for {
postedJson <- NewStyle.function.tryons(InvalidJsonFormat, 400, cc.callContext) {
json.extract[ConsumerPostJsonV510]
postedJwt <- NewStyle.function.tryons(InvalidJsonFormat, 400, cc.callContext) {
json.extract[ConsumerJwtPostJsonV510]
}
pem = APIUtil.`getPSD2-CERT`(cc.requestHeaders)
_ <- Helper.booleanToFuture(PostJsonIsNotSigned, 400, cc.callContext) {
verifyJwt(postedJwt.jwt, pem.getOrElse(""))
}
postedJson <- NewStyle.function.tryons(InvalidJsonFormat, 400, cc.callContext) {
parse(getSignedPayloadAsJson(postedJwt.jwt).getOrElse("{}")).extract[ConsumerPostJsonV510]
}
certificateInfo: CertificateInfoJsonV510 <- Future(X509.getCertificateInfo(pem)) map {
unboxFullOrFail(_, cc.callContext, X509GeneralError)
}

View File

@ -281,6 +281,8 @@ case class MetricJsonV510(
)
case class MetricsJsonV510(metrics: List[MetricJsonV510])
case class ConsumerJwtPostJsonV510(jwt: String)
case class ConsumerPostJsonV510(app_name: Option[String],
app_type: Option[String],
description: String,