Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
14 changes: 14 additions & 0 deletions obp-api/src/main/protobuf/metrics_stream.proto
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,20 @@ message MetricEvent {
// Reference id of the consent (if any) that authorised the request.
// Mirrors MetricJsonV600.consent_reference_id (REST v6.0.0+).
string consent_reference_id = 18;
// The hops the request passed through: the X-Forwarded-For chain it arrived
// with, followed by the TCP peer OBP-API saw. Matches MetricJsonV600.forwarded_for.
// source_ip (14) is the client address OBP-API decided on from that chain.
string forwarded_for = 19;
// Authentication scheme of the call, never the credential: "Consent", "OAuth2",
// "OAuth1", "DirectLogin", "GatewayLogin", "DAuth", "Anonymous" or "Other".
// Matches MetricJsonV600.auth_type.
string auth_type = 20;
// How the caller's certificate was established: "direct", "forwarded" or "none";
// empty when the request carried no certificate. Matches MetricJsonV600.certificate_trust.
string certificate_trust = 21;
// The specifics behind certificate_trust: the forwarding proxy's subject for
// "forwarded", the rejection reason for "none". Matches MetricJsonV600.certificate_trust_detail.
string certificate_trust_detail = 22;
}

// Live tail of API metrics as they are written.
Expand Down
12 changes: 12 additions & 0 deletions obp-api/src/main/scala/code/api/util/APIUtil.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2288,6 +2288,18 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
def isConsentUser(userId: String): Boolean =
Users.users.vend.getUserByUserId(userId).exists(_.isConsentUser)

/**
* This says whether an Entitlement or a Scope may be written at `bankId`.
*
* Three values are allowed: the empty bank id, where a system Role is held; SYS, the system space
* of Dynamic Entities, whose Roles are granted there although no Bank has that id; and the id of a
* Bank that exists, matched exactly, case included. A row at any other bank id would be one that no
* check ever reads. Every endpoint that grants a Role or a Scope, or records a request for one, asks
* this, so that SYS is accepted in all of them and not only in the versions written after it.
*/
def isBankIdWhereRolesCanBeHeld(bankId: String, callContext: Option[CallContext]): Boolean =
bankId.isEmpty || bankId == DYNAMIC_ENTITY_SYSTEM_LEVEL_BANK_ID || BankX(BankId(bankId), callContext).map(_._1).isDefined

def hasEntitlement(bankId: String, userId: String, apiRole: ApiRole): Boolean = apiRole match {
case RoleCombination(roles) => roles.forall(hasEntitlement(bankId, userId, _))
case role =>
Expand Down
12 changes: 8 additions & 4 deletions obp-api/src/main/scala/code/api/util/WriteMetricUtil.scala
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ object WriteMetricUtil extends MdcLoggable {
import fields._
publishMetricEvent(userId, cc.url, cc.startTime.getOrElse(null), duration, userName, appName,
developerEmail, consumerId, implementedByPartialFunction, cc.implementedInVersion, cc.verb,
cc.httpCode, cc.correlationId, sourceIp, targetIp, cc.operationId.getOrElse(""),
cc.consentReferenceId.orNull, cc.certificateTrust.orNull, cc.certificateTrustDetail.orNull)
cc.httpCode, cc.correlationId, sourceIp, targetIp, forwardedFor, cc.operationId.getOrElse(""),
cc.consentReferenceId.orNull, cc.certificateTrust.orNull, cc.certificateTrustDetail.orNull, authType)
}
}

