Merge pull request #2357 from OpenBankProject/develop

regular release update
This commit is contained in:
tesobe-daniel 2023-12-14 12:44:08 +01:00 committed by GitHub
commit 0124c52ce9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 1137 additions and 869 deletions

View File

@ -79,16 +79,6 @@
<groupId>org.slf4j</groupId>
<version>1.7.26</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
@ -360,16 +350,6 @@
<artifactId>geocalc</artifactId>
<version>0.5.7</version>
</dependency>
<!-- embeded kafka for unit test start -->
<!-- https://mvnrepository.com/artifact/io.github.embeddedkafka/embedded-kafka -->
<dependency>
<groupId>io.github.embeddedkafka</groupId>
<artifactId>embedded-kafka_2.12</artifactId>
<version>2.4.1.1</version>
<scope>test</scope>
</dependency>
<!-- embeded kafka for unit test end -->
<!-- https://mvnrepository.com/artifact/com.twilio.sdk/twilio -->
<dependency>
<groupId>com.twilio.sdk</groupId>
<artifactId>twilio</artifactId>

View File

@ -865,7 +865,9 @@ create_just_in_time_entitlements=false
# if create_just_in_time_entitlements=true then OBP does the following:
# If a user is trying to use a Role (via an endpoint) and the user could grant them selves the required Role(s), then OBP automatically grants the Role!
# i.e. if the User already has canCreateEntitlementAtOneBank or canCreateEntitlementAtAnyBank and then OBP will auto grant a role that could be granted by a manual process anyway.
# This speeds up the process of granting of roles. Certain roles are excluded from this automation.
# This speeds up the process of granting of roles. Certain roles are excluded from this automation:
# - CanCreateEntitlementAtOneBank
# - CanCreateEntitlementAtAnyBank
# If create_just_in_time_entitlements is again set to false after it was true for a while, any auto granted Entitlements to roles are kept in place.
# Note: In the entitlements model we set createdbyprocess="create_just_in_time_entitlements". For manual operations we set createdbyprocess="manual"
# -------------------------------------------------------------
@ -1039,9 +1041,18 @@ outboundAdapterCallContext.generalContext
# ------------------------------ default entitlements end ------------------------------
## Mirror request headers to response
# This feature is driven by FAPI requirements. For instance:
# The resource server with the FAPI endpoints
# - shall set the response header x-fapi-interaction-id to the value
# received from the corresponding fapi client request header or to a RFC4122 UUID value
# if the request header was not provided to track the interaction, e.g.,
# x-fapi-interaction-id: c770aef3-6784-41f7-8e0e-ff5f97bddb3a; and
# - shall log the value of x-fapi-interaction-id in the log entry.
# mirror_request_headers_to_response=x-fapi-interaction-id,x-jws-signature
## Echo all request headers to response
# Rename all request headers by prepending the word "echo_" and sends them back as response headers
# This feature helps to reveal information does every request header sent at a client side really reach a server side
echo_request_headers=false
### enable or disable the feature of send "Force-Error" header, default value is false
@ -1275,4 +1286,8 @@ validate_iban=false
# Show all dependent connector methods for each endpoint. The default value is false.
# If set to true, it may consume a significant amount of heap memory.
#show_used_connector_methods=false
#show_used_connector_methods=false
# This returns Regulated Entities
# sample props regulated_entities = [{"certificate_authority_ca_owner_id":"CY_CBC","entity_certificate_public_key":"-----BEGIN CERTIFICATE-----MIICsjCCAZqgAwIBAgIGAYwQ62R0MA0GCSqGSIb3DQEBCwUAMBoxGDAWBgNVBAMMD2FwcC5leGFtcGxlLmNvbTAeFw0yMzExMjcxMzE1MTFaFw0yNTExMjYxMzE1MTFaMBoxGDAWBgNVBAMMD2FwcC5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK9WIodZHWzKyCcf9YfWEhPURbfO6zKuMqzHN27GdqHsVVEGxP4F/J4mso+0ENcRr6ur4u81iREaVdCc40rHDHVJNEtniD8Icbz7tcsqAewIVhc/q6WXGqImJpCq7hA0m247dDsaZT0lb/MVBiMoJxDEmAE/GYYnWTEn84R35WhJsMvuQ7QmLvNg6RkChY6POCT/YKe9NKwa1NqI1U+oA5RFzAaFtytvZCE3jtp+aR0brL7qaGfgxm6B7dEpGyhg0NcVCV7xMQNq2JxZTVdAr6lcsRGaAFulakmW3aNnmK+L35Wu8uW+OxNxwUuC6f3b4FVBa276FMuUTRfu7gc+k6kCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAAU5CjEyAoyTn7PgFpQD48ZNPuUsEQ19gzYgJvHMzFIoZ7jKBodjO5mCzWBcR7A4mpeAsdyiNBl2sTiZscSnNqxk61jVzP5Ba1D7XtOjjr7+3iqowrThj6BY40QqhYh/6BSY9fDzVZQiHnvlo6ZUM5kUK6OavZOovKlp5DIl5sGqoP0qAJnpQ4nhB2WVVsKfPlOXc+2KSsbJ23g9l8zaTMr+X0umlvfEKqyEl1Fa2L1dO0y/KFQ+ILmxcZLpRdq1hRAjd0quq9qGC8ucXhRWDgM4hslVpau0da68g0aItWNez3mc5lB82b3dcZpFMzO41bgw7gvw10AvvTfQDqEYIuQ==-----END CERTIFICATE-----","entity_code":"PSD_PICY_CBC!12345","entity_type":"PSD_PI","entity_address":"EXAMPLE COMPANY LTD, 5 SOME STREET","entity_town_city":"SOME CITY","entity_post_code":"1060","entity_country":"CY","entity_web_site":"www.example.com","services":[{"CY":["PS_010","PS_020","PS_03C","PS_04C"]}]}]
regulated_entities = []

View File

