Merge pull request #1607 from hongwei1/develop

refresh User automatically
This commit is contained in:
Simon Redfern 2020-06-22 13:53:33 +02:00 committed by GitHub
commit ff744b1e66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 656 additions and 330 deletions

View File

@ -516,12 +516,12 @@ super_admin_user_ids=USER_ID1,USER_ID2,
# For a VERSION to be allowed it must be:
# 1) Absent from here:
#api_disabled_versions=[v3_0_0]
#api_disabled_versions=[v3.0.0]
# 2) Present here OR this entry must be empty.
#api_enabled_versions=[v2_2_0,v3_0_0]
#api_enabled_versions=[v2.2.0,v3.0.0]
# Note we use "v" and "_" in the name to match the ApiVersions enumeration in ApiUtil.scala
# Note we use "v" and "." in the name to match the ApiVersions enumeration in ApiUtil.scala
# For an ENDPOINT to be allowed it must be:
@ -814,4 +814,11 @@ dynamic_endpoints_url_prefix=dynamic
# In case is not defined default value is false
# display_accept_cookies_question = false
# More info at page https://wikis.ec.europa.eu/display/WEBGUIDE/04.+Cookies
# --------------------------------------------------------------------------
# --------------------------------------------------------------------------
# --- Refresh User ------
# The time to refresh user internally. default is 30 minutes
# Note: The user is also refreshed after every login.
# You can also explicitly refresh the user using the refresh user endpoint.
# refresh_user.interval=30
# --------------------------------------------------------------------

View File

