feature/Add endpoint mtlsClientCertificateInfo v5.1.0

This commit is contained in:
Marko Milić 2023-02-17 11:54:59 +01:00
parent 007c9d4351
commit d7b28ec535
4 changed files with 102 additions and 2 deletions

View File

@ -11,7 +11,7 @@ import code.api.dynamic.endpoint.helper.practise.PractiseEndpoint
import code.api.util.APIUtil.{defaultJValue, _}
import code.api.util.ApiRole._
import code.api.util.ExampleValue._
import code.api.util.{ApiTrigger, ExampleValue}
import code.api.util.{APIUtil, ApiTrigger, ExampleValue}
import code.api.v2_2_0.JSONFactory220.{AdapterImplementationJson, MessageDocJson, MessageDocsJson}
import code.api.v3_0_0.JSONFactory300.createBranchJsonV300
import code.api.v3_0_0.custom.JSONFactoryCustom300
@ -36,6 +36,7 @@ import com.openbankproject.commons.util.{ApiVersion, FieldNameApiVersions, Refle
import net.liftweb.json
import java.net.URLEncoder
import code.api.v5_1_0.CertificateInfoJsonV510
import code.endpointMapping.EndpointMappingCommons
import scala.collection.immutable.List
@ -4154,6 +4155,15 @@ object SwaggerDefinitionsJSON {
val oAuth2ServerJWKURIJson = OAuth2ServerJWKURIJson("https://www.googleapis.com/oauth2/v3/certs")
val oAuth2ServerJwksUrisJson = OAuth2ServerJwksUrisJson(List(oAuth2ServerJWKURIJson))
val certificateInfoJsonV510 = CertificateInfoJsonV510(
subject_dn = "OID.2.5.4.41=VPN, EMAILADDRESS=admin@tesobe.com, CN=TESOBE CA, OU=TESOBE Operations, O=TESOBE, L=Berlin, ST=Berlin, C=DE",
issuer_dn = "CN=localhost, O=TESOBE GmbH, ST=Berlin, C=DE",
not_before = "2022-04-01T10:13:00.000Z",
not_after = "2032-04-01T10:13:00.000Z",
roles = None,
roles_info = Some("PEM Encoded Certificate does not contain PSD2 roles.")
)
val updateAccountRequestJsonV310 = UpdateAccountRequestJsonV310(
label = "Label",

View File

@ -5,6 +5,7 @@ import java.security.PublicKey
import java.security.cert.{CertificateExpiredException, CertificateNotYetValidException, X509Certificate}
import java.security.interfaces.{ECPublicKey, RSAPublicKey}
import code.api.v5_1_0.CertificateInfoJsonV510
import code.util.Helper.MdcLoggable
import com.github.dwickern.macros.NameOf
import com.nimbusds.jose.jwk.RSAKey
@ -202,5 +203,48 @@ object X509 extends MdcLoggable {
val rsaJWK = RSAKey.parse(cert)
rsaJWK
}
private def extractCertificateInfo(pem: String): Box[CertificateInfoJsonV510] = {
// Parse X.509 certificate
val cert: X509Certificate = X509CertUtils.parse(pem)
if (cert == null) {
// Parsing failed
Failure(ErrorMessages.X509ParsingFailed)
} else {
val subjectDN = cert.getSubjectDN().getName()
val issuerDN = cert.getIssuerDN().getName()
val notBefore = cert.getNotBefore()
val notAfter = cert.getNotAfter()
var roles: Option[List[String]] = None
var rolesInfo: Option[String] = None
try {
val qcstatements = extractQcStatements(cert)
val asn1encodable = extractPsd2QcStatements(qcstatements)
roles = Some(getPsd2Roles(asn1encodable: Array[ASN1Encodable]))
}
catch {
case _: Throwable =>
Failure(ErrorMessages.X509ThereAreNoPsd2Roles)
rolesInfo = Some("PEM Encoded Certificate does not contain PSD2 roles.")
}
val result = CertificateInfoJsonV510(
subject_dn = subjectDN,
issuer_dn = issuerDN,
not_before = APIUtil.formatDate(notBefore),
not_after = APIUtil.formatDate(notAfter),
roles = roles,
roles_info = rolesInfo
)
Full(result)
}
}
def getCertificateInfo(pem: Option[String]): Box[CertificateInfoJsonV510] = {
pem match {
case Some(value) => extractCertificateInfo(value)
case None => Failure(ErrorMessages.X509CannotGetCertificate)
}
}
}

View File

@ -7,7 +7,7 @@ import code.api.util.APIUtil._
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.{ApiRole, NewStyle}
import code.api.util.{APIUtil, ApiRole, NewStyle, X509}
import code.api.util.NewStyle.HttpCode
import code.api.v3_0_0.JSONFactory300.createAggregateMetricJson
import code.api.v3_1_0.ConsentJsonV310
@ -208,6 +208,43 @@ trait APIMethods510 {
}
}
}
staticResourceDocs += ResourceDoc(
mtlsClientCertificateInfo,
implementedInApiVersion,
nameOf(mtlsClientCertificateInfo),
"GET",
"/my/mtls/certificate/info",
"Provide client's certificate info of a current call",
s"""
|Provide client's certificate info of a current call specified by PSD2-CERT value at Request Header
|
|${authenticationRequiredMessage(true)}
|
""".stripMargin,
EmptyBody,
certificateInfoJsonV510,
List(
UserNotLoggedIn,
BankNotFound,
UnknownError
),
List(apiTagConsent, apiTagPSD2AIS, apiTagPsd2, apiTagNewStyle)
)
lazy val mtlsClientCertificateInfo: OBPEndpoint = {
case "my" :: "mtls" :: "certificate" :: "info" :: Nil JsonGet _ => {
cc =>
for {
(Full(_), callContext) <- authenticatedAccess(cc)
info <- Future(X509.getCertificateInfo(APIUtil.`getPSD2-CERT`(cc.requestHeaders))) map {
unboxFullOrFail(_, callContext, X509GeneralError)
}
} yield {
(info, HttpCode.`200`(callContext))
}
}
}
staticResourceDocs += ResourceDoc(

View File

@ -47,6 +47,15 @@ case class APIInfoJsonV510(
resource_docs_requires_role: Boolean
)
case class CertificateInfoJsonV510(
subject_dn: String,
issuer_dn: String,
not_before: String,
not_after: String,
roles: Option[List[String]],
roles_info: Option[String] = None
)
object JSONFactory510 {
def getApiInfoJSON(apiVersion : ApiVersion, apiVersionStatus: String) = {
val organisation = APIUtil.getPropsValue("hosted_by.organisation", "TESOBE")