mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 15:47:01 +00:00
Merge pull request #2453 from constantine2nd/develop
Accounts held by user, keycloak to scope roles, Redis SSL
This commit is contained in:
commit
0691767549
@ -166,6 +166,18 @@ jwt.use.ssl=false
|
||||
#truststore.path=/path/to/api.truststore.jks
|
||||
|
||||
|
||||
## Enable mTLS for Redis, if set to true must set paths for the keystore and truststore locations
|
||||
# redis.use.ssl=false
|
||||
## Client
|
||||
## PKCS#12 Format: combine private keys and certificates into .p12 files for easier transport
|
||||
# keystore.path.redis = path/to/client-keystore.p12
|
||||
# keystore.password.redis = keystore-password
|
||||
## Trust stores is a list of trusted CA certificates
|
||||
## Public certificate for the CA (used by clients and servers to validate signatures)
|
||||
# truststore.path.redis = path/to/ca.p12
|
||||
# truststore.password.redis = truststore-password
|
||||
|
||||
|
||||
## Enable writing API metrics (which APIs are called) to RDBMS
|
||||
write_metrics=true
|
||||
## Enable writing connector metrics (which methods are called)to RDBMS
|
||||
@ -755,6 +767,8 @@ display_internal_errors=false
|
||||
# Keycloak Identity Provider Host
|
||||
# oauth2.keycloak.host=http://localhost:7070
|
||||
# oauth2.keycloak.well-known=http://localhost:7070/realms/master/.well-known/openid-configuration
|
||||
# Used to sync IAM of OBP-API and IAM of Keycloak
|
||||
# oauth2.keycloak.source-of-truth = false
|
||||
# ------------------------------------------------------------------------------ OAuth 2 ------
|
||||
|
||||
## This property is used for documenting at Resource Doc. It may include the port also (but not /obp)
|
||||
|
||||
@ -26,28 +26,29 @@ TESOBE (http://www.tesobe.com/)
|
||||
*/
|
||||
package code.api
|
||||
|
||||
import java.net.URI
|
||||
import java.util
|
||||
import code.api.util.ErrorMessages._
|
||||
import code.api.util.{APIUtil, CallContext, CertificateUtil, JwtUtil}
|
||||
import code.api.util._
|
||||
import code.consumer.Consumers
|
||||
import code.consumer.Consumers.consumers
|
||||
import code.loginattempts.LoginAttempt
|
||||
import code.model.{AppType, Consumer}
|
||||
import code.util.HydraUtil._
|
||||
import code.scope.Scope
|
||||
import code.users.Users
|
||||
import code.util.Helper.MdcLoggable
|
||||
import code.util.HydraUtil
|
||||
import code.util.HydraUtil._
|
||||
import com.nimbusds.jwt.JWTClaimsSet
|
||||
import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet
|
||||
import com.openbankproject.commons.ExecutionContext.Implicits.global
|
||||
import com.openbankproject.commons.model.User
|
||||
import net.liftweb.common.Box.tryo
|
||||
import net.liftweb.common._
|
||||
import net.liftweb.http.rest.RestHelper
|
||||
import net.liftweb.util.Helpers
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import sh.ory.hydra.model.OAuth2TokenIntrospection
|
||||
|
||||
import java.net.URI
|
||||
import scala.concurrent.Future
|
||||
import scala.jdk.CollectionConverters.mapAsJavaMapConverter
|
||||
|
||||
@ -226,7 +227,7 @@ object OAuth2Login extends RestHelper with MdcLoggable {
|
||||
}
|
||||
}
|
||||
|
||||
private def getClaim(name: String, idToken: String): Option[String] = {
|
||||
def getClaim(name: String, idToken: String): Option[String] = {
|
||||
val claim = JwtUtil.getClaim(name = name, jwtToken = idToken)
|
||||
claim match {
|
||||
case null => None
|
||||
@ -373,6 +374,7 @@ object OAuth2Login extends RestHelper with MdcLoggable {
|
||||
redirectURL = None,
|
||||
createdByUserId = userId.toOption
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
def applyIdTokenRules(token: String, cc: CallContext): (Box[User], Some[CallContext]) = {
|
||||
@ -471,10 +473,44 @@ object OAuth2Login extends RestHelper with MdcLoggable {
|
||||
def applyRules(token: String, cc: CallContext): (Box[User], Some[CallContext]) = {
|
||||
JwtUtil.getClaim("typ", token) match {
|
||||
case "ID" => super.applyIdTokenRules(token, cc)
|
||||
case "Bearer" => super.applyAccessTokenRules(token, cc)
|
||||
case "Bearer" =>
|
||||
val result = super.applyAccessTokenRules(token, cc)
|
||||
addScopesToConsumer(token)
|
||||
result
|
||||
case "" => super.applyAccessTokenRules(token, cc)
|
||||
}
|
||||
}
|
||||
|
||||
private def addScopesToConsumer(token: String): Unit = {
|
||||
val sourceOfTruth = APIUtil.getPropsAsBoolValue(nameOfProperty = "oauth2.keycloak.source-of-truth", defaultValue = false)
|
||||
val consumerId = getClaim(name = "azp", idToken = token).getOrElse("")
|
||||
if(sourceOfTruth) {
|
||||
logger.debug("Extracting roles from Access Token")
|
||||
import net.liftweb.json._
|
||||
val jsonString = JwtUtil.getSignedPayloadAsJson(token)
|
||||
val json = parse(jsonString.getOrElse(""))
|
||||
val openBankRoles: List[String] = {
|
||||
(json \ "resource_access" \ consumerId \ "roles").extract[List[String]]
|
||||
.filter(role => tryo(ApiRole.valueOf(role)).isDefined) // Keep only the roles OBP-API can recognise
|
||||
}
|
||||
val scopes = Scope.scope.vend.getScopesByConsumerId(consumerId).getOrElse(Nil)
|
||||
val databaseState = scopes.map(_.roleName)
|
||||
// Already exist at DB
|
||||
val existingRoles = openBankRoles.intersect(databaseState)
|
||||
// Roles to add into DB
|
||||
val rolesToAdd = openBankRoles.toSet diff databaseState.toSet
|
||||
rolesToAdd.foreach(roleName => Scope.scope.vend.addScope("", consumerId, roleName))
|
||||
// Roles to delete from DB
|
||||
val rolesToDelete = databaseState.toSet diff openBankRoles.toSet
|
||||
rolesToDelete.foreach( roleName =>
|
||||
Scope.scope.vend.deleteScope(scopes.find(s => s.roleName == roleName || s.consumerId == consumerId))
|
||||
)
|
||||
logger.debug(s"Consumer ID: $consumerId # Existing roles: ${existingRoles.mkString} # Added roles: ${rolesToAdd.mkString} # Deleted roles: ${rolesToDelete.mkString}")
|
||||
} else {
|
||||
logger.debug(s"Adding scopes omitted due to oauth2.keycloak.source-of-truth = $sourceOfTruth # Consumer ID: $consumerId")
|
||||
}
|
||||
}
|
||||
|
||||
def applyRulesFuture(value: String, cc: CallContext): Future[(Box[User], Some[CallContext])] = Future {
|
||||
applyRules(value, cc)
|
||||
}
|
||||
|
||||
@ -10,6 +10,13 @@ import scalacache.{Flags, ScalaCache}
|
||||
import scalacache.redis.RedisCache
|
||||
import scalacache.serialization.Codec
|
||||
|
||||
import redis.clients.jedis.{Jedis, JedisPool, JedisPoolConfig}
|
||||
import java.net.URI
|
||||
import javax.net.ssl.{KeyManagerFactory, SSLContext, TrustManagerFactory}
|
||||
import java.io.FileInputStream
|
||||
import java.security.KeyStore
|
||||
import com.typesafe.config.{Config, ConfigFactory}
|
||||
|
||||
import scala.concurrent.Future
|
||||
import scala.concurrent.duration.Duration
|
||||
import scala.language.postfixOps
|
||||
@ -18,6 +25,9 @@ object Redis extends MdcLoggable {
|
||||
|
||||
val url = APIUtil.getPropsValue("cache.redis.url", "127.0.0.1")
|
||||
val port = APIUtil.getPropsAsIntValue("cache.redis.port", 6379)
|
||||
val timeout = 4000
|
||||
val password: String = null // Replace with password if authentication is needed
|
||||
val useSsl = APIUtil.getPropsAsBoolValue("redis.use.ssl", false)
|
||||
|
||||
final val poolConfig = new JedisPoolConfig()
|
||||
poolConfig.setMaxTotal(128)
|
||||
@ -31,8 +41,50 @@ object Redis extends MdcLoggable {
|
||||
poolConfig.setNumTestsPerEvictionRun(3)
|
||||
poolConfig.setBlockWhenExhausted(true)
|
||||
|
||||
val jedisPool =
|
||||
if (useSsl) {
|
||||
// SSL connection: Use SSLContext with JedisPool
|
||||
val sslContext = configureSslContext()
|
||||
new JedisPool(poolConfig, url, port, timeout, password, true, sslContext.getSocketFactory, null, null)
|
||||
} else {
|
||||
// Non-SSL connection
|
||||
new JedisPool(poolConfig, url, port, timeout, password)
|
||||
}
|
||||
|
||||
def jedisPoolDestroy: Unit = jedisPool.destroy()
|
||||
val jedisPool = new JedisPool(poolConfig,url, port, 4000)
|
||||
|
||||
private def configureSslContext(): SSLContext = {
|
||||
|
||||
// Load the CA certificate
|
||||
val trustStore = KeyStore.getInstance("PKCS12")
|
||||
val trustStorePassword = APIUtil.getPropsValue("keystore.password.redis")
|
||||
.getOrElse(APIUtil.initPasswd).toCharArray
|
||||
val truststorePath = APIUtil.getPropsValue("truststore.path.redis").getOrElse("")
|
||||
val trustStoreStream = new FileInputStream(truststorePath)
|
||||
trustStore.load(trustStoreStream, trustStorePassword)
|
||||
trustStoreStream.close()
|
||||
|
||||
// Load the client certificate and private key
|
||||
val keyStore = KeyStore.getInstance("PKCS12")
|
||||
val keyStorePassword = APIUtil.getPropsValue("keystore.password.redis")
|
||||
.getOrElse(APIUtil.initPasswd).toCharArray
|
||||
val keystorePath = APIUtil.getPropsValue("keystore.path.redis").getOrElse("")
|
||||
val keyStoreStream = new FileInputStream(keystorePath)
|
||||
keyStore.load(keyStoreStream, keyStorePassword)
|
||||
keyStoreStream.close()
|
||||
|
||||
// Initialize KeyManager and TrustManager
|
||||
val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm)
|
||||
keyManagerFactory.init(keyStore, keyStorePassword)
|
||||
|
||||
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm)
|
||||
trustManagerFactory.init(trustStore)
|
||||
|
||||
// Configure and return the SSLContext
|
||||
val sslContext = SSLContext.getInstance("TLS")
|
||||
sslContext.init(keyManagerFactory.getKeyManagers, trustManagerFactory.getTrustManagers, null)
|
||||
sslContext
|
||||
}
|
||||
|
||||
/**
|
||||
* this is the help method, which can be used to auto close all the jedisConnection
|
||||
|
||||
@ -119,7 +119,10 @@ object Constant extends MdcLoggable {
|
||||
}
|
||||
|
||||
|
||||
|
||||
object CertificateConstants {
|
||||
final val BEGIN_CERT: String = "-----BEGIN CERTIFICATE-----"
|
||||
final val END_CERT: String = "-----END CERTIFICATE-----"
|
||||
}
|
||||
|
||||
object JedisMethod extends Enumeration {
|
||||
type JedisMethod = Value
|
||||
|
||||
@ -66,6 +66,11 @@ object RoleCombination {
|
||||
|
||||
object ApiRole extends MdcLoggable{
|
||||
|
||||
case class CanGetAccountsHeldAtOneBank(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canGetAccountsHeldAtOneBank: CanGetAccountsHeldAtOneBank = CanGetAccountsHeldAtOneBank()
|
||||
case class CanGetAccountsHeldAtAnyBank(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canGetAccountsHeldAtAnyBank: CanGetAccountsHeldAtAnyBank = CanGetAccountsHeldAtAnyBank()
|
||||
|
||||
case class CanCreateRegulatedEntity(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canCreateRegulatedEntity = CanCreateRegulatedEntity()
|
||||
case class CanDeleteRegulatedEntity(requiresBankId: Boolean = false) extends ApiRole
|
||||
|
||||
@ -1,10 +1,6 @@
|
||||
package code.api.util
|
||||
|
||||
import java.io.{FileInputStream, IOException}
|
||||
import java.security.cert.{Certificate, CertificateException, X509Certificate}
|
||||
import java.security.interfaces.{RSAPrivateKey, RSAPublicKey}
|
||||
import java.security.{PublicKey, _}
|
||||
|
||||
import code.api.CertificateConstants
|
||||
import code.api.util.CryptoSystem.CryptoSystem
|
||||
import code.api.util.SelfSignedCertificateUtil.generateSelfSignedCert
|
||||
import code.util.Helper.MdcLoggable
|
||||
@ -13,7 +9,11 @@ import com.nimbusds.jose.crypto.{MACSigner, RSAEncrypter, RSASSASigner}
|
||||
import com.nimbusds.jose.util.X509CertUtils
|
||||
import com.nimbusds.jwt.{EncryptedJWT, JWTClaimsSet}
|
||||
import net.liftweb.util.Props
|
||||
import org.bouncycastle.operator.OperatorCreationException
|
||||
|
||||
import java.io.{FileInputStream, IOException}
|
||||
import java.security.cert.{Certificate, CertificateException, X509Certificate}
|
||||
import java.security.interfaces.{RSAPrivateKey, RSAPublicKey}
|
||||
import java.security._
|
||||
|
||||
|
||||
object CryptoSystem extends Enumeration {
|
||||
@ -227,8 +227,8 @@ object CertificateUtil extends MdcLoggable {
|
||||
|
||||
// Remove all whitespace characters including spaces, tabs, newlines, and carriage returns
|
||||
def normalizePemX509Certificate(pem: String): String = {
|
||||
val pemHeader = "-----BEGIN CERTIFICATE-----"
|
||||
val pemFooter = "-----END CERTIFICATE-----"
|
||||
val pemHeader = CertificateConstants.BEGIN_CERT
|
||||
val pemFooter = CertificateConstants.END_CERT
|
||||
|
||||
def extractContent(pem: String): Option[String] = {
|
||||
val start = pem.indexOf(pemHeader)
|
||||
|
||||
@ -4,8 +4,7 @@ import java.security.interfaces.RSAPublicKey
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.{Duration, ZoneOffset, ZonedDateTime}
|
||||
import java.util
|
||||
|
||||
import code.api.Constant
|
||||
import code.api.{CertificateConstants, Constant}
|
||||
import code.util.Helper.MdcLoggable
|
||||
import com.nimbusds.jose.crypto.RSASSAVerifier
|
||||
import com.nimbusds.jose.jwk.JWK
|
||||
@ -16,7 +15,6 @@ import net.liftweb.common.{Box, Failure, Full}
|
||||
import net.liftweb.http.provider.HTTPParam
|
||||
import net.liftweb.json
|
||||
import net.liftweb.util.SecurityHelpers
|
||||
import sun.security.provider.X509Factory
|
||||
|
||||
import scala.collection.immutable.{HashMap, List}
|
||||
import scala.jdk.CollectionConverters.seqAsJavaListConverter
|
||||
@ -164,9 +162,9 @@ object JwsUtil extends MdcLoggable {
|
||||
header.x5c.map(_.headOption.getOrElse("None")).getOrElse("None")
|
||||
case None => "None"
|
||||
}
|
||||
s"""${X509Factory.BEGIN_CERT}
|
||||
s"""${CertificateConstants.BEGIN_CERT}
|
||||
|$x5c
|
||||
|${X509Factory.END_CERT}
|
||||
|${CertificateConstants.END_CERT}
|
||||
|""".stripMargin
|
||||
}
|
||||
|
||||
|
||||
@ -2537,6 +2537,11 @@ object NewStyle extends MdcLoggable{
|
||||
i => (unboxFullOrFail(i._1, callContext, s"$InvalidConnectorResponse Cannot ${nameOf(getAccountsHeld(bankId, user, callContext))} in the backend. ", 400), i._2)
|
||||
}
|
||||
}
|
||||
def getAccountsHeldByUser(user: User, callContext: Option[CallContext]): OBPReturnType[List[BankIdAccountId]] = {
|
||||
Connector.connector.vend.getAccountsHeldByUser(user, callContext) map {
|
||||
i => (unboxFullOrFail(i._1, callContext, s"$InvalidConnectorResponse Cannot ${nameOf(getAccountsHeldByUser(user, callContext))} in the backend. ", 400), i._2)
|
||||
}
|
||||
}
|
||||
|
||||
def createOrUpdateKycCheck(bankId: String,
|
||||
customerId: String,
|
||||
|
||||
@ -16,6 +16,7 @@ import code.api.util._
|
||||
import code.api.util.newstyle.BalanceNewStyle
|
||||
import code.api.util.newstyle.Consumer.createConsumerNewStyle
|
||||
import code.api.util.newstyle.RegulatedEntityNewStyle.{createRegulatedEntityNewStyle, deleteRegulatedEntityNewStyle, getRegulatedEntitiesNewStyle, getRegulatedEntityByEntityIdNewStyle}
|
||||
import code.api.v2_0_0.AccountsHelper.{accountTypeFilterText, getFilteredCoreAccounts}
|
||||
import code.api.v2_1_0.ConsumerRedirectUrlJSON
|
||||
import code.api.v3_0_0.JSONFactory300
|
||||
import code.api.v3_0_0.JSONFactory300.createAggregateMetricJson
|
||||
@ -624,6 +625,101 @@ trait APIMethods510 {
|
||||
}
|
||||
|
||||
|
||||
staticResourceDocs += ResourceDoc(
|
||||
getAccountsHeldByUserAtBank,
|
||||
implementedInApiVersion,
|
||||
nameOf(getAccountsHeldByUserAtBank),
|
||||
"GET",
|
||||
"/users/USER_ID/banks/BANK_ID/accounts-held",
|
||||
"Get Accounts Held By User",
|
||||
s"""Get Accounts held by the User if even the User has not been assigned the owner View yet.
|
||||
|
|
||||
|Can be used to onboard the account to the API - since all other account and transaction endpoints require views to be assigned.
|
||||
|
|
||||
|${accountTypeFilterText("/users/USER_ID/banks/BANK_ID/accounts-held")}
|
||||
|
|
||||
|
|
||||
|
|
||||
""".stripMargin,
|
||||
EmptyBody,
|
||||
coreAccountsHeldJsonV300,
|
||||
List(
|
||||
$UserNotLoggedIn,
|
||||
$BankNotFound,
|
||||
UserNotFoundByUserId,
|
||||
UnknownError
|
||||
),
|
||||
List(apiTagAccount),
|
||||
Some(List(canGetAccountsHeldAtOneBank, canGetAccountsHeldAtAnyBank))
|
||||
)
|
||||
|
||||
lazy val getAccountsHeldByUserAtBank: OBPEndpoint = {
|
||||
case "users" :: userId :: "banks" :: BankId(bankId) :: "accounts-held" :: Nil JsonGet req => {
|
||||
cc =>
|
||||
implicit val ec = EndpointContext(Some(cc))
|
||||
for {
|
||||
(u, callContext) <- NewStyle.function.getUserByUserId(userId, cc.callContext)
|
||||
(availableAccounts, callContext) <- NewStyle.function.getAccountsHeld(bankId, u, callContext)
|
||||
(accounts, callContext) <- NewStyle.function.getBankAccountsHeldFuture(availableAccounts.toList, callContext)
|
||||
|
||||
accountHelds <- getFilteredCoreAccounts(availableAccounts, req, callContext).map { it =>
|
||||
val coreAccountIds: List[String] = it._1.map(_.id)
|
||||
accounts.filter(accountHeld => coreAccountIds.contains(accountHeld.id))
|
||||
}
|
||||
} yield {
|
||||
(JSONFactory300.createCoreAccountsByCoreAccountsJSON(accountHelds), HttpCode.`200`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
staticResourceDocs += ResourceDoc(
|
||||
getAccountsHeldByUser,
|
||||
implementedInApiVersion,
|
||||
nameOf(getAccountsHeldByUser),
|
||||
"GET",
|
||||
"/users/USER_ID/accounts-held",
|
||||
"Get Accounts Held By User",
|
||||
s"""Get Accounts held by the User if even the User has not been assigned the owner View yet.
|
||||
|
|
||||
|Can be used to onboard the account to the API - since all other account and transaction endpoints require views to be assigned.
|
||||
|
|
||||
|${accountTypeFilterText("/users/USER_ID/accounts-held")}
|
||||
|
|
||||
|
|
||||
|
|
||||
""".stripMargin,
|
||||
EmptyBody,
|
||||
coreAccountsHeldJsonV300,
|
||||
List(
|
||||
$UserNotLoggedIn,
|
||||
$BankNotFound,
|
||||
UserNotFoundByUserId,
|
||||
UnknownError
|
||||
),
|
||||
List(apiTagAccount),
|
||||
Some(List(canGetAccountsHeldAtAnyBank))
|
||||
)
|
||||
|
||||
lazy val getAccountsHeldByUser: OBPEndpoint = {
|
||||
case "users" :: userId :: "accounts-held" :: Nil JsonGet req => {
|
||||
cc =>
|
||||
implicit val ec = EndpointContext(Some(cc))
|
||||
for {
|
||||
(u, callContext) <- NewStyle.function.getUserByUserId(userId, cc.callContext)
|
||||
(availableAccounts, callContext) <- NewStyle.function.getAccountsHeldByUser(u, callContext)
|
||||
(accounts, callContext) <- NewStyle.function.getBankAccountsHeldFuture(availableAccounts, callContext)
|
||||
|
||||
accountHelds <- getFilteredCoreAccounts(availableAccounts, req, callContext).map { it =>
|
||||
val coreAccountIds: List[String] = it._1.map(_.id)
|
||||
accounts.filter(accountHeld => coreAccountIds.contains(accountHeld.id))
|
||||
}
|
||||
} yield {
|
||||
(JSONFactory300.createCoreAccountsByCoreAccountsJSON(accountHelds), HttpCode.`200`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
staticResourceDocs += ResourceDoc(
|
||||
getEntitlementsAndPermissions,
|
||||
|
||||
@ -515,6 +515,7 @@ trait Connector extends MdcLoggable {
|
||||
def getBankAccountsHeldLegacy(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]) : Box[List[AccountHeld]]= Failure(setUnimplementedError(nameOf(getBankAccountsHeldLegacy _)))
|
||||
def getBankAccountsHeld(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]) : OBPReturnType[Box[List[AccountHeld]]]= Future {(Failure(setUnimplementedError(nameOf(getBankAccountsHeld _))), callContext)}
|
||||
def getAccountsHeld(bankId: BankId, user: User, callContext: Option[CallContext]): OBPReturnType[Box[List[BankIdAccountId]]]= Future {(Failure(setUnimplementedError(nameOf(getAccountsHeld _))), callContext)}
|
||||
def getAccountsHeldByUser(user: User, callContext: Option[CallContext]): OBPReturnType[Box[List[BankIdAccountId]]]= Future {(Failure(setUnimplementedError(nameOf(getAccountsHeld _))), callContext)}
|
||||
|
||||
def checkBankAccountExistsLegacy(bankId : BankId, accountId : AccountId, callContext: Option[CallContext] = None) : Box[(BankAccount, Option[CallContext])]= Failure(setUnimplementedError(nameOf(checkBankAccountExistsLegacy _)))
|
||||
def checkBankAccountExists(bankId : BankId, accountId : AccountId, callContext: Option[CallContext] = None) : OBPReturnType[Box[(BankAccount)]] = Future {(Failure(setUnimplementedError(nameOf(checkBankAccountExists _))), callContext)}
|
||||
|
||||
@ -1196,6 +1196,11 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
(Full(AccountHolders.accountHolders.vend.getAccountsHeld(bankId, user).toList), callContext)
|
||||
}
|
||||
}
|
||||
override def getAccountsHeldByUser(user: User, callContext: Option[CallContext]): OBPReturnType[Box[List[BankIdAccountId]]] = {
|
||||
Future {
|
||||
(Full(AccountHolders.accountHolders.vend.getAccountsHeldByUser(user).toList), callContext)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -27,9 +27,8 @@ TESOBE (http://www.tesobe.com/)
|
||||
|
||||
import java.lang.reflect.{Proxy => JProxy}
|
||||
import java.security.cert.X509Certificate
|
||||
|
||||
import bootstrap.liftweb.Boot
|
||||
import code.api.{Constant, RequestHeader}
|
||||
import code.api.{CertificateConstants, Constant, RequestHeader}
|
||||
import code.api.util.APIUtil
|
||||
import code.setup.PropsProgrammatically
|
||||
import net.liftweb.http.LiftRules
|
||||
@ -38,7 +37,6 @@ import org.apache.commons.codec.binary.Base64
|
||||
import org.eclipse.jetty.server._
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory
|
||||
import org.eclipse.jetty.webapp.WebAppContext
|
||||
import sun.security.provider.X509Factory
|
||||
|
||||
object RunMTLSWebApp extends App with PropsProgrammatically {
|
||||
val servletContextPath = "/"
|
||||
@ -76,9 +74,9 @@ object RunMTLSWebApp extends App with PropsProgrammatically {
|
||||
)
|
||||
).trim
|
||||
val certificate =
|
||||
s"""${X509Factory.BEGIN_CERT}
|
||||
s"""${CertificateConstants.BEGIN_CERT}
|
||||
|$content
|
||||
|${X509Factory.END_CERT}
|
||||
|${CertificateConstants.END_CERT}
|
||||
|""".stripMargin
|
||||
httpFields.add(RequestHeader.`PSD2-CERT`, certificate)
|
||||
}
|
||||
|
||||
@ -25,12 +25,8 @@ TESOBE (http://www.tesobe.com/)
|
||||
|
||||
*/
|
||||
|
||||
import java.lang.reflect.{Proxy => JProxy}
|
||||
import java.security.cert.X509Certificate
|
||||
|
||||
import bootstrap.liftweb.Boot
|
||||
import code.api.util.APIUtil
|
||||
import code.api.{Constant, RequestHeader}
|
||||
import code.api.{CertificateConstants, Constant, RequestHeader}
|
||||
import code.setup.PropsProgrammatically
|
||||
import net.liftweb.http.LiftRules
|
||||
import net.liftweb.http.provider.HTTPContext
|
||||
@ -38,7 +34,9 @@ import org.apache.commons.codec.binary.Base64
|
||||
import org.eclipse.jetty.server._
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory
|
||||
import org.eclipse.jetty.webapp.WebAppContext
|
||||
import sun.security.provider.X509Factory
|
||||
|
||||
import java.lang.reflect.{Proxy => JProxy}
|
||||
import java.security.cert.X509Certificate
|
||||
|
||||
object RunTLSWebApp extends App with PropsProgrammatically {
|
||||
val servletContextPath = "/"
|
||||
@ -76,9 +74,9 @@ object RunTLSWebApp extends App with PropsProgrammatically {
|
||||
)
|
||||
).trim
|
||||
val certificate =
|
||||
s"""${X509Factory.BEGIN_CERT}
|
||||
s"""${CertificateConstants.BEGIN_CERT}
|
||||
|$content
|
||||
|${X509Factory.END_CERT}
|
||||
|${CertificateConstants.END_CERT}
|
||||
|""".stripMargin
|
||||
httpFields.add(RequestHeader.`PSD2-CERT`, certificate)
|
||||
}
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
package code.api.v5_1_0
|
||||
|
||||
import code.api.util.APIUtil.OAuth._
|
||||
import code.api.util.ApiRole.CanSeeAccountAccessForAnyUser
|
||||
import code.api.util.ApiRole.{CanGetAccountsHeldAtAnyBank, CanGetAccountsHeldAtOneBank}
|
||||
import code.api.util.ErrorMessages.{UserHasMissingRoles, UserNotLoggedIn}
|
||||
import code.api.v4_0_0.AccountsMinimalJson400
|
||||
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
|
||||
@ -21,6 +19,10 @@ class AccountTest extends V510ServerSetup {
|
||||
*/
|
||||
object VersionOfApi extends Tag(ApiVersion.v5_1_0.toString)
|
||||
object GetCoreAccountByIdThroughView extends Tag(nameOf(Implementations5_1_0.getCoreAccountByIdThroughView))
|
||||
object getAccountsHeldByUserAtBank extends Tag(nameOf(Implementations5_1_0.getAccountsHeldByUserAtBank))
|
||||
object GetAccountsHeldByUser extends Tag(nameOf(Implementations5_1_0.getAccountsHeldByUser))
|
||||
|
||||
lazy val bankId = randomBankId
|
||||
|
||||
feature(s"test ${GetCoreAccountByIdThroughView.name}") {
|
||||
scenario(s"We will test ${GetCoreAccountByIdThroughView.name}", GetCoreAccountByIdThroughView, VersionOfApi) {
|
||||
@ -34,5 +36,43 @@ class AccountTest extends V510ServerSetup {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
feature(s"test ${getAccountsHeldByUserAtBank.name}") {
|
||||
scenario(s"We will test ${getAccountsHeldByUserAtBank.name}", getAccountsHeldByUserAtBank, VersionOfApi) {
|
||||
val requestGet = (v5_1_0_Request / "users" / resourceUser2.userId / "banks" / bankId / "accounts-held").GET
|
||||
// Anonymous call fails
|
||||
val anonymousResponseGet = makeGetRequest(requestGet)
|
||||
anonymousResponseGet.code should equal(401)
|
||||
anonymousResponseGet.body.extract[ErrorMessage].message should equal(UserNotLoggedIn)
|
||||
}
|
||||
scenario("We will call the endpoint with user credentials", getAccountsHeldByUserAtBank, VersionOfApi) {
|
||||
When(s"We make a request $getAccountsHeldByUserAtBank")
|
||||
val requestGet = (v5_1_0_Request / "users" / resourceUser2.userId / "banks" / bankId / "accounts-held").GET <@(user1)
|
||||
val response = makeGetRequest(requestGet)
|
||||
Then("We should get a 403")
|
||||
response.code should equal(403)
|
||||
val errorMessage = UserHasMissingRoles + s"${CanGetAccountsHeldAtOneBank} or $CanGetAccountsHeldAtAnyBank"
|
||||
response.body.extract[ErrorMessage].message contains errorMessage should be(true)
|
||||
}
|
||||
}
|
||||
|
||||
feature(s"test ${GetAccountsHeldByUser.name}") {
|
||||
scenario(s"We will test ${GetAccountsHeldByUser.name}", GetAccountsHeldByUser, VersionOfApi) {
|
||||
val requestGet = (v5_1_0_Request / "users" / resourceUser2.userId / "accounts-held").GET
|
||||
// Anonymous call fails
|
||||
val anonymousResponseGet = makeGetRequest(requestGet)
|
||||
anonymousResponseGet.code should equal(401)
|
||||
anonymousResponseGet.body.extract[ErrorMessage].message should equal(UserNotLoggedIn)
|
||||
}
|
||||
scenario("We will call the endpoint with user credentials", GetAccountsHeldByUser, VersionOfApi) {
|
||||
When(s"We make a request $GetAccountsHeldByUser")
|
||||
val requestGet = (v5_1_0_Request / "users" / resourceUser2.userId / "accounts-held").GET <@(user1)
|
||||
val response = makeGetRequest(requestGet)
|
||||
Then("We should get a 403")
|
||||
response.code should equal(403)
|
||||
val errorMessage = UserHasMissingRoles + s"$CanGetAccountsHeldAtAnyBank"
|
||||
response.body.extract[ErrorMessage].message contains errorMessage should be(true)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user