Skip to content

Commit

Permalink
Merge remote-tracking branch 'Official/develop' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
hongwei1 committed Feb 18, 2018
2 parents 44ac14f + 1367fa1 commit f187e18
Show file tree
Hide file tree
Showing 64 changed files with 502 additions and 188 deletions.
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,11 @@
<artifactId>java-jwt</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>4.23</version>
</dependency>


</dependencies>
Expand Down
23 changes: 12 additions & 11 deletions src/main/scala/bootstrap/liftweb/Boot.scala
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,8 @@ class Boot extends MdcLoggable {

print("Enter the Password for the SSL Certificate Stores: ")
//As most IDEs do not provide a Console, we fall back to readLine
code.api.util.APIUtil.initPasswd = if (Props.get("kafka.use.ssl").getOrElse("") == "true") {
code.api.util.APIUtil.initPasswd = if (Props.get("kafka.use.ssl").getOrElse("") == "true" ||
Props.get("jwt.use.ssl").getOrElse("") == "true") {
try {
System.console.readPassword().toString
} catch {
Expand Down Expand Up @@ -223,7 +224,7 @@ class Boot extends MdcLoggable {
LiftRules.statelessDispatch.append(OAuthHandshake)

// JWT auth endpoints
if(Props.getBool("allow_direct_login", true)) {
if(APIUtil.getPropsAsBoolValue("allow_direct_login", true)) {
LiftRules.statelessDispatch.append(DirectLogin)
}

Expand All @@ -233,7 +234,7 @@ class Boot extends MdcLoggable {


// OpenIdConnect endpoint and validator
if(Props.getBool("allow_openidconnect", false)) {
if(APIUtil.getPropsAsBoolValue("allow_openidconnect", false)) {
LiftRules.dispatch.append(OpenIdConnect)
}

Expand Down Expand Up @@ -269,7 +270,7 @@ class Boot extends MdcLoggable {
// LiftRules.statelessDispatch.append(Metrics) TODO: see metric menu entry below

//add sandbox api calls only if we're running in sandbox mode
if(Props.getBool("allow_sandbox_data_import", false)) {
if(APIUtil.getPropsAsBoolValue("allow_sandbox_data_import", false)) {
LiftRules.statelessDispatch.append(SandboxApiCalls)
} else {
logger.info("Not adding sandbox api calls")
Expand All @@ -279,7 +280,7 @@ class Boot extends MdcLoggable {
Schedule.schedule(()=> OAuthAuthorisation.dataBaseCleaner, 2 minutes)

val accountCreation = {
if(Props.getBool("allow_sandbox_account_creation", false)){
if(APIUtil.getPropsAsBoolValue("allow_sandbox_account_creation", false)){
//user must be logged in, as a created account needs an owner
// Not mentioning test and sandbox for App store purposes right now.
List(Menu("Sandbox Account Creation", "Create Bank Account") / "create-sandbox-account" >> AuthUser.loginFirst)
Expand All @@ -293,7 +294,7 @@ class Boot extends MdcLoggable {
KafkaHelperActors.startLocalKafkaHelperWorkers(actorSystem)
}

if (!Props.getBool("remotedata.enable", false)) {
if (!APIUtil.getPropsAsBoolValue("remotedata.enable", false)) {
try {
logger.info(s"RemotedataActors.startLocalRemotedataWorkers( ${actorSystem} ) starting")
RemotedataActors.startActors(actorSystem)
Expand All @@ -305,7 +306,7 @@ class Boot extends MdcLoggable {

// API Metrics (logs of API calls)
// If set to true we will write each URL with params to a datastore / log file
if (Props.getBool("write_metrics", false)) {
if (APIUtil.getPropsAsBoolValue("write_metrics", false)) {
logger.info("writeMetrics is true. We will write API metrics")
} else {
logger.info("writeMetrics is false. We will NOT write API metrics")
Expand Down Expand Up @@ -370,7 +371,7 @@ class Boot extends MdcLoggable {
S.addAround(DB.buildLoanWrapper)

try {
val useMessageQueue = Props.getBool("messageQueue.createBankAccounts", false)
val useMessageQueue = APIUtil.getPropsAsBoolValue("messageQueue.createBankAccounts", false)
if(useMessageQueue)
BankAccountCreationListener.startListen
} catch {
Expand All @@ -396,15 +397,15 @@ class Boot extends MdcLoggable {
}
}

if ( !Props.getLong("transaction_status_scheduler_delay").isEmpty ) {
val delay = Props.getLong("transaction_status_scheduler_delay").openOrThrowException("Incorrect value for transaction_status_scheduler_delay, please provide number of seconds.")
if ( !APIUtil.getPropsAsLongValue("transaction_status_scheduler_delay").isEmpty ) {
val delay = APIUtil.getPropsAsLongValue("transaction_status_scheduler_delay").openOrThrowException("Incorrect value for transaction_status_scheduler_delay, please provide number of seconds.")
TransactionStatusScheduler.start(delay)
}

APIUtil.akkaSanityCheck() match {
case Full(c) if c == true => logger.info(s"remotedata.secret matched = $c")
case Full(c) if c == false => throw new Exception(ErrorMessages.RemoteDataSecretMatchError)
case Empty => Props.getBool("use_akka", false) match {
case Empty => APIUtil.getPropsAsBoolValue("use_akka", false) match {
case true => throw new Exception(ErrorMessages.RemoteDataSecretObtainError)
case false => logger.info("Akka middleware layer is disabled.")
}
Expand Down
5 changes: 3 additions & 2 deletions src/main/scala/code/accountholder/AccountHolders.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package code.accountholder



import code.api.util.APIUtil
import code.model._
import net.liftweb.util.{Props, SimpleInjector}
import code.remotedata.{RemotedataAccountHolders}
import code.remotedata.RemotedataAccountHolders
import net.liftweb.common.Box


Expand All @@ -13,7 +14,7 @@ object AccountHolders extends SimpleInjector {
val accountHolders = new Inject(buildOne _) {}

def buildOne: AccountHolders =
Props.getBool("use_akka", false) match {
APIUtil.getPropsAsBoolValue("use_akka", false) match {
case false => MapperAccountHolders
case true => RemotedataAccountHolders // We will use Akka as a middleware
}
Expand Down
3 changes: 2 additions & 1 deletion src/main/scala/code/actorsystem/ObpActorInit.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package code.actorsystem

import akka.util.Timeout
import code.api.APIFailure
import code.api.util.APIUtil
import code.util.Helper.MdcLoggable
import net.liftweb.common._
import net.liftweb.util.Props
Expand All @@ -12,7 +13,7 @@ import scala.concurrent.{Await, Future}

trait ObpActorInit extends MdcLoggable{
// Default is 3 seconds, which should be more than enough for slower systems
val ACTOR_TIMEOUT: Long = Props.getLong("remotedata.timeout").openOr(3)
val ACTOR_TIMEOUT: Long = APIUtil.getPropsAsLongValue("remotedata.timeout").openOr(3)

val actorName = CreateActorNameFromClassName(this.getClass.getName)
val actor = ObpLookupSystem.getRemotedataActor(actorName)
Expand Down
3 changes: 2 additions & 1 deletion src/main/scala/code/actorsystem/ObpLookupSystem.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package code.actorsystem

import akka.actor.ActorSystem
import code.api.util.APIUtil
import code.util.Helper
import code.util.Helper.MdcLoggable
import com.typesafe.config.ConfigFactory
Expand Down Expand Up @@ -41,7 +42,7 @@ trait ObpLookupSystem extends MdcLoggable {

def getRemotedataActor(actorName: String) = {

val actorPath: String = Props.getBool("remotedata.enable", false) match {
val actorPath: String = APIUtil.getPropsAsBoolValue("remotedata.enable", false) match {
case true =>
val hostname = ObpActorConfig.remoteHostname
val port = ObpActorConfig.remotePort
Expand Down
13 changes: 9 additions & 4 deletions src/main/scala/code/api/GatewayLogin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ package code.api

import java.io.UnsupportedEncodingException

import code.api.util.{CertificateUtil, CryptoSystem, ErrorMessages}
import code.api.util.{APIUtil, CertificateUtil, CryptoSystem, ErrorMessages}
import code.bankconnectors.{Connector, InboundAccountCommon}
import code.consumer.Consumers
import code.model.dataAccess.AuthUser
Expand Down Expand Up @@ -86,7 +86,7 @@ object GatewayLogin extends RestHelper with MdcLoggable {

var jwt: String = ""
try {
val algorithm = Props.getBool("jwt.use.ssl", false) match {
val algorithm = APIUtil.getPropsAsBoolValue("jwt.use.ssl", false) match {
case true =>
Algorithm.RSA256(CertificateUtil.publicKey, CertificateUtil.privateKey)
case false =>
Expand All @@ -107,7 +107,12 @@ object GatewayLogin extends RestHelper with MdcLoggable {
//Invalid Signing configuration / Couldn't convert Claims.
logger.error(exception)
}
jwt
APIUtil.getPropsAsBoolValue("jwt.use.ssl", false) match {
case true =>
CertificateUtil.encryptJwtWithRsa(jwt)
case false =>
jwt
}
}

def parseJwt(parameters: Map[String, String]): Box[String] = {
Expand All @@ -134,7 +139,7 @@ object GatewayLogin extends RestHelper with MdcLoggable {
def validateJwtToken(token: String): Box[DecodedJWT] = {
try {
val jwtDecoded = JWT.decode(token)
val algorithm = Props.getBool("jwt.use.ssl", false) match {
val algorithm = APIUtil.getPropsAsBoolValue("jwt.use.ssl", false) match {
case true =>
Algorithm.RSA256(CertificateUtil.publicKey, CertificateUtil.privateKey)
case false =>
Expand Down
6 changes: 3 additions & 3 deletions src/main/scala/code/api/OBPRestHelper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ trait OBPRestHelper extends RestHelper with MdcLoggable {
errorJsonResponse(apiFailure.msg, apiFailure.responseCode)
}
case obj@Failure(msg, _, c) => {
val failuresMsg = Props.getBool("display_internal_errors").openOr(false) match {
val failuresMsg = APIUtil.getPropsAsBoolValue("display_internal_errors", false) match {
case true => // Show all error in a chain
obj.messageChain
case false => // Do not display internal errors
Expand Down Expand Up @@ -216,15 +216,15 @@ trait OBPRestHelper extends RestHelper with MdcLoggable {
case Failure(msg, t, c) => Failure(msg, t, c)
case _ => Failure("oauth error")
}
} else if (Props.getBool("allow_direct_login", true) && hasDirectLoginHeader(authorization)) {
} else if (APIUtil.getPropsAsBoolValue("allow_direct_login", true) && hasDirectLoginHeader(authorization)) {
DirectLogin.getUser match {
case Full(u) => fn(cc.copy(user = Full(u)))// Authentication is successful
case _ => {
var (httpCode, message, directLoginParameters) = DirectLogin.validator("protectedResource", DirectLogin.getHttpMethod)
Full(errorJsonResponse(message, httpCode))
}
}
} else if (Props.getBool("allow_gateway_login", false) && hasGatewayHeader(authorization)) {
} else if (APIUtil.getPropsAsBoolValue("allow_gateway_login", false) && hasGatewayHeader(authorization)) {
logger.info("allow_gateway_login-getRemoteIpAddress: " + getRemoteIpAddress() )
Props.get("gateway.host") match {
case Full(h) if h.split(",").toList.exists(_.equalsIgnoreCase(getRemoteIpAddress()) == true) => // Only addresses from white list can use this feature
Expand Down
6 changes: 3 additions & 3 deletions src/main/scala/code/api/directlogin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ object DirectLogin extends RestHelper with MdcLoggable {
//check if the application is registered and active
else if (
requestType == "authorizationToken" &&
Props.getBool("direct_login_consumer_key_mandatory", true) &&
APIUtil.getPropsAsBoolValue("direct_login_consumer_key_mandatory", true) &&
! APIUtil.registeredApplication(parameters.getOrElse("consumer_key", ""))) {

logger.error("application: " + parameters.getOrElse("consumer_key", "") + " not found")
Expand Down Expand Up @@ -364,7 +364,7 @@ object DirectLogin extends RestHelper with MdcLoggable {
if (requestType == "protectedResource") {
validAccessTokenFuture(parameters.getOrElse("token", ""))
} else if (requestType == "authorizationToken" &&
Props.getBool("direct_login_consumer_key_mandatory", true))
APIUtil.getPropsAsBoolValue("direct_login_consumer_key_mandatory", true))
{
APIUtil.registeredApplicationFuture(parameters.getOrElse("consumer_key", ""))
} else {
Expand Down Expand Up @@ -392,7 +392,7 @@ object DirectLogin extends RestHelper with MdcLoggable {
}
//check if the application is registered and active
else if ( requestType == "authorizationToken" &&
Props.getBool("direct_login_consumer_key_mandatory", true) &&
APIUtil.getPropsAsBoolValue("direct_login_consumer_key_mandatory", true) &&
!valid)
{
logger.error("application: " + parameters.getOrElse("consumer_key", "") + " not found")
Expand Down
49 changes: 35 additions & 14 deletions src/main/scala/code/api/util/APIUtil.scala
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ object APIUtil extends MdcLoggable {
def logAPICall(callContext: Option[CallContext]) = {
callContext match {
case Some(cc) =>
if(Props.getBool("write_metrics", false)) {
if(getPropsAsBoolValue("write_metrics", false)) {
val u: User = cc.user.orNull
val userId = if (u != null) u.userId else "null"
val userName = if (u != null) u.name else "null"
Expand All @@ -456,7 +456,7 @@ object APIUtil extends MdcLoggable {
case Full(c) => Full(c)
case _ => Empty
}
} else if (Props.getBool("allow_direct_login", true) && hasDirectLoginHeader(cc.authorization)) {
} else if (getPropsAsBoolValue("allow_direct_login", true) && hasDirectLoginHeader(cc.authorization)) {
DirectLogin.getConsumer(cc) match {
case Full(c) => Full(c)
case _ => Empty
Expand Down Expand Up @@ -493,14 +493,14 @@ object APIUtil extends MdcLoggable {

def logAPICall(date: TimeSpan, duration: Long, rd: Option[ResourceDoc]) = {
val authorization = S.request.map(_.header("Authorization")).flatten
if(Props.getBool("write_metrics", false)) {
if(getPropsAsBoolValue("write_metrics", false)) {
val user =
if (hasAnOAuthHeader(authorization)) {
getUser match {
case Full(u) => Full(u)
case _ => Empty
}
} else if (Props.getBool("allow_direct_login", true) && hasDirectLoginHeader(authorization)) {
} else if (getPropsAsBoolValue("allow_direct_login", true) && hasDirectLoginHeader(authorization)) {
DirectLogin.getUser match {
case Full(u) => Full(u)
case _ => Empty
Expand All @@ -515,7 +515,7 @@ object APIUtil extends MdcLoggable {
case Full(c) => Full(c)
case _ => Empty
}
} else if (Props.getBool("allow_direct_login", true) && hasDirectLoginHeader(authorization)) {
} else if (getPropsAsBoolValue("allow_direct_login", true) && hasDirectLoginHeader(authorization)) {
DirectLogin.getConsumer match {
case Full(c) => Full(c)
case _ => Empty
Expand Down Expand Up @@ -1464,7 +1464,7 @@ Returns a string showed to the developer
val result = blockOfCode
// call-by-name
val t1 = System.currentTimeMillis()
if (Props.getBool("write_metrics", false)){
if (getPropsAsBoolValue("write_metrics", false)){
val correlationId = getCorrelationId()
Future {
ConnectorMetricsProvider.metrics.vend.saveConnectorMetric(nameOfConnector, nameOfFunction, correlationId, now, t1 - t0)
Expand All @@ -1474,7 +1474,7 @@ Returns a string showed to the developer
}

def akkaSanityCheck (): Box[Boolean] = {
Props.getBool("use_akka", false) match {
getPropsAsBoolValue("use_akka", false) match {
case true =>
val remotedataSecret = Props.get("remotedata.secret").openOrThrowException("Cannot obtain property remotedata.secret")
SanityCheck.sanityCheck.vend.remoteAkkaSanityCheck(remotedataSecret)
Expand Down Expand Up @@ -1924,9 +1924,9 @@ Versions are groups of endpoints in a file
val res =
if (hasAnOAuthHeader(authorization)) {
getUserFromOAuthHeaderFuture(cc)
} else if (Props.getBool("allow_direct_login", true) && hasDirectLoginHeader(authorization)) {
} else if (getPropsAsBoolValue("allow_direct_login", true) && hasDirectLoginHeader(authorization)) {
DirectLogin.getUserFromDirectLoginHeaderFuture(cc)
} else if (Props.getBool("allow_gateway_login", false) && hasGatewayHeader(authorization)) {
} else if (getPropsAsBoolValue("allow_gateway_login", false) && hasGatewayHeader(authorization)) {
Props.get("gateway.host") match {
case Full(h) if h.split(",").toList.exists(_.equalsIgnoreCase(getRemoteIpAddress()) == true) => // Only addresses from white list can use this feature
val (httpCode, message, parameters) = GatewayLogin.validator(s.request)
Expand Down Expand Up @@ -2015,7 +2015,7 @@ Versions are groups of endpoints in a file
case ParamFailure(msg,_,_,_) =>
throw new Exception(msg)
case obj@Failure(msg, _, c) =>
val failuresMsg = Props.getBool("display_internal_errors").openOr(false) match {
val failuresMsg = getPropsAsBoolValue("display_internal_errors", false) match {
case true => // Show all error in a chain
obj.messageChain
case false => // Do not display internal errors
Expand Down Expand Up @@ -2093,25 +2093,46 @@ Versions are groups of endpoints in a file
/**
* This function is implemented in order to support encrypted values in props file.
* Please note that some value is considered as encrypted if has an encryption mark property in addition to regular props value in props file e.g
* db.url=SOME_ENCRYPTED_VALUE
* db.url=Helpers.base64Encode(SOME_ENCRYPTED_VALUE)
* db.url.is_encrypted=true
* getDecryptedPropsValue("db.url") = jdbc:postgresql://localhost:5432/han_obp_api_9?user=han_obp_api&password=mypassword
* Encrypt/Decrypt workflow:
* Encrypt: Array[Byte] -> Helpers.base64Encode(encrypted) -> Props file: String -> Helpers.base64Decode(encryptedValue) -> Decrypt: Array[Byte]
* @param nameOfProperty Name of property which value should be decrypted
* @return Decrypted value of a property
*/
def getPropsValue(nameOfProperty: String): Box[String] = {
(Props.get(nameOfProperty), Props.get(nameOfProperty + ".is_encrypted")) match {
case (Full(encryptedValue), Full(isEncrypted)) if isEncrypted == "true" =>
val decryptedValue: Array[Byte] = decrypt(privateKey, encryptedValue.getBytes(StandardCharsets.UTF_8), CryptoSystem.RSA)
Full(decryptedValue.toString)
case (Full(base64PropsValue), Full(isEncrypted)) if isEncrypted == "true" =>
val decryptedValueAsArray = decrypt(privateKey, Helpers.base64Decode(base64PropsValue), CryptoSystem.RSA)
val decryptedValueAsString = new String(decryptedValueAsArray)
Full(decryptedValueAsString)
case (Full(property), Full(isEncrypted)) if isEncrypted == "false" =>
Full(property)
case (Full(property), Empty) =>
Full(property)
case (Empty, Empty) =>
Empty
case _ =>
logger.error(cannotDecryptValueOfProperty + nameOfProperty)
Failure(cannotDecryptValueOfProperty + nameOfProperty)
}
}

def getPropsAsBoolValue(nameOfProperty: String, defaultValue: Boolean): Boolean = {
getPropsValue(nameOfProperty) map(toBoolean) openOr(defaultValue)
}
def getPropsAsIntValue(nameOfProperty: String): Box[Int] = {
getPropsValue(nameOfProperty) map(toInt)
}
def getPropsAsIntValue(nameOfProperty: String, defaultValue: Int): Int = {
getPropsAsIntValue(nameOfProperty) openOr(defaultValue)
}
def getPropsAsLongValue(nameOfProperty: String): Box[Long] = {
getPropsValue(nameOfProperty) flatMap(asLong)
}
def getPropsAsLongValue(nameOfProperty: String, defaultValue: Long): Long = {
getPropsAsLongValue(nameOfProperty) openOr(defaultValue)
}

}

0 comments on commit f187e18

Please sign in to comment.