@ -32,6 +32,7 @@ import java.util.{Locale, TimeZone}
import code.CustomerDependants.MappedCustomerDependant
import code.DynamicData.DynamicData
import code.DynamicEndpoint.DynamicEndpoint
import code.UserRefreshes.MappedUserRefreshes
import code.accountapplication.MappedAccountApplication
import code.accountattribute.MappedAccountAttribute
import code.accountholders.MapperAccountHolders
@ -696,7 +697,7 @@ class Boot extends MdcLoggable {
.shortBankName("OBP")
.national_identifier("OBP")
.mBankRoutingScheme("OBP")
.mBankRoutingAddress("OBP_DEFAULT_BANK")
.mBankRoutingAddress("obp1")
.logoURL("")
.websiteURL("")
.saveMe()
@ -816,7 +817,8 @@ object ToSchemify {
DynamicEndpoint,
AccountIdMapping,
DirectDebit,
StandingOrder
StandingOrder,
MappedUserRefreshes
)++ APIBuilder_Connector.allAPIBuilderModels
// start grpc server

View File

@ -33,6 +33,7 @@ import java.nio.charset.Charset
import java.text.{ParsePosition, SimpleDateFormat}
import java.util.{Date, UUID}
import code.UserRefreshes.UserRefreshes
import code.accountholders.AccountHolders
import code.api.Constant._
import code.api.OAuthHandshake._
@ -2036,7 +2037,7 @@ Returns a string showed to the developer
1) Absent from Props api_disabled_versions
2) Present here (api_enabled_versions=[v2_2_0,v3_0_0]) -OR- api_enabled_versions must be empty.
Note we use "v" and "_" in the name to match the ApiVersions enumeration in ApiUtil.scala
Note we use "v" and "." in the name to match the ApiVersions enumeration in ApiUtil.scala
*/
def versionIsAllowed(version: ApiVersion) : Boolean = {
def checkVersion: Boolean = {
@ -2479,18 +2480,32 @@ Returns a string showed to the developer
def unboxOptionFuture[T](option: Option[Future[T]]): Future[Box[T]] = unboxFuture(Box(option))
def unboxOptionOBPReturnType[T](option: Option[OBPReturnType[T]]): Future[Box[T]] = unboxOBPReturnType(Box(option))
/**
* This method will be executed only when user is defined and needToRefreshUser return true.
* Better also check the logic for needToRefreshUser method.
*/
def refreshUserIfRequired(user: Box[User], callContext: Option[CallContext]) = {
if(!APIUtil.isSandboxMode && user.isDefined && UserRefreshes.UserRefreshes.vend.needToRefreshUser(user.head.userId))
user.map(AuthUser.updateUserAccountViewsFuture(_, callContext))
else
None
}
/**
* This function is used to factor out common code at endpoints regarding Authorized access
* @param emptyUserErrorMsg is a message which will be provided as a response in case that Box[User] = Empty
*/
def authenticatedAccess(cc: CallContext, emptyUserErrorMsg: String = UserNotLoggedIn): OBPReturnType[Box[User]] = {
anonymousAccess(cc) map {
x => (fullBoxOrException(
x._1 ~> APIFailureNewStyle(emptyUserErrorMsg, 400, Some(cc.toLight))),
anonymousAccess(cc) map{
x => (
fullBoxOrException(x._1 ~> APIFailureNewStyle(emptyUserErrorMsg, 400, Some(cc.toLight))),
x._2
)
} map {
x =>
refreshUserIfRequired(x._1,x._2)
x
}
}
@ -2624,12 +2639,9 @@ Returns a string showed to the developer
otherAccountRoutingAddress: String
)= createOBPId(s"$thisBankId$thisAccountId$counterpartyName$otherAccountRoutingScheme$otherAccountRoutingAddress")
//TODO, now we have the star connector, it will break the isSandboxMode method logic. Need to double check how to use this method now.
val isSandboxMode: Boolean = (APIUtil.getPropsValue("connector").openOrThrowException(attemptedToOpenAnEmptyBox).toString).equalsIgnoreCase("mapped")
//If we use kafka connector, we need set up kafka server first. For some cases(eg: get Kafka MessageDoc), we do not need kafka..
val isStarConnectorButNoKafkaSupport: Boolean =
(APIUtil.getPropsValue("connector").openOrThrowException(attemptedToOpenAnEmptyBox).toString).equalsIgnoreCase("star") && (!(APIUtil.getPropsValue("starConnector_supported_types").openOrThrowException(attemptedToOpenAnEmptyBox).toString).contains("kafka"))
/**
* 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
@ -2854,7 +2866,7 @@ Returns a string showed to the developer
APIUtil.getPropsValue("defaultBank.bank_id", "DEFAULT_BANK_ID_NOT_SET_Test")
else {
//Note: now if the bank_id is not existing, we will create it during `boot`.
APIUtil.getPropsValue("defaultBank.bank_id", "OBP_DEFAULT_BANK_ID")
APIUtil.getPropsValue("defaultBank.bank_id", "obp1")
}
//This method will read sample.props.template file, and get all the fields which start with the webui_
//it will return the webui_ props paris:

View File

@ -385,6 +385,23 @@ object Glossary {
)
glossaryItems += GlossaryItem(
title = "Adapter.Stored_Procedure.Intro",
description =
s"""
|## Use Stored_Procedure as an interface between OBP and your Core Banking System (CBS).
|
|
|For an introduction to Stored Procedures see [here](https://en.wikipedia.org/wiki/Stored_procedure)
|
|### Installation Prerequisites
|
|
|* You have OBP-API running and it is connected to a stored procedure related database.
|* Ideally you have API Explorer running (the application serving this page) but its not necessary - you could use any other REST client.
|* You might want to also run API Manager as it makes it easier to grant yourself roles, but its not necessary - you could use the API Explorer / any REST client instead.
|"""
)

View File

@ -445,6 +445,8 @@ trait APIMethods220 {
cc =>
for {
bank <- tryo{ json.extract[BankJSONV220] } ?~! ErrorMessages.InvalidJsonFormat
_ <- Helper.booleanToBox(
bank.id.length > 5,s"$InvalidJsonFormat Min length of BANK_ID should be 5 characters.")
u <- cc.user ?~!ErrorMessages.UserNotLoggedIn
consumer <- cc.consumer ?~! ErrorMessages.InvalidConsumerCredentials
_ <- hasEntitlementAndScope("", u.userId, consumer.id.get.toString, canCreateBank)

View File

@ -1779,6 +1779,10 @@ trait APIMethods400 {
_ <- Helper.booleanToFuture(failMsg = ErrorMessages.InvalidConsumerCredentials) {
cc.callContext.map(_.consumer.isDefined == true).isDefined
}
_ <- Helper.booleanToFuture(failMsg = s"$InvalidJsonFormat Min length of BANK_ID should be 5 characters.") {
bank.id.length > 5
}
(success, callContext) <- NewStyle.function.createOrUpdateBank(
bank.id,
bank.full_name,

View File

@ -75,9 +75,9 @@ object ConnectorBuilderUtil {
val currentTime = APIUtil.DateWithSecondsFormat.format(new Date())
val insertCode =
s"""$start
|// ---------- create on $currentTime
|// ---------- created on $currentTime
|${codeList.mkString}
|// ---------- create on $currentTime
|// ---------- created on $currentTime
|$end """.stripMargin
val newSource = source.replaceFirst(placeHolderInSource, insertCode)
FileUtils.writeStringToFile(path, newSource, "utf-8")
@ -326,6 +326,7 @@ object ConnectorBuilderUtil {
"getBankAccountByIban",
"getBankAccountByRouting",
"getBankAccounts",
"checkBankAccountExists",
//"getCoreBankAccountsLegacy", // should not generate for Legacy methods
//"getBankAccountsHeldLegacy", // should not generate for Legacy methods
//"checkBankAccountExistsLegacy", // should not generate for Legacy methods

View File

@ -334,7 +334,7 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit {
//---------------- dynamic start -------------------please don't modify this line
// ---------- create on 2020-06-17T14:19:04Z
// ---------- created on 2020-06-17T14:19:04Z
messageDocs += getChallengeThresholdDoc
def getChallengeThresholdDoc = MessageDoc(
@ -5429,6 +5429,6 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit {
response.map(convertToTuple[Boolean](callContext))
}
// ---------- create on 2020-06-17T14:19:04Z
// ---------- created on 2020-06-17T14:19:04Z
//---------------- dynamic end ---------------------please don't modify this line
}

View File

@ -92,7 +92,7 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable
//---------------- dynamic start -------------------please don't modify this line
// ---------- create on Tue Sep 03 17:49:04 CEST 2019
// ---------- created on Tue Sep 03 17:49:04 CEST 2019
messageDocs += getAdapterInfoDoc
def getAdapterInfoDoc = MessageDoc(

View File

@ -73,7 +73,7 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable {
val connectorName = "stored_procedure_vDec2019"
//---------------- dynamic start -------------------please don't modify this line
// ---------- create on 2020-06-18T20:03:31Z
// ---------- created on 2020-06-19T17:24:19Z
messageDocs += getAdapterInfoDoc
def getAdapterInfoDoc = MessageDoc(
@ -753,6 +753,50 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable {
response.map(convertToTuple[List[AccountHeld]](callContext))
}
messageDocs += checkBankAccountExistsDoc
def checkBankAccountExistsDoc = MessageDoc(
process = "obp.checkBankAccountExists",
messageFormat = messageFormat,
description = "Check Bank Account Exists",
outboundTopic = None,
inboundTopic = None,
exampleOutboundMessage = (
OutBoundCheckBankAccountExists(outboundAdapterCallContext=MessageDocsSwaggerDefinitions.outboundAdapterCallContext,
bankId=BankId(bankIdExample.value),
accountId=AccountId(accountIdExample.value))
),
exampleInboundMessage = (
InBoundCheckBankAccountExists(inboundAdapterCallContext=MessageDocsSwaggerDefinitions.inboundAdapterCallContext,
status=MessageDocsSwaggerDefinitions.inboundStatus,
data= BankAccountCommons(accountId=AccountId(accountIdExample.value),
accountType=accountTypeExample.value,
balance=BigDecimal(balanceAmountExample.value),
currency=currencyExample.value,
name=bankAccountNameExample.value,
label=labelExample.value,
iban=Some(ibanExample.value),
number=bankAccountNumberExample.value,
bankId=BankId(bankIdExample.value),
lastUpdate=parseDate(bankAccountLastUpdateExample.value).getOrElse(sys.error("bankAccountLastUpdateExample.value is not validate date format.")),
branchId=branchIdExample.value,
accountRoutingScheme=accountRoutingSchemeExample.value,
accountRoutingAddress=accountRoutingAddressExample.value,
accountRoutings=List( AccountRouting(scheme=accountRoutingSchemeExample.value,
address=accountRoutingAddressExample.value)),
accountRules=List( AccountRule(scheme=accountRuleSchemeExample.value,
value=accountRuleValueExample.value)),
accountHolder=bankAccountAccountHolderExample.value))
),
adapterImplementation = Some(AdapterImplementation("- Core", 1))
)
override def checkBankAccountExists(bankId: BankId, accountId: AccountId, callContext: Option[CallContext]): OBPReturnType[Box[BankAccount]] = {
import com.openbankproject.commons.dto.{OutBoundCheckBankAccountExists => OutBound, InBoundCheckBankAccountExists => InBound}
val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull, bankId, accountId)
val response: Future[Box[InBound]] = sendRequest[InBound]("obp_check_bank_account_exists", req, callContext)
response.map(convertToTuple[BankAccountCommons](callContext))
}
messageDocs += getCounterpartyTraitDoc
def getCounterpartyTraitDoc = MessageDoc(
process = "obp.getCounterpartyTrait",
@ -5605,8 +5649,8 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable {
response.map(convertToTuple[Boolean](callContext))
}
// ---------- create on 2020-06-18T20:03:31Z
//---------------- dynamic end ---------------------please don't modify this line
// ---------- created on 2020-06-19T17:24:19Z
//---------------- dynamic end ---------------------please don't modify this line
private val availableOperation = DynamicEntityOperation.values.map(it => s""""$it"""").mkString("[", ", ", "]")

View File

@ -86,7 +86,7 @@ trait KafkaMappedConnector_vMay2019 extends Connector with KafkaHelper with MdcL
//---------------- dynamic start -------------------please don't modify this line
// ---------- create on Mon Jan 20 22:01:14 CET 2020
// ---------- created on Mon Jan 20 22:01:14 CET 2020
messageDocs += MessageDoc(
process = s"obp.${nameOf(getAdapterInfo _)}",

View File

@ -3078,7 +3078,7 @@ trait KafkaMappedConnector_vSept2018 extends Connector with KafkaHelper with Mdc
//---------------- dynamic start -------------------please don't modify this line
// ---------- create on Mon May 13 22:38:20 CST 2019
// ---------- created on Mon May 13 22:38:20 CST 2019
messageDocs += MessageDoc(
process = "obp.createBankAccount",

View File

@ -10,11 +10,7 @@ trait KafkaConfig {
val bootstrapServers = APIUtil.getPropsValue("kafka.bootstrap_hosts")openOr("localhost:9092")
val groupId = APIUtil.getPropsValue("kafka.group.id").openOr("obp-api")
val apiInstanceId =
if (APIUtil.isSandboxMode || APIUtil.isStarConnectorButNoKafkaSupport)
APIUtil.getPropsAsIntValue("api_instance_id").openOr("{api_instance_id}")
else
APIUtil.getPropsAsIntValue("api_instance_id").openOrThrowException(s"${ErrorMessages.MissingPropsValueAtThisInstance} api_instance_id")
val apiInstanceId = APIUtil.getPropsAsIntValue("api_instance_id").openOr("1")
val partitions = APIUtil.getPropsAsIntValue("kafka.partitions", 10)
val clientId = s"obp.api.$apiInstanceId"

View File

@ -26,6 +26,7 @@ TESOBE (http://www.tesobe.com/)
*/
package code.model.dataAccess
import code.UserRefreshes.UserRefreshes
import code.accountholders.AccountHolders
import code.api.util.APIUtil.{hasAnOAuthHeader, isValidStrongPassword, _}
import code.api.util.ErrorMessages._
@ -950,8 +951,10 @@ def restoreSomeSessions(): Unit = {
(accounts, _) <- Connector.connector.vend.getBankAccountsForUser(user.name,callContext) map {
connectorEmptyResponse(_, callContext)
}
}yield
updateUserAccountViews(user, accounts)
}yield {
updateUserAccountViews(user, accounts)
UserRefreshes.UserRefreshes.vend.createOrUpdateRefreshUser(user.userId)
}
}
/**

View File

@ -0,0 +1,51 @@
package code.UserRefreshes
import java.util.{Calendar, Date}
import code.api.util.APIUtil
import code.util.UUIDString
import net.liftweb.common.Full
import net.liftweb.mapper._
import net.liftweb.util.Helpers.now
object MappedUserRefreshesProvider extends UserRefreshesProvider {
//This method will check if we need to refresh user or not..
//1st: check if last update is empty or not,
// if empty --> UserRefreshes/true
// if not empty, compare last update and the props interval-->
// --> if (lastUpdate + interval) >= current --> UserRefreshes/true
// --> if (lastUpdate + interval) < current --> false
override def needToRefreshUser(userId: String) = {
MappedUserRefreshes.find(By(MappedUserRefreshes.mUserId, userId)) match {
case Full(user) =>{
val UserRefreshesInterval = APIUtil.getPropsAsIntValue("refresh_user.interval", 30)
val lastUpdate: Date = user.updatedAt.get
val lastUpdatePlusInterval: Calendar = Calendar.getInstance()
lastUpdatePlusInterval.setTime(lastUpdate)
lastUpdatePlusInterval.add(Calendar.MINUTE, UserRefreshesInterval)
val currentDate = Calendar.getInstance()
lastUpdatePlusInterval.before(currentDate)
}
case _ => true
}
}
override def createOrUpdateRefreshUser(userId: String): MappedUserRefreshes = MappedUserRefreshes.find(By(MappedUserRefreshes.mUserId, userId)) match {
case Full(user) => user.updatedAt(now).saveMe() //if we find user, just update the datetime
case _ => MappedUserRefreshes.create.mUserId(userId).saveMe() //if can not find user, just create the new one.
}
}
class MappedUserRefreshes extends UserRefreshes with LongKeyedMapper[MappedUserRefreshes] with IdPK with CreatedUpdated {
def getSingleton = MappedUserRefreshes
object mUserId extends UUIDString(this)
override def userId: String = mUserId.get
}
object MappedUserRefreshes extends MappedUserRefreshes with LongKeyedMetaMapper[MappedUserRefreshes] {
override def dbIndexes = UniqueIndex(mUserId) :: super.dbIndexes
}

View File

@ -0,0 +1,35 @@
package code.UserRefreshes
import code.api.util.APIUtil
import net.liftweb.util.SimpleInjector
object UserRefreshes extends SimpleInjector {
val UserRefreshes = new Inject(buildOne _) {}
def buildOne: UserRefreshesProvider =
APIUtil.getPropsAsBoolValue("use_akka", false) match {
case false => MappedUserRefreshesProvider
case true => MappedUserRefreshesProvider //RemotedataScopes // We will use Akka as a middleware
}
}
//This is used to control the refresh user process.
// refresh_user.interval props will control how often to make it
trait UserRefreshes {
def userId : String
}
trait UserRefreshesProvider {
// This method will check if we need to refresh user or not..
def needToRefreshUser(userId: String):Boolean
def createOrUpdateRefreshUser(userId: String):UserRefreshes
}
class RemotedataUserRefreshesCaseClasses {
case class needToUserRefreshes(userId: String)
}
object RemotedataUserRefreshesCaseClasses extends RemotedataUserRefreshesCaseClasses

View File

@ -3,6 +3,8 @@
### Most recent changes at top of file
```
Date Commit Action
19/06/2020 ea819aab Added props: refresh user.interval. default is 30 minutes.
This props will set the interval for the internal refresh user process.
29/04/2020 75925d8c Added props: allow_pre_filled_password. in Sign Up page the default password form filed is ****
This props can set the filed to empty .
29/04/2020 1ba4f3aa Added props: webui_signup_form_submit_button_value. this will overwrite the submit button value