Expand Down Expand Up @@ -206,10 +206,12 @@ object WriteMetricUtil extends MdcLoggable {
correlationId: String,
sourceIp: String,
targetIp: String,
forwardedFor: String,
operationId: String,
consentReferenceId: String,
certificateTrust: String,
certificateTrustDetail: String): Unit = {
certificateTrustDetail: String,
authType: String): Unit = {
if (!MetricsEventBus.isEnabled) return
try {
implicit val fmts = metricFormats
Expand All @@ -232,11 +234,13 @@ object WriteMetricUtil extends MdcLoggable {
"correlation_id" -> Option(correlationId).getOrElse(""),
"source_ip" -> Option(sourceIp).getOrElse(""),
"target_ip" -> Option(targetIp).getOrElse(""),
"forwarded_for" -> Option(forwardedFor).getOrElse(""),
"api_instance_id" -> code.api.Constant.ApiInstanceId,
"operation_id" -> Option(operationId).getOrElse(""),
"consent_reference_id" -> Option(consentReferenceId).getOrElse(""),
"certificate_trust" -> Option(certificateTrust).getOrElse(""),
"certificate_trust_detail" -> Option(certificateTrustDetail).getOrElse("")
"certificate_trust_detail" -> Option(certificateTrustDetail).getOrElse(""),
"auth_type" -> Option(authType).getOrElse("")
))
MetricsEventBus.publish(payload)
} catch {
Expand Down
2 changes: 1 addition & 1 deletion obp-api/src/main/scala/code/api/v2_0_0/Http4s200.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1262,7 +1262,7 @@ object Http4s200 {
APIUtil.hasAtLeastOneEntitlement(body.bank_id, user.userId, requiredEntitlements)
}
_ <- code.util.Helper.booleanToFuture(BankNotFound, cc = cc2) {
body.bank_id.isEmpty || BankX(BankId(body.bank_id), cc2).map(_._1).isDefined
APIUtil.isBankIdWhereRolesCanBeHeld(body.bank_id, cc2)
}
_ <- code.util.Helper.booleanToFuture(EntitlementAlreadyExists, cc = cc2) {
!hasEntitlement(body.bank_id, userId, role)
Expand Down
7 changes: 4 additions & 3 deletions obp-api/src/main/scala/code/api/v3_0_0/Http4s300.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1655,8 +1655,9 @@ object Http4s300 {
case req @ POST -> `prefixPath` / "entitlement-requests" =>
EndpointHelpers.withUserAndBodyCreated[CreateEntitlementRequestJSON, EntitlementRequestJSON](req) { (user, body, cc) =>
for {
_ <- if (body.bank_id.isEmpty) Future.successful(())
else NewStyle.function.getBank(BankId(body.bank_id), Some(cc)).map(_ => ())
_ <- code.util.Helper.booleanToFuture(s"$BankNotFound Current BankId is ${body.bank_id}", failCode = 404, cc = Some(cc)) {
APIUtil.isBankIdWhereRolesCanBeHeld(body.bank_id, Some(cc))
}
_ <- code.util.Helper.booleanToFuture(
IncorrectRoleName + body.role_name + ". Possible roles are " + ApiRole.availableRoles.sorted.mkString(", "),
cc = Some(cc)) { availableRoles.exists(_ == body.role_name) }
Expand Down Expand Up @@ -2088,7 +2089,7 @@ object Http4s300 {
allowedEntitlementsTxt = s"$UserHasMissingRoles ${allowedEntitlements.mkString(", ")}!"
_ <- NewStyle.function.hasAtLeastOneEntitlement(allowedEntitlementsTxt)(body.bank_id, user.userId, allowedEntitlements, Some(cc))
_ <- code.util.Helper.booleanToFuture(BankNotFound, cc = Some(cc)) {
body.bank_id.nonEmpty == false || BankX(BankId(body.bank_id), Some(cc)).map(_._1).isDefined
APIUtil.isBankIdWhereRolesCanBeHeld(body.bank_id, Some(cc))
}
_ <- code.util.Helper.booleanToFuture(EntitlementAlreadyExists, cc = Some(cc)) {
!hasScope(body.bank_id, consumerIdStr, role)
Expand Down
6 changes: 2 additions & 4 deletions obp-api/src/main/scala/code/api/v7_0_0/Http4s700.scala
Original file line number Diff line number Diff line change
Expand Up @@ -544,8 +544,7 @@ object Http4s700 {
// Bank ids are matched exactly, case included: a grant at a bank id naming no bank is a
// row no check will ever read. SYS is the system space of Dynamic Entities, not a bank.
_ <- Helper.booleanToFuture(failMsg = BankNotFound, failCode = 404, cc = Some(cc)) {
body.bank_id.isEmpty || body.bank_id == code.api.Constant.DYNAMIC_ENTITY_SYSTEM_LEVEL_BANK_ID ||
code.model.BankX(BankId(body.bank_id), Some(cc)).map(_._1).isDefined
APIUtil.isBankIdWhereRolesCanBeHeld(body.bank_id, Some(cc))
}
_ <- Helper.booleanToFuture(failMsg = EntitlementAlreadyExists, failCode = 409, cc = Some(cc))(
!hasEntitlement(body.bank_id, userId, role))
Expand Down Expand Up @@ -881,8 +880,7 @@ object Http4s700 {
APIUtil.hasAtLeastOneEntitlement(body.bank_id, user.userId, grantingRoles)
}
_ <- Helper.booleanToFuture(failMsg = BankNotFound, failCode = 404, cc = Some(cc)) {
body.bank_id.isEmpty || body.bank_id == code.api.Constant.DYNAMIC_ENTITY_SYSTEM_LEVEL_BANK_ID ||
code.model.BankX(BankId(body.bank_id), Some(cc)).map(_._1).isDefined
APIUtil.isBankIdWhereRolesCanBeHeld(body.bank_id, Some(cc))
}
_ <- Helper.booleanToFuture(failMsg = EntitlementAlreadyExists, failCode = 409, cc = Some(cc)) {
!APIUtil.hasScope(body.bank_id, consumer.id.get.toString, role)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ object MetricsStreamServiceImpl extends MetricsStreamServiceGrpc.MetricsStreamSe
matchExact(req.consentReferenceId, (jv \ "consent_reference_id").extractOrElse[String](""))
}

private def jsonToMetricEvent(jv: JValue): MetricEvent = {
private[metricsstream] def jsonToMetricEvent(jv: JValue): MetricEvent = {
MetricEvent(
url = (jv \ "url").extractOrElse[String](""),
date = (jv \ "date").extractOrElse[String](""),
Expand All @@ -142,7 +142,11 @@ object MetricsStreamServiceImpl extends MetricsStreamServiceGrpc.MetricsStreamSe
targetIp = (jv \ "target_ip").extractOrElse[String](""),
apiInstanceId = (jv \ "api_instance_id").extractOrElse[String](""),
operationId = (jv \ "operation_id").extractOrElse[String](""),
consentReferenceId = (jv \ "consent_reference_id").extractOrElse[String]("")
consentReferenceId = (jv \ "consent_reference_id").extractOrElse[String](""),
forwardedFor = (jv \ "forwarded_for").extractOrElse[String](""),
authType = (jv \ "auth_type").extractOrElse[String](""),
certificateTrust = (jv \ "certificate_trust").extractOrElse[String](""),
certificateTrustDetail = (jv \ "certificate_trust_detail").extractOrElse[String]("")
)
}
}
Loading
Loading