@ -29,7 +29,6 @@ package bootstrap.liftweb
import java.io.{File, FileInputStream}
import java.util.stream.Collectors
import java.util.{Locale, TimeZone}
import code.CustomerDependants.MappedCustomerDependant
import code.DynamicData.DynamicData
import code.DynamicEndpoint.DynamicEndpoint
@ -107,6 +106,7 @@ import code.productcollectionitem.MappedProductCollectionItem
import code.productfee.ProductFee
import code.products.MappedProduct
import code.ratelimiting.RateLimiting
import code.regulatedentities.MappedRegulatedEntity
import code.remotedata.RemotedataActors
import code.scheduler.{DataBaseCleanerScheduler, DatabaseDriverScheduler, JobScheduler, MetricsArchiveScheduler}
import code.scope.{MappedScope, MappedUserScope}
@ -135,6 +135,7 @@ import code.webuiprops.WebUiProps
import com.openbankproject.commons.model.ErrorMessage
import com.openbankproject.commons.util.Functions.Implicits._
import com.openbankproject.commons.util.{ApiVersion, Functions}
import javax.mail.internet.MimeMessage
import net.liftweb.common._
import net.liftweb.db.{DB, DBLogEntry}
@ -849,7 +850,7 @@ class Boot extends MdcLoggable {
.map[String](_.getClientId)
.collect(Collectors.toSet())
Consumers.consumers.vend.getConsumersFuture().foreach{ consumers =>
Consumers.consumers.vend.getConsumersFuture(Nil, None).foreach{ consumers =>
consumers.filter(consumer => consumer.isActive.get && !oAuth2ClientIds.contains(consumer.key.get))
.foreach(HydraUtil.createHydraClient(_))
}
@ -1034,6 +1035,7 @@ object ToSchemify {
AuthUser,
JobScheduler,
MappedETag,
MappedRegulatedEntity,
AtmAttribute,
Admin,
MappedBank,

View File

@ -52,6 +52,35 @@ import scala.collection.immutable.List
object SwaggerDefinitionsJSON {
lazy val regulatedEntitiesJsonV510: RegulatedEntitiesJsonV510 = RegulatedEntitiesJsonV510(List(regulatedEntityJsonV510))
lazy val regulatedEntityJsonV510: RegulatedEntityJsonV510 = RegulatedEntityJsonV510(
entity_id = "0af807d7-3c39-43ef-9712-82bcfde1b9ca",
certificate_authority_ca_owner_id = "CY_CBC",
entity_certificate_public_key = "-----BEGIN CERTIFICATE-----MIICsjCCAZqgAwIBAgIGAYwQ62R0MA0GCSqGSIb3DQEBCwUAMBoxGDAWBgNVBAMMD2FwcC5leGFtcGxlLmNvbTAeFw0yMzExMjcxMzE1MTFaFw0yNTExMjYxMzE1MTFaMBoxGDAWBgNVBAMMD2FwcC5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK9WIodZHWzKyCcf9YfWEhPURbfO6zKuMqzHN27GdqHsVVEGxP4F/J4mso+0ENcRr6ur4u81iREaVdCc40rHDHVJNEtniD8Icbz7tcsqAewIVhc/q6WXGqImJpCq7hA0m247dDsaZT0lb/MVBiMoJxDEmAE/GYYnWTEn84R35WhJsMvuQ7QmLvNg6RkChY6POCT/YKe9NKwa1NqI1U+oA5RFzAaFtytvZCE3jtp+aR0brL7qaGfgxm6B7dEpGyhg0NcVCV7xMQNq2JxZTVdAr6lcsRGaAFulakmW3aNnmK+L35Wu8uW+OxNxwUuC6f3b4FVBa276FMuUTRfu7gc+k6kCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAAU5CjEyAoyTn7PgFpQD48ZNPuUsEQ19gzYgJvHMzFIoZ7jKBodjO5mCzWBcR7A4mpeAsdyiNBl2sTiZscSnNqxk61jVzP5Ba1D7XtOjjr7+3iqowrThj6BY40QqhYh/6BSY9fDzVZQiHnvlo6ZUM5kUK6OavZOovKlp5DIl5sGqoP0qAJnpQ4nhB2WVVsKfPlOXc+2KSsbJ23g9l8zaTMr+X0umlvfEKqyEl1Fa2L1dO0y/KFQ+ILmxcZLpRdq1hRAjd0quq9qGC8ucXhRWDgM4hslVpau0da68g0aItWNez3mc5lB82b3dcZpFMzO41bgw7gvw10AvvTfQDqEYIuQ==-----END CERTIFICATE-----",
entity_name = "EXAMPLE COMPANY LTD",
entity_code = "PSD_PICY_CBC!12345",
entity_type = "PSD_PI",
entity_address = "EXAMPLE COMPANY LTD, 5 SOME STREET",
entity_town_city = "SOME CITY",
entity_post_code = "1060",
entity_country = "CY",
entity_web_site = "www.example.com",
services = json.parse("""[{"CY":["PS_010","PS_020","PS_03C","PS_04C"]}]""")
)
lazy val regulatedEntityPostJsonV510: RegulatedEntityPostJsonV510 = RegulatedEntityPostJsonV510(
certificate_authority_ca_owner_id = "CY_CBC",
entity_certificate_public_key = "-----BEGIN CERTIFICATE-----MIICsjCCAZqgAwIBAgIGAYwQ62R0MA0GCSqGSIb3DQEBCwUAMBoxGDAWBgNVBAMMD2FwcC5leGFtcGxlLmNvbTAeFw0yMzExMjcxMzE1MTFaFw0yNTExMjYxMzE1MTFaMBoxGDAWBgNVBAMMD2FwcC5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK9WIodZHWzKyCcf9YfWEhPURbfO6zKuMqzHN27GdqHsVVEGxP4F/J4mso+0ENcRr6ur4u81iREaVdCc40rHDHVJNEtniD8Icbz7tcsqAewIVhc/q6WXGqImJpCq7hA0m247dDsaZT0lb/MVBiMoJxDEmAE/GYYnWTEn84R35WhJsMvuQ7QmLvNg6RkChY6POCT/YKe9NKwa1NqI1U+oA5RFzAaFtytvZCE3jtp+aR0brL7qaGfgxm6B7dEpGyhg0NcVCV7xMQNq2JxZTVdAr6lcsRGaAFulakmW3aNnmK+L35Wu8uW+OxNxwUuC6f3b4FVBa276FMuUTRfu7gc+k6kCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAAU5CjEyAoyTn7PgFpQD48ZNPuUsEQ19gzYgJvHMzFIoZ7jKBodjO5mCzWBcR7A4mpeAsdyiNBl2sTiZscSnNqxk61jVzP5Ba1D7XtOjjr7+3iqowrThj6BY40QqhYh/6BSY9fDzVZQiHnvlo6ZUM5kUK6OavZOovKlp5DIl5sGqoP0qAJnpQ4nhB2WVVsKfPlOXc+2KSsbJ23g9l8zaTMr+X0umlvfEKqyEl1Fa2L1dO0y/KFQ+ILmxcZLpRdq1hRAjd0quq9qGC8ucXhRWDgM4hslVpau0da68g0aItWNez3mc5lB82b3dcZpFMzO41bgw7gvw10AvvTfQDqEYIuQ==-----END CERTIFICATE-----",
entity_name = "EXAMPLE COMPANY LTD",
entity_code = "PSD_PICY_CBC!12345",
entity_type = "PSD_PI",
entity_address = "EXAMPLE COMPANY LTD, 5 SOME STREET",
entity_town_city = "SOME CITY",
entity_post_code = "1060",
entity_country = "CY",
entity_web_site = "www.example.com",
services = """[{"CY":["PS_010","PS_020","PS_03C","PS_04C"]}]"""
)
val license = License(
id = licenseIdExample.value,
@ -2640,6 +2669,31 @@ object SwaggerDefinitionsJSON {
enabled = true,
created = DateWithDayExampleObject
)
lazy val pem = "-----BEGIN CERTIFICATE-----\nMIIFIjCCBAqgAwIBAgIIX3qsz7QQxngwDQYJKoZIhvcNAQELBQAwgZ8xCzAJBgNV\r\nBAYTAkRFMQ8wDQYDVQQIEwZCZXJsaW4xDzANBgNVBAcTBkJlcmxpbjEPMA0GA1UE\r\nChMGVEVTT0JFMRowGAYDVQQLExFURVNPQkUgT3BlcmF0aW9uczESMBAGA1UEAxMJ\r\nVEVTT0JFIENBMR8wHQYJKoZIhvcNAQkBFhBhZG1pbkB0ZXNvYmUuY29tMQwwCgYD\r\nVQQpEwNWUE4wHhcNMjMwNzE3MDg0MDAwWhcNMjQwNzE3MDg0MDAwWjCBizELMAkG\r\nA1UEBhMCREUxDzANBgNVBAgTBkJlcmxpbjEPMA0GA1UEBxMGQmVybGluMRQwEgYD\r\nVQQKEwtUZXNvYmUgR21iSDEPMA0GA1UECxMGc3lzb3BzMRIwEAYDVQQDEwlsb2Nh\r\nbGhvc3QxHzAdBgkqhkiG9w0BCQEWEGFkbWluQHRlc29iZS5jb20wggEiMA0GCSqG\r\nSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwxGuWUN1H0d0IeYPYWdLA0I/5BXx4DLO6\r\nzfi1GGJlF8BIXRN0VTJckIY9C3J1RnXDs6p6ufA01iHe1PQdL6VzfcaC3j+jUSgV\r\n1z9ybEUPyUwq3PCCxqoVI9n8yh+O6FDn3dvu/9Q2NtBpJHUBDCLf7OO9TgsFU2sE\r\nMys+Hw5DuuX5n5OQ2VIwH+qlMTQnd+yw5y8FKHqAZT5hE60lF/x6sQnwi58hLGRW\r\nSqo/548c2ZpoeWtnyY1I6PyR7zUYGuhruLY8gVFfLE+610u/lj2wYTXMxntpV+tV\r\nralLFRMhvbqZXW/EpuDb/pEbCnLDNDxq5NarLVDzcHs7VhT9MPChAgMBAAGjggFy\r\nMIIBbjATBgNVHSUEDDAKBggrBgEFBQcDAjAaBgNVHREEEzARgglsb2NhbGhvc3SH\r\nBH8AAAEwggEGBggrBgEFBQcBAwSB+TCB9jAIBgYEAI5GAQEwOAYGBACORgEFMC4w\r\nLBYhaHR0cHM6Ly9leGFtcGxlLm9yZy9wa2lkaXNjbG9zdXJlEwdleGFtcGxlMIGI\r\nBgYEAIGYJwIwfjBMMBEGBwQAgZgnAQMMBlBTUF9BSTARBgcEAIGYJwEBDAZQU1Bf\r\nQVMwEQYHBACBmCcBAgwGUFNQX1BJMBEGBwQAgZgnAQQMBlBTUF9JQwwlRHVtbXkg\r\nRmluYW5jaWFsIFN1cGVydmlzaW9uIEF1dGhvcml0eQwHWFgtREZTQTAlBgYEAI5G\r\nAQYwGwYHBACORgEGAQYHBACORgEGAgYHBACORgEGAzARBglghkgBhvhCAQEEBAMC\r\nB4AwHgYJYIZIAYb4QgENBBEWD3hjYSBjZXJ0aWZpY2F0ZTANBgkqhkiG9w0BAQsF\r\nAAOCAQEAKTS7exS9A7rWJLRzWrlHoTu68Avm5g9Dz1GKjgt8rnvj3D21SE14Rf5p\r\n0JWHYH4SiCdnh8Tx+IA7o0TmPJ1JRfAXR3i/5R7TJi/HrnqL+V7SIx2Cuq/hkZEU\r\nAhVs07nnvHURcrlQGwcfn4TbgpCURpCPpYZlNsYySb6BS6I4qFaadHGqMTyEkphV\r\nwfXyB3brmzxj9V4Qgp0t+s/uFuFirWyIayRc9nSSC7vuNVYvib2Kim4y8kvuWpA4\r\nZ51+fFOmBqCqpmwfAADNgDsLJiA/741eBflVd/ZUeAzgOjMCMIaDGlwiwZlePKT7\r\n553GtfsGxZMf05oqfUrQEQfJaU+/+Q==\n-----END CERTIFICATE-----\n"
lazy val certificateInfoJsonV510 = CertificateInfoJsonV510(
subject_domain_name = "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_domain_name = "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.")
)
lazy val consumerJsonV510: ConsumerJsonV510 = ConsumerJsonV510(
consumer_id = "d0d7b08c-f0ec-4e57-ac99-7d9eafe99225",
consumer_key = "d0d7b08c-f0ec-4e57-ac99-7d9eafe99225",
consumer_secret = "d0d7b08c-f0ec-4e57-ac99-7d9eafe99225",
app_name = "SOFI",
app_type = "Web",
description = "Account Management",
developer_email = ExampleValue.emailExample.value,
company = ExampleValue.companyExample.value,
redirect_url = "www.openbankproject.com",
certificate_pem = pem,
certificate_info = Some(certificateInfoJsonV510),
created_by_user = resourceUserJSON,
enabled = true,
created = DateWithDayExampleObject
)
val consumersJson = ConsumersJson(
list = List(consumerJSON)
@ -4173,15 +4227,6 @@ object SwaggerDefinitionsJSON {
val oAuth2ServerJWKURIJson = OAuth2ServerJWKURIJson("https://www.googleapis.com/oauth2/v3/certs")
val oAuth2ServerJwksUrisJson = OAuth2ServerJwksUrisJson(List(oAuth2ServerJWKURIJson))
val certificateInfoJsonV510 = CertificateInfoJsonV510(
subject_domain_name = "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_domain_name = "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

@ -118,6 +118,8 @@ import java.security.AccessControlException
import java.util.regex.Pattern
import code.api.util.FutureUtil.{EndpointContext, EndpointTimeout}
import code.api.v2_1_0.OBPAPI2_1_0.Implementations2_1_0
import code.api.v2_2_0.OBPAPI2_2_0.Implementations2_2_0
import code.etag.MappedETag
import code.users.Users
import net.liftweb.mapper.By
@ -643,7 +645,18 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
val excludedFieldValues = APIUtil.getPropsValue("excluded.response.field.values").map[JArray](it => json.parse(it).asInstanceOf[JArray])
def successJsonResponseNewStyle(cc: Any, callContext: Option[CallContext], httpCode : Int = 200)(implicit headers: CustomResponseHeaders = CustomResponseHeaders(Nil)) : JsonResponse = {
val jsonAst: JValue = ApiSession.processJson((Extraction.decompose(cc)), callContext)
val jsonAst: JValue = {
val partialFunctionName = callContext.map(_.resourceDocument.map(_.partialFunctionName)).flatten.getOrElse("")
if (
nameOf(code.api.v5_1_0.APIMethods510.Implementations5_1_0.getMetrics).equals(partialFunctionName) ||
nameOf(code.api.v5_0_0.APIMethods500.Implementations5_0_0.getMetricsAtBank).equals(partialFunctionName) ||
nameOf(Implementations2_2_0.getConnectorMetrics).equals(partialFunctionName)
) {
ApiSession.processJson(Extraction.decompose(cc)(CustomJsonFormats.losslessFormats), callContext)
} else {
ApiSession.processJson((Extraction.decompose(cc)), callContext)
}
}
val excludeOptionalFieldsParam = getHttpRequestUrlParam(callContext.map(_.url).getOrElse(""),"exclude-optional-fields")
val excludedResponseBehaviour = APIUtil.getPropsAsBoolValue("excluded.response.behaviour", false)
//excludeOptionalFieldsParamValue has top priority, then the excludedResponseBehaviour props.
@ -777,20 +790,6 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
}
}
def basicUrlValidation(urlString: String): Boolean = {
//in scala test - org.scalatest.FeatureSpecLike.scenario:
// redirectUrl = http%3A%2F%2Flocalhost%3A8016%3Foauth_token%3DEBRZBMOPDXEUGGJP421FPFGK01IY2DGM5O3TLVSK%26oauth_verifier%3D63461
// URLDecoder.decode(urlString,"UTF-8")-->http://localhost:8016?oauth_token=EBRZBMOPDXEUGGJP421FPFGK01IY2DGM5O3TLVSK&oauth_verifier=63461
val regex =
"""((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(:[0-9]+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_\/]*)#?(?:[\w]*))?)""".r
val decodeUrlValue = URLDecoder.decode(urlString, "UTF-8").trim()
decodeUrlValue match {
case regex(_*) if (decodeUrlValue.length <= 2048) => true
case _ => false
}
}
/** only A-Z, a-z, 0-9,-,_,. =, & and max length <= 2048 */
def basicUriAndQueryStringValidation(urlString: String): Boolean = {
val regex =
@ -2100,7 +2099,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
|Possible custom url parameters for pagination:
|
|* limit=NUMBER ==> default value: 50
|* limit=NUMBER ==> default value: ${Constant.Pagination.limit}
|* offset=NUMBER ==> default value: 0
|
|eg1:?limit=100&offset=0

View File

@ -66,6 +66,11 @@ object RoleCombination {
object ApiRole extends MdcLoggable{
case class CanCreateRegulatedEntity(requiresBankId: Boolean = false) extends ApiRole
lazy val canCreateRegulatedEntity = CanCreateRegulatedEntity()
case class CanDeleteRegulatedEntity(requiresBankId: Boolean = false) extends ApiRole
lazy val canDeleteRegulatedEntity = CanDeleteRegulatedEntity()
case class CanSearchWarehouse(requiresBankId: Boolean = false) extends ApiRole
lazy val canSearchWarehouse = CanSearchWarehouse()

View File

@ -100,6 +100,9 @@ object ApiTag {
val apiTagPSD2PIIS=ResourceDocTag("Confirmation of Funds Service (PIIS)")
val apiTagPSD2PIS=ResourceDocTag("Payment Initiation Service (PIS)")
val apiTagDirectory = ResourceDocTag("Directory")
//Note: the followings are for the code generator -- UKOpenBankingV3.1.0
val apiTagUkAccountAccess = ResourceDocTag("UK-AccountAccess")

View File

@ -516,7 +516,12 @@ object ErrorMessages {
// Bank related messages
val bankIdAlreadyExists = "OBP-34000: Bank Id already exists. Please specify a different value."
val updateBankError = "OBP-34001: Could not update the Bank"
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. "
val ConsentNotBeforeIssue = "OBP-35002: The time Consent-ID token was issued is set in the future. "

View File

@ -643,7 +643,9 @@ object Glossary extends MdcLoggable {
|If Just in Time Entitlements are enabled then OBP does the following:
|If a user is trying to use a Role (via an endpoint) and the user could grant them selves the required Role(s), then OBP automatically grants the Role.
|i.e. if the User already has canCreateEntitlementAtOneBank or canCreateEntitlementAtAnyBank then OBP will automatically grant a role that would be granted by a manual process anyway.
|This speeds up the process of granting of roles. Certain roles are excluded from this automation.
|This speeds up the process of granting of roles. Certain roles are excluded from this automation:
| - CanCreateEntitlementAtOneBank
| - CanCreateEntitlementAtAnyBank
|If create_just_in_time_entitlements is again set to false after it was true for a while, any auto granted Entitlements to roles are kept in place.
|Note: In the entitlements model we set createdbyprocess=create_just_in_time_entitlements. For manual operations we set createdbyprocess=manual
|
@ -2115,12 +2117,11 @@ object Glossary extends MdcLoggable {
|
|Register your App key [HERE]($getServerUrl/consumer-registration)
|
|Copy and paste the CONSUMER_KEY, CONSUMER_SECRET and REDIRECT_URL for the subsequent steps below.
|Copy and paste the CLIENT ID (AKA CONSUMER KEY), CLIENT SECRET (AKA CONSUMER SECRET) and REDIRECT_URL for the subsequent steps below.
|
|
|### Step 2: Initiate the OAuth 2.0 / OpenID Connect Flow
|
|
|Once you have registered your App you should initiate the OAuth2 / OIDC flow using the following URL
|
|${APIUtil.getHydraPublicServerUrl}/oauth2/auth
@ -2129,6 +2130,12 @@ object Glossary extends MdcLoggable {
|
|${APIUtil.getHydraPublicServerUrl}/oauth2/auth?client_id=YOUR-CLIENT-ID&response_type=code&state=GENERATED_BY_YOUR_APP&scope=openid+offline+ReadAccountsBasic+ReadAccountsDetail+ReadBalances+ReadTransactionsBasic+ReadTransactionsDebits+ReadTransactionsDetail&redirect_uri=https%3A%2F%2FYOUR-APP.com%2Fmain.html
|
|### Step 3: Exchange the authorisation code for an access token
|
|The token endpoint is:
|
|${APIUtil.getHydraPublicServerUrl}/oauth2/token
|
|
|For further information please see [here](https://www.ory.sh/hydra/docs/concepts/login#initiating-the-oauth-20--openid-connect-flow)
|

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

@ -3056,7 +3056,7 @@ object NewStyle extends MdcLoggable{
private[this] val endpointMappingTTL = APIUtil.getPropsValue(s"endpointMapping.cache.ttl.seconds", "0").toInt
def getEndpointMappings(bankId: Option[String], callContext: Option[CallContext]): OBPReturnType[List[EndpointMappingT]] = {
def getEndpointMappings(bankId: Option[String], callContext: Option[CallContext]): OBPReturnType[List[EndpointMappingT]] = Future{
import scala.concurrent.duration._
validateBankId(bankId, callContext)
@ -3064,7 +3064,7 @@ object NewStyle extends MdcLoggable{
var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString)
CacheKeyFromArguments.buildCacheKey {
Caching.memoizeSyncWithProvider(Some(cacheKey.toString()))(endpointMappingTTL second) {
Future{(EndpointMappingProvider.endpointMappingProvider.vend.getAllEndpointMappings(bankId), callContext)}
{(EndpointMappingProvider.endpointMappingProvider.vend.getAllEndpointMappings(bankId), callContext)}
}
}
}

View File

@ -10,7 +10,7 @@ import code.util.Helper.MdcLoggable
import com.github.dwickern.macros.NameOf
import com.nimbusds.jose.jwk.RSAKey
import com.nimbusds.jose.util.X509CertUtils
import net.liftweb.common.{Box, Failure, Full}
import net.liftweb.common.{Box, Failure, Full, Empty}
import org.bouncycastle.asn1._
import org.bouncycastle.asn1.x509.Extension
import org.bouncycastle.asn1.x509.qualified.QCStatement
@ -246,5 +246,37 @@ object X509 extends MdcLoggable {
case None => Failure(ErrorMessages.X509CannotGetCertificate)
}
}
def getCommonName(pem: Option[String]): Box[String] = {
getFieldCommon(pem, "CN")
}
def getOrganization(pem: Option[String]): Box[String] = {
getFieldCommon(pem, "O")
}
def getOrganizationUnit(pem: Option[String]): Box[String] = {
getFieldCommon(pem, "OU")
}
def getEmailAddress(pem: Option[String]): Box[String] = {
getFieldCommon(pem, "EMAILADDRESS")
.or(getFieldCommon(pem, "EMAILADDRESS".toLowerCase()))
}
private def getFieldCommon(pem: Option[String], field: String) = {
pem match {
case Some(unboxedPem) =>
extractCertificateInfo(unboxedPem).map { item =>
val splitByComma: Array[String] = item.subject_domain_name.split(",")
val splitByKeyValuePair: Array[(String, String)] = splitByComma.map(i => i.split("=")(0).trim -> i.split("=")(1).trim)
val valuesAsMap: Map[String, List[String]] = splitByKeyValuePair.toList.groupBy(_._1).map { case (k, v) => (k, v.map(_._2)) }
val result: String = valuesAsMap.get(field).map(_.mkString).getOrElse("")
result
} match {
case Full(value) if value.isEmpty => Empty
case everythingElse => everythingElse
}
case _ =>
Empty
}
}
}

View File

@ -1,7 +1,8 @@
package code.api.util.newstyle
import code.api.util.APIUtil.{OBPReturnType, unboxFull}
import code.api.util.APIUtil.{OBPReturnType, unboxFull, unboxFullOrFail}
import code.api.util.CallContext
import code.api.util.ErrorMessages.CreateConsumerError
import code.consumer.Consumers
import code.model.{AppType, Consumer}
@ -18,6 +19,7 @@ object Consumer {
appType: Option[AppType],
description: Option[String],
developerEmail: Option[String],
company: Option[String],
redirectURL: Option[String],
createdByUserId: Option[String],
clientCertificate: Option[String],
@ -33,12 +35,13 @@ object Consumer {
developerEmail,
redirectURL,
createdByUserId,
clientCertificate
) map {
(_, callContext)
}
clientCertificate,
company
)
} map {
unboxFull(_)
(_, callContext)
} map {
x => (unboxFullOrFail(x._1, callContext, CreateConsumerError, 400), x._2)
}
}

View File

@ -0,0 +1,82 @@
package code.api.util.newstyle
import code.api.util.APIUtil.{OBPReturnType, unboxFull, unboxFullOrFail}
import code.api.util.ErrorMessages.{RegulatedEntityNotDeleted, RegulatedEntityNotFound}
import code.api.util.{APIUtil, CallContext}
import code.consumer.Consumers
import code.model.{AppType, Consumer}
import code.regulatedentities.MappedRegulatedEntityProvider
import com.openbankproject.commons.model.RegulatedEntityTrait
import net.liftweb.common.Box
import scala.concurrent.Future
object RegulatedEntityNewStyle {
import com.openbankproject.commons.ExecutionContext.Implicits.global
def createRegulatedEntityNewStyle(certificateAuthorityCaOwnerId: Option[String],
entityCertificatePublicKey: Option[String],
entityName: Option[String],
entityCode: Option[String],
entityType: Option[String],
entityAddress: Option[String],
entityTownCity: Option[String],
entityPostCode: Option[String],
entityCountry: Option[String],
entityWebSite: Option[String],
services: Option[String],
callContext: Option[CallContext]): OBPReturnType[RegulatedEntityTrait] = {
Future {
MappedRegulatedEntityProvider.createRegulatedEntity(
certificateAuthorityCaOwnerId: Option[String],
entityCertificatePublicKey: Option[String],
entityName: Option[String],
entityCode: Option[String],
entityType: Option[String],
entityAddress: Option[String],
entityTownCity: Option[String],
entityPostCode: Option[String],
entityCountry: Option[String],
entityWebSite: Option[String],
services: Option[String]
) map {
(_, callContext)
}
} map {
unboxFull(_)
}
}
def getRegulatedEntitiesNewStyle(callContext: Option[CallContext]): OBPReturnType[List[RegulatedEntityTrait]] = {
Future {
MappedRegulatedEntityProvider.getRegulatedEntities()
} map {
(_, callContext)
}
}
def getRegulatedEntityByEntityIdNewStyle(id: String,
callContext: Option[CallContext]
): OBPReturnType[RegulatedEntityTrait] = {
Future {
MappedRegulatedEntityProvider.getRegulatedEntityByEntityId(id)
} map {
(_, callContext)
} map {
x => (unboxFullOrFail(x._1, callContext, RegulatedEntityNotFound, 404), x._2)
}
}
def deleteRegulatedEntityNewStyle(id: String,
callContext: Option[CallContext]
): OBPReturnType[Boolean] = {
Future {
MappedRegulatedEntityProvider.deleteRegulatedEntity(id)
} map {
(_, callContext)
} map {
x => (unboxFullOrFail(x._1, callContext, RegulatedEntityNotDeleted, 400), x._2)
}
}
}

View File

@ -771,6 +771,10 @@ trait APIMethods310 {
"Get Consumers",
s"""Get the all Consumers.
|
|${authenticationRequiredMessage(true)}
|
|${urlParametersDocument(true, true)}
|
|""",
EmptyBody,
consumersJson310,
@ -790,7 +794,9 @@ trait APIMethods310 {
for {
(Full(u), callContext) <- authenticatedAccess(cc)
_ <- NewStyle.function.hasEntitlement("", u.userId, ApiRole.canGetConsumers, callContext)
consumers <- Consumers.consumers.vend.getConsumersFuture()
httpParams <- NewStyle.function.extractHttpParamsFromUrl(cc.url)
(obpQueryParams, callContext) <- createQueriesByHttpParamsFuture(httpParams, callContext)
consumers <- Consumers.consumers.vend.getConsumersFuture(obpQueryParams, callContext)
users <- Users.users.vend.getUsersByUserIdsFuture(consumers.map(_.createdByUserId.get))
} yield {
(createConsumersJson(consumers, users), HttpCode.`200`(callContext))

View File

@ -5295,6 +5295,7 @@ trait APIMethods400 extends MdcLoggable {
appType = None,
description = Some(postedJson.description),
developerEmail = Some(postedJson.developer_email),
company = None,
redirectURL = Some(postedJson.redirect_url),
createdByUserId = Some(u.userId),
clientCertificate = Some(postedJson.clientCertificate),

View File

@ -2,27 +2,33 @@ package code.api.v5_1_0
import code.api.Constant
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.{apiCollectionJson400, apiCollectionsJson400, apiInfoJson400, postApiCollectionJson400, revokedConsentJsonV310, _}
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._
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.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._
import code.api.v2_0_0.{EntitlementJSONs, JSONFactory200}
import code.api.util.newstyle.Consumer.createConsumerNewStyle
import code.api.util.newstyle.RegulatedEntityNewStyle.{createRegulatedEntityNewStyle, deleteRegulatedEntityNewStyle, getRegulatedEntitiesNewStyle, getRegulatedEntityByEntityIdNewStyle}
import code.api.v2_1_0.{ConsumerRedirectUrlJSON, JSONFactory210}
import code.api.v3_0_0.JSONFactory300
import code.api.v3_0_0.JSONFactory300.createAggregateMetricJson
import code.api.v3_1_0.ConsentJsonV310
import code.api.v3_1_0.JSONFactory310.createBadLoginStatusJson
import code.api.v4_0_0.{JSONFactory400, PostApiCollectionJson400}
import code.api.v5_0_0.ConsentJsonV500
import code.api.v5_1_0.JSONFactory510.{createRegulatedEntitiesJson, createRegulatedEntityJson}
import code.atmattribute.AtmAttribute
import code.bankconnectors.Connector
import code.consent.Consents
import code.loginattempts.LoginAttempt
import code.metrics.APIMetrics
import code.model.AppType
import code.model.dataAccess.MappedBankAccount
import code.regulatedentities.MappedRegulatedEntityProvider
import code.transactionrequests.TransactionRequests.TransactionRequestTypes.{apply => _}
import code.userlocks.UserLocksProvider
import code.users.Users
@ -32,15 +38,14 @@ import code.views.Views
import code.views.system.{AccountAccess, ViewDefinition}
import com.github.dwickern.macros.NameOf.nameOf
import com.openbankproject.commons.ExecutionContext.Implicits.global
import com.openbankproject.commons.dto.CustomerAndAttribute
import com.openbankproject.commons.model.enums.{AtmAttributeType, UserAttributeType}
import com.openbankproject.commons.model.{AtmId, AtmT, BankId, Permission}
import com.openbankproject.commons.model._
import com.openbankproject.commons.util.{ApiVersion, ScannedApiVersion}
import net.liftweb.common.{Box, Full}
import net.liftweb.http.S
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
import scala.collection.immutable.{List, Nil}
@ -120,6 +125,154 @@ trait APIMethods510 {
(SuggestedSessionTimeoutV510(timeoutInSeconds.toString), HttpCode.`200`(cc.callContext))
}
}
staticResourceDocs += ResourceDoc(
regulatedEntities,
implementedInApiVersion,
nameOf(regulatedEntities),
"GET",
"/regulated-entities",
"Get Regulated Entities",
"""Returns information about:
|
|* Regulated Entities
""",
EmptyBody,
regulatedEntitiesJsonV510,
List(UnknownError),
apiTagDirectory :: apiTagApi :: Nil)
lazy val regulatedEntities: OBPEndpoint = {
case "regulated-entities" :: Nil JsonGet _ =>
cc => implicit val ec = EndpointContext(Some(cc))
for {
(entities, callContext) <- getRegulatedEntitiesNewStyle(cc.callContext)
} yield {
(createRegulatedEntitiesJson(entities), HttpCode.`200`(callContext))
}
}
staticResourceDocs += ResourceDoc(
getRegulatedEntityById,
implementedInApiVersion,
nameOf(getRegulatedEntityById),
"GET",
"/regulated-entities/REGULATED_ENTITY_ID",
"Get Regulated Entity",
"""Get Regulated Entity By REGULATED_ENTITY_ID
""",
EmptyBody,
regulatedEntitiesJsonV510,
List(UnknownError),
apiTagDirectory :: apiTagApi :: Nil)
lazy val getRegulatedEntityById: OBPEndpoint = {
case "regulated-entities" :: regulatedEntityId :: Nil JsonGet _ =>
cc => implicit val ec = EndpointContext(Some(cc))
for {
(entity, callContext) <- getRegulatedEntityByEntityIdNewStyle(regulatedEntityId, cc.callContext)
} yield {
(createRegulatedEntityJson(entity), HttpCode.`200`(callContext))
}
}
staticResourceDocs += ResourceDoc(
createRegulatedEntity,
implementedInApiVersion,
nameOf(createRegulatedEntity),
"POST",
"/regulated-entities",
"Create Regulated Entity",
s"""Create Regulated Entity
|
|${authenticationRequiredMessage(true)}
|
|""",
regulatedEntityPostJsonV510,
regulatedEntitiesJsonV510,
List(
$UserNotLoggedIn,
UserHasMissingRoles,
InvalidJsonFormat,
UnknownError
),
List(apiTagDirectory, apiTagApi),
Some(List(canCreateRegulatedEntity))
)
lazy val createRegulatedEntity: OBPEndpoint = {
case "regulated-entities" :: Nil JsonPost json -> _ => {
cc =>
implicit val ec = EndpointContext(Some(cc))
val failMsg = s"$InvalidJsonFormat The Json body should be the $RegulatedEntityPostJsonV510 "
for {
postedData <- NewStyle.function.tryons(failMsg, 400, cc.callContext) {
json.extract[RegulatedEntityPostJsonV510]
}
failMsg = s"$InvalidJsonFormat The `services` field is not valid JSON"
_ <- NewStyle.function.tryons(failMsg, 400, cc.callContext) {
parse(postedData.services)
}
(entity, callContext) <- createRegulatedEntityNewStyle(
certificateAuthorityCaOwnerId = Some(postedData.certificate_authority_ca_owner_id),
entityCertificatePublicKey = Some(postedData.entity_certificate_public_key),
entityName = Some(postedData.entity_name),
entityCode = Some(postedData.entity_code),
entityType = Some(postedData.entity_type),
entityAddress = Some(postedData.entity_address),
entityTownCity = Some(postedData.entity_town_city),
entityPostCode = Some(postedData.entity_post_code),
entityCountry = Some(postedData.entity_country),
entityWebSite = Some(postedData.entity_web_site),
services = Some(postedData.services),
cc.callContext
)
} yield {
(createRegulatedEntityJson(entity), HttpCode.`201`(callContext))
}
}
}
resourceDocs += ResourceDoc(
deleteRegulatedEntity,
implementedInApiVersion,
nameOf(deleteRegulatedEntity),
"DELETE",
"/regulated-entities/REGULATED_ENTITY_ID",
"Delete Regulated Entity",
s"""Delete Regulated Entity specified by REGULATED_ENTITY_ID
|
|${authenticationRequiredMessage(true)}
|""".stripMargin,
EmptyBody,
EmptyBody,
List(
$UserNotLoggedIn,
UserHasMissingRoles,
InvalidConnectorResponse,
UnknownError
),
List(apiTagDirectory, apiTagApi),
Some(List(canDeleteRegulatedEntity)))
lazy val deleteRegulatedEntity: OBPEndpoint = {
case "regulated-entities" :: regulatedEntityId :: Nil JsonDelete _ => {
cc =>
implicit val ec = EndpointContext(Some(cc))
for {
(deleted, callContext) <- deleteRegulatedEntityNewStyle(
regulatedEntityId: String,
cc.callContext: Option[CallContext]
)
} yield {
org.scalameta.logger.elem(deleted)
(Full(deleted), HttpCode.`200`(callContext))
}
}
}
staticResourceDocs += ResourceDoc(
waitingForGodot,
@ -1275,19 +1428,22 @@ trait APIMethods510 {
"GET",
"/management/metrics",
"Get Metrics",
s"""Get the all metrics
s"""Get API metrics rows. These are records of each REST API call.
|
|require CanReadMetrics role
|
|Filters Part 1.*filtering* (no wilde cards etc.) parameters to GET /management/metrics
|
|Should be able to filter on the following metrics fields
|You can filter by the following fields by applying url parameters
|
|eg: /management/metrics?from_date=$DateWithMsExampleString&to_date=$DateWithMsExampleString&limit=50&offset=2
|
|1 from_date (defaults to one week before current date): eg:from_date=$DateWithMsExampleString
|1 from_date e.g.:from_date=$DateWithMsExampleString Defaults to the Unix Epoch i.e. ${theEpochTime}
|
|2 to_date (defaults to current date) eg:to_date=$DateWithMsExampleString
|2 to_date e.g.:to_date=$DateWithMsExampleString Defaults to a far future date i.e. ${APIUtil.ToDateInFuture}
|
|Note: it is recommended you send a valid from_date (e.g. 5 seconds ago) and to_date (now + 1 second) if you want to get the latest records
| Otherwise you may receive stale cached results.
|
|3 limit (for pagination: defaults to 50) eg:limit=200
|
@ -1621,6 +1777,149 @@ trait APIMethods510 {
}
}
staticResourceDocs += ResourceDoc(
createConsumer,
implementedInApiVersion,
"createConsumer",
"POST",
"/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
|
|""",
ConsumerJwtPostJsonV510("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkZXNjcmlwdGlvbiI6IkRlc2NyaXB0aW9uIn0.qDnzk1dGK8akdLFRl8fmJV_SeoDjRTDG_eMogCIzZ7M"),
consumerJsonV510,
List(
InvalidJsonFormat,
UnknownError
),
List(apiTagDirectory, apiTagConsumer),
Some(Nil))
lazy val createConsumer: OBPEndpoint = {
case "dynamic-registration" :: "consumers" :: Nil JsonPost json -> _ => {
cc =>
implicit val ec = EndpointContext(Some(cc))
for {
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)
}
_ <- Helper.booleanToFuture(RegulatedEntityNotFoundByCertificate, 400, cc.callContext) {
MappedRegulatedEntityProvider.getRegulatedEntities()
.exists(_.entityCertificatePublicKey.replace("""\n""", "") == pem.getOrElse("").replace("""\n""", ""))
}
(consumer, callContext) <- createConsumerNewStyle(
key = Some(Helpers.randomString(40).toLowerCase),
secret = Some(Helpers.randomString(40).toLowerCase),
isActive = Some(true),
name = getCommonName(pem).or(postedJson.app_name) ,
appType = postedJson.app_type.map(AppType.valueOf).orElse(Some(AppType.valueOf("Confidential"))),
description = Some(postedJson.description),
developerEmail = getEmailAddress(pem).or(postedJson.developer_email),
company = getOrganization(pem),
redirectURL = postedJson.redirect_url,
createdByUserId = None,
clientCertificate = pem,
cc.callContext
)
} yield {
// Format the data as json
val json = JSONFactory510.createConsumerJSON(consumer, Some(certificateInfo))
// Return
(json, HttpCode.`201`(callContext))
}
}
}
private def consumerDisabledText() = {
if(APIUtil.getPropsAsBoolValue("consumers_enabled_by_default", false) == false) {
"Please note: Your consumer may be disabled as a result of this action."
} else {
""
}
}
staticResourceDocs += ResourceDoc(
updateConsumerRedirectUrl,
implementedInApiVersion,
"updateConsumerRedirectUrl",
"PUT",
"/management/consumers/CONSUMER_ID/consumer/redirect_url",
"Update Consumer RedirectUrl",
s"""Update an existing redirectUrl for a Consumer specified by CONSUMER_ID.
|
| ${consumerDisabledText()}
|
| CONSUMER_ID can be obtained after you register the application.
|
| Or use the endpoint 'Get Consumers' to get it
|
""".stripMargin,
consumerRedirectUrlJSON,
consumerJSON,
List(
UserNotLoggedIn,
UserHasMissingRoles,
UnknownError
),
List(apiTagConsumer),
Some(List(canUpdateConsumerRedirectUrl))
)
lazy val updateConsumerRedirectUrl: OBPEndpoint = {
case "management" :: "consumers" :: consumerId :: "consumer" :: "redirect_url" :: Nil JsonPut json -> _ => {
cc =>
implicit val ec = EndpointContext(Some(cc))
for {
(Full(u), callContext) <- authenticatedAccess(cc)
_ <- APIUtil.getPropsAsBoolValue("consumers_enabled_by_default", false) match {
case true => Future(Full(Unit))
case false => NewStyle.function.hasEntitlement("", u.userId, ApiRole.canUpdateConsumerRedirectUrl, callContext)
}
postJson <- NewStyle.function.tryons(InvalidJsonFormat, 400, callContext) {
json.extract[ConsumerRedirectUrlJSON]
}
consumer <- NewStyle.function.getConsumerByConsumerId(consumerId, callContext)
//only the developer that created the Consumer should be able to edit it
_ <- Helper.booleanToFuture(UserNoPermissionUpdateConsumer, 400, callContext) {
consumer.createdByUserId.equals(u.userId)
}
//update the redirectURL and isactive (set to false when change redirectUrl) field in consumer table
updatedConsumer <- NewStyle.function.updateConsumer(consumer.id.get, None, None, Some(APIUtil.getPropsAsBoolValue("consumers_enabled_by_default", false)), None, None, None, None, Some(postJson.redirect_url), None, callContext)
} yield {
val json = JSONFactory510.createConsumerJSON(updatedConsumer)
(json, HttpCode.`200`(callContext))
}
}
}
}
}

View File

@ -30,21 +30,24 @@ import code.api.Constant
import code.api.util.{APIUtil, ConsentJWT, CustomJsonFormats, JwtUtil, Role}
import code.api.util.APIUtil.gitCommit
import code.api.v1_4_0.JSONFactory1_4_0.{LocationJsonV140, MetaJsonV140, transformToLocationFromV140, transformToMetaFromV140}
import code.api.v2_1_0.ResourceUserJSON
import code.api.v3_0_0.JSONFactory300.{createLocationJson, createMetaJson, transformToAddressFromV300}
import code.api.v3_0_0.{AccountIdJson, AccountsIdsJsonV300, AddressJsonV300, OpeningTimesV300}
import code.api.v4_0_0.{EnergySource400, HostedAt400, HostedBy400}
import code.atmattribute.AtmAttribute
import code.atms.Atms.Atm
import code.users.UserAttribute
import code.users.{UserAttribute, Users}
import code.views.system.{AccountAccess, ViewDefinition}
import com.openbankproject.commons.model.{Address, AtmId, AtmT, BankId, BankIdAccountId, Customer, Location, Meta}
import com.openbankproject.commons.model.{Address, AtmId, AtmT, BankId, BankIdAccountId, Customer, Location, Meta, RegulatedEntityTrait}
import com.openbankproject.commons.util.{ApiVersion, ScannedApiVersion}
import java.util.Date
import code.consent.MappedConsent
import code.metrics.APIMetric
import net.liftweb.common.Box
import net.liftweb.json.parse
import code.model.Consumer
import net.liftweb.common.{Box, Full}
import net.liftweb.json
import net.liftweb.json.{JValue, parse}
import scala.collection.immutable.List
import scala.util.Try
@ -64,6 +67,36 @@ case class APIInfoJsonV510(
energy_source : EnergySource400,
resource_docs_requires_role: Boolean
)
case class RegulatedEntityJsonV510(
entity_id: String,
certificate_authority_ca_owner_id: String,
entity_certificate_public_key: String,
entity_name: String,
entity_code: String,
entity_type: String,
entity_address: String,
entity_town_city: String,
entity_post_code: String,
entity_country: String,
entity_web_site: String,
services: JValue
)
case class RegulatedEntityPostJsonV510(
certificate_authority_ca_owner_id: String,
entity_certificate_public_key: String,
entity_name: String,
entity_code: String,
entity_type: String,
entity_address: String,
entity_town_city: String,
entity_post_code: String,
entity_country: String,
entity_web_site: String,
services: String
)
case class RegulatedEntitiesJsonV510(entities: List[RegulatedEntityJsonV510])
case class WaitingForGodotJsonV510(sleep_in_milliseconds: Long)
case class CertificateInfoJsonV510(
@ -250,6 +283,30 @@ 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,
developer_email: Option[String],
redirect_url: Option[String],
)
case class ConsumerJsonV510(consumer_id: String,
consumer_key: String,
consumer_secret: String,
app_name: String,
app_type: String,
description: String,
developer_email: String,
company: String,
redirect_url: String,
certificate_pem: String,
certificate_info: Option[CertificateInfoJsonV510],
created_by_user: ResourceUserJSON,
enabled: Boolean,
created: Date
)
object JSONFactory510 extends CustomJsonFormats {
def createCustomersIds(customers : List[Customer]): CustomersIdsJsonV510 =
@ -534,6 +591,26 @@ object JSONFactory510 extends CustomJsonFormats {
UserAttributesResponseJsonV510(userAttribute.map(createUserAttributeJson))
}
def createRegulatedEntityJson(entity: RegulatedEntityTrait): RegulatedEntityJsonV510 = {
RegulatedEntityJsonV510(
entity_id = entity.entityId,
certificate_authority_ca_owner_id = entity.certificateAuthorityCaOwnerId,
entity_certificate_public_key = entity.entityCertificatePublicKey,
entity_name = entity.entityName,
entity_code = entity.entityCode,
entity_type = entity.entityType,
entity_address = entity.entityAddress,
entity_town_city = entity.entityTownCity,
entity_post_code = entity.entityPostCode,
entity_country = entity.entityCountry,
entity_web_site = entity.entityWebSite,
services = json.parse(entity.services)
)
}
def createRegulatedEntitiesJson(entities: List[RegulatedEntityTrait]): RegulatedEntitiesJsonV510 = {
RegulatedEntitiesJsonV510(entities.map(createRegulatedEntityJson))
}
def createMetricJson(metric: APIMetric): MetricJsonV510 = {
MetricJsonV510(
user_id = metric.getUserId(),
@ -548,8 +625,8 @@ object JSONFactory510 extends CustomJsonFormats {
implemented_by_partial_function = metric.getImplementedByPartialFunction(),
correlation_id = metric.getCorrelationId(),
duration = metric.getDuration(),
source_ip = metric.getTargetIp(),
target_ip = metric.getSourceIp(),
source_ip = metric.getSourceIp(),
target_ip = metric.getTargetIp(),
response_body = metric.getResponseBody()
)
}
@ -558,6 +635,37 @@ object JSONFactory510 extends CustomJsonFormats {
MetricsJsonV510(metrics.map(createMetricJson))
}
def createConsumerJSON(c: Consumer, certificateInfo: Option[CertificateInfoJsonV510] = None): ConsumerJsonV510 = {
val resourceUserJSON = Users.users.vend.getUserByUserId(c.createdByUserId.toString()) match {
case Full(resourceUser) => ResourceUserJSON(
user_id = resourceUser.userId,
email = resourceUser.emailAddress,
provider_id = resourceUser.idGivenByProvider,
provider = resourceUser.provider,
username = resourceUser.name
)
case _ => null
}
ConsumerJsonV510(
consumer_id = c.consumerId.get,
consumer_key = c.key.get,
consumer_secret = c.secret.get,
app_name = c.name.get,
app_type = c.appType.toString(),
description = c.description.get,
developer_email = c.developerEmail.get,
company = c.company.get,
redirect_url = c.redirectURL.get,
certificate_pem = c.clientCertificate.get,
certificate_info = certificateInfo,
created_by_user = resourceUserJSON,
enabled = c.isActive.get,
created = c.createdAt.get
)
}
}

View File

@ -1,6 +1,6 @@
package code.consumer
import code.api.util.APIUtil
import code.api.util.{APIUtil, CallContext, OBPQueryParam}
import code.model.{AppType, Consumer, MappedConsumersProvider}
import code.remotedata.RemotedataConsumers
import com.openbankproject.commons.model.{BankIdAccountId, User, View}
@ -31,7 +31,7 @@ trait ConsumersProvider {
def getConsumerByConsumerId(consumerId: String): Box[Consumer]
def getConsumerByConsumerIdFuture(consumerId: String): Future[Box[Consumer]]
def getConsumersByUserIdFuture(userId: String): Future[List[Consumer]]
def getConsumersFuture(): Future[List[Consumer]]
def getConsumersFuture(httpParams: List[OBPQueryParam], callContext: Option[CallContext]): Future[List[Consumer]]
def createConsumer(key: Option[String], secret: Option[String], isActive: Option[Boolean], name: Option[String], appType: Option[AppType], description: Option[String], developerEmail: Option[String], redirectURL: Option[String], createdByUserId: Option[String], clientCertificate: Option[String] = None, company: Option[String] = None): Box[Consumer]
def deleteConsumer(consumer: Consumer): Boolean
def updateConsumer(id: Long, key: Option[String], secret: Option[String], isActive: Option[Boolean], name: Option[String], appType: Option[AppType], description: Option[String], developerEmail: Option[String], redirectURL: Option[String], createdByUserId: Option[String]): Box[Consumer]
@ -64,7 +64,7 @@ class RemotedataConsumersCaseClasses {
case class getConsumerByConsumerId(consumerId: String)
case class getConsumerByConsumerIdFuture(consumerId: String)
case class getConsumersByUserIdFuture(userId: String)
case class getConsumersFuture()
case class getConsumersFuture(httpParams: List[OBPQueryParam], callContext: Option[CallContext])
case class createConsumer(key: Option[String], secret: Option[String], isActive: Option[Boolean], name: Option[String], appType: Option[AppType], description: Option[String], developerEmail: Option[String], redirectURL: Option[String], createdByUserId: Option[String], clientCertificate: Option[String], company: Option[String])
case class updateConsumer(id: Long, key: Option[String], secret: Option[String], isActive: Option[Boolean], name: Option[String], appType: Option[AppType], description: Option[String], developerEmail: Option[String], redirectURL: Option[String], createdByUserId: Option[String])
case class deleteConsumer(consumer: Consumer)

View File

@ -360,7 +360,7 @@ object MappedMetrics extends APIMetrics with MdcLoggable{
}
// TODO Cache this as long as fromDate and toDate are in the past (before now)
override def getTopApisFuture(queryParams: List[OBPQueryParam]): Future[Box[List[TopApi]]] = {
override def getTopApisFuture(queryParams: List[OBPQueryParam]): Future[Box[List[TopApi]]] = Future{
/**
* Please note that "var cacheKey = (randomUUID().toString, randomUUID().toString, randomUU
* is just a temporary value field with UUID values in order to prevent any ambiguity.
@ -369,7 +369,7 @@ object MappedMetrics extends APIMetrics with MdcLoggable{
*/
var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString)
CacheKeyFromArguments.buildCacheKey {Caching.memoizeSyncWithProvider(Some(cacheKey.toString()))(cachedTopApis seconds){
Future{
{
val fromDate = queryParams.collect { case OBPFromDate(value) => value }.headOption
val toDate = queryParams.collect { case OBPToDate(value) => value }.headOption
val consumerId = queryParams.collect { case OBPConsumerId(value) => value }.headOption.flatMap(consumerIdToPrimaryKey)
@ -440,7 +440,7 @@ object MappedMetrics extends APIMetrics with MdcLoggable{
}}
// TODO Cache this as long as fromDate and toDate are in the past (before now)
override def getTopConsumersFuture(queryParams: List[OBPQueryParam]): Future[Box[List[TopConsumer]]] = {
override def getTopConsumersFuture(queryParams: List[OBPQueryParam]): Future[Box[List[TopConsumer]]] = Future {
/**
* Please note that "var cacheKey = (randomUUID().toString, randomUUID().toString, randomUU
* is just a temporary value field with UUID values in order to prevent any ambiguity.
@ -449,7 +449,7 @@ object MappedMetrics extends APIMetrics with MdcLoggable{
*/
var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString)
CacheKeyFromArguments.buildCacheKey {Caching.memoizeSyncWithProvider(Some(cacheKey.toString()))(cachedTopConsumers seconds){
Future {
val fromDate = queryParams.collect { case OBPFromDate(value) => value }.headOption
val toDate = queryParams.collect { case OBPToDate(value) => value }.headOption
val consumerId = queryParams.collect { case OBPConsumerId(value) => value }.headOption.flatMap(consumerIdToPrimaryKey)
@ -519,7 +519,7 @@ object MappedMetrics extends APIMetrics with MdcLoggable{
}
tryo(result)
}
}}}
}}
}

View File

@ -27,7 +27,7 @@ TESOBE (http://www.tesobe.com/)
package code.model
import java.util.{Collections, Date}
import code.api.util.APIUtil
import code.api.util.{APIUtil, CallContext, OBPAscending, OBPDescending, OBPFromDate, OBPLimit, OBPOffset, OBPOrdering, OBPQueryParam, OBPToDate}
import code.api.util.CommonFunctions.validUri
import code.api.util.migration.Migration.DbFunction
import code.consumer.{Consumers, ConsumersProvider}
@ -119,11 +119,26 @@ object MappedConsumersProvider extends ConsumersProvider with MdcLoggable {
Future(getConsumersByUserId(userId))
}
def getConsumers(): List[Consumer] = {
Consumer.findAll()
def getConsumers(queryParams: List[OBPQueryParam], callContext: Option[CallContext]): List[Consumer] = {
val limit = queryParams.collect { case OBPLimit(value) => MaxRows[Consumer](value) }.headOption
val offset = queryParams.collect { case OBPOffset(value) => StartAt[Consumer](value) }.headOption
val fromDate = queryParams.collect { case OBPFromDate(date) => By_>=(Consumer.createdAt, date) }.headOption
val toDate = queryParams.collect { case OBPToDate(date) => By_<=(Consumer.createdAt, date) }.headOption
val ordering = queryParams.collect {
case OBPOrdering(_, direction) =>
direction match {
case OBPAscending => OrderBy(Consumer.createdAt, Ascending)
case OBPDescending => OrderBy(Consumer.createdAt, Descending)
}
}
val mapperParams: Seq[QueryParam[Consumer]] = Seq(limit.toSeq, offset.toSeq, fromDate.toSeq, toDate.toSeq, ordering.toSeq).flatten
Consumer.findAll(mapperParams: _*)
}
override def getConsumersFuture(): Future[List[Consumer]] = {
Future(getConsumers())
override def getConsumersFuture(httpParams: List[OBPQueryParam], callContext: Option[CallContext]): Future[List[Consumer]] = {
Future(getConsumers(httpParams: List[OBPQueryParam], callContext: Option[CallContext]))
}
override def createConsumer(key: Option[String],

View File

@ -0,0 +1,131 @@
package code.regulatedentities
import code.util.MappedUUID
import com.openbankproject.commons.model.RegulatedEntityTrait
import net.liftweb.common.Box
import net.liftweb.common.Box.tryo
import net.liftweb.mapper._
import scala.concurrent.Future
object MappedRegulatedEntityProvider extends RegulatedEntityProvider {
def getRegulatedEntities(): List[RegulatedEntityTrait] = {
MappedRegulatedEntity.findAll()
}
override def getRegulatedEntityByEntityId(entityId: String): Box[RegulatedEntityTrait] = {
MappedRegulatedEntity.find(By(MappedRegulatedEntity.EntityId, entityId))
}
override def createRegulatedEntity(certificateAuthorityCaOwnerId: Option[String],
entityCertificatePublicKey: Option[String],
entityName: Option[String],
entityCode: Option[String],
entityType: Option[String],
entityAddress: Option[String],
entityTownCity: Option[String],
entityPostCode: Option[String],
entityCountry: Option[String],
entityWebSite: Option[String],
services: Option[String]
): Box[RegulatedEntityTrait] = {
tryo {
val entity = MappedRegulatedEntity.create
certificateAuthorityCaOwnerId match {
case Some(v) => entity.CertificateAuthorityCaOwnerId(v)
case None =>
}
entityCertificatePublicKey match {
case Some(v) => entity.EntityCertificatePublicKey(v)
case None =>
}
entityName match {
case Some(v) => entity.EntityName(v)
case None =>
}
entityCode match {
case Some(v) => entity.EntityCode(v)
case None =>
}
entityType match {
case Some(v) => entity.EntityType(v)
case None =>
}
entityAddress match {
case Some(v) => entity.EntityAddress(v)
case None =>
}
entityTownCity match {
case Some(v) => entity.EntityTownCity(v)
case None =>
}
entityPostCode match {
case Some(v) => entity.EntityPostCode(v)
case None =>
}
entityCountry match {
case Some(v) => entity.EntityCountry(v)
case None =>
}
entityWebSite match {
case Some(v) => entity.EntityWebSite(v)
case None =>
}
services match {
case Some(v) => entity.Services(v)
case None =>
}
if (entity.validate.isEmpty) {
entity.saveMe()
} else {
throw new Error(entity.validate.map(_.msg.toString()).mkString(";"))
}
}
}
override def deleteRegulatedEntity(id: String): Box[Boolean] = {
tryo(
MappedRegulatedEntity.bulkDelete_!!(By(MappedRegulatedEntity.EntityId, id))
)
}
}
class MappedRegulatedEntity extends RegulatedEntityTrait with LongKeyedMapper[MappedRegulatedEntity] with IdPK {
override def getSingleton = MappedRegulatedEntity
object EntityId extends MappedUUID(this)
object CertificateAuthorityCaOwnerId extends MappedString(this, 256)
object EntityName extends MappedString(this, 256)
object EntityCode extends MappedString(this, 50)
object EntityCertificatePublicKey extends MappedText(this)
object EntityType extends MappedString(this, 50)
object EntityAddress extends MappedString(this, 256)
object EntityTownCity extends MappedString(this, 50)
object EntityPostCode extends MappedString(this, 50)
object EntityCountry extends MappedString(this, 50)
object EntityWebSite extends MappedString(this, 256)
object Services extends MappedText(this)
override def entityId: String = EntityId.get
override def certificateAuthorityCaOwnerId: String = CertificateAuthorityCaOwnerId.get
override def entityName: String = EntityName.get
override def entityCode: String = EntityCode.get
override def entityCertificatePublicKey: String = EntityCertificatePublicKey.get
override def entityType: String = EntityType.get
override def entityAddress: String = EntityAddress.get
override def entityTownCity: String = EntityTownCity.get
override def entityPostCode: String = EntityPostCode.get
override def entityCountry: String = EntityCountry.get
override def entityWebSite: String = EntityWebSite.get
override def services: String = Services.get
}
object MappedRegulatedEntity extends MappedRegulatedEntity with LongKeyedMetaMapper[MappedRegulatedEntity] {
override def dbTableName = "RegulatedEntity" // define the DB table name
override def dbIndexes = Index(CertificateAuthorityCaOwnerId) :: super.dbIndexes
}

View File

@ -0,0 +1,36 @@
package code.regulatedentities
import com.openbankproject.commons.model.RegulatedEntityTrait
import net.liftweb.common.{Box, Logger}
import net.liftweb.util.SimpleInjector
object RegulatedEntityX extends SimpleInjector {
val regulatedEntityProvider = new Inject(buildOne _) {}
def buildOne: RegulatedEntityProvider = MappedRegulatedEntityProvider
}
/* For ProductFee */
trait RegulatedEntityProvider {
private val logger = Logger(classOf[RegulatedEntityProvider])
def getRegulatedEntities(): List[RegulatedEntityTrait]
def getRegulatedEntityByEntityId(entityId: String): Box[RegulatedEntityTrait]
def createRegulatedEntity(certificateAuthorityCaOwnerId: Option[String],
entityCertificatePublicKey: Option[String],
entityName: Option[String],
entityCode: Option[String],
entityType: Option[String],
entityAddress: Option[String],
entityTownCity: Option[String],
entityPostCode: Option[String],
entityCountry: Option[String],
entityWebSite: Option[String],
services: Option[String]
): Box[RegulatedEntityTrait]
def deleteRegulatedEntity(id: String): Box[Boolean]
}

View File

@ -2,6 +2,7 @@ package code.remotedata
import akka.pattern.ask
import code.actorsystem.ObpActorInit
import code.api.util.{CallContext, OBPQueryParam}
import code.consumer.{ConsumersProvider, RemotedataConsumersCaseClasses}
import code.model._
import net.liftweb.common._
@ -35,8 +36,8 @@ object RemotedataConsumers extends ObpActorInit with ConsumersProvider {
def getConsumersByUserIdFuture(id: String): Future[List[Consumer]] =
(actor ? cc.getConsumersByUserIdFuture(id)).mapTo[List[Consumer]]
def getConsumersFuture(): Future[List[Consumer]] =
(actor ? cc.getConsumersFuture()).mapTo[List[Consumer]]
def getConsumersFuture(httpParams: List[OBPQueryParam], callContext: Option[CallContext]): Future[List[Consumer]] =
(actor ? cc.getConsumersFuture(httpParams: List[OBPQueryParam], callContext: Option[CallContext])).mapTo[List[Consumer]]
def createConsumer(key: Option[String], secret: Option[String], isActive: Option[Boolean], name: Option[String], appType: Option[AppType], description: Option[String], developerEmail: Option[String], redirectURL: Option[String], createdByUserId: Option[String], clientCertificate: Option[String] = None, company: Option[String] = None): Box[Consumer] = getValueFromFuture(
(actor ? cc.createConsumer(key, secret, isActive, name, appType, description, developerEmail, redirectURL, createdByUserId, clientCertificate, company)).mapTo[Box[Consumer]]

View File

@ -2,6 +2,7 @@ package code.remotedata
import akka.actor.Actor
import code.actorsystem.ObpActorHelper
import code.api.util.{CallContext, OBPQueryParam}
import code.consumer.RemotedataConsumersCaseClasses
import code.model.{MappedConsumersProvider, _}
import code.util.Helper.MdcLoggable
@ -41,9 +42,9 @@ class RemotedataConsumersActor extends Actor with ObpActorHelper with MdcLoggabl
logger.debug(s"getConsumersByUserIdFuture($id)")
sender ! (mapper.getConsumersByUserId(id))
case cc.getConsumersFuture() =>
case cc.getConsumersFuture(httpParams: List[OBPQueryParam], callContext: Option[CallContext]) =>
logger.debug(s"getConsumersFuture()")
sender ! (mapper.getConsumers())
sender ! (mapper.getConsumers(httpParams: List[OBPQueryParam], callContext: Option[CallContext]))
case cc.createConsumer(key: Option[String], secret: Option[String], isActive: Option[Boolean], name: Option[String], appType: Option[AppType], description: Option[String], developerEmail: Option[String], redirectURL: Option[String], createdByUserId: Option[String], clientCertificate: Option[String], company: Option[String]) =>
logger.debug(s"createConsumer(*****, *****, ${isActive.getOrElse("None")}, ${name.getOrElse("None")}, ${appType.getOrElse("None")}, ${description.getOrElse("None")}, ${developerEmail.getOrElse("None")}, ${redirectURL.getOrElse("None")}, ${createdByUserId.getOrElse("None")}, ${clientCertificate.getOrElse("None")}, ${company.getOrElse("None")})")

View File

@ -48,8 +48,10 @@ class OAuthWorkedThanks extends MdcLoggable {
val redirectUrl = ObpS.param("redirectUrl").map(urlDecode(_))
logger.debug(s"OAuthWorkedThanks.thanks.redirectUrl $redirectUrl")
//extract the clean(omit the parameters) redirect url from request url
val requestedRedirectURL = Helper.extractCleanRedirectURL(redirectUrl.openOr("invalidRequestedRedirectURL")) openOr("invalidRequestedRedirectURL")
logger.debug(s"OAuthWorkedThanks.thanks.requestedRedirectURL $requestedRedirectURL")
val staticPortionOfRedirectUrl = Helper.getStaticPortionOfRedirectURL(redirectUrl.openOr("invalidRequestedRedirectURL")) openOr("invalidRequestedRedirectURL")
val hostOnlyOfRedirectUrlLegacy = Helper.getHostOnlyOfRedirectURL(staticPortionOfRedirectUrl) openOr("invalidRequestedRedirectURL")
logger.debug(s"OAuthWorkedThanks.thanks.staticPortionOfRedirectUrl $staticPortionOfRedirectUrl")
logger.debug(s"OAuthWorkedThanks.thanks.hostOnlyOfRedirectUrlLegacy $hostOnlyOfRedirectUrlLegacy")
val requestedOauthToken = Helper.extractOauthToken(redirectUrl.openOr("No Oauth Token here")) openOr("No Oauth Token here")
logger.debug(s"OAuthWorkedThanks.thanks.requestedOauthToken $requestedOauthToken")
@ -62,13 +64,10 @@ class OAuthWorkedThanks extends MdcLoggable {
redirectUrl match {
case Full(url) =>
//this redirect url is checked by following, no open redirect issue.
// TODO maybe handle case of extra trailing / on the url ?
val incorrectRedirectUrlMessage = s"The validRedirectURL is $validRedirectURL but the staticPortionOfRedirectUrl was $staticPortionOfRedirectUrl"
val incorrectRedirectUrlMessage = s"The validRedirectURL is $validRedirectURL but the requestedRedirectURL was $requestedRedirectURL"
if(validRedirectURL.equals(requestedRedirectURL)) {
//hostOnlyOfRedirectUrlLegacy is deprecated now, we use the staticPortionOfRedirectUrl stead,
if(validRedirectURL.equals(staticPortionOfRedirectUrl)|| validRedirectURL.equals(hostOnlyOfRedirectUrlLegacy)) {
"#redirect-link [href]" #> url &
".app-name"#> appName //there may be several places to be modified in html, so here use the class, not the id.
}else{

View File

@ -1,9 +1,8 @@
package code.util
import java.net.{Socket, SocketException}
import java.net.{Socket, SocketException, URL}
import java.util.UUID.randomUUID
import java.util.{Date, GregorianCalendar}
import code.api.util.{APIUtil, CallContext, CallContextLight, CustomJsonFormats}
import code.api.{APIFailureNewStyle, Constant}
import code.api.util.APIUtil.fullBoxOrException
@ -20,7 +19,9 @@ import com.openbankproject.commons.util.{ReflectUtils, RequiredFieldValidation,
import com.tesobe.CacheKeyFromArguments
import net.liftweb.http.S
import net.liftweb.util.Helpers
import net.liftweb.util.Helpers.tryo
import net.sf.cglib.proxy.{Enhancer, MethodInterceptor, MethodProxy}
import java.lang.reflect.Method
import scala.concurrent.Future
import scala.util.Random
@ -167,27 +168,30 @@ object Helper extends Loggable {
prettyRender(decompose(input))
}
/**
* extract clean redirect url from input value, because input may have some parameters, such as the following examples <br/>
* eg1: http://localhost:8082/oauthcallback?....--> http://localhost:8082 <br/>
* eg2: http://localhost:8016?oautallback?=3NLMGV ...--> http://localhost:8016
*
* @param input a long url with parameters
* @return clean redirect url
*/
def extractCleanRedirectURL(input: String): Box[String] = {
/**
* pattern eg1: http://xxxxxx?oautxxxx -->http://xxxxxx
* pattern eg2: https://xxxxxx/oautxxxx -->http://xxxxxx
*/
//Note: the pattern should be : val pattern = "(https?):\\/\\/(.*)(?=((\\/)|(\\?))oauthcallback*)".r, but the OAuthTest is different, so add the following logic
val pattern = "([A-Za-z][A-Za-z0-9+.-]*):\\/\\/(.*)(?=((\\/)|(\\?))oauth*)".r
val validRedirectURL = pattern findFirstIn input
// Now for the OAuthTest, the redirect format is : http://localhost:8016?oauth_token=G5AEA2U1WG404EGHTIGBHKRR4YJZAPPHWKOMNEEV&oauth_verifier=53018
// It is not the normal case: http://localhost:8082/oauthcallback?oauth_token=LUDKELGJXRDOC1AK1X1TOYIXM5W1AORFJT5KE43B&oauth_verifier=14062
// So add the split function to select the first value; eg: Array(http://localhost:8082, thcallback) --> http://localhost:8082
val extractCleanURL = validRedirectURL.getOrElse("").split("/oauth")(0)
Full(extractCleanURL)
*
* @param redirectUrl eg: http://localhost:8082/oauthcallback?oauth_token=G5AEA2U1WG404EGHTIGBHKRR4YJZAPPHWKOMNEEV&oauth_verifier=53018
* @return http://localhost:8082/oauthcallback
*/
def getStaticPortionOfRedirectURL(redirectUrl: String): Box[String] = {
tryo(redirectUrl.split("\\?")(0)) //return everything before the "?"
}
/**
* extract clean redirect url from input value, because input may have some parameters, such as the following examples <br/>
* eg1: http://localhost:8082/oauthcallback?....--> http://localhost:8082 <br/>
* eg2: http://localhost:8016?oautallback?=3NLMGV ...--> http://localhost:8016
*
* @param redirectUrl -> http://localhost:8082/oauthcallback?oauth_token=G5AEA2U1WG404EGHTIGBHKRR4YJZAPPHWKOMNEEV&oauth_verifier=53018
* @return hostOnlyOfRedirectURL-> http://localhost:8082
*/
@deprecated("We can not only use hostname as the redirectUrl, now add new method `getStaticPortionOfRedirectURL` ","05.12.2023")
def getHostOnlyOfRedirectURL(redirectUrl: String): Box[String] = {
val url = new URL(redirectUrl) //eg: http://localhost:8082/oauthcallback?oauth_token=G5AEA2U1WG404EGHTIGBHKRR4YJZAPPHWKOMNEEV&oauth_verifier=53018
val protocol = url.getProtocol() // http
val authority = url.getAuthority()// localhost:8082, this will contain the port.
tryo(s"$protocol://$authority") // http://localhost:8082
}
/**
@ -480,7 +484,7 @@ object Helper extends Loggable {
}else if((args.length>0) && args.apply(0).toString.equalsIgnoreCase("consumer_key")){
result.asInstanceOf[Box[String]].filter(APIUtil.basicConsumerKeyValidation(_)==SILENCE_IS_GOLDEN)
}else if((args.length>0) && args.apply(0).toString.equalsIgnoreCase("redirectUrl")){
result.asInstanceOf[Box[String]].filter(APIUtil.basicUrlValidation(_))
result.asInstanceOf[Box[String]].filter(APIUtil.basicUriAndQueryStringValidation(_))
} else{
result.asInstanceOf[Box[String]].filter(APIUtil.checkMediumString(_)==SILENCE_IS_GOLDEN)
}

View File

@ -15,6 +15,13 @@
<filter-mapping>
<filter-name>LiftFilter</filter-name>
<!--TODO can prepare the white list later -->
<!-- We can specify folders or files that we want Liftweb to process.-->
<!-- For example, we generally want Liftweb to process all HTML files because they are dynamic, and all the RESTful endpoints.-->
<!-- On the other hand, we generally do not want Liftweb to process JS, JPG, and CSS files because they are static.-->
<!-- Any processing we can offload from Liftweb is good for performance.-->
<!-- Note: Jetty will serve the static files, so they will still appear in the Jetty logs.-->
<!-- Ideally, NGINX is used to serve the static files, preventing them from cluttering the Jetty logs.-->
<url-pattern>/*</url-pattern>
</filter-mapping>

View File

@ -0,0 +1,120 @@
package code.api.v5_1_0
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.regulatedEntityPostJsonV510
import code.api.util.APIUtil.OAuth._
import code.api.util.ApiRole.{CanCreateRegulatedEntity, CanDeleteRegulatedEntity, CanGetSystemIntegrity}
import code.api.util.ErrorMessages.{UserHasMissingRoles, UserNotLoggedIn}
import code.api.v5_1_0.OBPAPI5_1_0.Implementations5_1_0
import code.entitlement.Entitlement
import com.github.dwickern.macros.NameOf.nameOf
import com.openbankproject.commons.model.ErrorMessage
import com.openbankproject.commons.util.ApiVersion
import net.liftweb.json.Serialization._
import org.scalatest.Tag
class RegulatedEntityTest extends V510ServerSetup {
/**
* Test tags
* Example: To run tests with tag "getPermissions":
* mvn test -D tagsToInclude
*
* This is made possible by the scalatest maven plugin
*/
object VersionOfApi extends Tag(ApiVersion.v5_1_0.toString)
object ApiEndpoint1 extends Tag(nameOf(Implementations5_1_0.createRegulatedEntity))
object ApiEndpoint2 extends Tag(nameOf(Implementations5_1_0.getRegulatedEntityById))
object ApiEndpoint3 extends Tag(nameOf(Implementations5_1_0.getRegulatedEntityById))
object ApiEndpoint4 extends Tag(nameOf(Implementations5_1_0.deleteRegulatedEntity))
feature(s"test $ApiEndpoint1 version $VersionOfApi - Unauthorized access") {
scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) {
When("We make a request v5.1.0")
val request510 = (v5_1_0_Request / "regulated-entities").POST
val response510 = makePostRequest(request510, write(regulatedEntityPostJsonV510))
Then("We should get a 401")
response510.code should equal(401)
response510.body.extract[ErrorMessage].message should equal(UserNotLoggedIn)
}
}
feature(s"test $ApiEndpoint1 version $VersionOfApi - Authorized access") {
scenario("We will call the endpoint with user credentials but without a proper entitlement", ApiEndpoint1, VersionOfApi) {
When("We make a request v5.1.0")
val request510 = (v5_1_0_Request / "regulated-entities").POST <@(user1)
val response510 = makePostRequest(request510, write(regulatedEntityPostJsonV510))
Then("error should be " + UserHasMissingRoles + CanCreateRegulatedEntity)
response510.code should equal(403)
response510.body.extract[ErrorMessage].message should be (UserHasMissingRoles + CanCreateRegulatedEntity)
}
}
feature(s"test $ApiEndpoint1 version $VersionOfApi - Authorized access") {
scenario("We will call the endpoint with user credentials and a proper entitlement", ApiEndpoint1, VersionOfApi) {
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanCreateRegulatedEntity.toString)
When("We make a request v5.1.0")
val request510 = (v5_1_0_Request / "regulated-entities").POST <@ (user1)
val response510 = makePostRequest(request510, write(regulatedEntityPostJsonV510))
Then("We get successful response")
response510.code should equal(201)
response510.body.extract[RegulatedEntityJsonV510]
}
}
// ApiEndpoint4 - deleteRegulatedEntity
feature(s"test $ApiEndpoint4 version $VersionOfApi - Unauthorized access") {
scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) {
When("We make a request v5.1.0")
val request510 = (v5_1_0_Request / "regulated-entities" / "some id").DELETE
val response510 = makeDeleteRequest(request510)
Then("We should get a 401")
response510.code should equal(401)
response510.body.extract[ErrorMessage].message should equal(UserNotLoggedIn)
}
}
feature(s"test $ApiEndpoint4 version $VersionOfApi - Authorized access") {
scenario("We will call the endpoint with user credentials but without a proper entitlement", ApiEndpoint1, VersionOfApi) {
When("We make a request v5.1.0")
val request510 = (v5_1_0_Request / "regulated-entities" / "some id").DELETE <@ (user1)
val response510 = makeDeleteRequest(request510)
Then("error should be " + UserHasMissingRoles + CanDeleteRegulatedEntity)
response510.code should equal(403)
response510.body.extract[ErrorMessage].message should be(UserHasMissingRoles + CanDeleteRegulatedEntity)
}
}
feature(s"test $ApiEndpoint1, $ApiEndpoint2, $ApiEndpoint3, $ApiEndpoint4 version $VersionOfApi - CRUD") {
scenario("We will call the endpoint with user credentials but without a proper entitlement", ApiEndpoint1, VersionOfApi) {
// Create a row
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanCreateRegulatedEntity.toString)
val request510 = (v5_1_0_Request / "regulated-entities").POST <@ (user1)
val response510 = makePostRequest(request510, write(regulatedEntityPostJsonV510))
Then("We get successful response")
response510.code should equal(201)
val createdRow: RegulatedEntityJsonV510 = response510.body.extract[RegulatedEntityJsonV510]
// Get the row by id
val getRequest510 = (v5_1_0_Request / "regulated-entities" / createdRow.entity_id).GET
val getResponse510 = makeGetRequest(getRequest510)
getResponse510.code should equal(200)
val gottenRow: RegulatedEntityJsonV510 = getResponse510.body.extract[RegulatedEntityJsonV510]
// TRy to match responses
createdRow should equal(gottenRow)
// Delete the row
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanDeleteRegulatedEntity.toString)
val deleteRequest510 = (v5_1_0_Request / "regulated-entities" / gottenRow.entity_id).DELETE <@ (user1)
val deleteResponse510 = makeDeleteRequest(deleteRequest510)
deleteResponse510.code should equal(200)
// Get all rows
val getAllRequest510 = (v5_1_0_Request / "regulated-entities").GET
val getAllResponse510 = makeGetRequest(getAllRequest510)
getAllResponse510.code should equal(200)
val allRows = getResponse510.body.extract[RegulatedEntitiesJsonV510]
allRows.entities.length should equal(0)
}
}
}

View File

@ -1,101 +0,0 @@
package code.bankconnectors.vMay2019
/*
Open Bank Project - API
Copyright (C) 2011-2019, TESOBE GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see http://www.gnu.org/licenses/.
Email: contact@tesobe.com
TESOBE GmbH
Osloerstrasse 16/17
Berlin 13359, Germany
*/
import code.actorsystem.ObpActorInit
import code.api.JSONFactoryGateway.PayloadOfJwtJSON
import code.api.util.{APIUtil, CallContext, CustomJsonFormats}
import code.bankconnectors.Connector
import code.bankconnectors.storedprocedure.StoredProcedureConnector_vDec2019
import code.bankconnectors.vSept2018._
import code.kafka.KafkaHelper
import code.setup.{DefaultUsers, KafkaSetup, ServerSetupWithTestData}
import com.openbankproject.commons.dto.InBoundGetBanks
import com.openbankproject.commons.model._
import net.liftweb.common.{Box, Failure, Full}
import org.scalatest.Tag
class StoredProcedureConnector_vDec2019Test extends ServerSetupWithTestData with DefaultUsers with ObpActorInit{
override implicit val formats = CustomJsonFormats.formats
object StoredProcedureConnector_vDec2019Test extends Tag("StoredProcedureConnector_vDec2019")
val callContext = Some(
CallContext(
gatewayLoginRequestPayload = Some(PayloadOfJwtJSON(
login_user_name = "",
is_first = false,
app_id = "",
app_name = "",
time_stamp = "",
cbs_token = Some(""),
cbs_id = "",
session_id = Some(""))),
user = Full(resourceUser1)
)
)
val PropsConnectorVersion = APIUtil.getPropsValue("connector").openOrThrowException("connector props filed `connector` not set")
feature("Test all stored_procedure methods") {
if (PropsConnectorVersion == "stored_procedure_vDec2019") {
// scenario("test `checkBankAccountExists` method, there no need Adapter message for this method!", StoredProcedureConnector_vDec2019Test) {
// val checkBankAccountExists = StoredProcedureConnector_vDec2019.checkBankAccountExists(testBankId1,testAccountId1,callContext)
// getValueFromFuture(checkBankAccountExists)._1.isDefined equals (true)
// }
//
// scenario("test `getBankAccounts` method, there no need Adapter message for this method!", StoredProcedureConnector_vDec2019Test) {
// val checkBankAccountExists = StoredProcedureConnector_vDec2019.checkBankAccountExists(testBankId1,testAccountId1,callContext)
// getValueFromFuture(checkBankAccountExists)._1.isDefined equals (true)
// }
// scenario("test `getBank` method, there no need Adapter message for this method!", StoredProcedureConnector_vDec2019Test) {
// val transantionRequests210 = StoredProcedureConnector_vDec2019.getBank(testBankId1, callContext)
// getValueFromFuture(transantionRequests210).isDefined equals (true)
// }
//
//
// scenario("test `getBanks` method, there no need Adapter message for this method!", StoredProcedureConnector_vDec2019Test) {
// val transantionRequests210 = StoredProcedureConnector_vDec2019.getBanks(callContext)
// getValueFromFuture(transantionRequests210).isDefined equals (true)
// }
//
// scenario("test `getTransactionRequests210` method, there no need Adapter message for this method!", StoredProcedureConnector_vDec2019Test) {
// val transantionRequests210: Box[(List[TransactionRequest], Option[CallContext])] = StoredProcedureConnector_vDec2019.getTransactionRequests210(resourceUser1, null, callContext)
// transantionRequests210.isDefined equals(true)
// }
scenario("test `getTransactions` method, there no need Adapter message for this method!", StoredProcedureConnector_vDec2019Test) {
val transactions = StoredProcedureConnector_vDec2019.getTransactions(testBankId1, testAccountId1, callContext, Nil)
val trans = getValueFromFuture(transactions)._1.openOrThrowException("Should not be empty!")
trans.head.description.isDefined equals (true)
}
} else {
ignore("ignore test getObpConnectorLoopback, if it is mapped connector", StoredProcedureConnector_vDec2019Test) {}
}
}
}

View File

@ -1,157 +0,0 @@
package code.bankconnectors.vMay2019
/*
Open Bank Project - API
Copyright (C) 2011-2019, TESOBE GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see http://www.gnu.org/licenses/.
Email: contact@tesobe.com
TESOBE GmbH
Osloerstrasse 16/17
Berlin 13359, Germany
*/
import code.api.JSONFactoryGateway.PayloadOfJwtJSON
import code.api.util.{APIUtil, CallContext, CustomJsonFormats}
import code.bankconnectors.Connector
import code.bankconnectors.vSept2018._
import code.kafka.KafkaHelper
import code.setup.{KafkaSetup, ServerSetupWithTestData}
import com.openbankproject.commons.dto.InBoundGetBanks
import com.openbankproject.commons.model._
import net.liftweb.common.{Box, Failure, Full}
import org.scalatest.Tag
class KafkaMappedConnector_vMay2019Test extends KafkaSetup with ServerSetupWithTestData {
override implicit val formats = CustomJsonFormats.formats
object kafkaTest extends Tag("kafkaTest")
val callContext = Some(
CallContext(
gatewayLoginRequestPayload = Some(PayloadOfJwtJSON(
login_user_name = "",
is_first = false,
app_id = "",
app_name = "",
time_stamp = "",
cbs_token = Some(""),
cbs_id = "",
session_id = Some(""))),
user = Full(resourceUser1)
)
)
val PropsConnectorVersion = APIUtil.getPropsValue("connector").openOrThrowException("connector props filed `connector` not set")
feature("Send and retrieve message") {
if (PropsConnectorVersion =="mapped" || PropsConnectorVersion =="star") {
ignore("ignore test getObpConnectorLoopback, if it is mapped connector", kafkaTest) {}
} else
scenario("1st test `getObpConnectorLoopback` method, there no need Adapter message for this method!", kafkaTest) {
//This method is only used for `kafka` connector, should first set `connector=kafka_vSept2018` in test.default.props.
//and also need to set up `api_instance_id` and `remotedata.timeout` field for it.
val propsApiInstanceId = code.api.Constant.ApiInstanceId
val propsRemotedataTimeout = APIUtil.getPropsValue("remotedata.timeout").openOrThrowException("connector props filed `remotedata.timeout` not set")
PropsConnectorVersion contains ("kafka") should be(true)
propsApiInstanceId should be("1")
propsRemotedataTimeout should be("10")
When("We call this method, and get the response. ")
val future = KafkaHelper.echoKafkaServer
val result = future.getContent
Then("If it return value successfully, that mean api <--> kafka is working well. We only need check one filed of response.")
val connectorVersion = result.connectorVersion
connectorVersion should be(PropsConnectorVersion)
Then("For KafkaMappedConnector_vSept2018 connector, we need to make these two methods work `getAuthInfoFirstCbsCall` and `getAuthInfo`")
val firstAuthInfo: Box[AuthInfo] = for {
firstGetAuthInfo <- KafkaMappedConnector_vSept2018.getAuthInfoFirstCbsCall("","", callContext)
} yield {
(firstGetAuthInfo)
}
firstAuthInfo.openOrThrowException("firstAuthInfo Can not be empty here. ")
val authInfo: Box[AuthInfo] = for {
getAuthInfo <- KafkaMappedConnector_vSept2018.getAuthInfo(callContext)
} yield {
getAuthInfo
}
authInfo.openOrThrowException("firstAuthInfo Can not be empty here. ")
}
if (PropsConnectorVersion =="mapped" || PropsConnectorVersion =="star") {
ignore("ignore test processRequest, if it is mapped connector", kafkaTest) {}
} else
scenario("Send and retrieve message directly to and from kafka", kafkaTest) {
val emptyStatusMessage = InboundStatusMessage("", "", "", "")
val inBound = InboundGetBanks(InboundAuthInfo("", ""), Status("", List(emptyStatusMessage)), List(InboundBank("1", "2", "3", "4")))
When("send a OutboundGetBanks message")
dispathResponse(inBound)
val req = OutboundGetBanks(AuthInfo())
val future = processRequest[InboundGetBanks](req)
val result: Box[InboundGetBanks] = future.getContent
result should be(Full(inBound))
}
}
feature("Test the getBank error cases") {
if (PropsConnectorVersion =="mapped" || PropsConnectorVersion =="star") {
ignore("ignore test getBanks, if it is mapped connector", kafkaTest) {}
} else
scenario(s"test getBanksFuture -- status.hasError", kafkaTest) {
val inbound = Connector.connector.vend.messageDocs.filter(_.exampleInboundMessage.isInstanceOf[InBoundGetBanks]).map(_.exampleInboundMessage).head.asInstanceOf[InBoundGetBanks]
//This inBound.status.errorCode != "", so it will throw the error back.
val expectedValue = Failure("INTERNAL-"+ inbound.status.errorCode+". + CoreBank-Status:" + inbound.status.backendMessages)
dispathResponse(inbound)
val future = Connector.connector.vend.getBanks(callContext)
dispathResponse(inbound)
val result = future.getContent
result should be(expectedValue)
}
if (PropsConnectorVersion =="mapped" || PropsConnectorVersion =="star") {
ignore("ignore test getBanksFuture -- status.hasNoError, if it is mapped connector", kafkaTest) {}
} else
scenario(s"test getBanksFuture -- status.hasNoError", kafkaTest) {
val inbound = Connector.connector.vend.messageDocs
.filter(_.exampleInboundMessage.isInstanceOf[InBoundGetBanks])
.map(_.exampleInboundMessage).head.asInstanceOf[InBoundGetBanks]
.copy(status = Status("", Nil)) // This will set errorCode to "", no it works
//This inBound.status.errorCode != "", so it will throw the error back.
val expectedValue = Full(inbound.data.head.bankId).toString
dispathResponse(inbound)
val future = Connector.connector.vend.getBanks(callContext)
dispathResponse(inbound)
val result = future.getContent
result.map(_._1.head.bankId).toString should be(expectedValue)
}
}
}

View File

@ -1,401 +0,0 @@
package code.kafka
import java.util.{Date, UUID}
import code.api.JSONFactoryGateway.PayloadOfJwtJSON
import code.api.util.{APIUtil, CallContext, CustomJsonFormats}
import code.api.v2_1_0.TransactionRequestBodyCommonJSON
import code.bankconnectors.Connector
import code.bankconnectors.vSept2018._
import code.setup.{KafkaSetup, ServerSetupWithTestData}
import com.openbankproject.commons.dto.{InBoundGetKycChecks, InBoundGetKycMedias, InBoundGetKycStatuses}
import com.openbankproject.commons.model._
import net.liftweb.common.{Box, Full}
import org.scalatest.Tag
import scala.collection.immutable.List
class KafkaTest extends KafkaSetup with ServerSetupWithTestData {
override implicit val formats = CustomJsonFormats.formats
object kafkaTest extends Tag("kafkaTest")
val callContext = Some(
CallContext(
gatewayLoginRequestPayload = Some(PayloadOfJwtJSON(
login_user_name = "",
is_first = false,
app_id = "",
app_name = "",
time_stamp = "",
cbs_token = Some(""),
cbs_id = "",
session_id = Some(""))),
user = Full(resourceUser1)
)
)
val PropsConnectorVersion = APIUtil.getPropsValue("connector").openOrThrowException("connector props filed `connector` not set")
feature("Send and retrieve message") {
if (PropsConnectorVersion =="mapped" || PropsConnectorVersion =="star"){
ignore("ignore test getObpConnectorLoopback, if it is mapped connector", kafkaTest) {}
} else
scenario("1st test `getObpConnectorLoopback` method, there no need Adapter message for this method!", kafkaTest) {
//This method is only used for `kafka` connector, should first set `connector=kafka_vSept2018` in test.default.props.
//and also need to set up `api_instance_id` and `remotedata.timeout` field for it.
val propsApiInstanceId = code.api.Constant.ApiInstanceId
val propsRemotedataTimeout = APIUtil.getPropsValue("remotedata.timeout").openOrThrowException("connector props filed `remotedata.timeout` not set")
PropsConnectorVersion contains ("kafka") should be (true)
propsApiInstanceId should be ("1")
propsRemotedataTimeout should be ("10")
When("We call this method, and get the response. ")
val future = KafkaHelper.echoKafkaServer
val result = future.getContent
Then("If it return value successfully, that mean api <--> kafka is working well. We only need check one filed of response.")
val connectorVersion= result.connectorVersion
connectorVersion should be (PropsConnectorVersion)
Then("For KafkaMappedConnector_vSept2018 connector, we need to make these two methods work `getAuthInfoFirstCbsCall` and `getAuthInfo`")
val firstAuthInfo: Box[AuthInfo] = for{
firstGetAuthInfo <- KafkaMappedConnector_vSept2018.getAuthInfoFirstCbsCall("","", callContext)
} yield {
(firstGetAuthInfo)
}
firstAuthInfo.openOrThrowException("firstAuthInfo Can not be empty here. ")
val authInfo: Box[AuthInfo] = for{
getAuthInfo <- KafkaMappedConnector_vSept2018.getAuthInfo(callContext)
} yield {
getAuthInfo
}
authInfo.openOrThrowException("firstAuthInfo Can not be empty here. ")
}
// if (PropsConnectorVersion =="mapped" || PropsConnectorVersion =="star"){
// ignore("ignore test processRequest, if it is mapped connector", kafkaTest) {}
// } else
// scenario("Send and retrieve message directly to and from kafka", kafkaTest) {
// val emptyStatusMessage = InboundStatusMessage("", "", "", "")
// val inBound = InboundGetBanks(InboundAuthInfo("", ""), Status("", List(emptyStatusMessage)), List(InboundBank("1", "2", "3", "4")))
// When("send a OutboundGetBanks message")
//
// dispathResponse(inBound)
// val req = OutboundGetBanks(AuthInfo())
//
// val future = processRequest[InboundGetBanks](req)
// val result: Box[InboundGetBanks] = future.getContent
//
// result should be (Full(inBound))
// }
if (PropsConnectorVersion =="mapped" || PropsConnectorVersion =="star"){
ignore("ignore test getKycStatuses, if it is mapped connector", kafkaTest) {}
} else
scenario("test `getKycStatuses` method",kafkaTest) {
When("send a OutboundGetKycStatuses api message")
val emptyStatusMessage = InboundStatusMessage("", "", "", "")
val kycStatusCommons = KycStatusCommons(bankId = "hello_bank_id", customerId = "hello_customer_id", customerNumber = "hello_customer_number", ok = true, date = new Date())
val singleInboundBank = List(kycStatusCommons)
val inboundAdapterCallContext = InboundAdapterCallContext(correlationId="some_correlationId")
val inBound = InBoundGetKycStatuses(inboundAdapterCallContext, Status("", List(emptyStatusMessage)), singleInboundBank)
dispathResponse(inBound)
val future = Connector.connector.vend.getKycStatuses(kycStatusCommons.customerId, Some(CallContext()))
val result: (Box[List[KycStatus]], Option[CallContext]) = future.getContent
val expectResult = Full(singleInboundBank)
result._1.toString should be (expectResult.toString)
}
if (PropsConnectorVersion =="mapped" || PropsConnectorVersion =="star"){
ignore("ignore test getKycChecks, if it is mapped connector", kafkaTest) {}
} else
scenario("test `getKycChecks` method", kafkaTest) {
When("send a OutboundGetKycChecks api message")
val inBound = Connector.connector.vend.messageDocs.filter(_.process =="obp.getKycChecks").map(_.exampleInboundMessage).head.asInstanceOf[InBoundGetKycChecks]
dispathResponse(inBound)
val future = Connector.connector.vend.getKycChecks(inBound.data.head.customerId, Some(CallContext()))
val result: (Box[List[KycCheck]], Option[CallContext]) = future.getContent
val expectResult = Full(inBound.data)
result._1.toString should be (expectResult.toString)
}
if (PropsConnectorVersion =="mapped" || PropsConnectorVersion =="star"){
ignore("ignore test getKycMedias, if it is mapped connector", kafkaTest) {}
} else
scenario("test `getKycMedias` method",kafkaTest) {
When("send a OutboundetKycMedias api message")
val inBound = Connector.connector.vend.messageDocs.filter(_.process =="obp.getKycMedias").map(_.exampleInboundMessage).head.asInstanceOf[InBoundGetKycMedias]
dispathResponse(inBound)
val future = Connector.connector.vend.getKycMedias(inBound.data.head.customerId, Some(CallContext()))
val result: (Box[List[KycMedia]], Option[CallContext]) = future.getContent
val expectResult = Full(inBound.data)
result._1.toString should be (expectResult.toString)
}
if (PropsConnectorVersion =="mapped" || PropsConnectorVersion =="star"){
ignore("ignore test getAdapterInfo, if it is mapped connector", kafkaTest) {}
} else
scenario(s"test getAdapterInfo method",kafkaTest) {
When("send a getAdapterInfo api message")
val inBound = Connector.connector.vend.messageDocs.filter(_.process.toString.contains("getAdapterInfo")).map(_.exampleInboundMessage).head.asInstanceOf[InboundAdapterInfo]
dispathResponse(inBound)
val future = Connector.connector.vend.getAdapterInfo(None)
val result: Box[(InboundAdapterInfoInternal, Option[CallContext])] = future.getContent
result.map(_._1) should be (Full(inBound.data))
}
if (PropsConnectorVersion =="mapped" || PropsConnectorVersion =="star"){
ignore("ignore test getUser, if it is mapped connector", kafkaTest) {}
} else
scenario(s"test getUser method",kafkaTest) {
When("send a getUser api message")
val inBound = Connector.connector.vend.messageDocs.filter(_.process.toString.contains("getUser")).map(_.exampleInboundMessage).head.asInstanceOf[InboundGetUserByUsernamePassword]
dispathResponse(inBound)
val box = Connector.connector.vend.getUser("username","password")
box.map(_.displayName) should be (Full(inBound.data.displayName))
}
if (PropsConnectorVersion =="mapped" || PropsConnectorVersion =="star"){
ignore("ignore test getBanksFuture, if it is mapped connector", kafkaTest) {}
} else
scenario(s"test getBanksFuture method", kafkaTest) {
val inBound = Connector.connector.vend.messageDocs.filter(_.process.toString.contains("getBanks")).map(_.exampleInboundMessage).head.asInstanceOf[InboundGetBanks]
dispathResponse(inBound)
val future = Connector.connector.vend.getBanks(None)
val result = future.getContent
result.map(_._1.head.bankId).toString should be (Full(inBound.data.head.bankId).toString)
}
if (PropsConnectorVersion =="mapped" || PropsConnectorVersion =="star"){
ignore("ignore test getBanks, if it is mapped connector", kafkaTest) {}
} else
scenario(s"test getBanks method", kafkaTest) {
val inBound = Connector.connector.vend.messageDocs.filter(_.process.toString.contains("getBanks")).map(_.exampleInboundMessage).head.asInstanceOf[InboundGetBanks]
dispathResponse(inBound)
val box = Connector.connector.vend.getBanksLegacy(None)
box.map(_._1.head.bankId).toString should be (Full(inBound.data.head.bankId).toString)
}
if (PropsConnectorVersion =="mapped" || PropsConnectorVersion =="star"){
ignore("ignore test getBank, if it is mapped connector", kafkaTest) {}
} else
scenario(s"test getBank method", kafkaTest) {
val inBound = Connector.connector.vend.messageDocs.filter(_.exampleInboundMessage.isInstanceOf[InboundGetBank]).map(_.exampleInboundMessage).head.asInstanceOf[InboundGetBank]
dispathResponse(inBound)
val box = Connector.connector.vend.getBankLegacy(BankId(""), None)
box.map(_._1.bankId).toString should be (Full(inBound.data.bankId).toString)
}
if (PropsConnectorVersion =="mapped" || PropsConnectorVersion =="star"){
ignore("ignore test getBankFuture, if it is mapped connector", kafkaTest) {}
} else
scenario(s"test getBankFuture method",kafkaTest) {
val inBound = Connector.connector.vend.messageDocs.filter(_.exampleInboundMessage.isInstanceOf[InboundGetBank]).map(_.exampleInboundMessage).head.asInstanceOf[InboundGetBank]
dispathResponse(inBound)
val future = Connector.connector.vend.getBank(BankId(""), None)
val result = future.getContent
result.map(_._1.bankId).toString should be (Full(inBound.data.bankId).toString)
}
if (PropsConnectorVersion =="mapped" || PropsConnectorVersion =="star"){
ignore("ignore test getBankAccountsForUserFuture, if it is mapped connector", kafkaTest) {}
} else
scenario(s"test getBankAccountsForUserFuture method",kafkaTest) {
val inBound = Connector.connector.vend.messageDocs.filter(_.exampleInboundMessage.isInstanceOf[InboundGetAccounts]).map(_.exampleInboundMessage).head.asInstanceOf[InboundGetAccounts]
dispathResponse(inBound)
val future = Connector.connector.vend.getBankAccountsForUser("", "", callContext)
val result = future.getContent
result.map(_._1.head).toString should be (Full(inBound.data.head).toString)
}
if (PropsConnectorVersion =="mapped" || PropsConnectorVersion =="star"){
ignore("ignore test getBankAccountsForUser, if it is mapped connector", kafkaTest) {}
} else
scenario(s"test getBankAccountsForUser method",kafkaTest) {
val inBound = Connector.connector.vend.messageDocs.filter(_.exampleInboundMessage.isInstanceOf[InboundGetAccounts]).map(_.exampleInboundMessage).head.asInstanceOf[InboundGetAccounts]
dispathResponse(inBound)
val box = Connector.connector.vend.getBankAccountsForUserLegacy("","", callContext)
box.map(_._1.head).toString should be (Full(inBound.data.head).toString)
}
if (PropsConnectorVersion =="mapped" || PropsConnectorVersion =="star"){
ignore("ignore test getBankAccount, if it is mapped connector", kafkaTest) {}
} else
scenario(s"test getBankAccount method",kafkaTest) {
val inBound = Connector.connector.vend.messageDocs.filter(_.exampleInboundMessage.isInstanceOf[InboundGetAccountbyAccountID]).map(_.exampleInboundMessage).head.asInstanceOf[InboundGetAccountbyAccountID]
dispathResponse(inBound)
val box = Connector.connector.vend.getBankAccountLegacy(BankId(""), AccountId(""), callContext)
box.map(_._1.bankId).toString should be (Full(inBound.data.head.bankId).toString)
box.map(_._1.accountId).toString should be (Full(inBound.data.head.accountId).toString)
}
if (PropsConnectorVersion =="mapped" || PropsConnectorVersion =="star"){
ignore("ignore test getBankAccountFuture, if it is mapped connector", kafkaTest) {}
} else
scenario(s"test getBankAccountFuture method",kafkaTest) {
val inBound = Connector.connector.vend.messageDocs.filter(_.exampleInboundMessage.isInstanceOf[InboundGetAccountbyAccountID]).map(_.exampleInboundMessage).head.asInstanceOf[InboundGetAccountbyAccountID]
dispathResponse(inBound)
val future = Connector.connector.vend.checkBankAccountExists(BankId(""), AccountId(""), callContext)
val result = future.getContent
result._1.map(_.accountId.value).toString should be (Full(inBound.data.head.accountId).toString)
result._1.map(_.bankId.value).toString should be (Full(inBound.data.head.bankId).toString)
}
if (PropsConnectorVersion =="mapped" || PropsConnectorVersion =="star"){
ignore("ignore test getChallengeThreshold, if it is mapped connector", kafkaTest) {}
} else
scenario(s"test getChallengeThreshold method",kafkaTest) {
val inBound = Connector.connector.vend.messageDocs.filter(_.exampleInboundMessage.isInstanceOf[InboundGetChallengeThreshold]).map(_.exampleInboundMessage).head.asInstanceOf[InboundGetChallengeThreshold]
dispathResponse(inBound)
val future = Connector.connector.vend.getChallengeThreshold("","","","","","","", callContext)
val result = future.getContent
result._1.map(_.amount).toString should be (Full(inBound.data.amount).toString)
result._1.map(_.currency).toString should be (Full(inBound.data.currency).toString)
}
if (PropsConnectorVersion =="mapped" || PropsConnectorVersion =="star"){
ignore("ignore test makePaymentv210, if it is mapped connector", kafkaTest) {}
} else
scenario(s"test makePaymentv210 method",kafkaTest) {
val inBound = Connector.connector.vend.messageDocs.filter(_.exampleInboundMessage.isInstanceOf[InboundCreateTransactionId]).map(_.exampleInboundMessage).head.asInstanceOf[InboundCreateTransactionId]
dispathResponse(inBound)
val fromAccount = BankAccountSept2018(KafkaMappedConnector_vSept2018.inboundAccountSept2018Example)
val toAccount = BankAccountSept2018(KafkaMappedConnector_vSept2018.inboundAccountSept2018Example)
val transactionRequestId = TransactionRequestId(UUID.randomUUID().toString)
val transactionRequestCommonBody = TransactionRequestBodyCommonJSON(AmountOfMoneyJsonV121("",""),"")
val future = Connector.connector.vend.makePaymentv210(
fromAccount,
toAccount,
transactionRequestId,
transactionRequestCommonBody,
10,
"",
TransactionRequestType("SANDBOX_TAN"),
"",
callContext)
val result = future.getContent
result._1.map(_.value).toString should be (Full(inBound.data.id).toString)
}
if (PropsConnectorVersion =="mapped" || PropsConnectorVersion =="star"){
ignore("ignore test createChallenge, if it is mapped connector", kafkaTest) {}
} else
scenario(s"test createChallenge method",kafkaTest) {
val inBound = Connector.connector.vend.messageDocs.filter(_.exampleInboundMessage.isInstanceOf[InboundCreateChallengeSept2018]).map(_.exampleInboundMessage).head.asInstanceOf[InboundCreateChallengeSept2018]
dispathResponse(inBound)
val account = BankAccountSept2018(KafkaMappedConnector_vSept2018.inboundAccountSept2018Example)
val transactionRequestCommonBody = TransactionRequestBodyCommonJSON(AmountOfMoneyJsonV121("",""),"")
val future = Connector.connector.vend.createChallenge(
account.bankId,
account.accountId,
"",
TransactionRequestType("SANDBOX_TAN"),
"",
None,
callContext)
val result = future.getContent
result._1.toString should be (Full(inBound.data.answer).toString)
}
if (PropsConnectorVersion =="mapped" || PropsConnectorVersion =="star"){
ignore("ignore test createCounterparty, if it is mapped connector", kafkaTest) {}
} else
scenario(s"test createCounterparty method",kafkaTest) {
val inBound = Connector.connector.vend.messageDocs.filter(_.exampleInboundMessage.isInstanceOf[InboundCreateCounterparty]).map(_.exampleInboundMessage).head.asInstanceOf[InboundCreateCounterparty]
val outBound = Connector.connector.vend.messageDocs.filter(_.exampleOutboundMessage.isInstanceOf[OutboundCreateCounterparty]).map(_.exampleOutboundMessage).head.asInstanceOf[OutboundCreateCounterparty]
dispathResponse(inBound)
val account = BankAccountSept2018(KafkaMappedConnector_vSept2018.inboundAccountSept2018Example)
val transactionRequestCommonBody = TransactionRequestBodyCommonJSON(AmountOfMoneyJsonV121("",""),"")
val box = Connector.connector.vend.createCounterparty(
outBound.counterparty.name,
outBound.counterparty.description,
outBound.counterparty.currency,
outBound.counterparty.createdByUserId,
outBound.counterparty.thisBankId,
outBound.counterparty.thisAccountId,
outBound.counterparty.thisViewId,
outBound.counterparty.otherAccountRoutingScheme,
outBound.counterparty.otherAccountRoutingAddress,
outBound.counterparty.otherAccountSecondaryRoutingScheme,
outBound.counterparty.otherAccountSecondaryRoutingAddress,
outBound.counterparty.otherBankRoutingScheme,
outBound.counterparty.otherBankRoutingAddress,
outBound.counterparty.otherBranchRoutingScheme,
outBound.counterparty.otherBranchRoutingAddress,
outBound.counterparty.isBeneficiary,
outBound.counterparty.bespoke,
callContext)
box.map(_._1.counterpartyId) should be (Full(inBound.data.get.counterpartyId))
box.map(_._1.createdByUserId) should be (Full(inBound.data.get.createdByUserId))
}
if (PropsConnectorVersion =="mapped" || PropsConnectorVersion =="star"){
ignore("ignore test getTransactionRequests210, if it is mapped connector", kafkaTest) {}
} else
scenario(s"test getTransactionRequests210 method",kafkaTest) {
val inBound = Connector.connector.vend.messageDocs.filter(_.exampleInboundMessage.isInstanceOf[InboundGetTransactionRequests210]).map(_.exampleInboundMessage).head.asInstanceOf[InboundGetTransactionRequests210]
dispathResponse(inBound)
val account = BankAccountSept2018(KafkaMappedConnector_vSept2018.inboundAccountSept2018Example)
val transactionRequestCommonBody = TransactionRequestBodyCommonJSON(AmountOfMoneyJsonV121("",""),"")
val box = Connector.connector.vend.getTransactionRequests210(
resourceUser1,
account,
callContext)
box.map(_._1.head.body) should be (inBound.data.head.body)
}
}
}

View File

@ -1,76 +0,0 @@
package code.setup
import code.actorsystem.ObpActorSystem
import code.api.util.CustomJsonFormats
import code.kafka._
import code.util.Helper.MdcLoggable
import net.liftweb.json
import net.liftweb.json.Extraction
import net.manub.embeddedkafka.{EmbeddedKafka, EmbeddedKafkaConfig}
import org.apache.kafka.common.serialization.{StringDeserializer, StringSerializer}
import org.scalatest.{FeatureSpec, _}
import com.openbankproject.commons.ExecutionContext.Implicits.global
import scala.concurrent.{Await, Future}
import scala.concurrent.duration.{Duration, _}
trait KafkaSetup extends FeatureSpec with EmbeddedKafka with KafkaHelper
with GivenWhenThen with BeforeAndAfterAll
with Matchers with MdcLoggable {
implicit val formats = CustomJsonFormats.formats
implicit val config = EmbeddedKafkaConfig(kafkaPort = 9092, zooKeeperPort = 2181) //TODO the port should read from test.default.props, but fail
implicit val stringSerializer = new StringSerializer
implicit val stringDeserializer = new StringDeserializer
val requestMapResponseTopics:Map[String, String] = NorthSideConsumer.listOfTopics
.map(Topics.createTopicByClassName)
.map(pair => (pair.request, pair.response))
.toMap
val requestTopics = requestMapResponseTopics.keySet
override def beforeAll(): Unit = {
super.beforeAll()
EmbeddedKafka.start()
if(!OBPKafkaConsumer.primaryConsumer.started){
val actorSystem = ObpActorSystem.startLocalActorSystem
KafkaHelperActors.startLocalKafkaHelperWorkers(actorSystem)
// Start North Side Consumer if it's not already started
OBPKafkaConsumer.primaryConsumer.start()
}
}
override def afterAll(): Unit = {
super.afterAll()
OBPKafkaConsumer.primaryConsumer.complete()
EmbeddedKafka.stop()
}
/**
* send an object to kafka as response
*
* @param inBound inBound object that will send to kafka as a response
* @tparam T Outbound type
*/
def dispathResponse(inBound: AnyRef): Unit = {
val inBoundStr = inBound match {
case str: String => str
case _ =>json.compactRender(Extraction.decompose(inBound))
}
Future{
val requestKeyValue = consumeNumberKeyedMessagesFromTopics(requestTopics, 1, true)
val (requestTopic, keyValueList) = requestKeyValue.find(_._2.nonEmpty).get
val (key, _) = keyValueList.head
val responseTopic = requestMapResponseTopics(requestTopic)
publishToKafka(responseTopic, key, inBoundStr)
}
}
implicit class FutureExtract[T](future: Future[T]) {
def getContent: T = Await.result(future, 10 seconds)
}
}

View File

@ -698,12 +698,18 @@ class APIUtilTest extends FeatureSpec with Matchers with GivenWhenThen with Prop
APIUtil.getObpFormatOperationId("xxx") should be ("xxx")
}
feature("test APIUtil.basicUrlValidation method") {
feature("test APIUtil.basicUriAndQueryStringValidation method") {
val testString1 = "https%3A%2F%2Fapisandbox.openbankproject.com%2Foauth%2Fauthorize%3Fnext%3D%2Fen%2Fusers%2Fmyuser%26oauth_token%3DWTOBT2YRCTMI1BCCF4XAIKRXPLLZDZPFAIL5K03Z%26oauth_verifier%3D45381"
val testString2 = "http%3A%2F%2Flocalhost%3A8016%3Foauth_token%3DEBRZBMOPDXEUGGJP421FPFGK01IY2DGM5O3TLVSK%26oauth_verifier%3D63461"
val testString3 = "myapp://callback?oauth_token=%3DEBRZBMOPDXEUGGJP421FPFGK01IY2DGM5O3TLVSK%26oauth_verifier%3D63461"
val testString4 = "fb00000000:://callback?oauth_token=%3DEBRZBMOPDXEUGGJP421FPFGK01IY2DGM5O3TLVSK%26oauth_verifier%3D63461"
val testString5 = "http://127.0.0.1:8000/oauth/authorize?next=/en/metrics/api/&oauth_token=TN0124OCPRCL4KUJRF5LNLVMRNHTVZPJDBS2PNWU&oauth_verifier=10470"
APIUtil.basicUrlValidation(testString1) should be (true)
APIUtil.basicUrlValidation(testString2) should be (true)
APIUtil.basicUriAndQueryStringValidation(testString1) should be (true)
APIUtil.basicUriAndQueryStringValidation(testString2) should be (true)
APIUtil.basicUriAndQueryStringValidation(testString3) should be (true)
APIUtil.basicUriAndQueryStringValidation(testString4) should be (true)
APIUtil.basicUriAndQueryStringValidation(testString5) should be (true)
}

View File

@ -0,0 +1,66 @@
/**
* Open Bank Project - API
* Copyright (C) 2011-2019, TESOBE GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Email: contact@tesobe.com
* TESOBE GmbH.
* Osloer Strasse 16/17
* Berlin 13359, Germany
*
* This product includes software developed at
* TESOBE (http://www.tesobe.com/)
*
*/
package code.util
import code.api.util._
import code.setup.PropsReset
import org.scalatest.{FeatureSpec, GivenWhenThen, Matchers}
class HelperTest extends FeatureSpec with Matchers with GivenWhenThen with PropsReset {
feature("test APIUtil.getStaticPortionOfRedirectURL method") {
// The redirectURl is `http://localhost:8082/oauthcallback`
val testString1 = "http://localhost:8082/oauthcallback?oauth_token=G5AEA2U1WG404EGHTIGBHKRR4YJZAPPHWKOMNEEV&oauth_verifier=53018"
val testString2 = "http://localhost:8082?oauth_token=G5AEA2U1WG404EGHTIGBHKRR4YJZAPPHWKOMNEEV&oauth_verifier=53018"
val testString3 = "myapp://callback?oauth_token=%3DEBRZBMOPDXEUGGJP421FPFGK01IY2DGM5O3TLVSK%26oauth_verifier%3D63461"
val testString4 = "fb00000000:://callback?oauth_token=%3DEBRZBMOPDXEUGGJP421FPFGK01IY2DGM5O3TLVSK%26oauth_verifier%3D63461"
val testString5 = "http://127.0.0.1:8000/oauth/authorize?next=/en/metrics/api/&oauth_token=TN0124OCPRCL4KUJRF5LNLVMRNHTVZPJDBS2PNWU&oauth_verifier=10470"
Helper.getStaticPortionOfRedirectURL(testString1).head should be("http://localhost:8082/oauthcallback")
Helper.getStaticPortionOfRedirectURL(testString2).head should be("http://localhost:8082")
Helper.getStaticPortionOfRedirectURL(testString3).head should be("myapp://callback")
Helper.getStaticPortionOfRedirectURL(testString4).head should be("fb00000000:://callback")
Helper.getStaticPortionOfRedirectURL(testString5).head should be("http://127.0.0.1:8000/oauth/authorize")
}
feature("test APIUtil.getHostOnlyOfRedirectURL method") {
// The redirectURl is `http://localhost:8082/oauthcallback`
val testString1 = "http://localhost:8082/oauthcallback?oauth_token=G5AEA2U1WG404EGHTIGBHKRR4YJZAPPHWKOMNEEV&oauth_verifier=53018"
val testString2 = "http://localhost:8082/oauthcallback"
val testString3 = "http://localhost:8082?oauth_token=G5AEA2U1WG404EGHTIGBHKRR4YJZAPPHWKOMNEEV&oauth_verifier=53018"
val testString4 = "http://localhost:8082"
Helper.getHostOnlyOfRedirectURL(testString1).head should be("http://localhost:8082")
Helper.getHostOnlyOfRedirectURL(testString2).head should be("http://localhost:8082")
Helper.getHostOnlyOfRedirectURL(testString3).head should be("http://localhost:8082")
Helper.getHostOnlyOfRedirectURL(testString4).head should be("http://localhost:8082")
}
}

View File

@ -115,6 +115,7 @@ class MappedClassNameTest extends FeatureSpec {
"code.model.dataAccess.MappedBank",
"code.UserRefreshes.MappedUserRefreshes",
"code.DynamicEndpoint.DynamicEndpoint",
"code.regulatedentities.MappedRegulatedEntity",
"code.CustomerDependants.MappedCustomerDependant")
val newMappedTypes = ClassScanUtils.findTypes{ info =>

View File

@ -96,6 +96,22 @@ trait AccountApplication {
def status: String
}
trait RegulatedEntityTrait {
def entityId: String
def certificateAuthorityCaOwnerId: String
def entityName: String
def entityCode: String
def entityCertificatePublicKey: String
def entityType: String
def entityAddress: String
def entityTownCity: String
def entityPostCode: String
def entityCountry: String
def entityWebSite: String
def services: String
}
trait UserAttributeTrait {
def userAttributeId: String
def userId: String

View File

@ -18,7 +18,6 @@
<avro.version>1.8.2</avro.version>
<lift.version>3.5.0</lift.version>
<jetty.version>9.4.50.v20221201</jetty.version>
<log4j.version>2.17.1</log4j.version>
<obp-ri.version>2016.11-RC6-SNAPSHOT</obp-ri.version>
<!-- Common plugin settings -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>