mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 15:06:50 +00:00
commit
32d47782d5
21
README.md
21
README.md
@ -143,6 +143,27 @@ Please use next values:
|
||||
User Name:
|
||||
Password:
|
||||
|
||||
|
||||
### Notes on the basic ussage of Postgres:
|
||||
Once postgres is installed: (On Mac use brew)
|
||||
|
||||
psql postgres
|
||||
|
||||
create database obpdb; (or any other name of your choosing)
|
||||
|
||||
create user obp; (this is the user that OBP-API will use to create and access tables etc)
|
||||
|
||||
alter user obp with password 'daniel.says'; (put this password in the OBP-API Props)
|
||||
|
||||
grant all on database obpdb to obp; (So OBP-API can create tables etc.)
|
||||
|
||||
Then set the db.url in your Props:
|
||||
|
||||
db.driver=org.postgresql.Driver
|
||||
db.url=jdbc:postgresql://localhost:5432/obpdb?user=obp&password=daniel.says
|
||||
|
||||
The restart OBP-API
|
||||
|
||||
### Notes on using Postgres with SSL:
|
||||
|
||||
Postgres needs to be compiled with SSL support.
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.2</version>
|
||||
<version>4.5.13</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
@ -324,7 +324,7 @@
|
||||
<dependency>
|
||||
<groupId>com.nimbusds</groupId>
|
||||
<artifactId>nimbus-jose-jwt</artifactId>
|
||||
<version>9.7</version>
|
||||
<version>9.9.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.OpenBankProject</groupId>
|
||||
@ -347,7 +347,7 @@
|
||||
<dependency>
|
||||
<groupId>com.nimbusds</groupId>
|
||||
<artifactId>oauth2-oidc-sdk</artifactId>
|
||||
<version>7.4</version>
|
||||
<version>9.5.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.vladsch.flexmark</groupId>
|
||||
|
||||
@ -368,8 +368,8 @@ Deleted = Deleted
|
||||
consumer.registration.nav.name=Get API Key
|
||||
invalid.login.credentials=Invalid Login Credentials
|
||||
invalid.username=Invalid Username: \
|
||||
1) Username must be between 8 and 100 characters long \
|
||||
2) Username must not start with _ or . \
|
||||
3) Username cannot contain or . or ._ or .. \
|
||||
4) Allowed characters are: a-z A-Z 0-9 . _ \
|
||||
5) Username must not end with _ or .
|
||||
1) The ONLY allowed characters in Usernames are: a-z A-Z 0-9 . _ \
|
||||
2) Usernames MUST be between 8 and 100 characters long \
|
||||
3) Usernames MUST NOT start with _ or . \
|
||||
4) Usernames MUST NOT contain __ or ._ or ._ or .. \
|
||||
5) Usernames MUST NOT end with _ or .
|
||||
@ -1,6 +1,9 @@
|
||||
#this is a sample props file you should edit and rename
|
||||
#see https://www.assembla.com/wiki/show/liftweb/Properties for all the naming options, or just use "default.props" in this same folder
|
||||
|
||||
# See notes about WebUI Props below
|
||||
|
||||
|
||||
### OBP-API configuration
|
||||
|
||||
|
||||
@ -84,7 +87,8 @@ connectorMethod.cache.ttl.seconds=40
|
||||
## swagger file should not generated for every request, this is a time-to-live in seconds for the generated swagger of OBP api,
|
||||
## this value also represent how many seconds before the new endpoints will be shown after upload a new DynamicEntity.
|
||||
## So if you want the new endpoints shown timely, set this value to a small number.
|
||||
resourceDocsObp.cache.ttl.seconds=3600
|
||||
dynamicResourceDocsObp.cache.ttl.seconds=3600
|
||||
staticResourceDocsObp.cache.ttl.seconds=86400
|
||||
|
||||
## This can change the behavior of `Get Resource Docs`/`Get API Glossary`. If we set it to `true`, OBP will check the authentication and CanReadResourceDoc/CanReadGlossary Role
|
||||
# the default value is false, so the `Get Resource Docs`/`Get API Glossary` is anonymous as default.
|
||||
@ -336,6 +340,16 @@ BankMockKey=change_me
|
||||
# Add WebUiProps
|
||||
# Delete WebUiProps
|
||||
# Get WebUiProps
|
||||
|
||||
# Please note: The non-commented-out webui_ props in this file are used to generate the default webui_ props used by the getWebUiProps endpoint which is used by API Manager -> Configurations -> WebUi Props
|
||||
# i.e. If you add a webui_ props in code but don't add it here, it will be missing from the API Manager screen where users can set it.
|
||||
|
||||
# A note about WebUi Props precedence
|
||||
# If a database WebUI Props is found it will be used first.
|
||||
# If a database WebUI Props is not found, the version in the Props file will be used.
|
||||
# Note: Props can also be loaded from the environment.
|
||||
|
||||
|
||||
####################################################################################
|
||||
|
||||
|
||||
@ -952,4 +966,98 @@ default_auth_context_update_request_key=CUSTOMER_NUMBER
|
||||
#featured_api_collection_ids=
|
||||
|
||||
# the alias prefix path for BerlinGroupV1.3 (OBP built-in is berlin-group/v1.3), the format must be xxx/yyy, eg: 0.6/v1
|
||||
#berlin_group_v1.3_alias.path=
|
||||
#berlin_group_v1.3_alias.path=
|
||||
|
||||
|
||||
# Support multiple brands on one instance. Note this needs checking on a clustered environment
|
||||
#brands_enabled=false
|
||||
|
||||
# Support removing the app type checkbox during consumer registration
|
||||
#consumer_registration.display_app_type=true
|
||||
|
||||
# if set this props, we can automatically grant the Entitlements required to use all the Dynamic Endpoint roles belonging
|
||||
# to the bank_ids (Spaces) the User has access to via their validated email domain. Entitlements are generated /refreshed
|
||||
# both following manual login and Direct Login token generation (POST).
|
||||
# the default value is empty
|
||||
#email_domain_to_space_mappings=
|
||||
# And here we provide an example to show how to prepare the mappings
|
||||
#email_domain_to_space_mappings=[ \
|
||||
# { \
|
||||
# "domain": "example.com", \
|
||||
# "bank_ids": [ \
|
||||
# "gh.29.uk", \
|
||||
# "gh.29.fr" \
|
||||
# ] \
|
||||
# }, \
|
||||
# { \
|
||||
# "domain": "example2.com", \
|
||||
# "bank_ids": [ \
|
||||
# "gh.29.uk", \
|
||||
# "gh.29.it" \
|
||||
# ] \
|
||||
# }\
|
||||
# ]\
|
||||
#
|
||||
# if set this props, we can automatically grant the Entitlements required to the User has access to via their validated email domain.
|
||||
# Entitlements are generated /refreshed both following manual login and Direct Login token generation (POST).
|
||||
# the default value is empty
|
||||
#email_domain_to_entitlement_mappings=
|
||||
# And here we provide an example to show how to prepare the mappings
|
||||
#email_domain_to_entitlement_mappings = [\
|
||||
# {\
|
||||
# "domain": "example.com",\
|
||||
# "entitlements": [\
|
||||
# {\
|
||||
# "role_name": "CanReadResourceDoc",\
|
||||
# "bank_id": ""\
|
||||
# }\
|
||||
# ]\
|
||||
# }\
|
||||
# ]\
|
||||
#
|
||||
|
||||
|
||||
# User Invitation Time To Live
|
||||
# user_invitation.ttl.seconds=86400
|
||||
|
||||
# User (Developer) Invitation
|
||||
webui_post_user_invitation_submit_button_value=Register as a Developer
|
||||
webui_post_user_invitation_privacy_conditions_value=Privacy conditions..
|
||||
webui_post_user_invitation_terms_and_conditions_value=Terms and Conditions..
|
||||
webui_post_user_invitation_terms_and_conditions_checkbox_value=I agree to the above Developer Terms and Conditions
|
||||
|
||||
webui_developer_user_invitation_email_html_text=<!DOCTYPE html>\
|
||||
<html>\
|
||||
<head>\
|
||||
<style>\
|
||||
.a {\
|
||||
border: none;\
|
||||
color: white;\
|
||||
padding: 15px 32px;\
|
||||
text-align: center;\
|
||||
text-decoration: none;\
|
||||
display: inline-block;\
|
||||
font-size: 16px;\
|
||||
margin: 4px 2px;\
|
||||
cursor: pointer;\
|
||||
}\
|
||||
\
|
||||
.a1 {background-color: #4CAF50;} /* Green */\
|
||||
.a2 {background-color: #008CBA;} /* Blue */\
|
||||
</style>\
|
||||
</head>\
|
||||
<body>\
|
||||
<img src="https://static.openbankproject.com/images/OBP_full_web_25pc.png"></img>\
|
||||
<hr></hr><br></br>\
|
||||
<p>Hi ${emailRecipient},<br></br>\
|
||||
Welcome to the Open Bank Project API. Your account has been registered. Please use the below link to activate it.</p>\
|
||||
<a href="${activateYourAccount}" class="a a1">Activate your account</a>\
|
||||
<p>Our operations team has granted you the appropriate access to the OBP-API. If you have any questions, or you need any assistance, please contact our support.</p>\
|
||||
<p>Thanks,<br></br> Your OBP API team</p><br></br>\
|
||||
<hr></hr>\
|
||||
<p>\
|
||||
Please do not reply to this email. Should you wish to contact us, please raise a ticket at our support page. We maintain strict security standards and procedures to prevent unauthorised access to information about you. We will never contact you by email or otherwise and ask you to validate personal information such as your user ID, password or account numbers. This e-mail is confidential. It may also be legally privileged. If you are not the addressee you may not copy, forward, disclose or use any part of it. If you have received this message in error, please delete it and all copies from your system. Internet communications cannot be guaranteed to be timely, secure, error or virus-free. The sender does not accept liability for any errors or omissions.\
|
||||
</p>\
|
||||
</body>\
|
||||
</html>
|
||||
|
||||
|
||||
@ -118,6 +118,7 @@ import code.transactionattribute.MappedTransactionAttribute
|
||||
import code.transactionrequests.{MappedTransactionRequest, MappedTransactionRequestTypeCharge, TransactionRequestReasons}
|
||||
import code.usercustomerlinks.MappedUserCustomerLink
|
||||
import code.userlocks.UserLocks
|
||||
import code.users.{UserAgreement, UserInvitation}
|
||||
import code.util.Helper.MdcLoggable
|
||||
import code.util.{Helper, HydraUtil}
|
||||
import code.validation.JsonSchemaValidation
|
||||
@ -128,6 +129,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.{Authenticator, PasswordAuthentication}
|
||||
import javax.mail.internet.MimeMessage
|
||||
import net.liftweb.common._
|
||||
import net.liftweb.db.DBLogEntry
|
||||
@ -297,7 +299,7 @@ class Boot extends MdcLoggable {
|
||||
case true => // DB already exist
|
||||
// Migration Scripts are used to update the model of OBP-API DB to a latest version.
|
||||
// Please note that migration scripts are executed before Lift Mapper Schemifier
|
||||
Migration.database.executeScripts()
|
||||
Migration.database.executeScripts(startedBeforeSchemifier = true)
|
||||
logger.info("The Mapper database already exits. The scripts are executed BEFORE Lift Mapper Schemifier.")
|
||||
case false => // DB is still not created. The scripts will be executed after Lift Mapper Schemifier
|
||||
logger.info("The Mapper database is still not created. The scripts are going to be executed AFTER Lift Mapper Schemifier.")
|
||||
@ -483,30 +485,31 @@ class Boot extends MdcLoggable {
|
||||
|
||||
logger.info (s"props_identifier is : ${APIUtil.getPropsValue("props_identifier", "NONE-SET")}")
|
||||
|
||||
|
||||
// Build SiteMap
|
||||
val indexPage = APIUtil.getPropsValue("server_mode", "apis,portal") match {
|
||||
case mode if mode == "portal" => List(Menu.i("Home") / "index")
|
||||
case mode if mode == "apis" => List()
|
||||
case mode if mode.contains("apis") && mode.contains("portal") => List(Menu.i("Home") / "index")
|
||||
case _ => List(Menu.i("Home") / "index")
|
||||
}
|
||||
val sitemap = indexPage ::: List(
|
||||
Menu.i("Plain") / "plain",
|
||||
Menu.i("Consumer Admin") / "admin" / "consumers" >> Admin.loginFirst >> LocGroup("admin")
|
||||
submenus(Consumer.menus : _*),
|
||||
Menu("Consumer Registration", Helper.i18n("consumer.registration.nav.name")) / "consumer-registration" >> AuthUser.loginFirst,
|
||||
Menu("Dummy user tokens", "Get Dummy user tokens") / "dummy-user-tokens" >> AuthUser.loginFirst,
|
||||
|
||||
Menu("Validate OTP", "Validate OTP") / "otp" >> AuthUser.loginFirst,
|
||||
// Menu.i("Metrics") / "metrics", //TODO: allow this page once we can make the account number anonymous in the URL
|
||||
Menu.i("OAuth") / "oauth" / "authorize", //OAuth authorization page
|
||||
Menu.i("Consent") / "consent" >> AuthUser.loginFirst,//OAuth consent page
|
||||
OAuthWorkedThanks.menu, //OAuth thanks page that will do the redirect
|
||||
Menu.i("INTRODUCTION") / "introduction",
|
||||
Menu.i("add-user-auth-context-update-request") / "add-user-auth-context-update-request",
|
||||
Menu.i("confirm-user-auth-context-update-request") / "confirm-user-auth-context-update-request"
|
||||
val commonMap = List(Menu.i("Home") / "index") ::: List(
|
||||
Menu.i("Plain") / "plain",
|
||||
Menu.i("Consumer Admin") / "admin" / "consumers" >> Admin.loginFirst >> LocGroup("admin")
|
||||
submenus(Consumer.menus : _*),
|
||||
Menu("Consumer Registration", Helper.i18n("consumer.registration.nav.name")) / "consumer-registration" >> AuthUser.loginFirst,
|
||||
Menu("Dummy user tokens", "Get Dummy user tokens") / "dummy-user-tokens" >> AuthUser.loginFirst,
|
||||
|
||||
Menu("Validate OTP", "Validate OTP") / "otp" >> AuthUser.loginFirst,
|
||||
Menu("User Invitation", "User Invitation") / "user-invitation",
|
||||
// Menu.i("Metrics") / "metrics", //TODO: allow this page once we can make the account number anonymous in the URL
|
||||
Menu.i("OAuth") / "oauth" / "authorize", //OAuth authorization page
|
||||
Menu.i("Consent") / "consent" >> AuthUser.loginFirst,//OAuth consent page
|
||||
OAuthWorkedThanks.menu, //OAuth thanks page that will do the redirect
|
||||
Menu.i("Introduction") / "introduction",
|
||||
Menu.i("add-user-auth-context-update-request") / "add-user-auth-context-update-request",
|
||||
Menu.i("confirm-user-auth-context-update-request") / "confirm-user-auth-context-update-request"
|
||||
) ++ accountCreation ++ Admin.menus
|
||||
|
||||
// Build SiteMap
|
||||
val sitemap = APIUtil.getPropsValue("server_mode", "apis,portal") match {
|
||||
case mode if mode == "portal" => commonMap
|
||||
case mode if mode == "apis" => List()
|
||||
case mode if mode.contains("apis") && mode.contains("portal") => commonMap
|
||||
case _ => commonMap
|
||||
}
|
||||
|
||||
def sitemapMutators = AuthUser.sitemapMutator
|
||||
|
||||
@ -569,6 +572,13 @@ class Boot extends MdcLoggable {
|
||||
|
||||
implicit val formats = CustomJsonFormats.formats
|
||||
LiftRules.exceptionHandler.prepend{
|
||||
case(_, r, e) if DB.use(DefaultConnectionIdentifier){ conn => conn}.isClosed => {
|
||||
logger.error("Exception being returned to browser when processing " + r.uri.toString, e)
|
||||
JsonResponse(
|
||||
Extraction.decompose(ErrorMessage(code = 500, message = s"${ErrorMessages.DatabaseConnectionClosedError}")),
|
||||
500
|
||||
)
|
||||
}
|
||||
case(Props.RunModes.Development, r, e) => {
|
||||
logger.error("Exception being returned to browser when processing " + r.uri.toString, e)
|
||||
JsonResponse(
|
||||
@ -615,7 +625,7 @@ class Boot extends MdcLoggable {
|
||||
|
||||
// Migration Scripts are used to update the model of OBP-API DB to a latest version.
|
||||
// Please note that migration scripts are executed after Lift Mapper Schemifier
|
||||
Migration.database.executeScripts()
|
||||
Migration.database.executeScripts(startedBeforeSchemifier = false)
|
||||
|
||||
// export one Connector's methods as endpoints, it is just for develop
|
||||
APIUtil.getPropsValue("connector.name.export.as.endpoints").foreach { connectorName =>
|
||||
@ -821,6 +831,8 @@ object ToSchemify {
|
||||
AccountAccess,
|
||||
ViewDefinition,
|
||||
ResourceUser,
|
||||
UserInvitation,
|
||||
UserAgreement,
|
||||
MappedComment,
|
||||
MappedTag,
|
||||
MappedWhereTag,
|
||||
|
||||
@ -105,10 +105,14 @@ object GatewayLogin extends RestHelper with MdcLoggable {
|
||||
|
||||
def parseJwt(parameters: Map[String, String]): Box[String] = {
|
||||
val jwt = getToken(parameters)
|
||||
logger.debug("parseJwt says jwt.toString is: " + jwt.toString)
|
||||
logger.debug("parseJwt says: validateJwtToken(jwt) is:" + validateJwtToken(jwt))
|
||||
validateJwtToken(jwt) match {
|
||||
case Full(jwtPayload) =>
|
||||
logger.debug("parseJwt says: Full: " + jwtPayload.toString)
|
||||
Full(compactRender(Extraction.decompose(jwtPayload)))
|
||||
case _ =>
|
||||
logger.debug("parseJwt says: Not Full(jwtPayload)")
|
||||
Failure(ErrorMessages.GatewayLoginJwtTokenIsNotValid)
|
||||
}
|
||||
}
|
||||
@ -119,11 +123,16 @@ object GatewayLogin extends RestHelper with MdcLoggable {
|
||||
val claim = CertificateUtil.decryptJwtWithRsa(token)
|
||||
Box(parse(claim.toString).extractOpt[PayloadOfJwtJSON])
|
||||
case false =>
|
||||
logger.debug("validateJwtToken says: verifying jwt token: " + token)
|
||||
logger.debug(CertificateUtil.verifywtWithHmacProtection(token).toString)
|
||||
CertificateUtil.verifywtWithHmacProtection(token) match {
|
||||
case true =>
|
||||
logger.debug("validateJwtToken says: jwt is verified: " + token)
|
||||
val claim = CertificateUtil.parseJwtWithHmacProtection(token)
|
||||
logger.debug("validateJwtToken says: this is claim of verified jwt: " + claim.toString())
|
||||
Box(parse(claim.toString).extractOpt[PayloadOfJwtJSON])
|
||||
case _ =>
|
||||
logger.debug("validateJwtToken says: could not verify jwt")
|
||||
Failure(ErrorMessages.GatewayLoginJwtTokenIsNotValid)
|
||||
}
|
||||
}
|
||||
@ -251,7 +260,9 @@ object GatewayLogin extends RestHelper with MdcLoggable {
|
||||
None,
|
||||
name = Some(username),
|
||||
email = None,
|
||||
userId = None
|
||||
userId = None,
|
||||
createdByUserInvitationId = None,
|
||||
company = None
|
||||
)
|
||||
} match {
|
||||
case Full(u) =>
|
||||
@ -418,7 +429,9 @@ object GatewayLogin extends RestHelper with MdcLoggable {
|
||||
}
|
||||
|
||||
private def getToken(params: Map[String, String]): String = {
|
||||
logger.debug("getToken params are: " + params.toString())
|
||||
val token = params.getOrElse("token", "")
|
||||
logger.debug("getToken wants to return token: " + token)
|
||||
token
|
||||
}
|
||||
|
||||
|
||||
@ -154,6 +154,8 @@ object OAuth2Login extends RestHelper with MdcLoggable {
|
||||
hydraAdmin.deleteOAuth2Client(clientId)
|
||||
hydraAdmin.createOAuth2Client(oAuth2Client)
|
||||
} else if(stringNotEq(certInConsumer, cert)) {
|
||||
logger.debug("Cert in Consumer: " + certInConsumer)
|
||||
logger.debug("Cert in Request: " + cert)
|
||||
return (Failure(Oauth2TokenMatchCertificateFail), Some(cc.copy(consumer = Failure(Oauth2TokenMatchCertificateFail))))
|
||||
}
|
||||
}
|
||||
@ -279,7 +281,9 @@ object OAuth2Login extends RestHelper with MdcLoggable {
|
||||
None,
|
||||
name = getClaim(name = "given_name", idToken = idToken).orElse(Some(subject)),
|
||||
email = getClaim(name = "email", idToken = idToken),
|
||||
userId = None
|
||||
userId = None,
|
||||
createdByUserInvitationId = None,
|
||||
company = None
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -262,6 +262,7 @@ trait OBPRestHelper extends RestHelper with MdcLoggable {
|
||||
|
||||
def failIfBadAuthorizationHeader(rd: Option[ResourceDoc])(fn: CallContext => Box[JsonResponse]) : JsonResponse = {
|
||||
val authorization = S.request.map(_.header("Authorization")).flatten
|
||||
val directLogin: Box[String] = S.request.map(_.header("DirectLogin")).flatten
|
||||
val body: Box[String] = getRequestBody(S.request)
|
||||
val implementedInVersion = S.request.openOrThrowException(attemptedToOpenAnEmptyBox).view
|
||||
val verb = S.request.openOrThrowException(attemptedToOpenAnEmptyBox).requestType.method
|
||||
@ -315,7 +316,9 @@ trait OBPRestHelper extends RestHelper with MdcLoggable {
|
||||
case Failure(msg, t, c) => Failure(msg, t, c)
|
||||
case _ => Failure("oauth error")
|
||||
}
|
||||
} else if (APIUtil.getPropsAsBoolValue("allow_direct_login", true) && hasDirectLoginHeader(authorization)) {
|
||||
}
|
||||
// Direct Login Deprecated i.e Authorization: DirectLogin token=eyJhbGciOiJIUzI1NiJ9.eyIiOiIifQ.Y0jk1EQGB4XgdqmYZUHT6potmH3mKj5mEaA9qrIXXWQ
|
||||
else if (APIUtil.getPropsAsBoolValue("allow_direct_login", true) && directLogin.isDefined) {
|
||||
DirectLogin.getUser match {
|
||||
case Full(u) => {
|
||||
val consumer = DirectLogin.getConsumer
|
||||
@ -326,7 +329,21 @@ trait OBPRestHelper extends RestHelper with MdcLoggable {
|
||||
Full(errorJsonResponse(message, httpCode))
|
||||
}
|
||||
}
|
||||
} else if (APIUtil.getPropsAsBoolValue("allow_gateway_login", false) && hasGatewayHeader(authorization)) {
|
||||
}
|
||||
// Direct Login i.e DirectLogin: token=eyJhbGciOiJIUzI1NiJ9.eyIiOiIifQ.Y0jk1EQGB4XgdqmYZUHT6potmH3mKj5mEaA9qrIXXWQ
|
||||
else if (APIUtil.getPropsAsBoolValue("allow_direct_login", true) && hasDirectLoginHeader(authorization)) {
|
||||
DirectLogin.getUser match {
|
||||
case Full(u) => {
|
||||
val consumer = DirectLogin.getConsumer
|
||||
fn(cc.copy(user = Full(u), consumer=consumer))
|
||||
}// Authentication is successful
|
||||
case _ => {
|
||||
var (httpCode, message, directLoginParameters) = DirectLogin.validator("protectedResource")
|
||||
Full(errorJsonResponse(message, httpCode))
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (APIUtil.getPropsAsBoolValue("allow_gateway_login", false) && hasGatewayHeader(authorization)) {
|
||||
logger.info("allow_gateway_login-getRemoteIpAddress: " + remoteIpAddress )
|
||||
APIUtil.getPropsValue("gateway.host") match {
|
||||
case Full(h) if h.split(",").toList.exists(_.equalsIgnoreCase(remoteIpAddress) == true) => // Only addresses from white list can use this feature
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
package code.api.ResourceDocs1_4_0
|
||||
|
||||
import java.util.UUID.randomUUID
|
||||
|
||||
import code.api.OBPRestHelper
|
||||
import code.api.builder.OBP_APIBuilder
|
||||
import code.api.cache.Caching
|
||||
import code.api.util.APIUtil._
|
||||
import code.api.util.ApiTag._
|
||||
import code.api.util.ExampleValue.endpointMappingRequestBodyExample
|
||||
import code.api.util.{APIUtil, _}
|
||||
import code.api.v1_4_0.JSONFactory1_4_0.ResourceDocsJson
|
||||
import code.api.v1_4_0.{APIMethods140, JSONFactory1_4_0, OBPAPI1_4_0}
|
||||
@ -17,7 +19,7 @@ import code.api.v4_0_0.{APIMethods400, OBPAPI4_0_0}
|
||||
import code.apicollectionendpoint.MappedApiCollectionEndpointsProvider
|
||||
import code.util.Helper.MdcLoggable
|
||||
import com.github.dwickern.macros.NameOf.nameOf
|
||||
import com.openbankproject.commons.model.ListResult
|
||||
import com.openbankproject.commons.model.{ListResult, User}
|
||||
import com.openbankproject.commons.model.enums.ContentParam.{ALL, DYNAMIC, STATIC}
|
||||
import com.openbankproject.commons.model.enums.LanguageParam._
|
||||
import com.openbankproject.commons.model.enums.{ContentParam, LanguageParam}
|
||||
@ -31,9 +33,13 @@ import net.liftweb.json.JsonAST.{JField, JString, JValue}
|
||||
import net.liftweb.json._
|
||||
import net.liftweb.util.Helpers.tryo
|
||||
import net.liftweb.util.Props
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
import code.api.util.NewStyle.HttpCode
|
||||
import code.util.Helper
|
||||
|
||||
import scala.collection.immutable.{List, Nil}
|
||||
import scala.concurrent.Future
|
||||
|
||||
// JObject creation
|
||||
import code.api.v1_2_1.{APIInfoJSON, APIMethods121, HostedBy, OBPAPI1_2_1}
|
||||
@ -49,6 +55,8 @@ import code.util.Helper.booleanToBox
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import com.openbankproject.commons.ExecutionContext.Implicits.global
|
||||
|
||||
|
||||
trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMethods210 with APIMethods200 with APIMethods140 with APIMethods130 with APIMethods121{
|
||||
//needs to be a RestHelper to get access to JsonGet, JsonPost, etc.
|
||||
@ -203,7 +211,8 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
|
||||
//implicit val scalaCache = ScalaCache(GuavaCache(underlyingGuavaCache))
|
||||
// if upload DynamicEntity, will generate corresponding endpoints, when current cache timeout, the new endpoints will be shown.
|
||||
// so if you want the new generated endpoints shown timely, set this value to a small number, or set to a big number
|
||||
val getResourceDocsTTL : Int = APIUtil.getPropsValue(s"resourceDocsObp.cache.ttl.seconds", "3600").toInt
|
||||
val getDynamicResourceDocsTTL : Int = APIUtil.getPropsValue(s"dynamicResourceDocsObp.cache.ttl.seconds", "0").toInt
|
||||
val getStaticResourceDocsTTL : Int = APIUtil.getPropsValue(s"staticResourceDocsObp.cache.ttl.seconds", "0").toInt
|
||||
|
||||
/**
|
||||
*
|
||||
@ -213,7 +222,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
|
||||
* @param contentParam if this is Some(`true`), only show dynamic endpoints, if Some(`false`), only show static. If it is None, we will show all. default is None
|
||||
* @return
|
||||
*/
|
||||
private def getResourceDocsObpCached(requestedApiVersion : ApiVersion,
|
||||
private def getStaticResourceDocsObpCached(requestedApiVersion : ApiVersion,
|
||||
resourceDocTags: Option[List[ResourceDocTag]],
|
||||
partialFunctionNames: Option[List[String]]
|
||||
) : Box[JValue] = {
|
||||
@ -225,7 +234,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
|
||||
*/
|
||||
var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString)
|
||||
CacheKeyFromArguments.buildCacheKey {
|
||||
Caching.memoizeSyncWithProvider (Some(cacheKey.toString())) (getResourceDocsTTL second) {
|
||||
Caching.memoizeSyncWithProvider (Some(cacheKey.toString())) (getStaticResourceDocsTTL second) {
|
||||
logger.debug(s"Generating OBP Resource Docs requestedApiVersion is $requestedApiVersion")
|
||||
|
||||
val resourceDocJson = resourceDocsToResourceDocJson(getResourceDocsList(requestedApiVersion), resourceDocTags, partialFunctionNames)
|
||||
@ -234,39 +243,99 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
|
||||
}
|
||||
}
|
||||
|
||||
private def getResourceDocsObpDynamic(requestedApiVersion : ApiVersion,
|
||||
/**
|
||||
*
|
||||
* @param requestedApiVersion
|
||||
* @param resourceDocTags
|
||||
* @param partialFunctionNames
|
||||
* @param contentParam if this is Some(`true`), only show dynamic endpoints, if Some(`false`), only show static. If it is None, we will show all. default is None
|
||||
* @return
|
||||
*/
|
||||
private def getAllResourceDocsObpCached(requestedApiVersion : ApiVersion,
|
||||
resourceDocTags: Option[List[ResourceDocTag]],
|
||||
partialFunctionNames: Option[List[String]]
|
||||
) : Box[JValue] = {
|
||||
/**
|
||||
* Please note that "var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString)"
|
||||
* is just a temporary value field with UUID values in order to prevent any ambiguity.
|
||||
* The real value will be assigned by Macro during compile time at this line of a code:
|
||||
* https://github.com/OpenBankProject/scala-macros/blob/master/macros/src/main/scala/com/tesobe/CacheKeyFromArgumentsMacro.scala#L49
|
||||
*/
|
||||
var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString)
|
||||
CacheKeyFromArguments.buildCacheKey {
|
||||
Caching.memoizeSyncWithProvider (Some(cacheKey.toString())) (getStaticResourceDocsTTL second) {
|
||||
val dynamicDocs = (DynamicEntityHelper.doc ++ DynamicEndpointHelper.doc ++ DynamicEndpoints.dynamicResourceDocs)
|
||||
.filter(rd => rd.implementedInApiVersion == requestedApiVersion)
|
||||
.map(it => it.specifiedUrl match {
|
||||
case Some(_) => it
|
||||
case _ =>
|
||||
it.specifiedUrl = Some(s"/${it.implementedInApiVersion.urlPrefix}/${requestedApiVersion.vDottedApiVersion}${it.requestUrl}")
|
||||
it
|
||||
})
|
||||
.toList
|
||||
|
||||
val filteredDocs = resourceDocTags match {
|
||||
// We have tags
|
||||
case Some(tags) => {
|
||||
// This can create duplicates to use toSet below
|
||||
for {
|
||||
r <- dynamicDocs
|
||||
t <- tags
|
||||
if r.tags.contains(t)
|
||||
} yield {
|
||||
r
|
||||
}
|
||||
}
|
||||
// tags param was not mentioned in url or was empty, so return all
|
||||
case None => dynamicDocs
|
||||
}
|
||||
|
||||
val staticDocs = getResourceDocsList(requestedApiVersion)
|
||||
|
||||
val allDocs = staticDocs.map( _ ++ filteredDocs)
|
||||
|
||||
val resourceDocJson = resourceDocsToResourceDocJson(allDocs, resourceDocTags, partialFunctionNames)
|
||||
resourceDocJson.map(resourceDocsJsonToJsonResponse)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def getResourceDocsObpDynamicCached(requestedApiVersion : ApiVersion,
|
||||
resourceDocTags: Option[List[ResourceDocTag]],
|
||||
partialFunctionNames: Option[List[String]]
|
||||
): Option[JValue] = {
|
||||
val dynamicDocs = (DynamicEntityHelper.doc ++ DynamicEndpointHelper.doc ++ DynamicEndpoints.dynamicResourceDocs)
|
||||
.filter(rd => rd.implementedInApiVersion == requestedApiVersion)
|
||||
.map(it => it.specifiedUrl match {
|
||||
case Some(_) => it
|
||||
case _ =>
|
||||
it.specifiedUrl = Some(s"/${it.implementedInApiVersion.urlPrefix}/${requestedApiVersion.vDottedApiVersion}${it.requestUrl}")
|
||||
it
|
||||
})
|
||||
.toList
|
||||
|
||||
val filteredDocs = resourceDocTags match {
|
||||
// We have tags
|
||||
case Some(tags) => {
|
||||
// This can create duplicates to use toSet below
|
||||
for {
|
||||
r <- dynamicDocs
|
||||
t <- tags
|
||||
if r.tags.contains(t)
|
||||
} yield {
|
||||
r
|
||||
var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString)
|
||||
CacheKeyFromArguments.buildCacheKey {
|
||||
Caching.memoizeSyncWithProvider (Some(cacheKey.toString())) (getDynamicResourceDocsTTL second) {
|
||||
val dynamicDocs = (DynamicEntityHelper.doc ++ DynamicEndpointHelper.doc ++ DynamicEndpoints.dynamicResourceDocs)
|
||||
.filter(rd => rd.implementedInApiVersion == requestedApiVersion)
|
||||
.map(it => it.specifiedUrl match {
|
||||
case Some(_) => it
|
||||
case _ =>
|
||||
it.specifiedUrl = Some(s"/${it.implementedInApiVersion.urlPrefix}/${requestedApiVersion.vDottedApiVersion}${it.requestUrl}")
|
||||
it
|
||||
})
|
||||
.toList
|
||||
|
||||
val filteredDocs = resourceDocTags match {
|
||||
// We have tags
|
||||
case Some(tags) => {
|
||||
// This can create duplicates to use toSet below
|
||||
for {
|
||||
r <- dynamicDocs
|
||||
t <- tags
|
||||
if r.tags.contains(t)
|
||||
} yield {
|
||||
r
|
||||
}
|
||||
}
|
||||
// tags param was not mentioned in url or was empty, so return all
|
||||
case None => dynamicDocs
|
||||
}
|
||||
}
|
||||
// tags param was not mentioned in url or was empty, so return all
|
||||
case None => dynamicDocs
|
||||
}
|
||||
|
||||
val resourceDocJson = resourceDocsToResourceDocJson(Some(filteredDocs), resourceDocTags, partialFunctionNames)
|
||||
resourceDocJson.map(resourceDocsJsonToJsonResponse)
|
||||
}
|
||||
|
||||
val resourceDocJson = resourceDocsToResourceDocJson(Some(filteredDocs), resourceDocTags, partialFunctionNames)
|
||||
resourceDocJson.map(resourceDocsJsonToJsonResponse)
|
||||
}}}
|
||||
|
||||
|
||||
|
||||
@ -406,54 +475,45 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
|
||||
// Note: description uses html markup because original markdown doesn't easily support "_" and there are multiple versions of markdown.
|
||||
def getResourceDocsObp : OBPEndpoint = {
|
||||
case "resource-docs" :: requestedApiVersionString :: "obp" :: Nil JsonGet _ => {
|
||||
cc =>{
|
||||
val (tags, partialFunctions, languageParam, contentParam, apiCollectionIdParam) = ResourceDocsAPIMethodsUtil.getParams()
|
||||
cc =>
|
||||
for {
|
||||
_ <- if (resourceDocsRequireRole)//If set resource_docs_requires_role=true, we need check the authentication and the roles
|
||||
for{
|
||||
u <- cc.user ?~ UserNotLoggedIn
|
||||
hasCanReadResourceDocRole <- NewStyle.function.ownEntitlement("", u.userId, ApiRole.canReadResourceDoc, cc.callContext)
|
||||
} yield{
|
||||
hasCanReadResourceDocRole
|
||||
}
|
||||
else
|
||||
Full()//If set resource_docs_requires_role=false, just return the response directly..
|
||||
|
||||
(tags, partialFunctions, languageParam, contentParam, apiCollectionIdParam) <- Full(ResourceDocsAPIMethodsUtil.getParams())
|
||||
requestedApiVersion <- tryo {ApiVersionUtils.valueOf(requestedApiVersionString)} ?~! s"$InvalidApiVersionString $requestedApiVersionString"
|
||||
_ <- booleanToBox(versionIsAllowed(requestedApiVersion), s"$ApiVersionNotSupported $requestedApiVersionString")
|
||||
(u: Box[User], callContext: Option[CallContext]) <- resourceDocsRequireRole match {
|
||||
case false => anonymousAccess(cc)
|
||||
case true => authenticatedAccess(cc) // If set resource_docs_requires_role=true, we need check the authentication
|
||||
}
|
||||
_ <- resourceDocsRequireRole match {
|
||||
case false => Future()
|
||||
case true => // If set resource_docs_requires_role=true, we need check the the roles as well
|
||||
Future(NewStyle.function.ownEntitlement("", u.map(_.userId).getOrElse(""), ApiRole.canReadResourceDoc, cc.callContext))
|
||||
}
|
||||
requestedApiVersion <- NewStyle.function.tryons(s"$InvalidApiVersionString $requestedApiVersionString", 400, callContext) {ApiVersionUtils.valueOf(requestedApiVersionString)}
|
||||
_ <- Helper.booleanToFuture(s"$ApiVersionNotSupported $requestedApiVersionString", 400, callContext)(versionIsAllowed(requestedApiVersion))
|
||||
json <- languageParam match {
|
||||
case Some(ZH) => getChineseVersionResourceDocs
|
||||
case Some(ZH) => Future(getChineseVersionResourceDocs)
|
||||
case _ if(apiCollectionIdParam.isDefined) =>
|
||||
val operationIds = MappedApiCollectionEndpointsProvider.getApiCollectionEndpoints(apiCollectionIdParam.getOrElse("")).map(_.operationId).map(getObpFormatOperationId)
|
||||
val resourceDocs = ResourceDoc.getResourceDocs(operationIds)
|
||||
val resourceDocsJson = JSONFactory1_4_0.createResourceDocsJson(resourceDocs)
|
||||
val resourceDocsJsonJValue = Full(resourceDocsJsonToJsonResponse(resourceDocsJson))
|
||||
resourceDocsJsonJValue.map(successJsonResponse(_))
|
||||
Future(resourceDocsJsonJValue.map(successJsonResponse(_)))
|
||||
case _ =>
|
||||
contentParam match {
|
||||
case Some(DYNAMIC) =>
|
||||
val dynamicDocs: Box[JValue] = getResourceDocsObpDynamic(requestedApiVersion, tags, partialFunctions)
|
||||
dynamicDocs.map(successJsonResponse(_))
|
||||
val dynamicDocs: Box[JValue] = getResourceDocsObpDynamicCached(requestedApiVersion, tags, partialFunctions)
|
||||
Future(dynamicDocs.map(successJsonResponse(_)))
|
||||
case Some(STATIC) =>
|
||||
val staticDocs: Box[JValue] = getResourceDocsObpCached(requestedApiVersion, tags, partialFunctions)
|
||||
staticDocs.map(successJsonResponse(_))
|
||||
val staticDocs: Box[JValue] = getStaticResourceDocsObpCached(requestedApiVersion, tags, partialFunctions)
|
||||
Future(staticDocs.map(successJsonResponse(_)))
|
||||
case _ =>
|
||||
val dynamicDocs: Box[JValue] = getResourceDocsObpDynamic(requestedApiVersion, tags, partialFunctions)
|
||||
val staticDocs: Box[JValue] = getResourceDocsObpCached(requestedApiVersion, tags, partialFunctions)
|
||||
|
||||
for {
|
||||
dDocs <- dynamicDocs
|
||||
sDocs <- staticDocs
|
||||
} yield {
|
||||
val mergedJson = dDocs.merge(sDocs)
|
||||
successJsonResponse(mergedJson)
|
||||
}
|
||||
val docs: Box[JValue] = getAllResourceDocsObpCached(requestedApiVersion, tags, partialFunctions)
|
||||
Future(docs.map(successJsonResponse(_)))
|
||||
}
|
||||
}
|
||||
} yield {
|
||||
json
|
||||
(json, HttpCode.`200`(callContext))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -519,7 +579,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
|
||||
*/
|
||||
var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString)
|
||||
CacheKeyFromArguments.buildCacheKey {
|
||||
Caching.memoizeSyncWithProvider (Some(cacheKey.toString())) (getResourceDocsTTL second) {
|
||||
Caching.memoizeSyncWithProvider (Some(cacheKey.toString())) (getStaticResourceDocsTTL second) {
|
||||
logger.debug(s"Generating Swagger requestedApiVersion is $requestedApiVersionString")
|
||||
|
||||
Box.tryo(ApiVersionUtils.valueOf(requestedApiVersionString)) match {
|
||||
@ -582,16 +642,26 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
|
||||
* dynamic endpoints related structure is not STABLE structure, no need be parsed to a static structure.
|
||||
* So here filter out them.
|
||||
*/
|
||||
case doc if doc.partialFunctionName == nameOf(APIMethods400.Implementations4_0_0.createDynamicEndpoint) =>
|
||||
case doc if (doc.partialFunctionName == nameOf(APIMethods400.Implementations4_0_0.createDynamicEndpoint) ||
|
||||
doc.partialFunctionName == nameOf(APIMethods400.Implementations4_0_0.createBankLevelDynamicEndpoint) ) =>
|
||||
doc.copy(exampleRequestBody = ExampleValue.dynamicEndpointRequestBodyEmptyExample,
|
||||
successResponseBody = ExampleValue.dynamicEndpointResponseBodyEmptyExample
|
||||
)
|
||||
|
||||
case doc if doc.partialFunctionName == nameOf(APIMethods400.Implementations4_0_0.getDynamicEndpoint) =>
|
||||
case doc if (doc.partialFunctionName == nameOf(APIMethods400.Implementations4_0_0.createEndpointMapping) ||
|
||||
doc.partialFunctionName == nameOf(APIMethods400.Implementations4_0_0.createBankLevelEndpointMapping) ) =>
|
||||
doc.copy(
|
||||
exampleRequestBody = endpointMappingRequestBodyExample,
|
||||
successResponseBody = endpointMappingRequestBodyExample
|
||||
)
|
||||
|
||||
case doc if ( doc.partialFunctionName == nameOf(APIMethods400.Implementations4_0_0.getDynamicEndpoint) ||
|
||||
doc.partialFunctionName == nameOf(APIMethods400.Implementations4_0_0.getBankLevelDynamicEndpoint)) =>
|
||||
doc.copy(successResponseBody = ExampleValue.dynamicEndpointResponseBodyEmptyExample)
|
||||
|
||||
case doc if (doc.partialFunctionName == nameOf(APIMethods400.Implementations4_0_0.getDynamicEndpoints) ||
|
||||
doc.partialFunctionName == nameOf(APIMethods400.Implementations4_0_0.getMyDynamicEndpoints)
|
||||
doc.partialFunctionName == nameOf(APIMethods400.Implementations4_0_0.getMyDynamicEndpoints) ||
|
||||
doc.partialFunctionName == nameOf(APIMethods400.Implementations4_0_0.getBankLevelDynamicEndpoints)
|
||||
)=>
|
||||
doc.copy(successResponseBody = ListResult(
|
||||
"dynamic_endpoints",
|
||||
|
||||
@ -1691,6 +1691,27 @@ object SwaggerDefinitionsJSON {
|
||||
username = usernameExample.value,
|
||||
entitlements = entitlementJSONs
|
||||
)
|
||||
val userIdJsonV400 = UserIdJsonV400(
|
||||
user_id = ExampleValue.userIdExample.value
|
||||
)
|
||||
|
||||
val userInvitationPostJsonV400 = PostUserInvitationJsonV400(
|
||||
first_name = ExampleValue.nameExample.value,
|
||||
last_name = ExampleValue.nameExample.value,
|
||||
email = ExampleValue.emailExample.value,
|
||||
company = "Tesobe",
|
||||
country = "Germany",
|
||||
purpose = "Developer"
|
||||
)
|
||||
val userInvitationJsonV400 = UserInvitationJsonV400(
|
||||
first_name = ExampleValue.nameExample.value,
|
||||
last_name = ExampleValue.nameExample.value,
|
||||
email = ExampleValue.emailExample.value,
|
||||
company = "TESOBE",
|
||||
country = "Germany",
|
||||
purpose = "Developer",
|
||||
status = "CREATED"
|
||||
)
|
||||
|
||||
val entitlementRequestJSON =
|
||||
code.api.v3_0_0.EntitlementRequestJSON(
|
||||
@ -2290,6 +2311,24 @@ object SwaggerDefinitionsJSON {
|
||||
created = DateWithDayExampleObject
|
||||
)
|
||||
|
||||
val consumerJsonV400 = ConsumerJson(
|
||||
consumer_id = ExampleValue.consumerIdExample.value,
|
||||
key = ExampleValue.consumerSecretExample.value,
|
||||
secret = ExampleValue.consumerKeyExample.value,
|
||||
app_name = "SOFI",
|
||||
app_type = "Web",
|
||||
description = "Account Management",
|
||||
client_certificate = """-----BEGIN CERTIFICATE-----
|
||||
|client_certificate_content
|
||||
|-----END CERTIFICATE-----""".stripMargin,
|
||||
developer_email = ExampleValue.emailExample.value,
|
||||
redirect_url = "www.openbankproject.com",
|
||||
created_by_user_id = ExampleValue.userIdExample.value,
|
||||
created_by_user = resourceUserJSON,
|
||||
enabled = true,
|
||||
created = DateWithDayExampleObject
|
||||
)
|
||||
|
||||
val consumersJson310 = ConsumersJsonV310(
|
||||
List(consumerJsonV310)
|
||||
)
|
||||
@ -4136,13 +4175,6 @@ object SwaggerDefinitionsJSON {
|
||||
|
||||
val jsonCodeTemplate = "code" -> URLEncoder.encode("""println("hello")""", "UTF-8")
|
||||
|
||||
val endpointMappingJson = EndpointMappingCommons(
|
||||
Some("b4e0352a-9a0f-4bfa-b30b-9003aa467f50"),
|
||||
"OBPv4.0.0-dynamicEndpoint_GET_pet_PET_ID",
|
||||
"""{}""".stripMargin,
|
||||
"""{}""".stripMargin
|
||||
)
|
||||
|
||||
val supportedCurrenciesJson = SupportedCurrenciesJson(
|
||||
supportedCurrenciesExample.value
|
||||
.replaceAll(""""""","").replace("""[""","")
|
||||
|
||||
@ -491,13 +491,7 @@ object SwaggerJSONFactory extends MdcLoggable {
|
||||
tags = rd.tags.map(_.tag),
|
||||
summary = rd.summary,
|
||||
description = PegdownOptions.convertPegdownToHtmlTweaked(rd.description.stripMargin).replaceAll("\n", ""),
|
||||
operationId =
|
||||
rd.partialFunctionName match {
|
||||
//No longer need this special case since all transaction request Resource Docs have explicit URL
|
||||
//case "createTransactionRequest" => s"${rd.apiVersion.toString }-${rd.apiFunction.toString}-${UUID.randomUUID().toString}"
|
||||
// Note: The operationId should not start with a number becuase Javascript constructors may use it to build variables.
|
||||
case _ => s"${rd.implementedInApiVersion.fullyQualifiedVersion }-${rd.partialFunctionName.toString }"
|
||||
},
|
||||
operationId =s"${rd.implementedInApiVersion.fullyQualifiedVersion }-${rd.partialFunctionName.toString }",
|
||||
parameters ={
|
||||
val description = rd.exampleRequestBody match {
|
||||
case EmptyBody => ""
|
||||
|
||||
@ -260,33 +260,35 @@ of the PSU at this ASPSP.
|
||||
""",
|
||||
emptyObjectJson,
|
||||
json.parse("""{
|
||||
| "accounts":[{
|
||||
| "resourceId":"8ca8a7e4-6d02-40e3-a129-0b2bf89de9f0",
|
||||
| "iban":"DE91 1000 0000 0123 4567 89",
|
||||
| "bban":" 1000 0000 0123 4567 89",
|
||||
| "currency":"EUR",
|
||||
| "name":"TOM",
|
||||
| "product":"AC",
|
||||
| "cashAccountType":"AC",
|
||||
| "bic":"AAAADEBBXXX",
|
||||
| "balances":{
|
||||
| "balanceAmount":{
|
||||
| "currency":"EUR",
|
||||
| "amount":"50.89"
|
||||
| },
|
||||
| "balanceType":"AC",
|
||||
| "lastChangeDateTime":"2020-07-02T10:23:57.81Z",
|
||||
| "referenceDate":"2020-07-02",
|
||||
| "lastCommittedTransaction":"entryReference of the last commited transaction to support the TPP in identifying whether all PSU transactions are already known."
|
||||
| "accounts": [
|
||||
| {
|
||||
| "resourceId": "3dc3d5b3-7023-4848-9853-f5400a64e80f",
|
||||
| "iban": "DE2310010010123456789",
|
||||
| "currency": "EUR",
|
||||
| "product": "Girokonto",
|
||||
| "cashAccountType": "CACC",
|
||||
| "name": "Main Account",
|
||||
| "_links": {
|
||||
| "balances": {
|
||||
| "href": "/v1/accounts/3dc3d5b3-7023-4848-9853-f5400a64e80f/balances"
|
||||
| }
|
||||
| }
|
||||
| },
|
||||
| "_links":{
|
||||
| "balances":{
|
||||
| "href":"/v1.3/accounts/8ca8a7e4-6d02-40e3-a129-0b2bf89de9f0/balances"
|
||||
| {
|
||||
| "resourceId": "3dc3d5b3-7023-4848-9853-f5400a64e81g",
|
||||
| "iban": "DE2310010010123456788",
|
||||
| "currency": "USD",
|
||||
| "product": "Fremdwährungskonto",
|
||||
| "cashAccountType": "CACC",
|
||||
| "name": "US Dollar Account",
|
||||
| "_links": {
|
||||
| "balances": {
|
||||
| "href": "/v1/accounts/3dc3d5b3-7023-4848-9853-f5400a64e81g/balances"
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
| }]
|
||||
|}
|
||||
|""".stripMargin),
|
||||
| ]
|
||||
|}""".stripMargin),
|
||||
List(UserNotLoggedIn, UnknownError),
|
||||
ApiTag("Account Information Service (AIS)") :: apiTagBerlinGroupM :: Nil
|
||||
)
|
||||
@ -357,8 +359,9 @@ The account-id is constant at least throughout the lifecycle of a given consent.
|
||||
_ <- passesPsd2Aisp(callContext)
|
||||
(account: BankAccount, callContext) <- NewStyle.function.getBankAccountByAccountId(accountId, callContext)
|
||||
_ <- checkAccountAccess(ViewId(SYSTEM_READ_BALANCES_BERLIN_GROUP_VIEW_ID), u, account, callContext)
|
||||
(accountBalances, callContext)<- NewStyle.function.getBankAccountBalances(BankIdAccountId(account.bankId,account.accountId), callContext)
|
||||
} yield {
|
||||
(JSONFactory_BERLIN_GROUP_1_3.createAccountBalanceJSON(account), HttpCode.`200`(callContext))
|
||||
(JSONFactory_BERLIN_GROUP_1_3.createAccountBalanceJSON(account, accountBalances), HttpCode.`200`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -390,25 +393,9 @@ respectively the OAuth2 access token.
|
||||
"currency": "EUR",
|
||||
"amount": 15000
|
||||
},
|
||||
"balances": [
|
||||
{
|
||||
"balanceType": "interimBooked",
|
||||
"balanceAmount": {
|
||||
"currency": "EUR",
|
||||
"amount": 14355.78
|
||||
}
|
||||
},
|
||||
{
|
||||
"balanceType": "nonBilled",
|
||||
"balanceAmount": {
|
||||
"currency": "EUR",
|
||||
"amount": 4175.86
|
||||
}
|
||||
}
|
||||
],
|
||||
"_links": {
|
||||
"transactions": {
|
||||
"href": "/v1/card-accounts/3d9a81b3-a47d-4130-8765-a9c0ff861b99/transactions"
|
||||
"balances": {
|
||||
"href": "/v1/card-accounts/3d9a81b3-a47d-4130-8765-a9c0ff861b99/balances"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -487,8 +474,9 @@ This account-id then can be retrieved by the
|
||||
_ <- passesPsd2Aisp(callContext)
|
||||
(account: BankAccount, callContext) <- NewStyle.function.getBankAccountByAccountId(AccountId(accountId), callContext)
|
||||
_ <- checkAccountAccess(ViewId(SYSTEM_READ_BALANCES_BERLIN_GROUP_VIEW_ID), u, account, callContext)
|
||||
(accountBalances, callContext)<- NewStyle.function.getBankAccountBalances(BankIdAccountId(account.bankId,account.accountId), callContext)
|
||||
} yield {
|
||||
(JSONFactory_BERLIN_GROUP_1_3.createCardAccountBalanceJSON(account), HttpCode.`200`(callContext))
|
||||
(JSONFactory_BERLIN_GROUP_1_3.createCardAccountBalanceJSON(account, accountBalances), HttpCode.`200`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -942,24 +930,22 @@ Give detailed information about the addressed account together with balance info
|
||||
""",
|
||||
emptyObjectJson,
|
||||
json.parse("""{
|
||||
"cashAccountType" : { },
|
||||
"product" : "product",
|
||||
"resourceId" : "resourceId",
|
||||
"bban" : "BARC12345612345678",
|
||||
"_links" : {
|
||||
"balances" : "/v1.3/payments/sepa-credit-transfers/1234-wertiq-983",
|
||||
"transactions" : "/v1.3/payments/sepa-credit-transfers/1234-wertiq-983"
|
||||
},
|
||||
"usage" : "PRIV",
|
||||
"balances" : "",
|
||||
"iban" : "FR7612345987650123456789014",
|
||||
"linkedAccounts" : "linkedAccounts",
|
||||
"name" : "name",
|
||||
"currency" : "EUR",
|
||||
"details" : "details",
|
||||
"msisdn" : "+49 170 1234567",
|
||||
"bic" : "AAAADEBBXXX",
|
||||
"status" : { }
|
||||
"account": {
|
||||
"resourceId": "3dc3d5b3-7023-4848-9853-f5400a64e80f",
|
||||
"iban": "FR7612345987650123456789014",
|
||||
"currency": "EUR",
|
||||
"product": "Girokonto",
|
||||
"cashAccountType": "CACC",
|
||||
"name": "Main Account",
|
||||
"_links": {
|
||||
"balances": {
|
||||
"href": "/v1/accounts/3dc3d5b3-7023-4848-9853-f5400a64e80f/balances"
|
||||
},
|
||||
"transactions": {
|
||||
"href": "/v1/accounts/3dc3d5b3-7023-4848-9853-f5400a64e80f/transactions"
|
||||
}
|
||||
}
|
||||
}
|
||||
}"""),
|
||||
List(UserNotLoggedIn, UnknownError),
|
||||
ApiTag("Account Information Service (AIS)") :: apiTagBerlinGroupM :: Nil
|
||||
@ -994,24 +980,27 @@ respectively the OAuth2 access token.
|
||||
""",
|
||||
emptyObjectJson,
|
||||
json.parse("""{
|
||||
"balances" : "",
|
||||
"product" : "product",
|
||||
"resourceId" : "resourceId",
|
||||
"maskedPan" : "123456xxxxxx1234",
|
||||
"_links" : {
|
||||
"balances" : "/v1.3/payments/sepa-credit-transfers/1234-wertiq-983",
|
||||
"transactions" : "/v1.3/payments/sepa-credit-transfers/1234-wertiq-983"
|
||||
},
|
||||
"usage" : "PRIV",
|
||||
"name" : "name",
|
||||
"creditLimit" : {
|
||||
"amount" : "123",
|
||||
"currency" : "EUR"
|
||||
},
|
||||
"currency" : "EUR",
|
||||
"details" : "details",
|
||||
"status" : { }
|
||||
}"""),
|
||||
| "cardAccount": {
|
||||
| "resourceId": "3d9a81b3-a47d-4130-8765-a9c0ff861b99",
|
||||
| "maskedPan": "525412******3241",
|
||||
| "currency": "EUR",
|
||||
| "name": "Main",
|
||||
| "product": "Basic Credit",
|
||||
| "status": "enabled",
|
||||
| "creditLimit": {
|
||||
| "currency": "EUR",
|
||||
| "amount": "15000"
|
||||
| },
|
||||
| "_links": {
|
||||
| "balances": {
|
||||
| "href": "/v1/card-accounts/3d9a81b3-a47d-4130-8765-a9c0ff861b99/balances"
|
||||
| },
|
||||
| "transactions": {
|
||||
| "href": "/v1/card-accounts/3d9a81b3-a47d-4130-8765-a9c0ff861b99/transactions"
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
|}""".stripMargin),
|
||||
List(UserNotLoggedIn, UnknownError),
|
||||
ApiTag("Account Information Service (AIS)") :: Nil
|
||||
)
|
||||
|
||||
@ -51,7 +51,7 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats {
|
||||
// linkedAccounts: String ="string",
|
||||
// usage: String ="PRIV",
|
||||
// details: String ="",
|
||||
balances: CoreAccountBalancesJson,
|
||||
// balances: CoreAccountBalancesJson,// We put this under the _links, not need to show it here.
|
||||
_links: CoreAccountLinksJsonV13,
|
||||
)
|
||||
|
||||
@ -277,14 +277,7 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats {
|
||||
x =>
|
||||
val (iBan: String, bBan: String) = getIbanAndBban(x)
|
||||
|
||||
val balance =
|
||||
CoreAccountBalancesJson(
|
||||
balanceAmount = AmountOfMoneyV13(x.currency,x.balance.toString()),
|
||||
balanceType = APIUtil.stringOrNull(x.accountType),
|
||||
lastChangeDateTime= APIUtil.dateOrNull(x.lastUpdate),
|
||||
referenceDate = APIUtil.dateOrNull(x.lastUpdate),
|
||||
lastCommittedTransaction = ""
|
||||
)
|
||||
|
||||
CoreAccountJsonV13(
|
||||
resourceId = x.accountId.value,
|
||||
iban = iBan,
|
||||
@ -294,7 +287,6 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats {
|
||||
bic = getBicFromBankId(x.bankId.value),
|
||||
cashAccountType = x.accountType,
|
||||
product = x.accountType,
|
||||
balances = balance,
|
||||
_links = CoreAccountLinksJsonV13(LinkHrefJson(s"/${OBP_BERLIN_GROUP_1_3.apiVersion.urlPrefix}/${OBP_BERLIN_GROUP_1_3.version}/accounts/${x.accountId.value}/balances"))
|
||||
)
|
||||
}
|
||||
@ -306,14 +298,6 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats {
|
||||
x =>
|
||||
val (iBan: String, bBan: String) = getIbanAndBban(x)
|
||||
|
||||
val balance =
|
||||
CoreAccountBalancesJson(
|
||||
balanceAmount = AmountOfMoneyV13(x.currency,x.balance.toString()),
|
||||
balanceType = APIUtil.stringOrNull(x.accountType),
|
||||
lastChangeDateTime= APIUtil.dateOrNull(x.lastUpdate),
|
||||
referenceDate = APIUtil.dateOrNull(x.lastUpdate),
|
||||
lastCommittedTransaction = "String"
|
||||
)
|
||||
CoreAccountJsonV13(
|
||||
resourceId = x.accountId.value,
|
||||
iban = iBan,
|
||||
@ -323,7 +307,6 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats {
|
||||
bic = getBicFromBankId(x.bankId.value),
|
||||
cashAccountType = x.accountType,
|
||||
product = x.accountType,
|
||||
balances = balance,
|
||||
_links = CoreAccountLinksJsonV13(LinkHrefJson(s"/${OBP_BERLIN_GROUP_1_3.apiVersion.urlPrefix}/${OBP_BERLIN_GROUP_1_3.version}/accounts/${x.accountId.value}/balances"))
|
||||
)
|
||||
}
|
||||
@ -359,12 +342,12 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats {
|
||||
(iBan, bBan)
|
||||
}
|
||||
|
||||
def createCardAccountBalanceJSON(bankAccount: BankAccount): CardAccountBalancesV13 = {
|
||||
val accountBalancesV13 = createAccountBalanceJSON(bankAccount: BankAccount)
|
||||
def createCardAccountBalanceJSON(bankAccount: BankAccount, accountBalances: AccountBalances): CardAccountBalancesV13 = {
|
||||
val accountBalancesV13 = createAccountBalanceJSON(bankAccount: BankAccount, accountBalances)
|
||||
CardAccountBalancesV13(accountBalancesV13.account,accountBalancesV13.`balances`)
|
||||
}
|
||||
|
||||
def createAccountBalanceJSON(bankAccount: BankAccount): AccountBalancesV13 = {
|
||||
def createAccountBalanceJSON(bankAccount: BankAccount, accountBalances: AccountBalances): AccountBalancesV13 = {
|
||||
|
||||
val (iban: String, bban: String) = getIbanAndBban(bankAccount)
|
||||
|
||||
@ -372,17 +355,14 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats {
|
||||
account = FromAccount(
|
||||
iban = iban,
|
||||
),
|
||||
`balances` = AccountBalance(
|
||||
balanceAmount = AmountOfMoneyV13(
|
||||
currency = APIUtil.stringOrNull(bankAccount.currency),
|
||||
amount = bankAccount.balance.toString()
|
||||
),
|
||||
balanceType = APIUtil.stringOrNull(bankAccount.accountType),
|
||||
`balances` = accountBalances.balances.map(accountBalance => AccountBalance(
|
||||
balanceAmount = AmountOfMoneyV13(accountBalance.balance.currency, accountBalance.balance.amount),
|
||||
balanceType = accountBalance.balanceType,
|
||||
lastChangeDateTime = APIUtil.dateOrNull(bankAccount.lastUpdate),
|
||||
referenceDate = APIUtil.dateOrNull(bankAccount.lastUpdate),
|
||||
lastCommittedTransaction = "String"
|
||||
) :: Nil
|
||||
)
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
def createTransactionJSON(bankAccount: BankAccount, transaction : ModeratedTransaction, creditorAccount: CreditorAccountJson) : TransactionJsonV13 = {
|
||||
|
||||
@ -29,11 +29,12 @@ package code.api
|
||||
import java.util.Date
|
||||
|
||||
import code.api.util.APIUtil._
|
||||
import code.api.util.ErrorMessages.InvalidDirectLoginParameters
|
||||
import code.api.util.NewStyle.HttpCode
|
||||
import code.api.util._
|
||||
import code.consumer.Consumers._
|
||||
import code.model.dataAccess.AuthUser
|
||||
import code.model.{Consumer, Token, TokenType}
|
||||
import code.model.{Consumer, Token, TokenType, UserX}
|
||||
import code.token.Tokens
|
||||
import code.util.Helper.{MdcLoggable, SILENCE_IS_GOLDEN}
|
||||
import com.nimbusds.jwt.JWTClaimsSet
|
||||
@ -43,6 +44,7 @@ import net.liftweb.common._
|
||||
import net.liftweb.http._
|
||||
import net.liftweb.http.rest.RestHelper
|
||||
import net.liftweb.util.Helpers
|
||||
import net.liftweb.util.Helpers.tryo
|
||||
|
||||
import scala.compat.Platform
|
||||
import scala.concurrent.Future
|
||||
@ -93,9 +95,10 @@ object DirectLogin extends RestHelper with MdcLoggable {
|
||||
{
|
||||
//Handling get request for a token
|
||||
case Req("my" :: "logins" :: "direct" :: Nil,_ , PostRequest) => {
|
||||
for(
|
||||
(httpCode: Int, message: String) <- createTokenFuture(getAllParameters)
|
||||
) yield {
|
||||
for{
|
||||
(httpCode: Int, message: String, userId:Long) <- createTokenFuture(getAllParameters)
|
||||
_ <- Future{grantEntitlementsToUseDynamicEndpointsInSpacesInDirectLogin(userId)}
|
||||
} yield {
|
||||
if (httpCode == 200) {
|
||||
(JSONFactory.createTokenJSON(message), HttpCode.`201`(CallContext()))
|
||||
} else {
|
||||
@ -105,12 +108,28 @@ object DirectLogin extends RestHelper with MdcLoggable {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def grantEntitlementsToUseDynamicEndpointsInSpacesInDirectLogin(userId:Long) = {
|
||||
try {
|
||||
val resourceUser = UserX.findByResourceUserId(userId).openOrThrowException(s"$InvalidDirectLoginParameters can not find the resourceUser!")
|
||||
val authUser = AuthUser.findUserByUsernameLocally(resourceUser.name).openOrThrowException(s"$InvalidDirectLoginParameters can not find the auth user!")
|
||||
if(!emailDomainToSpaceMappings.isEmpty){
|
||||
AuthUser.grantEntitlementsToUseDynamicEndpointsInSpaces(authUser)
|
||||
}
|
||||
if(!emailDomainToEntitlementMappings.isEmpty){
|
||||
AuthUser.grantEmailDomainEntitlementsToUser(authUser)
|
||||
}
|
||||
} catch {
|
||||
case e: Throwable => // error handling, found wrong props value as early as possible.
|
||||
this.logger.error(s"directLogin.grantEntitlementsToUseDynamicEndpointsInSpacesInDirectLogin throw exception, details: $e" );
|
||||
}
|
||||
}
|
||||
/**
|
||||
* according username, password, consumer_key to generate a DirectLogin token
|
||||
* @param allParameters map {"username": "some_username", "password": "some_password", "consumer_key": "some_consumer_key"}
|
||||
* @return httpCode and token value
|
||||
*/
|
||||
def createTokenFuture(allParameters: Map[String, String]): Future[(Int, String)] = {
|
||||
def createTokenFuture(allParameters: Map[String, String]): Future[(Int, String, Long)] = {
|
||||
val httpMethod = S.request match {
|
||||
case Full(r) => r.request.method
|
||||
case _ => "GET"
|
||||
@ -134,12 +153,11 @@ object DirectLogin extends RestHelper with MdcLoggable {
|
||||
createTokenCommonPart(httpCode, message, directLoginParameters)
|
||||
}
|
||||
|
||||
def createTokenCommonPart(code: Int, msg: String, directLoginParameters: Map[String, String]): (Int, String) = {
|
||||
def createTokenCommonPart(code: Int, msg: String, directLoginParameters: Map[String, String]): (Int, String, Long) = {
|
||||
var message = msg
|
||||
var httpCode = code
|
||||
val userId: Long = (for {id <- getUserId(directLoginParameters)} yield id).getOrElse(0)
|
||||
if (httpCode == 200) {
|
||||
val userId: Long = (for {id <- getUserId(directLoginParameters)} yield id).getOrElse(0)
|
||||
|
||||
if (userId == 0) {
|
||||
message = ErrorMessages.InvalidLoginCredentials
|
||||
httpCode = 401
|
||||
@ -164,7 +182,7 @@ object DirectLogin extends RestHelper with MdcLoggable {
|
||||
}
|
||||
}
|
||||
}
|
||||
(httpCode, message)
|
||||
(httpCode, message, userId)
|
||||
}
|
||||
|
||||
def getHttpMethod = S.request match {
|
||||
@ -244,7 +262,11 @@ object DirectLogin extends RestHelper with MdcLoggable {
|
||||
else
|
||||
Map("error" -> "header incorrect")
|
||||
}
|
||||
case _ => Map("error" -> "missing header")
|
||||
case _ =>
|
||||
a.header("DirectLogin") match {
|
||||
case Full(header) => toMap(header)
|
||||
case _ => Map("error" -> "missing header")
|
||||
}
|
||||
}
|
||||
case _ => Map("error" -> "request incorrect")
|
||||
}
|
||||
|
||||
@ -184,7 +184,9 @@ object OpenIdConnect extends OBPRestHelper with MdcLoggable {
|
||||
createdByConsentId = None,
|
||||
name = getClaim(name = "given_name", idToken = idToken).orElse(subject),
|
||||
email = getClaim(name = "email", idToken = idToken),
|
||||
userId = None
|
||||
userId = None,
|
||||
createdByUserInvitationId = None,
|
||||
company = None
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -257,7 +259,7 @@ object OpenIdConnect extends OBPRestHelper with MdcLoggable {
|
||||
JwtUtil.getSubject(idToken),
|
||||
Some(true),
|
||||
name = Some(Helpers.randomString(10).toLowerCase),
|
||||
appType = Some(AppType.Web),
|
||||
appType = Some(AppType.Confidential),
|
||||
description = Some(openIdConnect),
|
||||
developerEmail = getClaim(name = "email", idToken = idToken),
|
||||
redirectURL = None,
|
||||
|
||||
@ -47,6 +47,7 @@ import code.api.util.Glossary.GlossaryItem
|
||||
import code.api.util.JwsUtil.getJwsHeaderValue
|
||||
import code.api.util.RateLimitingJson.CallLimit
|
||||
import code.api.v1_2.ErrorMessage
|
||||
import code.api.v2_0_0.CreateEntitlementJSON
|
||||
import code.api.{DirectLogin, _}
|
||||
import code.api.v4_0_0.dynamic.{DynamicEndpointHelper, DynamicEndpoints, DynamicEntityHelper}
|
||||
import code.authtypevalidation.AuthenticationTypeValidationProvider
|
||||
@ -78,7 +79,7 @@ import net.liftweb.http.js.JE.JsRaw
|
||||
import net.liftweb.http.provider.HTTPParam
|
||||
import net.liftweb.http.rest.RestContinuation
|
||||
import net.liftweb.json
|
||||
import net.liftweb.json.JsonAST.{JField, JObject, JString, JValue}
|
||||
import net.liftweb.json.JsonAST.{JField, JNothing, JObject, JString, JValue}
|
||||
import net.liftweb.json.JsonParser.ParseException
|
||||
import net.liftweb.json._
|
||||
import net.liftweb.util.Helpers._
|
||||
@ -161,6 +162,8 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
}
|
||||
|
||||
def hasDirectLoginHeader(authorization: Box[String]): Boolean = hasHeader("DirectLogin", authorization)
|
||||
|
||||
def has2021DirectLoginHeader(requestHeaders: List[HTTPParam]): Boolean = requestHeaders.find(_.name == "DirectLogin").isDefined
|
||||
|
||||
def hasAnOAuthHeader(authorization: Box[String]): Boolean = hasHeader("OAuth", authorization)
|
||||
|
||||
@ -298,6 +301,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
|
||||
def logAPICall(date: TimeSpan, duration: Long, rd: Option[ResourceDoc]) = {
|
||||
val authorization = S.request.map(_.header("Authorization")).flatten
|
||||
val directLogin: Box[String] = S.request.map(_.header("DirectLogin")).flatten
|
||||
if(getPropsAsBoolValue("write_metrics", false)) {
|
||||
val user =
|
||||
if (hasAnOAuthHeader(authorization)) {
|
||||
@ -305,7 +309,14 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
case Full(u) => Full(u)
|
||||
case _ => Empty
|
||||
}
|
||||
} else if (getPropsAsBoolValue("allow_direct_login", true) && hasDirectLoginHeader(authorization)) {
|
||||
} // Direct Login
|
||||
else if (getPropsAsBoolValue("allow_direct_login", true) && directLogin.isDefined) {
|
||||
DirectLogin.getUser match {
|
||||
case Full(u) => Full(u)
|
||||
case _ => Empty
|
||||
}
|
||||
} // Direct Login Deprecated
|
||||
else if (getPropsAsBoolValue("allow_direct_login", true) && hasDirectLoginHeader(authorization)) {
|
||||
DirectLogin.getUser match {
|
||||
case Full(u) => Full(u)
|
||||
case _ => Empty
|
||||
@ -320,7 +331,14 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
case Full(c) => Full(c)
|
||||
case _ => Empty
|
||||
}
|
||||
} else if (getPropsAsBoolValue("allow_direct_login", true) && hasDirectLoginHeader(authorization)) {
|
||||
} // Direct Login
|
||||
else if (getPropsAsBoolValue("allow_direct_login", true) && directLogin.isDefined) {
|
||||
DirectLogin.getConsumer match {
|
||||
case Full(c) => Full(c)
|
||||
case _ => Empty
|
||||
}
|
||||
} // Direct Login Deprecated
|
||||
else if (getPropsAsBoolValue("allow_direct_login", true) && hasDirectLoginHeader(authorization)) {
|
||||
DirectLogin.getConsumer match {
|
||||
case Full(c) => Full(c)
|
||||
case _ => Empty
|
||||
@ -506,6 +524,16 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
def getHeaders() = getHeadersCommonPart() ::: getGatewayResponseHeader()
|
||||
|
||||
case class CustomResponseHeaders(list: List[(String, String)])
|
||||
//This is used for get the value from props `email_domain_to_space_mappings`
|
||||
case class EmailDomainToSpaceMapping(
|
||||
domain: String,
|
||||
bank_ids: List[String]
|
||||
)
|
||||
|
||||
case class EmailDomainToEntitlementMapping(
|
||||
domain: String,
|
||||
entitlements: List[CreateEntitlementJSON]
|
||||
)
|
||||
|
||||
//Note: changed noContent--> defaultSuccess, because of the Swagger format. (Not support empty in DataType, maybe fix it latter.)
|
||||
def noContentJsonResponse(implicit headers: CustomResponseHeaders = CustomResponseHeaders(Nil)) : JsonResponse =
|
||||
@ -877,7 +905,11 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
value <- tryo(values.head.toBoolean)?~! FilterAnonFormatError
|
||||
anon = OBPAnon(value)
|
||||
}yield anon
|
||||
|
||||
case "is_deleted" =>
|
||||
for {
|
||||
value <- tryo(values.head.toBoolean) ?~! FilterIsDeletedFormatError
|
||||
deleted = OBPIsDeleted(value)
|
||||
} yield deleted
|
||||
case "consumer_id" => Full(OBPConsumerId(values.head))
|
||||
case "user_id" => Full(OBPUserId(values.head))
|
||||
case "bank_id" => Full(OBPBankId(values.head))
|
||||
@ -920,6 +952,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
offset <- getOffset(httpParams)
|
||||
//all optional fields
|
||||
anon <- getHttpParamValuesByName(httpParams,"anon")
|
||||
deletedStatus <- getHttpParamValuesByName(httpParams,"is_deleted")
|
||||
consumerId <- getHttpParamValuesByName(httpParams,"consumer_id")
|
||||
userId <- getHttpParamValuesByName(httpParams, "user_id")
|
||||
bankId <- getHttpParamValuesByName(httpParams, "bank_id")
|
||||
@ -957,7 +990,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
List(limit, offset, ordering, fromDate, toDate,
|
||||
anon, consumerId, userId, url, appName, implementedByPartialFunction, implementedInVersion,
|
||||
verb, correlationId, duration, excludeAppNames, excludeUrlPattern, excludeImplementedByPartialfunctions,
|
||||
connectorName,functionName, bankId, accountId, customerId, lockedStatus
|
||||
connectorName,functionName, bankId, accountId, customerId, lockedStatus, deletedStatus
|
||||
).filter(_ != OBPEmpty())
|
||||
}
|
||||
}
|
||||
@ -980,6 +1013,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
val limit = getHttpRequestUrlParam(httpRequestUrl,"limit")
|
||||
val offset = getHttpRequestUrlParam(httpRequestUrl,"offset")
|
||||
val anon = getHttpRequestUrlParam(httpRequestUrl,"anon")
|
||||
val isDeleted = getHttpRequestUrlParam(httpRequestUrl, "is_deleted")
|
||||
val consumerId = getHttpRequestUrlParam(httpRequestUrl,"consumer_id")
|
||||
val userId = getHttpRequestUrlParam(httpRequestUrl, "user_id")
|
||||
val bankId = getHttpRequestUrlParam(httpRequestUrl, "bank_id")
|
||||
@ -1018,6 +1052,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
HTTPParam("account_id", accountId),
|
||||
HTTPParam("connector_name", connectorName),
|
||||
HTTPParam("customer_id", customerId),
|
||||
HTTPParam("is_deleted", isDeleted),
|
||||
HTTPParam("locked_status", lockedStatus)
|
||||
).filter(_.values.head != ""))//Here filter the field when value = "".
|
||||
}
|
||||
@ -2626,11 +2661,14 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
AuthUser.updateUserAccountViewsFuture(user.openOrThrowException("Can not be empty here"), callContext)
|
||||
(user, callContext)
|
||||
}
|
||||
// Direct Login
|
||||
} else if (getPropsAsBoolValue("allow_direct_login", true) && hasDirectLoginHeader(cc.authReqHeaderField)) {
|
||||
} // Direct Login i.e DirectLogin: token=eyJhbGciOiJIUzI1NiJ9.eyIiOiIifQ.Y0jk1EQGB4XgdqmYZUHT6potmH3mKj5mEaA9qrIXXWQ
|
||||
else if (getPropsAsBoolValue("allow_direct_login", true) && has2021DirectLoginHeader(cc.requestHeaders)) {
|
||||
DirectLogin.getUserFromDirectLoginHeaderFuture(cc)
|
||||
// Gateway Login
|
||||
} else if (getPropsAsBoolValue("allow_gateway_login", false) && hasGatewayHeader(cc.authReqHeaderField)) {
|
||||
} // Direct Login Deprecated i.e Authorization: DirectLogin token=eyJhbGciOiJIUzI1NiJ9.eyIiOiIifQ.Y0jk1EQGB4XgdqmYZUHT6potmH3mKj5mEaA9qrIXXWQ
|
||||
else if (getPropsAsBoolValue("allow_direct_login", true) && hasDirectLoginHeader(cc.authReqHeaderField)) {
|
||||
DirectLogin.getUserFromDirectLoginHeaderFuture(cc)
|
||||
} // Gateway Login
|
||||
else if (getPropsAsBoolValue("allow_gateway_login", false) && hasGatewayHeader(cc.authReqHeaderField)) {
|
||||
APIUtil.getPropsValue("gateway.host") match {
|
||||
case Full(h) if h.split(",").toList.exists(_.equalsIgnoreCase(remoteIpAddress) == true) => // Only addresses from white list can use this feature
|
||||
val (httpCode, message, parameters) = GatewayLogin.validator(s.request)
|
||||
@ -2816,6 +2854,10 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
)
|
||||
} map {
|
||||
x =>
|
||||
//TODO due to performance issue, first comment this out,
|
||||
// val authUser = AuthUser.findUserByUsernameLocally(x._1.head.name).openOrThrowException("")
|
||||
// tryo{AuthUser.grantEntitlementsToUseDynamicEndpointsInSpaces(authUser, x._2)}.openOr(logger.error(s"${x._1} authenticatedAccess.grantEntitlementsToUseDynamicEndpointsInSpaces throw exception! "))
|
||||
|
||||
// make sure, if `refreshUserIfRequired` throw exception, do not break the `authenticatedAccess`,
|
||||
// TODO better move `refreshUserIfRequired` to other place.
|
||||
tryo{refreshUserIfRequired(x._1,x._2)}.openOr(logger.error(s"${x._1} authenticatedAccess.refreshUserIfRequired throw exception! "))
|
||||
@ -3119,12 +3161,23 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
Else just return the session
|
||||
Note there are Read and Write side effects here!
|
||||
*/
|
||||
// getPropsAsBoolValue cannot be called directly inside the function activeBrand due to java.lang.StackOverflowError
|
||||
val brandsEnabled = APIUtil.getPropsAsBoolValue("brands_enabled", false)
|
||||
def activeBrand() : Option[String] = {
|
||||
|
||||
brandsEnabled match {
|
||||
case true =>
|
||||
getActiveBrand()
|
||||
case false =>
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// TODO This function needs testing in a cluster environment
|
||||
private def getActiveBrand(): Option[String] = {
|
||||
val brandParameter = "brand"
|
||||
|
||||
// Use brand in parameter (query or form)
|
||||
val brand : Option[String] = S.param(brandParameter) match {
|
||||
val brand: Option[String] = S.param(brandParameter) match {
|
||||
case Full(value) => {
|
||||
// If found, and has a valid format, set the session.
|
||||
if (isValidID(value)) {
|
||||
@ -3132,11 +3185,11 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
logger.debug(s"activeBrand says: I found a $brandParameter param. $brandParameter session has been set to: ${S.getSessionAttribute(brandParameter)}")
|
||||
Some(value)
|
||||
} else {
|
||||
logger.warn (s"activeBrand says: ${ErrorMessages.InvalidBankIdFormat}")
|
||||
logger.warn(s"activeBrand says: ${ErrorMessages.InvalidBankIdFormat}")
|
||||
None
|
||||
}
|
||||
}
|
||||
case _ => {
|
||||
case _ => {
|
||||
// Else look in the session
|
||||
S.getSessionAttribute(brandParameter)
|
||||
}
|
||||
@ -3168,14 +3221,17 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
|
||||
|
||||
|
||||
val ALLOW_PUBLIC_VIEWS: Boolean = getPropsAsBoolValue("allow_public_views", false)
|
||||
val ALLOW_ACCOUNT_FIREHOSE: Boolean = ApiPropsWithAlias.allowAccountFirehose
|
||||
val ALLOW_CUSTOMER_FIREHOSE: Boolean = ApiPropsWithAlias.allowCustomerFirehose
|
||||
def allowPublicViews: Boolean = getPropsAsBoolValue("allow_public_views", false)
|
||||
def allowAccountFirehose: Boolean = ApiPropsWithAlias.allowAccountFirehose
|
||||
def allowCustomerFirehose: Boolean = ApiPropsWithAlias.allowCustomerFirehose
|
||||
def canUseAccountFirehose(user: User): Boolean = {
|
||||
ALLOW_ACCOUNT_FIREHOSE && hasEntitlement("", user.userId, ApiRole.canUseAccountFirehoseAtAnyBank)
|
||||
allowAccountFirehose && hasEntitlement("", user.userId, ApiRole.canUseAccountFirehoseAtAnyBank)
|
||||
}
|
||||
def canUseAccountFirehoseAtBank(user: User, bankId: BankId): Boolean = {
|
||||
allowAccountFirehose && hasEntitlement(bankId.value, user.userId, ApiRole.canUseAccountFirehose)
|
||||
}
|
||||
def canUseCustomerFirehose(user: User): Boolean = {
|
||||
ALLOW_CUSTOMER_FIREHOSE && hasEntitlement("", user.userId, ApiRole.canUseCustomerFirehoseAtAnyBank)
|
||||
allowCustomerFirehose && hasEntitlement("", user.userId, ApiRole.canUseCustomerFirehoseAtAnyBank)
|
||||
}
|
||||
/**
|
||||
* This will accept all kinds of view and user.
|
||||
@ -3190,6 +3246,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
true
|
||||
else
|
||||
user match {
|
||||
case Some(u) if hasAccountFirehoseAccessAtBank(view,u, bankIdAccountId.bankId) => true //Login User and Firehose access
|
||||
case Some(u) if hasAccountFirehoseAccess(view,u) => true//Login User and Firehose access
|
||||
case Some(u) if u.hasAccountAccess(view, bankIdAccountId)=> true // Login User and check view access
|
||||
case _ =>
|
||||
@ -3205,7 +3262,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
val customView = Views.views.vend.customView(viewId, bankIdAccountId)
|
||||
customView match { // CHECK CUSTOM VIEWS
|
||||
// 1st: View is Pubic and Public views are NOT allowed on this instance.
|
||||
case Full(v) if(v.isPublic && !ALLOW_PUBLIC_VIEWS) => Failure(PublicViewsNotAllowedOnThisInstance)
|
||||
case Full(v) if(v.isPublic && !allowPublicViews) => Failure(PublicViewsNotAllowedOnThisInstance)
|
||||
// 2nd: View is Pubic and Public views are allowed on this instance.
|
||||
case Full(v) if(isPublicView(v)) => customView
|
||||
// 3rd: The user has account access to this custom view
|
||||
@ -3215,13 +3272,15 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
val systemView = Views.views.vend.systemView(viewId)
|
||||
systemView match { // CHECK SYSTEM VIEWS
|
||||
// 1st: View is Pubic and Public views are NOT allowed on this instance.
|
||||
case Full(v) if(v.isPublic && !ALLOW_PUBLIC_VIEWS) => Failure(PublicViewsNotAllowedOnThisInstance)
|
||||
case Full(v) if(v.isPublic && !allowPublicViews) => Failure(PublicViewsNotAllowedOnThisInstance)
|
||||
// 2nd: View is Pubic and Public views are allowed on this instance.
|
||||
case Full(v) if(isPublicView(v)) => systemView
|
||||
// 3rd: The user has account access to this system view
|
||||
case Full(v) if (user.isDefined && user.get.hasAccountAccess(v, bankIdAccountId)) => systemView
|
||||
// 4th: The user has firehose access to this system view
|
||||
case Full(v) if (user.isDefined && hasAccountFirehoseAccess(v, user.get)) => systemView
|
||||
// 5th: The user has firehose access at a bank to this system view
|
||||
case Full(v) if (user.isDefined && hasAccountFirehoseAccessAtBank(v, user.get, bankIdAccountId.bankId)) => systemView
|
||||
// The user has NO account access at all
|
||||
case _ => Empty
|
||||
}
|
||||
@ -3243,7 +3302,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
case true if view.isPublic => // Sanity check. We don't want a public owner view.
|
||||
logger.warn(s"Public owner encountered. Primary view id: ${view.id}")
|
||||
false
|
||||
case _ => view.isPublic && APIUtil.ALLOW_PUBLIC_VIEWS
|
||||
case _ => view.isPublic && APIUtil.allowPublicViews
|
||||
}
|
||||
}
|
||||
/**
|
||||
@ -3253,6 +3312,13 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
if(view.isFirehose && canUseAccountFirehose(user)) true
|
||||
else false
|
||||
}
|
||||
/**
|
||||
* This view Firehose is true and set `allow_account_firehose = true` and the user has `CanUseAccountFirehoseAtAnyBank` role
|
||||
*/
|
||||
def hasAccountFirehoseAccessAtBank(view: View, user: User, bankId: BankId) : Boolean = {
|
||||
if(view.isFirehose && canUseAccountFirehoseAtBank(user, bankId)) true
|
||||
else false
|
||||
}
|
||||
|
||||
/**
|
||||
* This value is used to construct some urls in Resource Docs
|
||||
@ -3432,14 +3498,18 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
case true =>
|
||||
`getPSD2-CERT`(cc.map(_.requestHeaders).getOrElse(Nil)) match {
|
||||
case Some(pem) =>
|
||||
val validatedPem = X509.validate(pem)
|
||||
logger.debug("PSD2-CERT pem: " + pem)
|
||||
val decodedPem = URLDecoder.decode(pem,"UTF-8")
|
||||
val validatedPem = X509.validate(decodedPem)
|
||||
logger.debug("validatedPem: " + validatedPem)
|
||||
validatedPem match {
|
||||
case Full(true) =>
|
||||
val roles = X509.extractPsd2Roles(pem).map(_.exists(_ == serviceProvider))
|
||||
roles match {
|
||||
val hasServiceProvider = X509.extractPsd2Roles(decodedPem).map(_.exists(_ == serviceProvider))
|
||||
logger.debug("hasServiceProvider: " + hasServiceProvider)
|
||||
hasServiceProvider match {
|
||||
case Full(true) => Full(true)
|
||||
case Full(false) => Failure(X509ActionIsNotAllowed)
|
||||
case _ => roles
|
||||
case _ => hasServiceProvider
|
||||
}
|
||||
case _ =>
|
||||
validatedPem
|
||||
@ -3948,4 +4018,38 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
val berlinGroupV13AliasPath = APIUtil.getPropsValue("berlin_group_v1.3_alias.path","").split("/").toList.map(_.trim)
|
||||
|
||||
val getAtmsIsPublic = APIUtil.getPropsAsBoolValue("apiOptions.getAtmsIsPublic", true)
|
||||
|
||||
val emailDomainToSpaceMappings: List[EmailDomainToSpaceMapping] = {
|
||||
def extractor(str: String) = try {
|
||||
val emailToSpaceMappings = json.parse(str).extract[List[EmailDomainToSpaceMapping]]
|
||||
//The props value can be parse to JNothing.
|
||||
if(str.nonEmpty && emailToSpaceMappings == Nil)
|
||||
throw new RuntimeException("props [email_domain_to_space_mappings] parse -> extract to Nil!")
|
||||
else
|
||||
emailToSpaceMappings
|
||||
} catch {
|
||||
case e: Throwable => // error handling, found wrong props value as early as possible.
|
||||
this.logger.error(s"props [email_domain_to_space_mappings] value is invalid, it should be the class($EmailDomainToSpaceMapping) json format, current value is $str ." );
|
||||
throw e;
|
||||
}
|
||||
|
||||
APIUtil.getPropsValue("email_domain_to_space_mappings").map(extractor).getOrElse(Nil)
|
||||
}
|
||||
|
||||
val emailDomainToEntitlementMappings: List[EmailDomainToEntitlementMapping] = {
|
||||
def extractor(str: String) = try {
|
||||
val emailDomainToEntitlementMappings = json.parse(str).extract[List[EmailDomainToEntitlementMapping]]
|
||||
//The props value can be parse to JNothing.
|
||||
if(str.nonEmpty && emailDomainToEntitlementMappings == Nil)
|
||||
throw new RuntimeException("props [email_domain_to_entitlement_mappings] parse -> extract to Nil!")
|
||||
else
|
||||
emailDomainToEntitlementMappings
|
||||
} catch {
|
||||
case e: Throwable => // error handling, found wrong props value as early as possible.
|
||||
this.logger.error(s"props [email_domain_to_entitlement_mappings] value is invalid, it should be the class($EmailDomainToEntitlementMapping) json format, current value is $str ." );
|
||||
throw e;
|
||||
}
|
||||
|
||||
APIUtil.getPropsValue("email_domain_to_entitlement_mappings").map(extractor).getOrElse(Nil)
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,19 +13,19 @@ import net.liftweb.common.Full
|
||||
*/
|
||||
object ApiPropsWithAlias {
|
||||
import HelperFunctions._
|
||||
val requireScopesForAllRoles = getValueByNameOrAliasAsBoolean(
|
||||
def requireScopesForAllRoles = getValueByNameOrAliasAsBoolean(
|
||||
name="require_scopes_for_all_roles",
|
||||
alias="require_scopes",
|
||||
defaultValue="false")
|
||||
val migrationScriptsEnabled = getValueByNameOrAliasAsBoolean(
|
||||
def migrationScriptsEnabled = getValueByNameOrAliasAsBoolean(
|
||||
name="migration_scripts.enabled",
|
||||
alias="migration_scripts.execute",
|
||||
defaultValue="false")
|
||||
val allowAccountFirehose = getValueByNameOrAliasAsBoolean(
|
||||
def allowAccountFirehose = getValueByNameOrAliasAsBoolean(
|
||||
name="allow_account_firehose",
|
||||
alias="allow_firehose_views",
|
||||
defaultValue="false")
|
||||
val allowCustomerFirehose = getValueByNameOrAliasAsBoolean(
|
||||
def allowCustomerFirehose = getValueByNameOrAliasAsBoolean(
|
||||
name="allow_customer_firehose",
|
||||
alias="allow_firehose_views",
|
||||
defaultValue="false")
|
||||
|
||||
@ -320,6 +320,9 @@ object ApiRole {
|
||||
case class CanUseAccountFirehoseAtAnyBank(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canUseAccountFirehoseAtAnyBank = CanUseAccountFirehoseAtAnyBank()
|
||||
|
||||
case class CanUseAccountFirehose(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canUseAccountFirehose = CanUseAccountFirehose()
|
||||
|
||||
case class CanUseCustomerFirehoseAtAnyBank(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canUseCustomerFirehoseAtAnyBank = CanUseCustomerFirehoseAtAnyBank()
|
||||
|
||||
@ -343,6 +346,9 @@ object ApiRole {
|
||||
|
||||
case class CanLockUser (requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canLockUser = CanLockUser()
|
||||
|
||||
case class CanDeleteUser (requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canDeleteUser = CanDeleteUser()
|
||||
|
||||
case class CanReadUserLockedStatus(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canReadUserLockedStatus = CanReadUserLockedStatus()
|
||||
@ -456,12 +462,21 @@ object ApiRole {
|
||||
case class CanCreateDynamicEntity(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canCreateDynamicEntity = CanCreateDynamicEntity()
|
||||
|
||||
case class CanCreateBankLevelDynamicEntity(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canCreateBankLevelDynamicEntity = CanCreateBankLevelDynamicEntity()
|
||||
|
||||
case class CanUpdateDynamicEntity(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canUpdateDynamicEntity = CanUpdateDynamicEntity()
|
||||
|
||||
case class CanUpdateBankLevelDynamicEntity(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canUpdateBankLevelDynamicEntity = CanUpdateBankLevelDynamicEntity()
|
||||
|
||||
case class CanDeleteDynamicEntity(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canDeleteDynamicEntity = CanDeleteDynamicEntity()
|
||||
|
||||
case class CanDeleteBankLevelDynamicEntity(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canDeleteBankLevelDynamicEntity = CanDeleteBankLevelDynamicEntity()
|
||||
|
||||
case class CanGetBankLevelDynamicEntities(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canGetBankLevelDynamicEntities = CanGetBankLevelDynamicEntities()
|
||||
|
||||
@ -471,14 +486,29 @@ object ApiRole {
|
||||
case class CanGetDynamicEndpoints(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canGetDynamicEndpoints = CanGetDynamicEndpoints()
|
||||
|
||||
case class CanGetBankLevelDynamicEndpoint(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canGetBankLevelDynamicEndpoint = CanGetBankLevelDynamicEndpoint()
|
||||
|
||||
case class CanGetBankLevelDynamicEndpoints(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canGetBankLevelDynamicEndpoints = CanGetBankLevelDynamicEndpoints()
|
||||
|
||||
case class CanCreateDynamicEndpoint(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canCreateDynamicEndpoint = CanCreateDynamicEndpoint()
|
||||
|
||||
case class CanCreateBankLevelDynamicEndpoint(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canCreateBankLevelDynamicEndpoint = CanCreateBankLevelDynamicEndpoint()
|
||||
|
||||
case class CanUpdateDynamicEndpoint(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canUpdateDynamicEndpoint = CanUpdateDynamicEndpoint()
|
||||
|
||||
case class CanUpdateBankLevelDynamicEndpoint(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canUpdateBankLevelDynamicEndpoint = CanUpdateBankLevelDynamicEndpoint()
|
||||
|
||||
case class CanDeleteDynamicEndpoint(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canDeleteDynamicEndpoint = CanDeleteDynamicEndpoint()
|
||||
|
||||
case class CanDeleteBankLevelDynamicEndpoint(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canDeleteBankLevelDynamicEndpoint = CanDeleteBankLevelDynamicEndpoint()
|
||||
|
||||
case class CanCreateResetPasswordUrl(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canCreateResetPasswordUrl = CanCreateResetPasswordUrl()
|
||||
@ -714,6 +744,26 @@ object ApiRole {
|
||||
case class CanDeleteEndpointMapping(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canDeleteEndpointMapping = CanDeleteEndpointMapping()
|
||||
|
||||
case class CanCreateBankLevelEndpointMapping(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canCreateBankLevelEndpointMapping = CanCreateBankLevelEndpointMapping()
|
||||
|
||||
case class CanUpdateBankLevelEndpointMapping(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canUpdateBankLevelEndpointMapping = CanUpdateBankLevelEndpointMapping()
|
||||
|
||||
case class CanGetBankLevelEndpointMapping(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canGetBankLevelEndpointMapping = CanGetBankLevelEndpointMapping()
|
||||
|
||||
case class CanGetAllBankLevelEndpointMappings(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canGetAllBankLevelEndpointMappings = CanGetAllBankLevelEndpointMappings()
|
||||
|
||||
case class CanDeleteBankLevelEndpointMapping(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canDeleteBankLevelEndpointMapping = CanDeleteBankLevelEndpointMapping()
|
||||
|
||||
case class CanCreateUserInvitation(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canCreateUserInvitation = CanCreateUserInvitation()
|
||||
case class CanGetUserInvitation(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canGetUserInvitation = CanGetUserInvitation()
|
||||
|
||||
private val dynamicApiRoles = new ConcurrentHashMap[String, ApiRole]
|
||||
|
||||
private case class DynamicApiRole(role: String, requiresBankId: Boolean = false) extends ApiRole{
|
||||
|
||||
@ -137,7 +137,9 @@ case class CallContext(
|
||||
def authType: AuthenticationType = {
|
||||
if(hasGatewayHeader(authReqHeaderField)) {
|
||||
GatewayLogin
|
||||
} else if(hasDirectLoginHeader(authReqHeaderField)) {
|
||||
} else if(has2021DirectLoginHeader(requestHeaders)) { // Direct Login
|
||||
DirectLogin
|
||||
} else if(hasDirectLoginHeader(authReqHeaderField)) { // Direct Login Deprecated
|
||||
DirectLogin
|
||||
} else if(hasAnOAuthHeader(authReqHeaderField)) {
|
||||
AuthenticationType.`OAuth1.0a`
|
||||
|
||||
@ -42,6 +42,7 @@ object ApiTag {
|
||||
val apiTagCustomer = ResourceDocTag("Customer")
|
||||
val apiTagOnboarding = ResourceDocTag("Onboarding")
|
||||
val apiTagUser = ResourceDocTag("User") // Use for User Management / Info APIs
|
||||
val apiTagUserInvitation = ResourceDocTag("User-Invitation")
|
||||
val apiTagMeeting = ResourceDocTag("Customer-Meeting")
|
||||
val apiTagExperimental = ResourceDocTag("Experimental")
|
||||
val apiTagPerson = ResourceDocTag("Person")
|
||||
@ -68,10 +69,10 @@ object ApiTag {
|
||||
val apiTagMockedData = ResourceDocTag("Mocked-Data")
|
||||
val apiTagConsent = ResourceDocTag("Consent")
|
||||
val apiTagMethodRouting = ResourceDocTag("Method-Routing")
|
||||
val apiTagEndpointMapping = ResourceDocTag("Endpoint-Mapping")
|
||||
val apiTagWebUiProps = ResourceDocTag("WebUi-Props")
|
||||
val apiTagEndpointMapping = ResourceDocTag("Endpoint-Mapping-Manage")
|
||||
val apiTagManageDynamicEndpoint = ResourceDocTag("Dynamic-Endpoint-Manage")
|
||||
val apiTagManageDynamicEntity = ResourceDocTag("Dynamic-Entity-Manage")
|
||||
val apiTagDynamicSwaggerDoc = ResourceDocTag("Dynamic-Swagger-Doc-Manage")
|
||||
val apiTagDynamicResourceDoc = ResourceDocTag("Dynamic-Resource-Doc-Manage")
|
||||
val apiTagDynamicMessageDoc = ResourceDocTag("Dynamic-Message-Doc-Manage")
|
||||
val apiTagApiCollection = ResourceDocTag("Api-Collection")
|
||||
|
||||
@ -69,6 +69,7 @@ object CertificateUtil extends MdcLoggable {
|
||||
val jkspath = APIUtil.getPropsValue("keystore.path").getOrElse("")
|
||||
val jkspasswd = APIUtil.getPropsValue("keystore.password").getOrElse(APIUtil.initPasswd)
|
||||
val keypasswd = APIUtil.getPropsValue("keystore.passphrase").getOrElse(APIUtil.initPasswd)
|
||||
// This is used for QWAC certificate. Alias needs to be of that certificate.
|
||||
val alias = APIUtil.getPropsValue("keystore.alias").getOrElse("")
|
||||
val keyStore = KeyStore.getInstance(KeyStore.getDefaultType)
|
||||
val inputStream = new FileInputStream(jkspath)
|
||||
@ -103,7 +104,14 @@ object CertificateUtil extends MdcLoggable {
|
||||
.keyIDFromThumbprint()
|
||||
.build()
|
||||
jwk.toJSONString()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used for QWAC certificate.
|
||||
* x5s is the part of te JOSE Protected header we use it in case of Java Web Signature.
|
||||
* We sign response with rsaSigner and send it via "x-jws-signature" response header.
|
||||
* it's verified via x5c value at third party app.
|
||||
*/
|
||||
lazy val (rsaSigner, x5c, rsaPublicKey) = {
|
||||
val (privateKey: PrivateKey, certificate: Certificate) =
|
||||
Props.mode match {
|
||||
|
||||
@ -196,7 +196,9 @@ object Consent {
|
||||
createdByConsentId = consentId,
|
||||
name = name,
|
||||
email = email,
|
||||
userId = None
|
||||
userId = None,
|
||||
createdByUserInvitationId = None,
|
||||
company = None
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,6 +99,7 @@ object ErrorMessages {
|
||||
val FilterDateFormatError = s"OBP-10026: Failed to parse date string. Please use this format ${DateWithMsFormat.toPattern}!" // OBP-20026
|
||||
val FilterAnonFormatError = s"OBP-10028: anon parameter can only take two values: TRUE or FALSE!"
|
||||
val FilterDurationFormatError = s"OBP-10029: wrong value for `duration` parameter. Please send a positive integer (=>0)!"
|
||||
val FilterIsDeletedFormatError = s"OBP-10036: is_deleted parameter can only take two values: TRUE or FALSE!"
|
||||
|
||||
val InvalidApiVersionString = "OBP-00027: Invalid API Version string. We could not find the version specified."
|
||||
val IncorrectTriggerName = "OBP-10028: Incorrect Trigger name: "
|
||||
@ -455,6 +456,11 @@ object ErrorMessages {
|
||||
val InvalidEndpointMapping = "OBP-36006: Invalid Endpoint Mapping. "
|
||||
// General Resource related messages above here
|
||||
|
||||
// User Invitation
|
||||
val CannotCreateUserInvitation = "OBP-37081: Cannot create user invitation."
|
||||
val CannotGetUserInvitation = "OBP-37882: Cannot get user invitation."
|
||||
val CannotFindUserInvitation = "OBP-37883: Cannot find user invitation."
|
||||
|
||||
|
||||
// Transaction Request related messages (OBP-40XXX)
|
||||
val InvalidTransactionRequestType = "OBP-40001: Invalid value for TRANSACTION_REQUEST_TYPE"
|
||||
@ -526,6 +532,8 @@ object ErrorMessages {
|
||||
val InternalServerError = "OBP-50015: The server encountered an unexpected condition which prevented it from fulfilling the request."
|
||||
val KafkaServerUnavailable = "OBP-50016: The kafka server is unavailable."
|
||||
val NotAllowedEndpoint = "OBP-50017: The endpoint is forbidden at this API instance."
|
||||
val UnderConstructionError = "OBP-50018: Under Construction Error."
|
||||
val DatabaseConnectionClosedError = "OBP-50019: Cannot connect to the OBP database."
|
||||
|
||||
|
||||
// Connector Data Exceptions (OBP-502XX)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1794,11 +1794,14 @@ object Glossary {
|
||||
|
|
||||
|```
|
||||
|{
|
||||
| "username": "simonr",
|
||||
| "is_first": true,
|
||||
| "timestamp": "timestamp",
|
||||
| "consumer_id": "123",
|
||||
| "consumer_name": "Name of Consumer"
|
||||
| "login_user_name": "username",
|
||||
| "is_first": false,
|
||||
| "app_id": "85a965f0-0d55-4e0a-8b1c-649c4b01c4fb",
|
||||
| "app_name": "GWL",
|
||||
| "time_stamp": "2018-08-20T14:13:40Z",
|
||||
| "cbs_token": "your_token",
|
||||
| "cbs_id": "your_cbs_id",
|
||||
| "session_id": "123456789"
|
||||
|}
|
||||
|```
|
||||
|VERIFY SIGNATURE
|
||||
@ -1884,6 +1887,36 @@ object Glossary {
|
||||
|AS8D76F7A89S87D6F7A9SD876FA789SD78F6A7S9D78F6AS79DF87A6S7D9F7A6S7D9F78A6SD798F78679D786S789D78F6A7S9D78F6AS79DF876A7S89DF786AS9D87F69AS7D6FN1bWVyIn0.
|
||||
|KEuvjv3dmwkOhQ3JJ6dIShK8CG_fd2REApOGn1TRmgU"
|
||||
|
|
||||
|### Example python script
|
||||
|```
|
||||
|import jwt
|
||||
|from datetime import datetime, timezone
|
||||
|from obp_python.config import obp_api_host
|
||||
|import requests
|
||||
|
|
||||
|env = 'local'
|
||||
|DATE_FORMAT = '%Y-%m-%dT%H:%M:%SZ'
|
||||
|
|
||||
|payload = {
|
||||
| "login_user_name": "username",
|
||||
| "is_first": False,
|
||||
| "app_id": "85a965f0-0d55-4e0a-8b1c-649c4b01c4fb",
|
||||
| "app_name": "Name",
|
||||
| "time_stamp": datetime.now(timezone.utc).strftime(DATE_FORMAT),
|
||||
| "cbs_token": "yourtokenforcbs",
|
||||
| "cbs_id": "yourcbs_id",
|
||||
| "session_id": "123456789"
|
||||
|}
|
||||
|
|
||||
|
|
||||
|token = jwt.encode(payload, 'secretsecretsecretstsecretssssss', algorithm='HS256')
|
||||
|authorization = 'GatewayLogin token="{}"'.format(token)
|
||||
|headers = {'Authorization': authorization}
|
||||
|url = obp_api_host + '/obp/v4.0.0/users/current'
|
||||
|req = requests.get(url, headers=headers)
|
||||
|print(req.text)
|
||||
|```
|
||||
|
|
||||
|### Under the hood
|
||||
|
|
||||
|The file, GatewayLogin.scala handles the Gateway Login.
|
||||
@ -2037,6 +2070,38 @@ object Glossary {
|
||||
""")
|
||||
|
||||
|
||||
// TODO put the following wiki text here in source code with soft coded hosts etc. The problem is the text is currently too long
|
||||
glossaryItems += GlossaryItem(
|
||||
title = "Hola App log trace",
|
||||
description =
|
||||
s"""
|
||||
Please see:
|
||||
[OBP Hola App Log Trace](https://github.com/OpenBankProject/OBP-API/wiki/Log-trace-of-the-Hola-App-performing-Georgian-flavour-of-Berlin-Group-authentication,-consent-generation-and-consuming-Berlin-Group-Account,-Balance-and-Transaction-resources)
|
||||
""")
|
||||
|
||||
|
||||
glossaryItems += GlossaryItem(
|
||||
title = "API Collection",
|
||||
description =
|
||||
s"""|An API Collection is a collection of endpoints grouped together for a certain purpose.
|
||||
|
|
||||
|Having read access to a Collection does not constitute execute access on the endpoints in the Collection.
|
||||
|
|
||||
|(Execute access is governed by Entitlements to Roles - and in some cases, Views.)
|
||||
|
|
||||
|Collections can be created and shared. You can make a collection non-sharable but the default is sharable.
|
||||
|
|
||||
|Your "Favourites" in API Explorer is actually a collection you control named "Favourites".
|
||||
|
|
||||
|To share a Collection (e.g. your Favourites) just click on your Favourites in the API Explorer and share the URL in the browser. If you want to share the Collection via an API, just share the collection_id with a developer.
|
||||
|
|
||||
|If you share a Collection it can't be modified by anyone else, but anyone can use it as a basis for their own Favourites or another collection.
|
||||
|
|
||||
|At the time of writing (July 2021), there are 13 endpoints for controlling Collections.
|
||||
|
|
||||
""")
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// NOTE! Some glossary items are generated in ExampleValue.scala
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -81,7 +81,6 @@ object JwsUtil extends MdcLoggable {
|
||||
requestHeaders.find(_.name == "x-jws-signature").isDefined ||
|
||||
requestHeaders.find(_.name == "digest").isDefined
|
||||
}
|
||||
def createDigestHeader(input: String): String = s"digest: SHA-256=$input"
|
||||
private def getDeferredCriticalHeaders() = {
|
||||
val deferredCriticalHeaders = new util.HashSet[String]()
|
||||
deferredCriticalHeaders.add("sigT")
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package code.api.util
|
||||
|
||||
import java.io
|
||||
import java.util.Date
|
||||
import java.util.UUID.randomUUID
|
||||
|
||||
@ -11,6 +12,7 @@ import code.api.cache.Caching
|
||||
import code.api.util.APIUtil.{EntitlementAndScopeStatus, JsonResponseExtractor, OBPReturnType, afterAuthenticateInterceptResult, canGrantAccessToViewCommon, canRevokeAccessToViewCommon, connectorEmptyResponse, createHttpParamsByUrlFuture, createQueriesByHttpParamsFuture, fullBoxOrException, generateUUID, unboxFull, unboxFullOrFail}
|
||||
import code.api.util.ApiRole.canCreateAnyTransactionRequest
|
||||
import code.api.util.ErrorMessages.{InsufficientAuthorisationToCreateTransactionRequest, _}
|
||||
import code.api.ResourceDocs1_4_0.ResourceDocs140.ImplementationsResourceDocs
|
||||
import code.api.v1_2_1.OBPAPI1_2_1.Implementations1_2_1
|
||||
import code.api.v1_4_0.OBPAPI1_4_0.Implementations1_4_0
|
||||
import code.api.v2_0_0.OBPAPI2_0_0.Implementations2_0_0
|
||||
@ -31,10 +33,10 @@ import code.methodrouting.{MethodRoutingCommons, MethodRoutingProvider, MethodRo
|
||||
import code.model._
|
||||
import code.apicollectionendpoint.{ApiCollectionEndpointTrait, MappedApiCollectionEndpointsProvider}
|
||||
import code.apicollection.{ApiCollectionTrait, MappedApiCollectionsProvider}
|
||||
import code.model.dataAccess.BankAccountRouting
|
||||
import code.model.dataAccess.{AuthUser, BankAccountRouting}
|
||||
import code.standingorders.StandingOrderTrait
|
||||
import code.usercustomerlinks.UserCustomerLink
|
||||
import code.users.Users
|
||||
import code.users.{UserInvitation, UserInvitationProvider, Users}
|
||||
import code.util.Helper
|
||||
import com.openbankproject.commons.util.{ApiVersion, JsonUtils}
|
||||
import code.views.Views
|
||||
@ -70,7 +72,9 @@ import net.liftweb.json
|
||||
|
||||
object NewStyle {
|
||||
lazy val endpoints: List[(String, String)] = List(
|
||||
(nameOf(ImplementationsResourceDocs.getResourceDocsObp), ApiVersion.v1_4_0.toString),
|
||||
(nameOf(Implementations1_2_1.deleteWhereTagForViewOnTransaction), ApiVersion.v1_2_1.toString),
|
||||
(nameOf(Implementations1_2_1.getOtherAccountForTransaction), ApiVersion.v1_2_1.toString),
|
||||
(nameOf(Implementations1_2_1.getOtherAccountMetadata), ApiVersion.v1_2_1.toString),
|
||||
(nameOf(Implementations1_2_1.getCounterpartyPublicAlias), ApiVersion.v1_2_1.toString),
|
||||
(nameOf(Implementations1_2_1.addCounterpartyMoreInfo), ApiVersion.v1_2_1.toString),
|
||||
@ -311,11 +315,6 @@ object NewStyle {
|
||||
}
|
||||
} map { unboxFull(_) }
|
||||
}
|
||||
def getBalances(callContext: Option[CallContext]) : OBPReturnType[List[Bank]] = {
|
||||
Connector.connector.vend.getBanks(callContext: Option[CallContext]) map {
|
||||
connectorEmptyResponse(_, callContext)
|
||||
}
|
||||
}
|
||||
def getBankAccount(bankId : BankId, accountId : AccountId, callContext: Option[CallContext]): OBPReturnType[BankAccount] = {
|
||||
Connector.connector.vend.checkBankAccountExists(bankId, accountId, callContext) map { i =>
|
||||
(unboxFullOrFail(i._1, callContext,s"$BankAccountNotFound Current BankId is $bankId and Current AccountId is $accountId", 404 ), i._2)
|
||||
@ -344,6 +343,12 @@ object NewStyle {
|
||||
}
|
||||
}
|
||||
|
||||
def getBankAccountBalances(bankIdAccountId: BankIdAccountId, callContext: Option[CallContext]): OBPReturnType[AccountBalances] = {
|
||||
Connector.connector.vend.getBankAccountBalances(bankIdAccountId: BankIdAccountId, callContext: Option[CallContext]) map { i =>
|
||||
(unboxFullOrFail(i._1, callContext,s"$InvalidConnectorResponseForGetBankAccounts", 400 ), i._2)
|
||||
}
|
||||
}
|
||||
|
||||
def getAccountRouting(bankId: Option[BankId], scheme: String, address: String, callContext: Option[CallContext]) : OBPReturnType[BankAccountRouting] = {
|
||||
Future(Connector.connector.vend.getAccountRouting(bankId: Option[BankId], scheme: String, address : String, callContext: Option[CallContext])) map { i =>
|
||||
unboxFullOrFail(i, callContext,s"$AccountRoutingNotFound Current scheme is $scheme, current address is $address, current bankId is $bankId", 404 )
|
||||
@ -730,7 +735,19 @@ object NewStyle {
|
||||
i => (connectorEmptyResponse(i._1, callContext), i._2)
|
||||
}
|
||||
}
|
||||
|
||||
def createUserInvitation(bankId: BankId, firstName: String, lastName: String, email: String, company: String, country: String, purpose: String, callContext: Option[CallContext]): OBPReturnType[UserInvitation] = Future {
|
||||
val response: Box[UserInvitation] = UserInvitationProvider.userInvitationProvider.vend.createUserInvitation(bankId, firstName, lastName, email, company, country, purpose)
|
||||
(unboxFullOrFail(response, callContext, s"$CannotCreateUserInvitation", 400), callContext)
|
||||
}
|
||||
def getUserInvitation(bankId: BankId, secretLink: Long, callContext: Option[CallContext]): OBPReturnType[UserInvitation] = Future {
|
||||
val response: Box[UserInvitation] = UserInvitationProvider.userInvitationProvider.vend.getUserInvitation(bankId, secretLink)
|
||||
(unboxFullOrFail(response, callContext, s"$CannotGetUserInvitation", 400), callContext)
|
||||
}
|
||||
def getUserInvitations(bankId: BankId, callContext: Option[CallContext]): OBPReturnType[List[UserInvitation]] = Future {
|
||||
val response = UserInvitationProvider.userInvitationProvider.vend.getUserInvitations(bankId)
|
||||
(unboxFullOrFail(response, callContext, s"$CannotGetUserInvitation", 400), callContext)
|
||||
}
|
||||
|
||||
def getAdapterInfo(callContext: Option[CallContext]): OBPReturnType[InboundAdapterInfoInternal] = {
|
||||
Connector.connector.vend.getAdapterInfo(callContext) map {
|
||||
connectorEmptyResponse(_, callContext)
|
||||
@ -845,8 +862,10 @@ object NewStyle {
|
||||
}
|
||||
|
||||
def hasEntitlement(bankId: String, userId: String, role: ApiRole, callContext: Option[CallContext], errorMsg: String = ""): Future[Box[Unit]] = {
|
||||
val errorInfo = if(StringUtils.isBlank(errorMsg)) UserHasMissingRoles + role.toString()
|
||||
else errorMsg
|
||||
val errorInfo =
|
||||
if(StringUtils.isBlank(errorMsg)&& !bankId.isEmpty) UserHasMissingRoles + role.toString() + s" at Bank($bankId)"
|
||||
else if(StringUtils.isBlank(errorMsg)&& bankId.isEmpty) UserHasMissingRoles + role.toString()
|
||||
else errorMsg
|
||||
|
||||
Helper.booleanToFuture(errorInfo, cc=callContext) {
|
||||
APIUtil.hasEntitlement(bankId, userId, role)
|
||||
@ -865,11 +884,18 @@ object NewStyle {
|
||||
APIUtil.hasAtLeastOneEntitlement(bankId, userId, roles)
|
||||
} map validateRequestPayload(callContext)
|
||||
|
||||
def hasAtLeastOneEntitlement(bankId: String, userId: String, roles: List[ApiRole], callContext: Option[CallContext]): Future[Box[Unit]] =
|
||||
hasAtLeastOneEntitlement(UserHasMissingRoles + roles.mkString(" or "))(bankId, userId, roles, callContext)
|
||||
def hasAtLeastOneEntitlement(bankId: String, userId: String, roles: List[ApiRole], callContext: Option[CallContext]): Future[Box[Unit]] = {
|
||||
val errorMessage = if (roles.filter(_.requiresBankId).isEmpty) UserHasMissingRoles + roles.mkString(" or ") else UserHasMissingRoles + roles.mkString(" or ") + s" for BankId($bankId)."
|
||||
hasAtLeastOneEntitlement(errorMessage)(bankId, userId, roles, callContext)
|
||||
}
|
||||
|
||||
def hasAllEntitlements(bankId: String, userId: String, roles: List[ApiRole], callContext: Option[CallContext]): Box[Unit] = {
|
||||
val boxResult = Helper.booleanToBox(APIUtil.hasAllEntitlements(bankId, userId, roles), s"$UserHasMissingRoles${roles.mkString(" and ")} entitlements are required.")
|
||||
val errorMessage = if (roles.filter(_.requiresBankId).isEmpty)
|
||||
s"$UserHasMissingRoles${roles.mkString(" and ")} entitlements are required."
|
||||
else
|
||||
s"$UserHasMissingRoles${roles.mkString(" and ")} entitlements are required for BankId($bankId)."
|
||||
|
||||
val boxResult = Helper.booleanToBox(APIUtil.hasAllEntitlements(bankId, userId, roles), errorMessage)
|
||||
validateRequestPayload(callContext)(boxResult)
|
||||
}
|
||||
|
||||
@ -912,7 +938,21 @@ object NewStyle {
|
||||
i => (connectorEmptyResponse(i._1, callContext), i._2)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def deleteUser(userPrimaryKey: UserPrimaryKey, callContext: Option[CallContext]): OBPReturnType[Boolean] = Future {
|
||||
AuthUser.scrambleAuthUser(userPrimaryKey) match {
|
||||
case Full(true) =>
|
||||
Users.users.vend.scrambleDataOfResourceUser(userPrimaryKey) match {
|
||||
case Full(true) =>
|
||||
val createdByUserInvitationId: String = Users.users.vend.getUserByResourceUserId(userPrimaryKey.value).flatMap(_.createdByUserInvitationId).getOrElse(generateUUID())
|
||||
(UserInvitationProvider.userInvitationProvider.vend.scrambleUserInvitation(createdByUserInvitationId).getOrElse(false), callContext)
|
||||
case _ =>
|
||||
(false, callContext)
|
||||
}
|
||||
case _ =>
|
||||
(false, callContext)
|
||||
}
|
||||
}
|
||||
|
||||
def findByUserId(userId: String, callContext: Option[CallContext]): OBPReturnType[User] = {
|
||||
Future { UserX.findByUserId(userId).map(user =>(user, callContext))} map {
|
||||
@ -2340,28 +2380,28 @@ object NewStyle {
|
||||
}
|
||||
}
|
||||
|
||||
def createOrUpdateEndpointMapping(endpointMapping: EndpointMappingT, callContext: Option[CallContext]) = Future {
|
||||
(EndpointMappingProvider.endpointMappingProvider.vend.createOrUpdate(endpointMapping), callContext)
|
||||
def createOrUpdateEndpointMapping(bankId: Option[String], endpointMapping: EndpointMappingT, callContext: Option[CallContext]) = Future {
|
||||
(EndpointMappingProvider.endpointMappingProvider.vend.createOrUpdate(bankId, endpointMapping), callContext)
|
||||
} map {
|
||||
i => (connectorEmptyResponse(i._1, callContext), i._2)
|
||||
}
|
||||
|
||||
def deleteEndpointMapping(endpointMappingId: String, callContext: Option[CallContext]) = Future {
|
||||
(EndpointMappingProvider.endpointMappingProvider.vend.delete(endpointMappingId), callContext)
|
||||
def deleteEndpointMapping(bankId: Option[String], endpointMappingId: String, callContext: Option[CallContext]) = Future {
|
||||
(EndpointMappingProvider.endpointMappingProvider.vend.delete(bankId, endpointMappingId), callContext)
|
||||
} map {
|
||||
i => (connectorEmptyResponse(i._1, callContext), i._2)
|
||||
}
|
||||
|
||||
def getEndpointMappingById(endpointMappingId : String, callContext: Option[CallContext]): OBPReturnType[EndpointMappingT] = {
|
||||
val endpointMappingBox: Box[EndpointMappingT] = EndpointMappingProvider.endpointMappingProvider.vend.getById(endpointMappingId)
|
||||
def getEndpointMappingById(bankId: Option[String], endpointMappingId : String, callContext: Option[CallContext]): OBPReturnType[EndpointMappingT] = {
|
||||
val endpointMappingBox: Box[EndpointMappingT] = EndpointMappingProvider.endpointMappingProvider.vend.getById(bankId, endpointMappingId)
|
||||
Future{
|
||||
val endpointMapping = unboxFullOrFail(endpointMappingBox, callContext, s"$EndpointMappingNotFoundByEndpointMappingId Current ENDPOINT_MAPPING_ID is $endpointMappingId", 404)
|
||||
(endpointMapping, callContext)
|
||||
}
|
||||
}
|
||||
|
||||
def getEndpointMappingByOperationId(operationId : String, callContext: Option[CallContext]): OBPReturnType[EndpointMappingT] = {
|
||||
val endpointMappingBox: Box[EndpointMappingT] = EndpointMappingProvider.endpointMappingProvider.vend.getByOperationId(operationId)
|
||||
def getEndpointMappingByOperationId(bankId: Option[String], operationId : String, callContext: Option[CallContext]): OBPReturnType[EndpointMappingT] = {
|
||||
val endpointMappingBox: Box[EndpointMappingT] = EndpointMappingProvider.endpointMappingProvider.vend.getByOperationId(bankId, operationId)
|
||||
Future{
|
||||
val endpointMapping = unboxFullOrFail(endpointMappingBox, callContext, s"$EndpointMappingNotFoundByOperationId Current OPERATION_ID is $operationId",404)
|
||||
(endpointMapping, callContext)
|
||||
@ -2370,13 +2410,13 @@ object NewStyle {
|
||||
|
||||
private[this] val endpointMappingTTL = APIUtil.getPropsValue(s"endpointMapping.cache.ttl.seconds", "0").toInt
|
||||
|
||||
def getEndpointMappings(callContext: Option[CallContext]): OBPReturnType[List[EndpointMappingT]] = {
|
||||
def getEndpointMappings(bankId: Option[String], callContext: Option[CallContext]): OBPReturnType[List[EndpointMappingT]] = {
|
||||
import scala.concurrent.duration._
|
||||
|
||||
var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString)
|
||||
CacheKeyFromArguments.buildCacheKey {
|
||||
Caching.memoizeSyncWithProvider(Some(cacheKey.toString()))(endpointMappingTTL second) {
|
||||
Future{(EndpointMappingProvider.endpointMappingProvider.vend.getAllEndpointMappings(), callContext)}
|
||||
Future{(EndpointMappingProvider.endpointMappingProvider.vend.getAllEndpointMappings(bankId), callContext)}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2395,7 +2435,7 @@ object NewStyle {
|
||||
}
|
||||
|
||||
private def updateDynamicEntity(dynamicEntity: DynamicEntityT, dynamicEntityId: String , callContext: Option[CallContext]): Future[Box[DynamicEntityT]] = {
|
||||
val originEntity = DynamicEntityProvider.connectorMethodProvider.vend.getById(dynamicEntityId)
|
||||
val originEntity = DynamicEntityProvider.connectorMethodProvider.vend.getById(dynamicEntity.bankId, dynamicEntityId)
|
||||
// if can't find by id, return 404 error
|
||||
val idNotExistsMsg = s"$DynamicEntityNotFoundByDynamicEntityId dynamicEntityId = ${dynamicEntity.dynamicEntityId.get}."
|
||||
|
||||
@ -2431,9 +2471,9 @@ object NewStyle {
|
||||
* @param dynamicEntityId
|
||||
* @return
|
||||
*/
|
||||
def deleteDynamicEntity(dynamicEntityId: String): Future[Box[Boolean]] = Future {
|
||||
def deleteDynamicEntity(bankId: Option[String], dynamicEntityId: String): Future[Box[Boolean]] = Future {
|
||||
for {
|
||||
entity <- DynamicEntityProvider.connectorMethodProvider.vend.getById(dynamicEntityId)
|
||||
entity <- DynamicEntityProvider.connectorMethodProvider.vend.getById(bankId, dynamicEntityId)
|
||||
deleteEntityResult <- DynamicEntityProvider.connectorMethodProvider.vend.delete(entity)
|
||||
deleteEntitleMentResult <- if(deleteEntityResult) {
|
||||
Entitlement.entitlement.vend.deleteDynamicEntityEntitlement(entity.entityName, entity.bankId)
|
||||
@ -2448,15 +2488,15 @@ object NewStyle {
|
||||
}
|
||||
}
|
||||
|
||||
def getDynamicEntityById(dynamicEntityId : String, callContext: Option[CallContext]): OBPReturnType[DynamicEntityT] = {
|
||||
val dynamicEntityBox: Box[DynamicEntityT] = DynamicEntityProvider.connectorMethodProvider.vend.getById(dynamicEntityId)
|
||||
def getDynamicEntityById(bankId: Option[String], dynamicEntityId : String, callContext: Option[CallContext]): OBPReturnType[DynamicEntityT] = {
|
||||
val dynamicEntityBox: Box[DynamicEntityT] = DynamicEntityProvider.connectorMethodProvider.vend.getById(bankId, dynamicEntityId)
|
||||
val dynamicEntity = unboxFullOrFail(dynamicEntityBox, callContext, DynamicEntityNotFoundByDynamicEntityId, 404)
|
||||
Future{
|
||||
(dynamicEntity, callContext)
|
||||
}
|
||||
}
|
||||
|
||||
def getDynamicEntityByEntityName(entityName : String, callContext: Option[CallContext]): OBPReturnType[Box[DynamicEntityT]] = Future {
|
||||
def getDynamicEntityByEntityName(bankId: Option[String], entityName : String, callContext: Option[CallContext]): OBPReturnType[Box[DynamicEntityT]] = Future {
|
||||
val boxedDynamicEntity = DynamicEntityProvider.connectorMethodProvider.vend.getByEntityName(entityName)
|
||||
(boxedDynamicEntity, callContext)
|
||||
}
|
||||
@ -2466,28 +2506,17 @@ object NewStyle {
|
||||
else APIUtil.getPropsValue(s"dynamicEntity.cache.ttl.seconds", "30").toInt
|
||||
}
|
||||
|
||||
def getDynamicEntities(): List[DynamicEntityT] = {
|
||||
def getDynamicEntities(bankId: Option[String]): List[DynamicEntityT] = {
|
||||
import scala.concurrent.duration._
|
||||
|
||||
var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString)
|
||||
CacheKeyFromArguments.buildCacheKey {
|
||||
Caching.memoizeSyncWithProvider(Some(cacheKey.toString()))(dynamicEntityTTL second) {
|
||||
DynamicEntityProvider.connectorMethodProvider.vend.getDynamicEntities()
|
||||
DynamicEntityProvider.connectorMethodProvider.vend.getDynamicEntities(bankId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def getDynamicEntitiesByBankId(bankId: String): List[DynamicEntityT] = {
|
||||
import scala.concurrent.duration._
|
||||
|
||||
var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString)
|
||||
CacheKeyFromArguments.buildCacheKey {
|
||||
Caching.memoizeSyncWithProvider(Some(cacheKey.toString()))(dynamicEntityTTL second) {
|
||||
DynamicEntityProvider.connectorMethodProvider.vend.getDynamicEntitiesByBankId(bankId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def getDynamicEntitiesByUserId(userId: String): List[DynamicEntityT] = {
|
||||
import scala.concurrent.duration._
|
||||
|
||||
@ -2688,28 +2717,33 @@ object NewStyle {
|
||||
getConnectorByName(connectorName).flatMap(_.callableMethods.get(methodName))
|
||||
}
|
||||
|
||||
def createDynamicEndpoint(userId: String, swaggerString: String, callContext: Option[CallContext]): OBPReturnType[DynamicEndpointT] = Future {
|
||||
(DynamicEndpointProvider.connectorMethodProvider.vend.create(userId, swaggerString), callContext)
|
||||
def createDynamicEndpoint(bankId:Option[String], userId: String, swaggerString: String, callContext: Option[CallContext]): OBPReturnType[DynamicEndpointT] = Future {
|
||||
(DynamicEndpointProvider.connectorMethodProvider.vend.create(bankId:Option[String], userId, swaggerString), callContext)
|
||||
} map {
|
||||
i => (connectorEmptyResponse(i._1, callContext), i._2)
|
||||
}
|
||||
|
||||
def updateDynamicEndpointHost(userId: String, swaggerString: String, callContext: Option[CallContext]): OBPReturnType[DynamicEndpointT] = Future {
|
||||
(DynamicEndpointProvider.connectorMethodProvider.vend.updateHost(userId, swaggerString), callContext)
|
||||
def updateDynamicEndpointHost(bankId: Option[String], userId: String, swaggerString: String, callContext: Option[CallContext]): OBPReturnType[DynamicEndpointT] = Future {
|
||||
(DynamicEndpointProvider.connectorMethodProvider.vend.updateHost(bankId, userId, swaggerString), callContext)
|
||||
} map {
|
||||
i => (connectorEmptyResponse(i._1, callContext), i._2)
|
||||
}
|
||||
|
||||
def getDynamicEndpoint(dynamicEndpointId: String, callContext: Option[CallContext]): OBPReturnType[DynamicEndpointT] = {
|
||||
val dynamicEndpointBox: Box[DynamicEndpointT] = DynamicEndpointProvider.connectorMethodProvider.vend.get(dynamicEndpointId)
|
||||
val dynamicEndpoint = unboxFullOrFail(dynamicEndpointBox, callContext, DynamicEndpointNotFoundByDynamicEndpointId, 404)
|
||||
def getDynamicEndpoint(bankId: Option[String], dynamicEndpointId: String, callContext: Option[CallContext]): OBPReturnType[DynamicEndpointT] = {
|
||||
val dynamicEndpointBox: Box[DynamicEndpointT] = DynamicEndpointProvider.connectorMethodProvider.vend.get(bankId, dynamicEndpointId)
|
||||
val errorMessage =
|
||||
if(bankId.isEmpty)
|
||||
DynamicEndpointNotFoundByDynamicEndpointId.replace("DYNAMIC_ENDPOINT_ID.", s"DYNAMIC_ENDPOINT_ID($dynamicEndpointId).")
|
||||
else
|
||||
DynamicEndpointNotFoundByDynamicEndpointId.replace("for DYNAMIC_ENDPOINT_ID.",s"for DYNAMIC_ENDPOINT_ID($dynamicEndpointId) and BANK_ID(${bankId.getOrElse("")}).")
|
||||
val dynamicEndpoint = unboxFullOrFail(dynamicEndpointBox, callContext, errorMessage, 404)
|
||||
Future{
|
||||
(dynamicEndpoint, callContext)
|
||||
}
|
||||
}
|
||||
|
||||
def getDynamicEndpoints(callContext: Option[CallContext]): OBPReturnType[List[DynamicEndpointT]] = Future {
|
||||
(DynamicEndpointProvider.connectorMethodProvider.vend.getAll(), callContext)
|
||||
def getDynamicEndpoints(bankId: Option[String], callContext: Option[CallContext]): OBPReturnType[List[DynamicEndpointT]] = Future {
|
||||
(DynamicEndpointProvider.connectorMethodProvider.vend.getAll(bankId), callContext)
|
||||
}
|
||||
|
||||
def getDynamicEndpointsByUserId(userId: String, callContext: Option[CallContext]): OBPReturnType[List[DynamicEndpointT]] = Future {
|
||||
@ -2721,18 +2755,18 @@ object NewStyle {
|
||||
* @param callContext
|
||||
* @return
|
||||
*/
|
||||
def deleteDynamicEndpoint(dynamicEndpointId: String, callContext: Option[CallContext]): Future[Box[Boolean]] = {
|
||||
val dynamicEndpoint: OBPReturnType[DynamicEndpointT] = this.getDynamicEndpoint(dynamicEndpointId, callContext)
|
||||
def deleteDynamicEndpoint(bankId: Option[String], dynamicEndpointId: String, callContext: Option[CallContext]): Future[Box[Boolean]] = {
|
||||
val dynamicEndpoint: OBPReturnType[DynamicEndpointT] = this.getDynamicEndpoint(bankId, dynamicEndpointId, callContext)
|
||||
for {
|
||||
(entity, _) <- dynamicEndpoint
|
||||
deleteEndpointResult: Box[Boolean] = {
|
||||
val roles = DynamicEndpointHelper.getRoles(dynamicEndpointId).map(_.toString())
|
||||
val roles = DynamicEndpointHelper.getRoles(bankId, dynamicEndpointId).map(_.toString())
|
||||
roles.foreach(ApiRole.removeDynamicApiRole(_))
|
||||
val rolesDeleteResult: Box[Boolean] = Entitlement.entitlement.vend.deleteEntitlements(roles)
|
||||
Box !! (rolesDeleteResult == Full(true))
|
||||
}
|
||||
deleteSuccess = if(deleteEndpointResult.isDefined && deleteEndpointResult.head) {
|
||||
tryo {DynamicEndpointProvider.connectorMethodProvider.vend.delete(dynamicEndpointId)}
|
||||
tryo {DynamicEndpointProvider.connectorMethodProvider.vend.delete(bankId, dynamicEndpointId)}
|
||||
}else{
|
||||
Full(false)
|
||||
}
|
||||
|
||||
@ -45,6 +45,7 @@ case class OBPConnectorName(value: String) extends OBPQueryParam
|
||||
case class OBPEmpty() extends OBPQueryParam
|
||||
case class OBPCustomerId(value: String) extends OBPQueryParam
|
||||
case class OBPLockedStatus(value: String) extends OBPQueryParam
|
||||
case class OBPIsDeleted(value: Boolean) extends OBPQueryParam
|
||||
|
||||
object OBPQueryParam {
|
||||
val LIMIT = "limit"
|
||||
|
||||
@ -33,7 +33,9 @@ object Migration extends MdcLoggable {
|
||||
val toExecute: Boolean = executeAll || scriptsToExecute.contains(name)
|
||||
val isExecuted = MigrationScriptLogProvider.migrationScriptLogProvider.vend.isExecuted(name)
|
||||
(toExecute, isExecuted) match {
|
||||
case (true, false) => blockOfCode
|
||||
case (true, false) =>
|
||||
logger.warn(s"Migration.database.$name is started at this instance.")
|
||||
blockOfCode
|
||||
case _ => true
|
||||
}
|
||||
}
|
||||
@ -49,6 +51,7 @@ object Migration extends MdcLoggable {
|
||||
}
|
||||
MigrationScriptLogProvider.migrationScriptLogProvider.vend.saveLog(name, commitId, isSuccessful, startDate, endDate, remark) match {
|
||||
case true =>
|
||||
logger.warn(s"Migration.database.$name is executed at this instance.")
|
||||
case false =>
|
||||
logger.warn(s"Migration.database.$name is executed at this instance but the corresponding log is not saved!!!!!!")
|
||||
}
|
||||
@ -56,7 +59,7 @@ object Migration extends MdcLoggable {
|
||||
|
||||
object database {
|
||||
|
||||
def executeScripts(): Boolean = executeScript {
|
||||
def executeScripts(startedBeforeSchemifier: Boolean): Boolean = executeScript {
|
||||
dummyScript()
|
||||
populateTableViewDefinition()
|
||||
populateTableAccountAccess()
|
||||
@ -77,6 +80,7 @@ object Migration extends MdcLoggable {
|
||||
alterColumnStatusAtTableMappedConsent()
|
||||
alterColumnDetailsAtTableTransactionRequest()
|
||||
deleteDuplicatedRowsInTheTableUserAuthContext()
|
||||
populateTheFieldDeletedAtResourceUser(startedBeforeSchemifier)
|
||||
}
|
||||
|
||||
private def dummyScript(): Boolean = {
|
||||
@ -232,6 +236,17 @@ object Migration extends MdcLoggable {
|
||||
MigrationOfUserAuthContext.removeDuplicates(name)
|
||||
}
|
||||
}
|
||||
private def populateTheFieldDeletedAtResourceUser(startedBeforeSchemifier: Boolean): Boolean = {
|
||||
if(startedBeforeSchemifier == true) {
|
||||
logger.warn(s"Migration.database.populateTheFieldDeletedAtResourceUser(true) cannot be run before Schemifier.")
|
||||
true
|
||||
} else {
|
||||
val name = nameOf(populateTheFieldDeletedAtResourceUser(startedBeforeSchemifier))
|
||||
runOnce(name) {
|
||||
MigrationOfResourceUser.populateNewFieldIsDeleted(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -36,7 +36,7 @@ object MigrationOfConsumer {
|
||||
consumer <- Consumer.findAll() if consumer.appType.get.isEmpty()
|
||||
} yield {
|
||||
consumer
|
||||
.appType(AppType.Web.toString())
|
||||
.appType(AppType.Confidential.toString())
|
||||
.saveMe()
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,56 @@
|
||||
package code.api.util.migration
|
||||
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.{ZoneId, ZonedDateTime}
|
||||
|
||||
import code.api.util.APIUtil
|
||||
import code.api.util.migration.Migration.{DbFunction, saveLog}
|
||||
import code.model.dataAccess.ResourceUser
|
||||
import code.model.{AppType, Consumer}
|
||||
import net.liftweb.mapper.DB
|
||||
import net.liftweb.util.{DefaultConnectionIdentifier, Helpers}
|
||||
|
||||
object MigrationOfResourceUser {
|
||||
|
||||
val oneDayAgo = ZonedDateTime.now(ZoneId.of("UTC")).minusDays(1)
|
||||
val oneYearInFuture = ZonedDateTime.now(ZoneId.of("UTC")).plusYears(1)
|
||||
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm'Z'")
|
||||
|
||||
def populateNewFieldIsDeleted(name: String): Boolean = {
|
||||
DbFunction.tableExists(ResourceUser, (DB.use(DefaultConnectionIdentifier){ conn => conn})) match {
|
||||
case true =>
|
||||
val startDate = System.currentTimeMillis()
|
||||
val commitId: String = APIUtil.gitCommit
|
||||
var isSuccessful = false
|
||||
|
||||
// Make back up
|
||||
DbFunction.makeBackUpOfTable(ResourceUser)
|
||||
|
||||
val emptyDeletedField =
|
||||
for {
|
||||
user <- ResourceUser.findAll() if user.isDeleted == false
|
||||
} yield {
|
||||
user.IsDeleted(false).saveMe()
|
||||
}
|
||||
|
||||
val endDate = System.currentTimeMillis()
|
||||
val comment: String =
|
||||
s"""Updated number of rows:
|
||||
|${emptyDeletedField.size}
|
||||
|""".stripMargin
|
||||
isSuccessful = true
|
||||
saveLog(name, commitId, isSuccessful, startDate, endDate, comment)
|
||||
isSuccessful
|
||||
|
||||
case false =>
|
||||
val startDate = System.currentTimeMillis()
|
||||
val commitId: String = APIUtil.gitCommit
|
||||
val isSuccessful = false
|
||||
val endDate = System.currentTimeMillis()
|
||||
val comment: String =
|
||||
s"""${Consumer._dbTableNameLC} table does not exist""".stripMargin
|
||||
saveLog(name, commitId, isSuccessful, startDate, endDate, comment)
|
||||
isSuccessful
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3106,13 +3106,18 @@ trait APIMethods121 {
|
||||
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "transactions":: TransactionId(transactionId) :: "other_account" :: Nil JsonGet req => {
|
||||
cc =>
|
||||
for {
|
||||
account <- BankAccountX(bankId, accountId) ?~! BankAccountNotFound
|
||||
view <- APIUtil.checkViewAccessAndReturnView(viewId, BankIdAccountId(account.bankId, account.accountId), cc.user) ?~! ViewNotFound
|
||||
(transaction, callerContext) <- account.moderatedTransaction(transactionId, view, BankIdAccountId(bankId,accountId), cc.user, Some(cc))
|
||||
moderatedOtherBankAccount <- transaction.otherBankAccount
|
||||
(Full(u), callContext) <- authenticatedAccess(cc)
|
||||
(account, callContext) <- NewStyle.function.checkBankAccountExists(bankId, accountId, callContext)
|
||||
view <- NewStyle.function.checkViewAccessAndReturnView(viewId, BankIdAccountId(bankId, accountId), Some(u), callContext)
|
||||
(moderatedTransaction, callContext) <- account.moderatedTransactionFuture(transactionId, view, Full(u), callContext) map {
|
||||
unboxFullOrFail(_, callContext, GetTransactionsException)
|
||||
}
|
||||
_ <- NewStyle.function.tryons(GetTransactionsException, 400, callContext) {
|
||||
moderatedTransaction.otherBankAccount.isDefined
|
||||
}
|
||||
} yield {
|
||||
val otherBankAccountJson = JSONFactory.createOtherBankAccount(moderatedOtherBankAccount)
|
||||
successJsonResponse(Extraction.decompose(otherBankAccountJson))
|
||||
val otherBankAccountJson = JSONFactory.createOtherBankAccount(moderatedTransaction.otherBankAccount.get)
|
||||
(otherBankAccountJson, HttpCode.`200`(callContext))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -481,9 +481,9 @@ trait APIMethods300 {
|
||||
|""".stripMargin,
|
||||
emptyObjectJson,
|
||||
moderatedCoreAccountsJsonV300,
|
||||
List(UserNotLoggedIn,UnknownError),
|
||||
List(UserNotLoggedIn,AccountFirehoseNotAllowedOnThisInstance,UnknownError),
|
||||
List(apiTagAccount, apiTagAccountFirehose, apiTagFirehoseData, apiTagNewStyle),
|
||||
Some(List(canUseAccountFirehoseAtAnyBank))
|
||||
Some(List(canUseAccountFirehoseAtAnyBank, ApiRole.canUseAccountFirehose))
|
||||
)
|
||||
|
||||
lazy val getFirehoseAccountsAtOneBank : OBPEndpoint = {
|
||||
@ -492,11 +492,12 @@ trait APIMethods300 {
|
||||
cc =>
|
||||
for {
|
||||
(Full(u), callContext) <- authenticatedAccess(cc)
|
||||
_ <- Helper.booleanToFuture(failMsg = AccountFirehoseNotAllowedOnThisInstance +" or " + UserHasMissingRoles + CanUseAccountFirehoseAtAnyBank, cc=callContext) {
|
||||
canUseAccountFirehose(u)
|
||||
_ <- Helper.booleanToFuture(failMsg = AccountFirehoseNotAllowedOnThisInstance , cc=cc.callContext) {
|
||||
allowAccountFirehose
|
||||
}
|
||||
_ <- NewStyle.function.hasAtLeastOneEntitlement(bankId.value, u.userId, ApiRole.canUseAccountFirehose :: canUseAccountFirehoseAtAnyBank :: Nil, callContext)
|
||||
(bank, callContext) <- NewStyle.function.getBank(bankId, callContext)
|
||||
view <- NewStyle.function.checkViewAccessAndReturnView(viewId, BankIdAccountId(BankId(""), AccountId("")), Some(u), callContext)
|
||||
view <- NewStyle.function.checkViewAccessAndReturnView(viewId, BankIdAccountId(bankId, AccountId("")), Some(u), callContext)
|
||||
availableBankIdAccountIdList <- Future {
|
||||
Views.views.vend.getAllFirehoseAccounts(bank.bankId).map(a => BankIdAccountId(a.bankId,a.accountId))
|
||||
}
|
||||
@ -578,9 +579,10 @@ trait APIMethods300 {
|
||||
cc =>
|
||||
for {
|
||||
(Full(u), callContext) <- authenticatedAccess(cc)
|
||||
_ <- Helper.booleanToFuture(failMsg = AccountFirehoseNotAllowedOnThisInstance +" or " + UserHasMissingRoles + CanUseAccountFirehoseAtAnyBank , cc=callContext) {
|
||||
canUseAccountFirehose(u)
|
||||
_ <- Helper.booleanToFuture(failMsg = AccountFirehoseNotAllowedOnThisInstance , cc=callContext) {
|
||||
allowAccountFirehose
|
||||
}
|
||||
_ <- NewStyle.function.hasEntitlement("", u.userId, ApiRole.canUseAccountFirehoseAtAnyBank, callContext)
|
||||
(bank, callContext) <- NewStyle.function.getBank(bankId, callContext)
|
||||
(bankAccount, callContext) <- NewStyle.function.getBankAccount(bankId, accountId, callContext)
|
||||
view <- NewStyle.function.checkViewAccessAndReturnView(viewId, BankIdAccountId(bankAccount.bankId, bankAccount.accountId),Some(u), callContext)
|
||||
|
||||
@ -465,10 +465,11 @@ trait APIMethods310 {
|
||||
cc =>
|
||||
for {
|
||||
(Full(u), callContext) <- authenticatedAccess(cc)
|
||||
(_, callContext) <- NewStyle.function.getBank(bankId, callContext)
|
||||
_ <- Helper.booleanToFuture(failMsg = CustomerFirehoseNotAllowedOnThisInstance +" or " + UserHasMissingRoles + CanUseCustomerFirehoseAtAnyBank, cc=callContext) {
|
||||
canUseCustomerFirehose(u)
|
||||
_ <- Helper.booleanToFuture(failMsg = AccountFirehoseNotAllowedOnThisInstance , cc=callContext) {
|
||||
allowCustomerFirehose
|
||||
}
|
||||
_ <- NewStyle.function.hasEntitlement("", u.userId, ApiRole.canUseCustomerFirehoseAtAnyBank, callContext)
|
||||
(_, callContext) <- NewStyle.function.getBank(bankId, callContext)
|
||||
allowedParams = List("sort_direction", "limit", "offset", "from_date", "to_date")
|
||||
httpParams <- NewStyle.function.extractHttpParamsFromUrl(cc.url)
|
||||
obpQueryParams <- NewStyle.function.createObpParams(httpParams, allowedParams, callContext)
|
||||
@ -1191,8 +1192,10 @@ trait APIMethods310 {
|
||||
"/banks/BANK_ID/customers",
|
||||
"Create Customer",
|
||||
s"""
|
||||
|The Customer resource stores the customer number, legal name, email, phone number, their date of birth, relationship status, education attained, a url for a profile image, KYC status etc.
|
||||
|The Customer resource stores the customer number (which is set by the backend), legal name, email, phone number, their date of birth, relationship status, education attained, a url for a profile image, KYC status etc.
|
||||
|Dates need to be in the format 2013-01-21T23:08:00Z
|
||||
|
|
||||
|Note: If you need to set a specific customer number, use the Update Customer Number endpoint after this call.
|
||||
|
|
||||
|${authenticationRequiredMessage(true)}
|
||||
|""",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -28,17 +28,18 @@ package code.api.v4_0_0
|
||||
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
|
||||
import code.api.attributedefinition.AttributeDefinition
|
||||
import code.api.util.APIUtil
|
||||
import code.api.util.APIUtil.{DateWithDay, DateWithSeconds, stringOptionOrNull, stringOrNull}
|
||||
import code.api.v1_2_1.JSONFactory.{createAmountOfMoneyJSON, createOwnersJSON}
|
||||
import code.api.v1_2_1.{BankRoutingJsonV121, JSONFactory, UserJSONV121, ViewJSONV121}
|
||||
import code.api.v1_4_0.JSONFactory1_4_0.{LocationJsonV140, MetaJsonV140, TransactionRequestAccountJsonV140, transformToLocationFromV140, transformToMetaFromV140}
|
||||
import code.api.v2_0_0.TransactionRequestChargeJsonV200
|
||||
import code.api.v2_0_0.{EntitlementJSONs, JSONFactory200, TransactionRequestChargeJsonV200}
|
||||
import code.api.v2_1_0.{IbanJson, JSONFactory210, PostCounterpartyBespokeJson, ResourceUserJSON}
|
||||
import code.api.v2_2_0.CounterpartyMetadataJson
|
||||
import code.api.v3_0_0.JSONFactory300.{createAccountRoutingsJSON, createAccountRulesJSON, createLocationJson, createMetaJson, transformToAddressFromV300}
|
||||
import code.api.v3_0_0.{AccountRuleJsonV300, AddressJsonV300, CustomerAttributeResponseJsonV300, OpeningTimesV300}
|
||||
import code.api.v3_0_0.{AccountRuleJsonV300, AddressJsonV300, CustomerAttributeResponseJsonV300, OpeningTimesV300, ViewJSON300, ViewsJSON300}
|
||||
import code.api.v3_1_0.JSONFactory310.createAccountAttributeJson
|
||||
import code.api.v3_1_0.{AccountAttributeResponseJson, RedisCallLimitJson}
|
||||
import code.apicollection.ApiCollectionTrait
|
||||
@ -51,6 +52,7 @@ import code.ratelimiting.RateLimiting
|
||||
import code.standingorders.StandingOrderTrait
|
||||
import code.transactionrequests.TransactionRequests.TransactionChallengeTypes
|
||||
import code.userlocks.UserLocks
|
||||
import code.users.UserInvitation
|
||||
import com.openbankproject.commons.model.{DirectDebitTrait, _}
|
||||
import net.liftweb.common.{Box, Full}
|
||||
import net.liftweb.json.JValue
|
||||
@ -123,6 +125,24 @@ case class TransactionRequestWithChargeJSON400(
|
||||
case class PostResetPasswordUrlJsonV400(username: String, email: String, user_id: String)
|
||||
case class ResetPasswordUrlJsonV400(reset_password_url: String)
|
||||
|
||||
case class PostUserInvitationAnonymousJsonV400(secret_key: Long)
|
||||
case class PostUserInvitationJsonV400(first_name: String,
|
||||
last_name: String,
|
||||
email: String,
|
||||
company: String,
|
||||
country: String,
|
||||
purpose: String)
|
||||
case class UserInvitationJsonV400(first_name: String,
|
||||
last_name: String,
|
||||
email: String,
|
||||
company: String,
|
||||
country: String,
|
||||
purpose: String,
|
||||
status: String)
|
||||
case class UserInvitationsJsonV400(user_invitations: List[UserInvitationJsonV400])
|
||||
|
||||
case class UserIdJsonV400(user_id: String)
|
||||
|
||||
case class APIInfoJson400(
|
||||
version : String,
|
||||
version_status: String,
|
||||
@ -232,6 +252,15 @@ case class AccountBalanceJsonV400(
|
||||
balances: List[BalanceJsonV400]
|
||||
)
|
||||
|
||||
case class AccountBalancesJsonV400(
|
||||
account_id: String,
|
||||
bank_id: String,
|
||||
account_routings: List[AccountRouting],
|
||||
label: String,
|
||||
balances: List[BalanceJsonV400],
|
||||
|
||||
)
|
||||
|
||||
case class PostCustomerPhoneNumberJsonV400(mobile_phone_number: String)
|
||||
case class PostDirectDebitJsonV400(customer_id: String,
|
||||
user_id: String,
|
||||
@ -738,8 +767,32 @@ case class AtmJsonV400 (
|
||||
|
||||
case class AtmsJsonV400(atms : List[AtmJsonV400])
|
||||
|
||||
case class UserJsonV400(
|
||||
user_id: String,
|
||||
email : String,
|
||||
provider_id: String,
|
||||
provider : String,
|
||||
username : String,
|
||||
entitlements : EntitlementJSONs,
|
||||
views: Option[ViewsJSON300],
|
||||
is_deleted: Boolean
|
||||
)
|
||||
|
||||
object JSONFactory400 {
|
||||
|
||||
def createUserInfoJSON(user : User, entitlements: List[Entitlement]) : UserJsonV400 = {
|
||||
UserJsonV400(
|
||||
user_id = user.userId,
|
||||
email = user.emailAddress,
|
||||
username = stringOrNull(user.name),
|
||||
provider_id = user.idGivenByProvider,
|
||||
provider = stringOrNull(user.provider),
|
||||
entitlements = JSONFactory200.createEntitlementJSONs(entitlements),
|
||||
views = None,
|
||||
is_deleted = user.isDeleted.getOrElse(false)
|
||||
)
|
||||
}
|
||||
|
||||
def createCallsLimitJson(rateLimiting: RateLimiting) : CallLimitJsonV400 = {
|
||||
CallLimitJsonV400(
|
||||
rateLimiting.fromDate,
|
||||
@ -780,6 +833,10 @@ object JSONFactory400 {
|
||||
BanksJson400(l.map(createBankJSON400))
|
||||
}
|
||||
|
||||
def createUserIdInfoJson(user : User) : UserIdJsonV400 = {
|
||||
UserIdJsonV400(user_id = user.userId)
|
||||
}
|
||||
|
||||
def createSettlementAccountJson(userId: String, account: BankAccount, accountAttributes: List[AccountAttribute]): SettlementAccountResponseJson =
|
||||
SettlementAccountResponseJson(
|
||||
account_id = account.accountId.value,
|
||||
@ -1152,6 +1209,22 @@ object JSONFactory400 {
|
||||
def createCounterpartiesJson400(counterparties: List[CounterpartyTrait]): CounterpartiesJson400 =
|
||||
CounterpartiesJson400(counterparties.map(createCounterpartyJson400))
|
||||
|
||||
def createUserInvitationJson(userInvitation: UserInvitation): UserInvitationJsonV400 = {
|
||||
UserInvitationJsonV400(
|
||||
first_name = userInvitation.firstName,
|
||||
last_name = userInvitation.lastName,
|
||||
email = userInvitation.email,
|
||||
company = userInvitation.company,
|
||||
country = userInvitation.country,
|
||||
purpose = userInvitation.purpose,
|
||||
status = userInvitation.status
|
||||
)
|
||||
}
|
||||
|
||||
def createUserInvitationJson(userInvitations: List[UserInvitation]): UserInvitationsJsonV400 = {
|
||||
UserInvitationsJsonV400(userInvitations.map(createUserInvitationJson))
|
||||
}
|
||||
|
||||
def createBalancesJson(accountsBalances: AccountsBalances) = {
|
||||
AccountsBalancesJsonV400(
|
||||
accounts = accountsBalances.accounts.map(
|
||||
@ -1161,12 +1234,24 @@ object JSONFactory400 {
|
||||
account_routings = account.accountRoutings,
|
||||
label = account.label,
|
||||
balances = List(
|
||||
BalanceJsonV400(`type` = "", currency = account.balance.currency, amount = account.balance.amount)
|
||||
BalanceJsonV400(`type` = "OpeningBooked", currency = account.balance.currency, amount = account.balance.amount)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
def createAccountBalancesJson(accountBalances: AccountBalances) = {
|
||||
AccountBalanceJsonV400(
|
||||
account_id = accountBalances.id,
|
||||
bank_id = accountBalances.bankId,
|
||||
account_routings = accountBalances.accountRoutings,
|
||||
label = accountBalances.label,
|
||||
balances = accountBalances.balances.map( balance =>
|
||||
BalanceJsonV400(`type`=balance.balanceType, currency = balance.balance.currency, amount = balance.balance.amount)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
def createConsentsJsonV400(consents: List[MappedConsent]): ConsentsJsonV400= {
|
||||
ConsentsJsonV400(consents.map(c => ConsentJsonV400(c.consentId, c.jsonWebToken, c.status, c.apiStandard, c.apiVersion)))
|
||||
|
||||
@ -25,7 +25,7 @@ import net.liftweb.json.JsonDSL._
|
||||
import net.liftweb.json.JsonParser.ParseException
|
||||
import org.apache.commons.lang3.{StringUtils, Validate}
|
||||
import net.liftweb.util.{StringHelpers, ThreadGlobal}
|
||||
import org.apache.commons.collections4.MapUtils
|
||||
import org.apache.commons.collections4.{ListUtils, MapUtils}
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
|
||||
@ -40,7 +40,7 @@ import net.liftweb.json.Formats
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.collection.immutable.List
|
||||
import scala.collection.mutable
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.collection.mutable.{ArrayBuffer, ListBuffer}
|
||||
|
||||
|
||||
object DynamicEndpointHelper extends RestHelper {
|
||||
@ -51,27 +51,30 @@ object DynamicEndpointHelper extends RestHelper {
|
||||
*/
|
||||
val urlPrefix = APIUtil.getPropsValue("dynamic_endpoints_url_prefix", "dynamic")
|
||||
private val implementedInApiVersion = ApiVersion.v4_0_0
|
||||
private val IsDynamicEntityUrl = """https?://dynamic_entity.*"""
|
||||
private val IsDynamicEntityUrl = """.*dynamic_entity.*"""
|
||||
private val IsMockUrlString = """.*obp_mock(?::\d+)?.*"""
|
||||
private val IsMockUrl = IsMockUrlString.r
|
||||
|
||||
def isDynamicEntityResponse (serverUrl : String) = serverUrl matches (IsDynamicEntityUrl)
|
||||
def isMockedResponse (serverUrl : String) = serverUrl matches (IsMockUrlString)
|
||||
|
||||
private def dynamicEndpointInfos: List[DynamicEndpointInfo] = {
|
||||
val dynamicEndpoints: List[DynamicEndpointT] = DynamicEndpointProvider.connectorMethodProvider.vend.getAll()
|
||||
val infos = dynamicEndpoints.map(it => buildDynamicEndpointInfo(it.swaggerString, it.dynamicEndpointId.get))
|
||||
val dynamicEndpoints: List[DynamicEndpointT] = DynamicEndpointProvider.connectorMethodProvider.vend.getAll(None)
|
||||
val infos = dynamicEndpoints.map(it => buildDynamicEndpointInfo(it.swaggerString, it.dynamicEndpointId.get, it.bankId))
|
||||
infos
|
||||
}
|
||||
|
||||
def allDynamicEndpointRoles: List[ApiRole] = {
|
||||
for {
|
||||
dynamicEndpoint <- DynamicEndpointProvider.connectorMethodProvider.vend.getAll()
|
||||
info = buildDynamicEndpointInfo(dynamicEndpoint.swaggerString, dynamicEndpoint.dynamicEndpointId.get)
|
||||
dynamicEndpoint <- DynamicEndpointProvider.connectorMethodProvider.vend.getAll(None)
|
||||
info = buildDynamicEndpointInfo(dynamicEndpoint.swaggerString, dynamicEndpoint.dynamicEndpointId.get, dynamicEndpoint.bankId)
|
||||
role <- getRoles(info)
|
||||
} yield role
|
||||
}
|
||||
|
||||
def getRoles(dynamicEndpointId: String): List[ApiRole] = {
|
||||
val foundInfos: Box[DynamicEndpointInfo] = DynamicEndpointProvider.connectorMethodProvider.vend.get(dynamicEndpointId)
|
||||
.map(dynamicEndpoint => buildDynamicEndpointInfo(dynamicEndpoint.swaggerString, dynamicEndpoint.dynamicEndpointId.get))
|
||||
def getRoles(bankId: Option[String], dynamicEndpointId: String): List[ApiRole] = {
|
||||
val foundInfos: Box[DynamicEndpointInfo] = DynamicEndpointProvider.connectorMethodProvider.vend.get(bankId, dynamicEndpointId)
|
||||
.map(dynamicEndpoint => buildDynamicEndpointInfo(dynamicEndpoint.swaggerString, dynamicEndpoint.dynamicEndpointId.get, dynamicEndpoint.bankId))
|
||||
|
||||
|
||||
val roles: List[ApiRole] = foundInfos match {
|
||||
@ -82,6 +85,13 @@ object DynamicEndpointHelper extends RestHelper {
|
||||
roles
|
||||
}
|
||||
|
||||
def listOfRolesToUseAllDynamicEndpointsAOneBank(bankId: Option[String]): List[ApiRole] = {
|
||||
val foundInfos: List[DynamicEndpointInfo] = DynamicEndpointProvider.connectorMethodProvider.vend.getAll(bankId)
|
||||
.map(dynamicEndpoint => buildDynamicEndpointInfo(dynamicEndpoint.swaggerString, dynamicEndpoint.dynamicEndpointId.get, dynamicEndpoint.bankId))
|
||||
|
||||
foundInfos.map(getRoles(_)).flatten.toSet.toList
|
||||
}
|
||||
|
||||
def getRoles(dynamicEndpointInfo: DynamicEndpointInfo): List[ApiRole] =
|
||||
for {
|
||||
resourceDoc <- dynamicEndpointInfo.resourceDocs.toList
|
||||
@ -96,7 +106,6 @@ object DynamicEndpointHelper extends RestHelper {
|
||||
object DynamicReq extends JsonTest with JsonBody {
|
||||
|
||||
private val ExpressionRegx = """\{(.+?)\}""".r
|
||||
private val IsMockUrl = """https?://obp_mock(?::\d+)?/.*""".r
|
||||
/**
|
||||
* unapply Request to (request url, json, http method, request parameters, path parameters, role)
|
||||
* request url is current request target url to remote server
|
||||
@ -108,7 +117,7 @@ object DynamicEndpointHelper extends RestHelper {
|
||||
* @param r HttpRequest
|
||||
* @return (adapterUrl, requestBodyJson, httpMethod, requestParams, pathParams, role, operationId, mockResponseCode->mockResponseBody)
|
||||
*/
|
||||
def unapply(r: Req): Option[(String, JValue, AkkaHttpMethod, Map[String, List[String]], Map[String, String], ApiRole, String, Option[(Int, JValue)])] = {
|
||||
def unapply(r: Req): Option[(String, JValue, AkkaHttpMethod, Map[String, List[String]], Map[String, String], ApiRole, String, Option[(Int, JValue)], Option[String])] = {
|
||||
val partPath = r.path.partPath//eg: List("dynamic","feature-test")
|
||||
if (!testResponse_?(r) || partPath.headOption != Option(urlPrefix))//if check the Content-Type contains json or not, and check the if it is the `dynamic_endpoints_url_prefix`
|
||||
None //if do not match `URL and Content-Type`, then can not find this endpoint. return None.
|
||||
@ -118,7 +127,7 @@ object DynamicEndpointHelper extends RestHelper {
|
||||
val urlQueryParameters = r.params
|
||||
// url that match original swagger endpoint.
|
||||
val url = partPath.tail.mkString("/", "/", "") // eg: --> /feature-test
|
||||
val foundDynamicEndpoint: Option[(String, String, Int, ResourceDoc)] = dynamicEndpointInfos
|
||||
val foundDynamicEndpoint: Option[(String, String, Int, ResourceDoc, Option[String])] = dynamicEndpointInfos
|
||||
.map(_.findDynamicEndpoint(httpMethod, url))
|
||||
.collectFirst {
|
||||
case Some(x) => x
|
||||
@ -126,7 +135,7 @@ object DynamicEndpointHelper extends RestHelper {
|
||||
|
||||
foundDynamicEndpoint
|
||||
.flatMap { it =>
|
||||
val (serverUrl, endpointUrl, code, doc) = it
|
||||
val (serverUrl, endpointUrl, code, doc, bankId) = it
|
||||
|
||||
val pathParams: Map[String, String] = if(endpointUrl == url) {
|
||||
Map.empty[String, String]
|
||||
@ -152,7 +161,7 @@ object DynamicEndpointHelper extends RestHelper {
|
||||
|
||||
val Some(role::_) = doc.roles
|
||||
val requestBodyJValue = body(r).getOrElse(JNothing)
|
||||
Full(s"""$serverUrl$url""", requestBodyJValue, akkaHttpMethod, urlQueryParameters, pathParams, role, doc.operationId, mockResponse)
|
||||
Full(s"""$serverUrl$url""", requestBodyJValue, akkaHttpMethod, urlQueryParameters, pathParams, role, doc.operationId, mockResponse, bankId)
|
||||
}
|
||||
|
||||
}
|
||||
@ -160,7 +169,7 @@ object DynamicEndpointHelper extends RestHelper {
|
||||
}
|
||||
|
||||
|
||||
def findExistsEndpoints(openAPI: OpenAPI): List[(HttpMethod, String)] = {
|
||||
def findExistingDynamicEndpoints(openAPI: OpenAPI): List[(HttpMethod, String)] = {
|
||||
for {
|
||||
(path, pathItem) <- openAPI.getPaths.asScala.toList
|
||||
(method: HttpMethod, _) <- pathItem.readOperationsMap.asScala
|
||||
@ -171,14 +180,14 @@ object DynamicEndpointHelper extends RestHelper {
|
||||
|
||||
private val dynamicEndpointInfoMemo = new Memo[String, DynamicEndpointInfo]
|
||||
|
||||
private def buildDynamicEndpointInfo(content: String, id: String): DynamicEndpointInfo =
|
||||
private def buildDynamicEndpointInfo(content: String, id: String, bankId:Option[String]): DynamicEndpointInfo =
|
||||
dynamicEndpointInfoMemo.memoize(content) {
|
||||
val openAPI: OpenAPI = parseSwaggerContent(content)
|
||||
buildDynamicEndpointInfo(openAPI, id)
|
||||
buildDynamicEndpointInfo(openAPI, id, bankId)
|
||||
}
|
||||
|
||||
private def buildDynamicEndpointInfo(openAPI: OpenAPI, id: String): DynamicEndpointInfo = {
|
||||
val tags: List[ResourceDocTag] = List(ApiTag(openAPI.getInfo.getTitle), apiTagNewStyle, apiTagDynamicEntity, apiTagDynamic)
|
||||
def buildDynamicEndpointInfo(openAPI: OpenAPI, id: String, bankId:Option[String]): DynamicEndpointInfo = {
|
||||
val tags: List[ResourceDocTag] = List(ApiTag(openAPI.getInfo.getTitle), apiTagNewStyle, apiTagDynamicEndpoint, apiTagDynamic)
|
||||
|
||||
val serverUrl = {
|
||||
val servers = openAPI.getServers
|
||||
@ -249,7 +258,7 @@ object DynamicEndpointHelper extends RestHelper {
|
||||
val opName = method.name().toLowerCase().capitalize
|
||||
s"Can${opName}DynamicEndpoint_"
|
||||
}
|
||||
val roleName = if(StringUtils.isNotBlank(op.getOperationId)) {
|
||||
var roleName = if(StringUtils.isNotBlank(op.getOperationId)) {
|
||||
val prettyOperationId = op.getOperationId
|
||||
.replaceAll("""(?i)(get|find|search|add|create|delete|update|of|new|the|one|that|\s)""", "")
|
||||
.capitalize
|
||||
@ -265,9 +274,12 @@ object DynamicEndpointHelper extends RestHelper {
|
||||
|
||||
s"$roleNamePrefix$prettySummary${entitlementSuffix(path)}"
|
||||
}
|
||||
|
||||
// substring role name to avoid it have over the maximum length of db column.
|
||||
if(roleName.size > 64) {
|
||||
roleName = StringUtils.substring(roleName, 0, 53) + roleName.hashCode()
|
||||
}
|
||||
Some(List(
|
||||
ApiRole.getOrCreateDynamicApiRole(roleName)
|
||||
ApiRole.getOrCreateDynamicApiRole(roleName, bankId.isDefined)
|
||||
))
|
||||
}
|
||||
val doc = ResourceDoc(
|
||||
@ -287,7 +299,7 @@ object DynamicEndpointHelper extends RestHelper {
|
||||
DynamicEndpointItem(path, successCode, doc)
|
||||
}
|
||||
|
||||
DynamicEndpointInfo(id, dynamicEndpointItems, serverUrl)
|
||||
DynamicEndpointInfo(id, dynamicEndpointItems, serverUrl, bankId)
|
||||
}
|
||||
|
||||
private val PathParamRegx = """\{(.+?)\}""".r
|
||||
@ -477,96 +489,117 @@ object DynamicEndpointHelper extends RestHelper {
|
||||
implicit val formats = CustomJsonFormats.formats
|
||||
|
||||
val example: Any = getExampleBySchema(openAPI, schema)
|
||||
convertToProduct(example)
|
||||
}
|
||||
|
||||
example match {
|
||||
case null => EmptyBody
|
||||
case v: String => StringBody(v)
|
||||
case v: Boolean => BooleanBody(v)
|
||||
case v: Int => IntBody(v)
|
||||
case v: Long => LongBody(v)
|
||||
case v: BigInt => BigIntBody(v)
|
||||
case v: Float => FloatBody(v)
|
||||
case v: Double => DoubleBody(v)
|
||||
case v: BigDecimal => BigDecimalBody(v)
|
||||
case v: JArray => JArrayBody(v)
|
||||
case v: JObject => v
|
||||
case v :scala.Product => v
|
||||
case v => json.Extraction.decompose(v) match {
|
||||
case o: JObject => o
|
||||
case JArray(arr) => arr
|
||||
case _ => throw new RuntimeException(s"Not supporting example type: $v, ${v.getClass}")
|
||||
}
|
||||
private def convertToProduct(example: Any): Product = example match {
|
||||
case null => EmptyBody
|
||||
case v: String => StringBody(v)
|
||||
case v: Boolean => BooleanBody(v)
|
||||
case v: Int => IntBody(v)
|
||||
case v: Long => LongBody(v)
|
||||
case v: BigInt => BigIntBody(v)
|
||||
case v: Float => FloatBody(v)
|
||||
case v: Double => DoubleBody(v)
|
||||
case v: BigDecimal => BigDecimalBody(v)
|
||||
case v: JArray => JArrayBody(v)
|
||||
case v: JObject => v
|
||||
case v :scala.Product => v
|
||||
case v => json.Extraction.decompose(v) match {
|
||||
case o: JObject => o
|
||||
case JArray(arr) => arr
|
||||
case _ => throw new RuntimeException(s"Not supporting example type: $v, ${v.getClass}")
|
||||
}
|
||||
}
|
||||
|
||||
private def getExampleBySchema(openAPI: OpenAPI, schema: Schema[_]):Any = {
|
||||
|
||||
def getDefaultValue[T](schema: Schema[_<:T], t: => T): T = Option(schema.getExample.asInstanceOf[T])
|
||||
.orElse(Option(schema.getDefault))
|
||||
.orElse{
|
||||
Option(schema.getEnum())
|
||||
.filterNot(_.isEmpty)
|
||||
.map(_.get(0))
|
||||
schema.getEnum() match {
|
||||
case null => None
|
||||
case l if l.isEmpty => None
|
||||
case l => Option(l.get(0))
|
||||
}
|
||||
}
|
||||
.getOrElse(t)
|
||||
|
||||
schema match {
|
||||
case null => null
|
||||
case v: BooleanSchema => getDefaultValue(v, true)
|
||||
case v if v.getType() =="boolean" => true
|
||||
case v: DateSchema => getDefaultValue(v, {
|
||||
APIUtil.DateWithDayFormat.format(new Date())
|
||||
})
|
||||
case v if v.getFormat() == "date" => getDefaultValue(v, {
|
||||
APIUtil.DateWithDayFormat.format(new Date())
|
||||
})
|
||||
case v: DateTimeSchema => getDefaultValue(v, {
|
||||
APIUtil.DateWithSecondsFormat.format(new Date())
|
||||
})
|
||||
case v if v.getFormat() == "date-time" => getDefaultValue(v, {
|
||||
APIUtil.DateWithSecondsFormat.format(new Date())
|
||||
})
|
||||
case v: IntegerSchema => getDefaultValue(v, 1)
|
||||
case v if v.getFormat() == "int32" => 1
|
||||
case v: NumberSchema => getDefaultValue(v, 1.2)
|
||||
case v if v.getType() == "number" => 1.2
|
||||
case v: StringSchema => getDefaultValue(v, "string")
|
||||
case v: UUIDSchema => getDefaultValue(v, UUID.randomUUID())
|
||||
case v if v.getFormat() == "uuid" => UUID.randomUUID()
|
||||
case v: EmailSchema => getDefaultValue(v, "example@tesobe.com")
|
||||
case v if v.getFormat() == "email" => "example@tesobe.com"
|
||||
case v: FileSchema => getDefaultValue(v, "file_example.txt")
|
||||
case v if v.getFormat() == "binary" => "file_example.txt"
|
||||
case v: PasswordSchema => getDefaultValue(v, "very_complex_password_I_promise_!!")
|
||||
case v if v.getFormat() == "password" => "very_complex_password_I_promise_!!"
|
||||
case v: ArraySchema =>
|
||||
getDefaultValue(v, {
|
||||
val itemsSchema: Schema[_] = v.getItems
|
||||
val singleItemExample = getExampleBySchema(openAPI, itemsSchema)
|
||||
singleItemExample match {
|
||||
case v: JValue => JArray(v::Nil)
|
||||
case v => json.Extraction.decompose(Array(v))
|
||||
}
|
||||
val schemas:ListBuffer[Schema[_]] = ListBuffer()
|
||||
def rec(schema: Schema[_]): Any = {
|
||||
if(schema.isInstanceOf[ObjectSchema]) {
|
||||
schemas += schema
|
||||
}
|
||||
// check whether this schema already recurse two times
|
||||
if(schemas.count(schema ==) > 3) {
|
||||
return JObject(Nil)
|
||||
}
|
||||
|
||||
schema match {
|
||||
|
||||
case null => null
|
||||
case v: BooleanSchema => getDefaultValue(v, true)
|
||||
case v if v.getType() =="boolean" => true
|
||||
case v: DateSchema => getDefaultValue(v, {
|
||||
APIUtil.DateWithDayFormat.format(new Date())
|
||||
})
|
||||
case v: MapSchema => getDefaultValue(v, Map("name"-> "John", "age" -> 12))
|
||||
case v if v.getFormat() == "date" => getDefaultValue(v, {
|
||||
APIUtil.DateWithDayFormat.format(new Date())
|
||||
})
|
||||
case v: DateTimeSchema => getDefaultValue(v, {
|
||||
APIUtil.DateWithSecondsFormat.format(new Date())
|
||||
})
|
||||
case v if v.getFormat() == "date-time" => getDefaultValue(v, {
|
||||
APIUtil.DateWithSecondsFormat.format(new Date())
|
||||
})
|
||||
case v: IntegerSchema => getDefaultValue(v, 1)
|
||||
case v if v.getFormat() == "int32" => 1
|
||||
case v: NumberSchema => getDefaultValue(v, 1.2)
|
||||
case v if v.getType() == "number" => 1.2
|
||||
case v: StringSchema => getDefaultValue(v, "string")
|
||||
case v: UUIDSchema => getDefaultValue(v, UUID.randomUUID())
|
||||
case v if v.getFormat() == "uuid" => UUID.randomUUID()
|
||||
case v: EmailSchema => getDefaultValue(v, "example@tesobe.com")
|
||||
case v if v.getFormat() == "email" => "example@tesobe.com"
|
||||
case v: FileSchema => getDefaultValue(v, "file_example.txt")
|
||||
case v if v.getFormat() == "binary" => "file_example.txt"
|
||||
case v: PasswordSchema => getDefaultValue(v, "very_complex_password_I_promise_!!")
|
||||
case v if v.getFormat() == "password" => "very_complex_password_I_promise_!!"
|
||||
case v: ArraySchema =>
|
||||
getDefaultValue(v, {
|
||||
rec(v.getItems) match {
|
||||
case v: JValue => JArray(v::Nil)
|
||||
case v => json.Extraction.decompose(Array(v))
|
||||
}
|
||||
})
|
||||
case v: MapSchema => getDefaultValue(v, Map("name"-> "John", "age" -> 12))
|
||||
//The swagger object schema may not contain any properties: eg:
|
||||
// "Account": {
|
||||
// "title": "accountTransactibility",
|
||||
// "type": "object"
|
||||
// }
|
||||
case v if v.isInstanceOf[ObjectSchema] && MapUtils.isEmpty(v.getProperties()) =>
|
||||
EmptyBody
|
||||
|
||||
case v if v.isInstanceOf[ObjectSchema] || MapUtils.isNotEmpty(v.getProperties()) =>
|
||||
val properties: util.Map[String, Schema[_]] = v.getProperties
|
||||
case v if v.isInstanceOf[ObjectSchema] || MapUtils.isNotEmpty(v.getProperties()) =>
|
||||
val properties: util.Map[String, Schema[_]] = v.getProperties
|
||||
|
||||
val jFields: mutable.Iterable[JField] = properties.asScala.map { kv =>
|
||||
val (name, value) = kv
|
||||
val valueExample = getExampleBySchema(openAPI, value)
|
||||
JField(name, json.Extraction.decompose(valueExample))
|
||||
}
|
||||
JObject(jFields.toList)
|
||||
val jFields: mutable.Iterable[JField] = properties.asScala.map { kv =>
|
||||
val (name, value) = kv
|
||||
val valueExample = rec(value)
|
||||
JField(name, json.Extraction.decompose(valueExample))
|
||||
}
|
||||
JObject(jFields.toList)
|
||||
|
||||
case v: Schema[_] if StringUtils.isNotBlank(v.get$ref()) =>
|
||||
val refSchema = getRefSchema(openAPI, v.get$ref())
|
||||
case v: Schema[_] if StringUtils.isNotBlank(v.get$ref()) =>
|
||||
val refSchema = getRefSchema(openAPI, v.get$ref())
|
||||
convertToProduct(rec(refSchema))
|
||||
|
||||
getExample(openAPI, refSchema)
|
||||
|
||||
case v if v.getType() == "string" => "string"
|
||||
case _ => throw new RuntimeException(s"Not support type $schema, please support it if necessary.")
|
||||
case v if v.getType() == "string" => "string"
|
||||
case _ => throw new RuntimeException(s"Not support type $schema, please support it if necessary.")
|
||||
}
|
||||
}
|
||||
rec(schema)
|
||||
}
|
||||
|
||||
|
||||
@ -805,6 +838,27 @@ object DynamicEndpointHelper extends RestHelper {
|
||||
val (dynamicEntityName, dynamicDateId) = findDynamicData(dynamicDataList, dynamicDataJson)
|
||||
JBool(DynamicDataProvider.connectorMethodProvider.vend.delete(dynamicEntityName, dynamicDateId).getOrElse(false))
|
||||
}
|
||||
|
||||
def addedBankToPath(swagger: String, bankId: Option[String]): JValue = {
|
||||
val jvalue = json.parse(swagger)
|
||||
addedBankToPath(jvalue, bankId)
|
||||
}
|
||||
|
||||
// If it is bank is defined, we will add the bank into the path, better check the scala tests
|
||||
// eg: /fashion-brand-list --> /banks/gh.29.uk/fashion-brand-list
|
||||
def addedBankToPath(swagger: JValue, bankId: Option[String]): JValue = {
|
||||
if(bankId.isDefined){
|
||||
swagger transformField {
|
||||
case JField(name, JObject(obj)) =>
|
||||
if (name.startsWith("/"))
|
||||
JField(s"/banks/${bankId.get}$name", JObject(obj))
|
||||
else
|
||||
JField(name, JObject(obj))
|
||||
}
|
||||
} else{
|
||||
swagger
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -822,7 +876,7 @@ case class DynamicEndpointItem(path: String, successCode: Int, resourceDoc: Reso
|
||||
* @param dynamicEndpointItems ResourceDoc to url that defined in swagger content
|
||||
* @param serverUrl base url that defined in swagger content
|
||||
*/
|
||||
case class DynamicEndpointInfo(id: String, dynamicEndpointItems: mutable.Iterable[DynamicEndpointItem], serverUrl: String) {
|
||||
case class DynamicEndpointInfo(id: String, dynamicEndpointItems: mutable.Iterable[DynamicEndpointItem], serverUrl: String, bankId:Option[String]) {
|
||||
val resourceDocs: mutable.Iterable[ResourceDoc] = dynamicEndpointItems.map(_.resourceDoc)
|
||||
|
||||
private val existsUrlToMethod: mutable.Iterable[(HttpMethod, String, Int, ResourceDoc)] =
|
||||
@ -833,10 +887,10 @@ case class DynamicEndpointInfo(id: String, dynamicEndpointItems: mutable.Iterabl
|
||||
})
|
||||
|
||||
// return (serverUrl, endpointUrl, successCode, resourceDoc)
|
||||
def findDynamicEndpoint(newMethod: HttpMethod, newUrl: String): Option[(String, String, Int, ResourceDoc)] =
|
||||
def findDynamicEndpoint(newMethod: HttpMethod, newUrl: String): Option[(String, String, Int, ResourceDoc, Option[String])] =
|
||||
existsUrlToMethod collectFirst {
|
||||
case (method, url, code, doc) if isSameUrl(newUrl, url) && newMethod == method =>
|
||||
(this.serverUrl, url, code, doc)
|
||||
(this.serverUrl, url, code, doc, this.bankId)
|
||||
}
|
||||
|
||||
def existsEndpoint(newMethod: HttpMethod, newUrl: String): Boolean = findDynamicEndpoint(newMethod, newUrl).isDefined
|
||||
|
||||
@ -47,9 +47,9 @@ object EntityName {
|
||||
object DynamicEntityHelper {
|
||||
private val implementedInApiVersion = ApiVersion.v4_0_0
|
||||
|
||||
def definitionsMap: Map[String, DynamicEntityInfo] = NewStyle.function.getDynamicEntities().map(it => (it.entityName, DynamicEntityInfo(it.metadataJson, it.entityName, it.bankId))).toMap
|
||||
def definitionsMap: Map[String, DynamicEntityInfo] = NewStyle.function.getDynamicEntities(None).map(it => (it.entityName, DynamicEntityInfo(it.metadataJson, it.entityName, it.bankId))).toMap
|
||||
|
||||
def dynamicEntityRoles: List[String] = NewStyle.function.getDynamicEntities().flatMap(dEntity => DynamicEntityInfo.roleNames(dEntity.entityName, dEntity.bankId))
|
||||
def dynamicEntityRoles: List[String] = NewStyle.function.getDynamicEntities(None).flatMap(dEntity => DynamicEntityInfo.roleNames(dEntity.entityName, dEntity.bankId))
|
||||
|
||||
def doc: ArrayBuffer[ResourceDoc] = {
|
||||
val docs = operationToResourceDoc.values.toList
|
||||
@ -153,7 +153,7 @@ object DynamicEntityHelper {
|
||||
UserHasMissingRoles,
|
||||
UnknownError
|
||||
),
|
||||
List(apiTag, apiTagNewStyle, apiTagDynamicEndpoint, apiTagDynamic),
|
||||
List(apiTag, apiTagNewStyle, apiTagDynamicEntity, apiTagDynamic),
|
||||
Some(List(dynamicEntityInfo.canGetRole))
|
||||
)
|
||||
resourceDocs += (DynamicEntityOperation.GET_ONE, entityName) -> ResourceDoc(
|
||||
@ -179,7 +179,7 @@ object DynamicEntityHelper {
|
||||
UserHasMissingRoles,
|
||||
UnknownError
|
||||
),
|
||||
List(apiTag, apiTagNewStyle, apiTagDynamicEndpoint, apiTagDynamic),
|
||||
List(apiTag, apiTagNewStyle, apiTagDynamicEntity, apiTagDynamic),
|
||||
Some(List(dynamicEntityInfo.canGetRole))
|
||||
)
|
||||
|
||||
@ -208,7 +208,7 @@ object DynamicEntityHelper {
|
||||
InvalidJsonFormat,
|
||||
UnknownError
|
||||
),
|
||||
List(apiTag, apiTagNewStyle, apiTagDynamicEndpoint, apiTagDynamic),
|
||||
List(apiTag, apiTagNewStyle, apiTagDynamicEntity, apiTagDynamic),
|
||||
Some(List(dynamicEntityInfo.canCreateRole))
|
||||
)
|
||||
|
||||
@ -237,7 +237,7 @@ object DynamicEntityHelper {
|
||||
InvalidJsonFormat,
|
||||
UnknownError
|
||||
),
|
||||
List(apiTag, apiTagNewStyle, apiTagDynamicEndpoint, apiTagDynamic),
|
||||
List(apiTag, apiTagNewStyle, apiTagDynamicEntity, apiTagDynamic),
|
||||
Some(List(dynamicEntityInfo.canUpdateRole))
|
||||
)
|
||||
|
||||
@ -263,7 +263,7 @@ object DynamicEntityHelper {
|
||||
InvalidJsonFormat,
|
||||
UnknownError
|
||||
),
|
||||
List(apiTag, apiTagNewStyle, apiTagDynamicEndpoint, apiTagDynamic),
|
||||
List(apiTag, apiTagNewStyle, apiTagDynamicEntity, apiTagDynamic),
|
||||
Some(List(dynamicEntityInfo.canDeleteRole))
|
||||
)
|
||||
|
||||
|
||||
@ -492,6 +492,8 @@ trait Connector extends MdcLoggable {
|
||||
def getBankAccounts(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]) : OBPReturnType[Box[List[BankAccount]]]= Future{(Failure(setUnimplementedError), callContext)}
|
||||
|
||||
def getBankAccountsBalances(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]) : OBPReturnType[Box[AccountsBalances]]= Future{(Failure(setUnimplementedError), callContext)}
|
||||
|
||||
def getBankAccountBalances(bankIdAccountId: BankIdAccountId, callContext: Option[CallContext]) : OBPReturnType[Box[AccountBalances]]= Future{(Failure(setUnimplementedError), callContext)}
|
||||
|
||||
def getCoreBankAccountsLegacy(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]) : Box[(List[CoreAccount], Option[CallContext])] =
|
||||
Failure(setUnimplementedError)
|
||||
|
||||
@ -47,6 +47,7 @@ import code.metadata.tags.Tags
|
||||
import code.metadata.transactionimages.TransactionImages
|
||||
import code.metadata.wheretags.WhereTags
|
||||
import code.model._
|
||||
import code.model.dataAccess.AuthUser.findUserByUsernameLocally
|
||||
import code.model.dataAccess._
|
||||
import code.productAttributeattribute.MappedProductAttribute
|
||||
import code.productattribute.ProductAttributeX
|
||||
@ -81,7 +82,7 @@ import com.tesobe.model.UpdateBankAccount
|
||||
import net.liftweb.common._
|
||||
import net.liftweb.json
|
||||
import net.liftweb.json.JsonAST.JField
|
||||
import net.liftweb.json.{JArray, JBool, JInt, JObject, JValue,JString}
|
||||
import net.liftweb.json.{JArray, JBool, JInt, JObject, JString, JValue}
|
||||
import net.liftweb.mapper.{By, _}
|
||||
import net.liftweb.util.Helpers.{hours, now, time, tryo}
|
||||
import net.liftweb.util.Mailer
|
||||
@ -743,6 +744,24 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
)), callContext)
|
||||
}
|
||||
|
||||
override def getBankAccountBalances(bankIdAccountId: BankIdAccountId, callContext: Option[CallContext]): OBPReturnType[Box[AccountBalances]] =
|
||||
Future {
|
||||
for {
|
||||
bankAccount <- getBankAccountOld(bankIdAccountId.bankId, bankIdAccountId.accountId) ?~! s"${ErrorMessages.BankAccountNotFound} current BANK_ID(${bankIdAccountId.bankId}) and ACCOUNT_ID(${bankIdAccountId.accountId})"
|
||||
accountBalances = AccountBalances(
|
||||
id = bankAccount.accountId.value,
|
||||
label = bankAccount.label,
|
||||
bankId = bankAccount.bankId.value,
|
||||
accountRoutings = bankAccount.accountRoutings.map(accountRounting => AccountRouting(accountRounting.scheme, accountRounting.address)),
|
||||
balances = List(BankAccountBalance(AmountOfMoney(bankAccount.currency, bankAccount.balance.toString),"OpeningBooked")),
|
||||
overallBalance = AmountOfMoney(bankAccount.currency, bankAccount.balance.toString),
|
||||
overallBalanceDate = now
|
||||
)
|
||||
} yield {
|
||||
(accountBalances,callContext)
|
||||
}
|
||||
}
|
||||
|
||||
override def checkBankAccountExistsLegacy(bankId: BankId, accountId: AccountId, callContext: Option[CallContext]): Box[(BankAccount, Option[CallContext])] = {
|
||||
getBankAccountLegacy(bankId: BankId, accountId: AccountId, callContext)
|
||||
}
|
||||
@ -4943,7 +4962,20 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
|
||||
//NOTE: this method is not for mapped connector, we put it here for the star default implementation.
|
||||
// : we call that method only when we set external authentication and provider is not OBP-API
|
||||
override def checkExternalUserExists(username: String, callContext: Option[CallContext]): Box[InboundExternalUser] = Failure("")
|
||||
override def checkExternalUserExists(username: String, callContext: Option[CallContext]): Box[InboundExternalUser] = {
|
||||
findUserByUsernameLocally(username).map( user =>
|
||||
InboundExternalUser(aud = "",
|
||||
exp = "",
|
||||
iat = "",
|
||||
iss = "",
|
||||
sub = user.username.get,
|
||||
azp = None,
|
||||
email = None,
|
||||
emailVerified = None,
|
||||
name = None
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
override def validateUserAuthContextUpdateRequest(
|
||||
|
||||
@ -31,7 +31,7 @@ trait ConsumersProvider {
|
||||
def getConsumerByConsumerIdFuture(consumerId: String): Future[Box[Consumer]]
|
||||
def getConsumersByUserIdFuture(userId: String): Future[List[Consumer]]
|
||||
def getConsumersFuture(): 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): Box[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]
|
||||
def updateConsumerCallLimits(id: Long, perSecond: Option[String], perMinute: Option[String], perHour: Option[String], perDay: Option[String], perWeek: Option[String], perMonth: Option[String]): Future[Box[Consumer]]
|
||||
@ -63,7 +63,7 @@ class RemotedataConsumersCaseClasses {
|
||||
case class getConsumerByConsumerIdFuture(consumerId: String)
|
||||
case class getConsumersByUserIdFuture(userId: String)
|
||||
case class getConsumersFuture()
|
||||
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])
|
||||
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)
|
||||
case class updateConsumerCallLimits(id: Long, perSecond: Option[String], perMinute: Option[String], perHour: Option[String], perDay: Option[String], perWeek: Option[String], perMonth: Option[String])
|
||||
|
||||
@ -18,12 +18,14 @@ trait DynamicEndpointT {
|
||||
* The user who create this DynamicEndpoint
|
||||
*/
|
||||
def userId: String
|
||||
def bankId: Option[String]
|
||||
}
|
||||
|
||||
case class DynamicEndpointCommons(
|
||||
dynamicEndpointId: Option[String] = None,
|
||||
swaggerString: String,
|
||||
userId: String
|
||||
userId: String,
|
||||
bankId: Option[String]
|
||||
) extends DynamicEndpointT with JsonFieldReName
|
||||
|
||||
object DynamicEndpointCommons extends Converter[DynamicEndpointT, DynamicEndpointCommons]
|
||||
@ -31,11 +33,11 @@ object DynamicEndpointCommons extends Converter[DynamicEndpointT, DynamicEndpoin
|
||||
case class DynamicEndpointSwagger(swaggerString: String, dynamicEndpointId: Option[String] = None)
|
||||
|
||||
trait DynamicEndpointProvider {
|
||||
def create(userId: String, swaggerString: String): Box[DynamicEndpointT]
|
||||
def update(dynamicEndpointId: String, swaggerString: String): Box[DynamicEndpointT]
|
||||
def updateHost(dynamicEndpointId: String, hostString: String): Box[DynamicEndpointT]
|
||||
def get(dynamicEndpointId: String): Box[DynamicEndpointT]
|
||||
def getAll(): List[DynamicEndpointT]
|
||||
def create(bankId:Option[String], userId: String, swaggerString: String): Box[DynamicEndpointT]
|
||||
def update(bankId:Option[String], dynamicEndpointId: String, swaggerString: String): Box[DynamicEndpointT]
|
||||
def updateHost(bankId:Option[String], dynamicEndpointId: String, hostString: String): Box[DynamicEndpointT]
|
||||
def get(bankId:Option[String],dynamicEndpointId: String): Box[DynamicEndpointT]
|
||||
def getAll(bankId:Option[String]): List[DynamicEndpointT]
|
||||
def getDynamicEndpointsByUserId(userId: String): List[DynamicEndpointT]
|
||||
def delete(dynamicEndpointId: String): Boolean
|
||||
def delete(bankId:Option[String], dynamicEndpointId: String): Boolean
|
||||
}
|
||||
@ -17,36 +17,78 @@ import scala.concurrent.duration.DurationInt
|
||||
object MappedDynamicEndpointProvider extends DynamicEndpointProvider with CustomJsonFormats{
|
||||
val dynamicEndpointTTL : Int = {
|
||||
if(Props.testMode) 0
|
||||
else APIUtil.getPropsValue(s"dynamicEndpoint.cache.ttl.seconds", "32").toInt
|
||||
else //Better set this to 0, we maybe create multiple endpoints, when we create new ones.
|
||||
APIUtil.getPropsValue(s"dynamicEndpoint.cache.ttl.seconds", "32").toInt
|
||||
}
|
||||
|
||||
override def create(userId: String, swaggerString: String): Box[DynamicEndpointT] = {
|
||||
tryo{DynamicEndpoint.create.UserId(userId).SwaggerString(swaggerString).saveMe()}
|
||||
override def create(bankId:Option[String], userId: String, swaggerString: String): Box[DynamicEndpointT] = {
|
||||
tryo{DynamicEndpoint.create
|
||||
.UserId(userId)
|
||||
.BankId(bankId.getOrElse(null))
|
||||
.SwaggerString(swaggerString)
|
||||
.saveMe()
|
||||
}
|
||||
}
|
||||
override def update(dynamicEndpointId: String, swaggerString: String): Box[DynamicEndpointT] = {
|
||||
DynamicEndpoint.find(By(DynamicEndpoint.DynamicEndpointId, dynamicEndpointId)).map(_.SwaggerString(swaggerString).saveMe())
|
||||
override def update(bankId:Option[String], dynamicEndpointId: String, swaggerString: String): Box[DynamicEndpointT] = {
|
||||
(if (bankId.isEmpty)
|
||||
DynamicEndpoint.find(By(DynamicEndpoint.DynamicEndpointId, dynamicEndpointId))
|
||||
else
|
||||
DynamicEndpoint.find(
|
||||
By(DynamicEndpoint.DynamicEndpointId, dynamicEndpointId),
|
||||
By(DynamicEndpoint.BankId, bankId.getOrElse(""))
|
||||
)
|
||||
).map(_.SwaggerString(swaggerString).saveMe())
|
||||
|
||||
|
||||
}
|
||||
override def updateHost(dynamicEndpointId: String, hostString: String): Box[DynamicEndpointT] = {
|
||||
DynamicEndpoint.find(By(DynamicEndpoint.DynamicEndpointId, dynamicEndpointId))
|
||||
.map(dynamicEndpoint => {
|
||||
override def updateHost(bankId: Option[String], dynamicEndpointId: String, hostString: String): Box[DynamicEndpointT] = {
|
||||
(if (bankId.isEmpty)
|
||||
DynamicEndpoint.find(By(DynamicEndpoint.DynamicEndpointId, dynamicEndpointId))
|
||||
else
|
||||
DynamicEndpoint.find(
|
||||
By(DynamicEndpoint.DynamicEndpointId, dynamicEndpointId),
|
||||
By(DynamicEndpoint.BankId, bankId.getOrElse(""))
|
||||
)
|
||||
).map(dynamicEndpoint => {
|
||||
dynamicEndpoint.SwaggerString(json.compactRender(json.parse(dynamicEndpoint.swaggerString).replace("host" :: Nil, JString(hostString)))).saveMe()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override def get(dynamicEndpointId: String): Box[DynamicEndpointT] = DynamicEndpoint.find(By(DynamicEndpoint.DynamicEndpointId, dynamicEndpointId))
|
||||
override def get(bankId: Option[String], dynamicEndpointId: String): Box[DynamicEndpointT] = {
|
||||
if (bankId.isEmpty)
|
||||
DynamicEndpoint.find(By(DynamicEndpoint.DynamicEndpointId, dynamicEndpointId))
|
||||
else
|
||||
DynamicEndpoint.find(
|
||||
By(DynamicEndpoint.DynamicEndpointId, dynamicEndpointId),
|
||||
By(DynamicEndpoint.BankId, bankId.getOrElse(""))
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
override def getAll(): List[DynamicEndpointT] = {
|
||||
override def getAll(bankId: Option[String]): List[DynamicEndpointT] = {
|
||||
var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString)
|
||||
CacheKeyFromArguments.buildCacheKey {
|
||||
Caching.memoizeSyncWithProvider (Some(cacheKey.toString())) (dynamicEndpointTTL second) {
|
||||
DynamicEndpoint.findAll()
|
||||
}}
|
||||
if (bankId.isEmpty)
|
||||
DynamicEndpoint.findAll()
|
||||
else
|
||||
DynamicEndpoint.findAll(By(DynamicEndpoint.BankId, bankId.getOrElse("")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override def getDynamicEndpointsByUserId(userId: String): List[DynamicEndpointT] = DynamicEndpoint.findAll(By(DynamicEndpoint.UserId, userId))
|
||||
|
||||
override def delete(dynamicEndpointId: String): Boolean = DynamicEndpoint.bulkDelete_!!(By(DynamicEndpoint.DynamicEndpointId, dynamicEndpointId))
|
||||
override def delete(bankId: Option[String], dynamicEndpointId: String): Boolean = {
|
||||
if (bankId.isEmpty)
|
||||
DynamicEndpoint.bulkDelete_!!(By(DynamicEndpoint.DynamicEndpointId, dynamicEndpointId))
|
||||
else
|
||||
DynamicEndpoint.bulkDelete_!!(
|
||||
By(DynamicEndpoint.DynamicEndpointId, dynamicEndpointId),
|
||||
By(DynamicEndpoint.BankId, bankId.getOrElse(""))
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -59,10 +101,13 @@ class DynamicEndpoint extends DynamicEndpointT with LongKeyedMapper[DynamicEndpo
|
||||
object SwaggerString extends MappedText(this)
|
||||
|
||||
object UserId extends MappedString(this, 255)
|
||||
|
||||
object BankId extends MappedString(this, 255)
|
||||
|
||||
override def dynamicEndpointId: Option[String] = Option(DynamicEndpointId.get)
|
||||
override def swaggerString: String = SwaggerString.get
|
||||
override def userId: String = UserId.get
|
||||
override def bankId: Option[String] = if (BankId.get == null || BankId.get.isEmpty) None else Some(BankId.get)
|
||||
}
|
||||
|
||||
object DynamicEndpoint extends DynamicEndpoint with LongKeyedMetaMapper[DynamicEndpoint] {
|
||||
|
||||
@ -169,7 +169,7 @@ object ReferenceType {
|
||||
.recover(recoverFn(fieldName, value, "MethodRouting"))
|
||||
},
|
||||
"reference:DynamicEntity" -> {(fieldName, value, callContext) =>
|
||||
NewStyle.function.getDynamicEntityById(value, callContext)
|
||||
NewStyle.function.getDynamicEntityById(None, value, callContext)
|
||||
.map(mapFn(fieldName, value, "DynamicEntity"))
|
||||
.recover(recoverFn(fieldName, value, "DynamicEntity"))
|
||||
},
|
||||
@ -283,7 +283,7 @@ object ReferenceType {
|
||||
)
|
||||
|
||||
def referenceTypeNames: List[String] = {
|
||||
val dynamicRefs: List[String] = NewStyle.function.getDynamicEntities()
|
||||
val dynamicRefs: List[String] = NewStyle.function.getDynamicEntities(None)
|
||||
.map(entity => s"reference:${entity.entityName}")
|
||||
|
||||
val staticRefs: List[String] = staticRefTypeToValidateFunction.keys.toList
|
||||
@ -534,16 +534,16 @@ case class DynamicEntityIntTypeExample(`type`: DynamicEntityFieldType, example:
|
||||
|
||||
|
||||
trait DynamicEntityProvider {
|
||||
def getById(dynamicEntityId: String): Box[DynamicEntityT]
|
||||
def getById(bankId: Option[String], dynamicEntityId: String): Box[DynamicEntityT]
|
||||
|
||||
//Note, we use entity name to create the roles, and bank level and system level can not be mixed,
|
||||
// so --> here can not use bankId as parameters:
|
||||
def getByEntityName(entityName: String): Box[DynamicEntityT]
|
||||
|
||||
def getDynamicEntities(): List[DynamicEntityT]
|
||||
def getDynamicEntities(bankId: Option[String]): List[DynamicEntityT]
|
||||
|
||||
def getDynamicEntitiesByUserId(userId: String): List[DynamicEntity]
|
||||
|
||||
def getDynamicEntitiesByBankId(bankId: String): List[DynamicEntity]
|
||||
|
||||
def createOrUpdate(dynamicEntity: DynamicEntityT): Box[DynamicEntityT]
|
||||
|
||||
def delete(dynamicEntity: DynamicEntityT):Box[Boolean]
|
||||
|
||||
@ -10,22 +10,27 @@ import org.apache.commons.lang3.StringUtils
|
||||
|
||||
object MappedDynamicEntityProvider extends DynamicEntityProvider with CustomJsonFormats with MdcLoggable {
|
||||
|
||||
override def getById(dynamicEntityId: String): Box[DynamicEntityT] = DynamicEntity.find(
|
||||
By(DynamicEntity.DynamicEntityId, dynamicEntityId)
|
||||
)
|
||||
|
||||
override def getByEntityName(entityName: String): Box[DynamicEntityT] = DynamicEntity.find(
|
||||
By(DynamicEntity.EntityName, entityName)
|
||||
)
|
||||
|
||||
override def getDynamicEntities(): List[DynamicEntity] = {
|
||||
DynamicEntity.findAll()
|
||||
override def getById(bankId: Option[String], dynamicEntityId: String): Box[DynamicEntityT] = {
|
||||
if (bankId.isEmpty)
|
||||
DynamicEntity.find(By(DynamicEntity.DynamicEntityId, dynamicEntityId))
|
||||
else
|
||||
DynamicEntity.find(
|
||||
By(DynamicEntity.DynamicEntityId, dynamicEntityId),
|
||||
By(DynamicEntity.BankId, bankId.getOrElse("")
|
||||
))
|
||||
}
|
||||
|
||||
override def getDynamicEntitiesByBankId(bankId: String): List[DynamicEntity] = {
|
||||
DynamicEntity.findAll(By(DynamicEntity.BankId, bankId))
|
||||
override def getByEntityName(entityName: String): Box[DynamicEntityT] =
|
||||
DynamicEntity.find(By(DynamicEntity.EntityName, entityName))
|
||||
|
||||
|
||||
override def getDynamicEntities(bankId: Option[String]): List[DynamicEntity] = {
|
||||
if (bankId.isEmpty)
|
||||
DynamicEntity.findAll()
|
||||
else
|
||||
DynamicEntity.findAll(By(DynamicEntity.BankId, bankId.getOrElse("")))
|
||||
}
|
||||
|
||||
|
||||
override def getDynamicEntitiesByUserId(userId: String): List[DynamicEntity] = {
|
||||
DynamicEntity.findAll(By(DynamicEntity.UserId, userId))
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import com.openbankproject.commons.model.{Converter, JsonFieldReName}
|
||||
import net.liftweb.common.Box
|
||||
import net.liftweb.json
|
||||
import net.liftweb.json.Formats
|
||||
import net.liftweb.json.JsonAST.{JArray, JField, JNull, JObject, JString}
|
||||
import net.liftweb.json.JsonAST.{JArray, JField, JNull, JObject, JString, JValue}
|
||||
import net.liftweb.util.SimpleInjector
|
||||
|
||||
object EndpointMappingProvider extends SimpleInjector {
|
||||
@ -22,14 +22,16 @@ trait EndpointMappingT {
|
||||
def operationId: String
|
||||
def requestMapping: String
|
||||
def responseMapping: String
|
||||
def bankId: Option[String]
|
||||
}
|
||||
|
||||
case class EndpointMappingCommons(
|
||||
endpointMappingId: Option[String],
|
||||
operationId: String,
|
||||
requestMapping: String,
|
||||
responseMapping: String
|
||||
) extends EndpointMappingT with JsonFieldReName {
|
||||
responseMapping: String,
|
||||
bankId: Option[String]
|
||||
) extends EndpointMappingT with JsonFieldReName {
|
||||
/**
|
||||
* when serialized to json, the Option field will be not shown, this endpoint just generate a full fields json, include all None value fields
|
||||
* @return JObject include all fields
|
||||
@ -39,7 +41,8 @@ case class EndpointMappingCommons(
|
||||
JField("operation_id", JString(this.operationId)),
|
||||
JField("request_mapping", json.parse(this.requestMapping)),
|
||||
JField("response_mapping", json.parse(this.responseMapping)),
|
||||
JField("endpoint_mapping_id", this.endpointMappingId.map(JString(_)).getOrElse(JNull))
|
||||
JField("endpoint_mapping_id", this.endpointMappingId.map(JString(_)).getOrElse(JNull)),
|
||||
JField("bank_id", this.bankId.map(JString(_)).getOrElse(JNull))
|
||||
))
|
||||
}
|
||||
}
|
||||
@ -47,15 +50,15 @@ case class EndpointMappingCommons(
|
||||
object EndpointMappingCommons extends Converter[EndpointMappingT, EndpointMappingCommons]
|
||||
|
||||
trait EndpointMappingProvider {
|
||||
def getById(endpointMappingId: String): Box[EndpointMappingT]
|
||||
def getById(bankId: Option[String], endpointMappingId: String): Box[EndpointMappingT]
|
||||
|
||||
def getByOperationId(operationId: String): Box[EndpointMappingT]
|
||||
def getByOperationId(bankId: Option[String], operationId: String): Box[EndpointMappingT]
|
||||
|
||||
def getAllEndpointMappings: List[EndpointMappingT]
|
||||
def getAllEndpointMappings(bankId: Option[String]): List[EndpointMappingT]
|
||||
|
||||
def createOrUpdate(endpointMapping: EndpointMappingT): Box[EndpointMappingT]
|
||||
def createOrUpdate(bankId: Option[String], endpointMapping: EndpointMappingT): Box[EndpointMappingT]
|
||||
|
||||
def delete(endpointMappingId: String):Box[Boolean]
|
||||
def delete(bankId: Option[String], endpointMappingId: String):Box[Boolean]
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -13,11 +13,20 @@ import net.liftweb.json.JsonAST.JArray
|
||||
|
||||
object MappedEndpointMappingProvider extends EndpointMappingProvider with CustomJsonFormats{
|
||||
|
||||
override def getById(endpointMappingId: String): Box[EndpointMappingT] = getByEndpointMappingId(endpointMappingId)
|
||||
|
||||
override def getByOperationId(operationId: String): Box[EndpointMappingT] = EndpointMapping.find(By(EndpointMapping.OperationId, operationId))
|
||||
override def getById(bankId: Option[String], endpointMappingId: String): Box[EndpointMappingT] = {
|
||||
if (bankId.isEmpty) getByEndpointMappingId(endpointMappingId)
|
||||
else getByEndpointMappingId(bankId.getOrElse(""), endpointMappingId)
|
||||
}
|
||||
|
||||
override def createOrUpdate(endpointMapping: EndpointMappingT): Box[EndpointMappingT] = {
|
||||
override def getByOperationId(bankId: Option[String], operationId: String): Box[EndpointMappingT] = {
|
||||
if (bankId.isEmpty) EndpointMapping.find(By(EndpointMapping.OperationId, operationId))
|
||||
else EndpointMapping.find(
|
||||
By(EndpointMapping.OperationId, operationId),
|
||||
By(EndpointMapping.BankId, bankId.getOrElse(""))
|
||||
)
|
||||
}
|
||||
|
||||
override def createOrUpdate(bankId: Option[String], endpointMapping: EndpointMappingT): Box[EndpointMappingT] = {
|
||||
//to find exists endpointMapping, if endpointMappingId supplied, query by endpointMappingId, or use endpointName and endpointMappingId to do query
|
||||
val existsEndpointMapping: Box[EndpointMapping] = endpointMapping.endpointMappingId match {
|
||||
case Some(id) if (StringUtils.isNotBlank(id)) => getByEndpointMappingId(id)
|
||||
@ -33,16 +42,24 @@ object MappedEndpointMappingProvider extends EndpointMappingProvider with Custom
|
||||
.OperationId(endpointMapping.operationId)
|
||||
.RequestMapping(endpointMapping.requestMapping)
|
||||
.ResponseMapping(endpointMapping.responseMapping)
|
||||
.BankId(endpointMapping.bankId.getOrElse(null))
|
||||
.saveMe()
|
||||
}
|
||||
}
|
||||
|
||||
override def delete(endpointMappingId: String): Box[Boolean] = getByEndpointMappingId(endpointMappingId).map(_.delete_!)
|
||||
override def delete(bankId: Option[String], endpointMappingId: String): Box[Boolean] =
|
||||
if (bankId.isEmpty) getByEndpointMappingId(endpointMappingId).map(_.delete_!)
|
||||
else getByEndpointMappingId(bankId.getOrElse(""),endpointMappingId).map(_.delete_!)
|
||||
|
||||
private[this] def getByEndpointMappingId(endpointMappingId: String): Box[EndpointMapping] = EndpointMapping.find(By(EndpointMapping.EndpointMappingId, endpointMappingId))
|
||||
private[this] def getByEndpointMappingId(bankId: String, endpointMappingId: String): Box[EndpointMapping] = EndpointMapping.find(
|
||||
By(EndpointMapping.EndpointMappingId, endpointMappingId),
|
||||
By(EndpointMapping.BankId, bankId),
|
||||
)
|
||||
|
||||
override def getAllEndpointMappings(): List[EndpointMappingT] = EndpointMapping.findAll()
|
||||
|
||||
override def getAllEndpointMappings(bankId: Option[String]): List[EndpointMappingT] =
|
||||
if (bankId.isEmpty) EndpointMapping.findAll()
|
||||
else EndpointMapping.findAll(By(EndpointMapping.BankId, bankId.getOrElse("")))
|
||||
}
|
||||
|
||||
class EndpointMapping extends EndpointMappingT with LongKeyedMapper[EndpointMapping] with IdPK with CustomJsonFormats{
|
||||
@ -53,11 +70,13 @@ class EndpointMapping extends EndpointMappingT with LongKeyedMapper[EndpointMapp
|
||||
object OperationId extends MappedString(this, 255)
|
||||
object RequestMapping extends MappedText(this)
|
||||
object ResponseMapping extends MappedText(this)
|
||||
object BankId extends MappedString(this, 255)
|
||||
|
||||
override def endpointMappingId: Option[String] = Option(EndpointMappingId.get)
|
||||
override def operationId: String = OperationId.get
|
||||
override def requestMapping: String = RequestMapping.get
|
||||
override def responseMapping: String = ResponseMapping.get
|
||||
override def bankId: Option[String] = if (BankId.get == null || BankId.get.isEmpty) None else Some(BankId.get)
|
||||
}
|
||||
|
||||
object EndpointMapping extends EndpointMapping with LongKeyedMetaMapper[EndpointMapping] {
|
||||
|
||||
@ -24,13 +24,13 @@ trait EntitlementProvider {
|
||||
def getEntitlementById(entitlementId: String) : Box[Entitlement]
|
||||
def getEntitlementsByUserId(userId: String) : Box[List[Entitlement]]
|
||||
def getEntitlementsByUserIdFuture(userId: String) : Future[Box[List[Entitlement]]]
|
||||
def getEntitlementsByBankId(userId: String) : Future[Box[List[Entitlement]]]
|
||||
def getEntitlementsByBankId(bankId: String) : Future[Box[List[Entitlement]]]
|
||||
def deleteEntitlement(entitlement: Box[Entitlement]) : Box[Boolean]
|
||||
def getEntitlements() : Box[List[Entitlement]]
|
||||
def getEntitlementsByRole(roleName: String): Box[List[Entitlement]]
|
||||
def getEntitlementsFuture() : Future[Box[List[Entitlement]]]
|
||||
def getEntitlementsByRoleFuture(roleName: String) : Future[Box[List[Entitlement]]]
|
||||
def addEntitlement(bankId: String, userId: String, roleName: String) : Box[Entitlement]
|
||||
def addEntitlement(bankId: String, userId: String, roleName: String, createdByProcess: String="manual") : Box[Entitlement]
|
||||
def deleteDynamicEntityEntitlement(entityName: String, bankId:Option[String]) : Box[Boolean]
|
||||
def deleteEntitlements(entityNames: List[String]) : Box[Boolean]
|
||||
}
|
||||
@ -40,6 +40,7 @@ trait Entitlement {
|
||||
def bankId : String
|
||||
def userId : String
|
||||
def roleName : String
|
||||
def createdByProcess : String
|
||||
}
|
||||
|
||||
class RemotedataEntitlementsCaseClasses {
|
||||
@ -53,7 +54,7 @@ class RemotedataEntitlementsCaseClasses {
|
||||
case class getEntitlementsByRole(roleName: String)
|
||||
case class getEntitlementsFuture()
|
||||
case class getEntitlementsByRoleFuture(roleName: String)
|
||||
case class addEntitlement(bankId: String, userId: String, roleName: String)
|
||||
case class addEntitlement(bankId: String, userId: String, roleName: String, createdByProcess: String="manual")
|
||||
case class deleteDynamicEntityEntitlement(entityName: String, bankId:Option[String])
|
||||
case class deleteEntitlements(entityNames: List[String])
|
||||
}
|
||||
|
||||
@ -102,12 +102,13 @@ object MappedEntitlementsProvider extends EntitlementProvider {
|
||||
}
|
||||
}
|
||||
|
||||
override def addEntitlement(bankId: String, userId: String, roleName: String): Box[Entitlement] = {
|
||||
override def addEntitlement(bankId: String, userId: String, roleName: String, createdByProcess: String ="manual"): Box[Entitlement] = {
|
||||
// Return a Box so we can handle errors later.
|
||||
val addEntitlement = MappedEntitlement.create
|
||||
.mBankId(bankId)
|
||||
.mUserId(userId)
|
||||
.mRoleName(roleName)
|
||||
.mCreatedByProcess(createdByProcess)
|
||||
.saveMe()
|
||||
Some(addEntitlement)
|
||||
}
|
||||
@ -122,11 +123,14 @@ class MappedEntitlement extends Entitlement
|
||||
object mBankId extends UUIDString(this)
|
||||
object mUserId extends UUIDString(this)
|
||||
object mRoleName extends MappedString(this, 64)
|
||||
object mCreatedByProcess extends MappedString(this, 255)
|
||||
|
||||
override def entitlementId: String = mEntitlementId.get.toString
|
||||
override def bankId: String = mBankId.get
|
||||
override def userId: String = mUserId.get
|
||||
override def roleName: String = mRoleName.get
|
||||
override def createdByProcess: String =
|
||||
if(mCreatedByProcess.get == null || mCreatedByProcess.get.isEmpty) "manual" else mCreatedByProcess.get
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -197,6 +197,8 @@ case class BankAccountExtended(val bankAccount: BankAccount) extends MdcLoggable
|
||||
val emailAddress = ""
|
||||
val name : String = bankAccount.accountHolder
|
||||
val createdByConsentId = None
|
||||
val createdByUserInvitationId = None
|
||||
val isDeleted = None
|
||||
})
|
||||
} else {
|
||||
accountHolders
|
||||
|
||||
@ -30,7 +30,7 @@ import java.util.{Collections, Date}
|
||||
import code.api.util.APIUtil
|
||||
import code.api.util.migration.Migration.DbFunction
|
||||
import code.consumer.{Consumers, ConsumersProvider}
|
||||
import code.model.AppType.{Mobile, Web}
|
||||
import code.model.AppType.{Public, Confidential}
|
||||
import code.model.dataAccess.ResourceUser
|
||||
import code.nonce.NoncesProvider
|
||||
import code.token.TokensProvider
|
||||
@ -53,11 +53,15 @@ import scala.concurrent.Future
|
||||
|
||||
sealed trait AppType
|
||||
object AppType {
|
||||
case object Web extends AppType
|
||||
case object Mobile extends AppType
|
||||
case object Confidential extends AppType
|
||||
case object Public extends AppType
|
||||
case object Unknown extends AppType
|
||||
def valueOf(value: String): AppType = value match {
|
||||
case "Web" => Web
|
||||
case "Mobile" => Mobile
|
||||
case "Web" => Confidential
|
||||
case "Confidential" => Confidential
|
||||
case "Mobile" => Public
|
||||
case "Public" => Public
|
||||
case "Unknown" => Unknown
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,7 +132,9 @@ object MappedConsumersProvider extends ConsumersProvider with MdcLoggable {
|
||||
developerEmail: Option[String],
|
||||
redirectURL: Option[String],
|
||||
createdByUserId: Option[String],
|
||||
clientCertificate: Option[String] = None): Box[Consumer] = {
|
||||
clientCertificate: Option[String] = None,
|
||||
company: Option[String] = None
|
||||
): Box[Consumer] = {
|
||||
tryo {
|
||||
val c = Consumer.create
|
||||
key match {
|
||||
@ -154,8 +160,8 @@ object MappedConsumersProvider extends ConsumersProvider with MdcLoggable {
|
||||
}
|
||||
appType match {
|
||||
case Some(v) => v match {
|
||||
case Web => c.appType(Web.toString)
|
||||
case Mobile => c.appType(Mobile.toString)
|
||||
case Confidential => c.appType(Confidential.toString)
|
||||
case Public => c.appType(Public.toString)
|
||||
}
|
||||
case None =>
|
||||
}
|
||||
@ -175,6 +181,10 @@ object MappedConsumersProvider extends ConsumersProvider with MdcLoggable {
|
||||
case Some(v) => c.createdByUserId(v)
|
||||
case None =>
|
||||
}
|
||||
company match {
|
||||
case Some(v) => c.company(v)
|
||||
case None =>
|
||||
}
|
||||
|
||||
clientCertificate.filter(StringUtils.isNotBlank).foreach(c.clientCertificate(_))
|
||||
|
||||
@ -223,8 +233,8 @@ object MappedConsumersProvider extends ConsumersProvider with MdcLoggable {
|
||||
}
|
||||
appType match {
|
||||
case Some(v) => v match {
|
||||
case Web => c.appType(Web.toString)
|
||||
case Mobile => c.appType(Mobile.toString)
|
||||
case Confidential => c.appType(Confidential.toString)
|
||||
case Public => c.appType(Public.toString)
|
||||
}
|
||||
case None =>
|
||||
}
|
||||
@ -395,8 +405,8 @@ object MappedConsumersProvider extends ConsumersProvider with MdcLoggable {
|
||||
}
|
||||
appType match {
|
||||
case Some(v) => v match {
|
||||
case Web => c.appType(Web.toString)
|
||||
case Mobile => c.appType(Mobile.toString)
|
||||
case Confidential => c.appType(Confidential.toString)
|
||||
case Public => c.appType(Public.toString)
|
||||
}
|
||||
case None =>
|
||||
}
|
||||
@ -568,6 +578,9 @@ class Consumer extends LongKeyedMapper[Consumer] with CreatedUpdated{
|
||||
override def defaultValue = -1
|
||||
}
|
||||
object clientCertificate extends MappedString(this, 4000)
|
||||
object company extends MappedString(this, 100) {
|
||||
override def displayName = "Company:"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -130,15 +130,8 @@ object UserX {
|
||||
}
|
||||
}
|
||||
|
||||
def findAll() = {
|
||||
Users.users.vend.getAllUsers() match {
|
||||
case Full(list) => list
|
||||
case _ => List()
|
||||
}
|
||||
}
|
||||
|
||||
def createResourceUser(provider: String, providerId: Option[String], createdByConsentId: Option[String], name: Option[String], email: Option[String], userId: Option[String]) = {
|
||||
Users.users.vend.createResourceUser(provider, providerId, createdByConsentId, name, email, userId)
|
||||
def createResourceUser(provider: String, providerId: Option[String], createdByConsentId: Option[String], name: Option[String], email: Option[String], userId: Option[String], company: Option[String]) = {
|
||||
Users.users.vend.createResourceUser(provider, providerId, createdByConsentId, name, email, userId, None, company)
|
||||
}
|
||||
|
||||
def createUnsavedResourceUser(provider: String, providerId: Option[String], name: Option[String], email: Option[String], userId: Option[String]) = {
|
||||
|
||||
@ -161,7 +161,7 @@ case class ViewExtended(val view: View) {
|
||||
else None
|
||||
|
||||
val transactionBalance =
|
||||
if (view.canSeeTransactionBalance) transaction.balance.toString()
|
||||
if (view.canSeeTransactionBalance && transaction.balance != null) transaction.balance.toString()
|
||||
else ""
|
||||
|
||||
new ModeratedTransaction(
|
||||
@ -228,7 +228,7 @@ case class ViewExtended(val view: View) {
|
||||
else None
|
||||
|
||||
val transactionBalance =
|
||||
if (view.canSeeTransactionBalance) transactionCore.balance.toString()
|
||||
if (view.canSeeTransactionBalance && transactionCore.balance != null) transactionCore.balance.toString()
|
||||
else ""
|
||||
|
||||
new ModeratedTransactionCore(
|
||||
@ -312,7 +312,7 @@ case class ViewExtended(val view: View) {
|
||||
if(view.canSeeTransactionThisBankAccount)
|
||||
{
|
||||
val owners : Set[User] = if(view.canSeeBankAccountOwners) bankAccount.userOwners else Set()
|
||||
val balance = if(view.canSeeBankAccountBalance) bankAccount.balance.toString else ""
|
||||
val balance = if(view.canSeeBankAccountBalance && bankAccount.balance != null) bankAccount.balance.toString else ""
|
||||
val accountType = if(view.canSeeBankAccountType) Some(bankAccount.accountType) else None
|
||||
val currency = if(view.canSeeBankAccountCurrency) Some(bankAccount.currency) else None
|
||||
val label = if(view.canSeeBankAccountLabel) Some(bankAccount.label) else None
|
||||
@ -362,7 +362,7 @@ case class ViewExtended(val view: View) {
|
||||
if(view.canSeeTransactionThisBankAccount)
|
||||
{
|
||||
val owners : Set[User] = if(view.canSeeBankAccountOwners) bankAccount.userOwners else Set()
|
||||
val balance = if(view.canSeeBankAccountBalance) bankAccount.balance.toString else ""
|
||||
val balance = if(view.canSeeBankAccountBalance && bankAccount.balance !=null) bankAccount.balance.toString else ""
|
||||
val accountType = if(view.canSeeBankAccountType) Some(bankAccount.accountType) else None
|
||||
val currency = if(view.canSeeBankAccountCurrency) Some(bankAccount.currency) else None
|
||||
val label = if(view.canSeeBankAccountLabel) Some(bankAccount.label) else None
|
||||
@ -409,7 +409,7 @@ case class ViewExtended(val view: View) {
|
||||
if(view.canSeeTransactionThisBankAccount)
|
||||
{
|
||||
val owners : Set[User] = if(view.canSeeBankAccountOwners) bankAccount.userOwners else Set()
|
||||
val balance = if(view.canSeeBankAccountBalance) Some(bankAccount.balance.toString) else None
|
||||
val balance = if(view.canSeeBankAccountBalance && bankAccount.balance != null) Some(bankAccount.balance.toString) else None
|
||||
val accountType = if(view.canSeeBankAccountType) Some(bankAccount.accountType) else None
|
||||
val currency = if(view.canSeeBankAccountCurrency) Some(bankAccount.currency) else None
|
||||
val label = if(view.canSeeBankAccountLabel) Some(bankAccount.label) else None
|
||||
|
||||
@ -31,9 +31,11 @@ import code.accountholders.AccountHolders
|
||||
import code.api.util.APIUtil.{hasAnOAuthHeader, isValidStrongPassword, logger, _}
|
||||
import code.api.util.ErrorMessages._
|
||||
import code.api.util._
|
||||
import code.api.v4_0_0.dynamic.DynamicEndpointHelper
|
||||
import code.api.{APIFailure, DirectLogin, GatewayLogin, OAuthHandshake}
|
||||
import code.bankconnectors.Connector
|
||||
import code.context.UserAuthContextProvider
|
||||
import code.entitlement.Entitlement
|
||||
import code.loginattempts.LoginAttempt
|
||||
import code.users.Users
|
||||
import code.util.Helper
|
||||
@ -52,9 +54,12 @@ import com.openbankproject.commons.ExecutionContext.Implicits.global
|
||||
import code.webuiprops.MappedWebUiPropsProvider.getWebUiPropsValue
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import code.util.HydraUtil._
|
||||
import com.github.dwickern.macros.NameOf.nameOf
|
||||
import sh.ory.hydra.model.AcceptLoginRequest
|
||||
import net.liftweb.http.S.fmapFunc
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
/**
|
||||
* An O-R mapped "User" class that includes first name, last name, password
|
||||
*
|
||||
@ -332,13 +337,6 @@ class AuthUser extends MegaProtoUser[AuthUser] with MdcLoggable {
|
||||
}
|
||||
}
|
||||
|
||||
def getResourceUsers(): List[ResourceUser] = {
|
||||
Users.users.vend.getAllUsers match {
|
||||
case Full(userList) => userList
|
||||
case _ => List()
|
||||
}
|
||||
}
|
||||
|
||||
def getResourceUserByUsername(username: String) : Box[User] = {
|
||||
Users.users.vend.getUserByUserName(username)
|
||||
}
|
||||
@ -421,8 +419,8 @@ import net.liftweb.util.Helpers._
|
||||
override def fieldOrder = List(id, firstName, lastName, email, username, password, provider)
|
||||
override def signupFields = List(firstName, lastName, email, username, password)
|
||||
|
||||
// If we want to validate email addresses set this to false
|
||||
override def skipEmailValidation = APIUtil.getPropsAsBoolValue("authUser.skipEmailValidation", true)
|
||||
// To force validation of email addresses set this to false (default as of 29 June 2021)
|
||||
override def skipEmailValidation = APIUtil.getPropsAsBoolValue("authUser.skipEmailValidation", false)
|
||||
|
||||
override def loginXhtml = {
|
||||
val loginXml = Templates(List("templates-hidden","_login")).map({
|
||||
@ -455,12 +453,15 @@ import net.liftweb.util.Helpers._
|
||||
*
|
||||
*/
|
||||
def getCurrentUser: Box[User] = {
|
||||
val authorization = S.request.map(_.header("Authorization")).flatten
|
||||
val authorization: Box[String] = S.request.map(_.header("Authorization")).flatten
|
||||
val directLogin: Box[String] = S.request.map(_.header("DirectLogin")).flatten
|
||||
for {
|
||||
resourceUser <- if (AuthUser.currentUser.isDefined)
|
||||
//AuthUser.currentUser.get.user.foreign // this will be issue when the resource user is in remote side
|
||||
Users.users.vend.getUserByUserName(AuthUser.currentUser.openOrThrowException(ErrorMessages.attemptedToOpenAnEmptyBox).username.get)
|
||||
else if (hasDirectLoginHeader(authorization))
|
||||
else if (directLogin.isDefined) // Direct Login
|
||||
DirectLogin.getUser
|
||||
else if (hasDirectLoginHeader(authorization)) // Direct Login Deprecated
|
||||
DirectLogin.getUser
|
||||
else if (hasAnOAuthHeader(authorization)) {
|
||||
OAuthHandshake.getUser
|
||||
@ -923,6 +924,16 @@ def restoreSomeSessions(): Unit = {
|
||||
logUserIn(user, () => {
|
||||
S.notice(S.?("logged.in"))
|
||||
preLoginState()
|
||||
if(emailDomainToSpaceMappings.nonEmpty){
|
||||
Future{
|
||||
tryo{AuthUser.grantEntitlementsToUseDynamicEndpointsInSpaces(user)}
|
||||
.openOr(logger.error(s"${user} checkInternalRedirectAndLogUseIn.grantEntitlementsToUseDynamicEndpointsInSpaces throw exception! "))
|
||||
}}
|
||||
if(emailDomainToEntitlementMappings.nonEmpty){
|
||||
Future{
|
||||
tryo{AuthUser.grantEmailDomainEntitlementsToUser(user)}
|
||||
.openOr(logger.error(s"${user} checkInternalRedirectAndLogUseIn.grantEmailDomainEntitlementsToUser throw exception! "))
|
||||
}}
|
||||
S.redirectTo(redirect)
|
||||
})
|
||||
} else {
|
||||
@ -1101,6 +1112,117 @@ def restoreSomeSessions(): Unit = {
|
||||
} yield v
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Space is an alias for the OBP Bank. Each Bank / Space can contain many Dynamic Endpoints. If a User belongs to a Space,
|
||||
* the User can use those endpoints but not modify them. If a User creates a Bank (aka Space) the user can create
|
||||
* and modify Dynamic Endpoints and other objects in that Bank / Space.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
def mySpaces(user: AuthUser): List[BankId] = {
|
||||
//1st: first check the user is validated
|
||||
if (user.validated_?) {
|
||||
//userEmail = robert.uk.29@example.com
|
||||
// 2st get the email domain - `example.com`
|
||||
val emailDomain = StringUtils.substringAfterLast(user.email.get, "@")
|
||||
|
||||
//3 return the bankIds
|
||||
emailDomainToSpaceMappings.collectFirst {
|
||||
case EmailDomainToSpaceMapping(`emailDomain`, ids) => ids.map(BankId(_));
|
||||
} getOrElse Nil
|
||||
|
||||
} else {
|
||||
Nil
|
||||
}
|
||||
}
|
||||
|
||||
def grantEntitlementsToUseDynamicEndpointsInSpaces(user: AuthUser) = {
|
||||
val createdByProcess = "grantEntitlementsToUseDynamicEndpointsInSpaces"
|
||||
val userId = user.user.obj.map(_.userId).getOrElse("")
|
||||
|
||||
// user's already auto granted entitlements.
|
||||
val entitlementsGrantedByThisProcess = Entitlement.entitlement.vend.getEntitlementsByUserId(userId)
|
||||
.map(_.filter(role => role.createdByProcess == createdByProcess))
|
||||
.getOrElse(Nil)
|
||||
|
||||
def alreadyHasEntitlement(role:ApiRole, bankId: String): Boolean =
|
||||
entitlementsGrantedByThisProcess.exists(entitlement => entitlement.roleName == role.toString() && entitlement.bankId == bankId)
|
||||
|
||||
//call mySpaces --> get BankIds --> listOfRolesToUseAllDynamicEndpointsAOneBank (at each bank)--> Grant roles (for each role)
|
||||
val allCurrentDynamicRoleToBankIdPairs: List[(ApiRole, String)] = for {
|
||||
BankId(bankId) <- mySpaces(user: AuthUser)
|
||||
role <- DynamicEndpointHelper.listOfRolesToUseAllDynamicEndpointsAOneBank(Some(bankId))
|
||||
} yield {
|
||||
if (!alreadyHasEntitlement(role, bankId)) {
|
||||
Entitlement.entitlement.vend.addEntitlement(bankId, userId, role.toString, createdByProcess)
|
||||
}
|
||||
|
||||
role -> bankId
|
||||
}
|
||||
|
||||
// if user's auto granted entitlement invalid, delete it.
|
||||
// invalid happens when some dynamic endpoints are removed, so the entitlements linked to the deleted dynamic endpoints are invalid.
|
||||
for {
|
||||
grantedEntitlement <- entitlementsGrantedByThisProcess
|
||||
grantedEntitlementRoleName = grantedEntitlement.roleName
|
||||
grantedEntitlementBankId = grantedEntitlement.bankId
|
||||
} {
|
||||
val isInValidEntitlement = !allCurrentDynamicRoleToBankIdPairs.exists { roleToBankIdPair =>
|
||||
val(role, roleBankId) = roleToBankIdPair
|
||||
role.toString() == grantedEntitlementRoleName && roleBankId == grantedEntitlementBankId
|
||||
}
|
||||
|
||||
if(isInValidEntitlement) {
|
||||
Entitlement.entitlement.vend.deleteEntitlement(Full(grantedEntitlement))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def grantEmailDomainEntitlementsToUser(user: AuthUser) = {
|
||||
if(emailDomainToEntitlementMappings.nonEmpty){
|
||||
val createdByProcess = "grantEmailDomainEntitlementsToUser"
|
||||
val userId = user.user.obj.map(_.userId).getOrElse("")
|
||||
|
||||
// user's already auto granted entitlements.
|
||||
val entitlementsGrantedByThisProcess = Entitlement.entitlement.vend.getEntitlementsByUserId(userId)
|
||||
.map(_.filter(role => role.createdByProcess == createdByProcess))
|
||||
.getOrElse(Nil)
|
||||
|
||||
def alreadyHasEntitlement(bankId: String, roleName:String): Boolean =
|
||||
entitlementsGrantedByThisProcess.exists(entitlement => entitlement.roleName == roleName && entitlement.bankId == bankId)
|
||||
|
||||
val allEntitlementsFromCurrentProps: List[(String, String)] = for{
|
||||
emailDomainToEntitlementMapping <- emailDomainToEntitlementMappings
|
||||
domain = emailDomainToEntitlementMapping.domain
|
||||
entitlement <- emailDomainToEntitlementMapping.entitlements if StringUtils.substringAfterLast(user.email.get, "@") == domain
|
||||
roleName = entitlement.role_name
|
||||
roleBankId = entitlement.bank_id
|
||||
} yield {
|
||||
if (!alreadyHasEntitlement(roleBankId, roleName)) {
|
||||
Entitlement.entitlement.vend.addEntitlement(roleBankId, userId, roleName, createdByProcess)
|
||||
}
|
||||
roleName -> roleBankId
|
||||
}
|
||||
|
||||
// if user's auto granted entitlement invalid, delete it.
|
||||
// invalid happens when some dynamic endpoints are removed, so the entitlements linked to the deleted dynamic endpoints are invalid.
|
||||
for {
|
||||
grantedEntitlement <- entitlementsGrantedByThisProcess
|
||||
grantedEntitlementRoleName = grantedEntitlement.roleName
|
||||
grantedEntitlementBankId = grantedEntitlement.bankId
|
||||
} {
|
||||
val isInValidEntitlement = !allEntitlementsFromCurrentProps.exists { roleNameToBankIdPair =>
|
||||
val(roleName, roleBankId) = roleNameToBankIdPair
|
||||
roleName == grantedEntitlementRoleName && roleBankId == grantedEntitlementBankId
|
||||
}
|
||||
|
||||
if(isInValidEntitlement) {
|
||||
Entitlement.entitlement.vend.deleteEntitlement(Full(grantedEntitlement))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a helper method
|
||||
@ -1149,7 +1271,7 @@ def restoreSomeSessions(): Unit = {
|
||||
* Find the authUser by author user name(authUser and resourceUser are the same).
|
||||
* Only search for the local database.
|
||||
*/
|
||||
protected def findUserByUsernameLocally(name: String): Box[TheUserType] = {
|
||||
def findUserByUsernameLocally(name: String): Box[TheUserType] = {
|
||||
find(By(this.username, name))
|
||||
}
|
||||
|
||||
@ -1169,6 +1291,24 @@ def restoreSomeSessions(): Unit = {
|
||||
case _ => ""
|
||||
}
|
||||
}
|
||||
|
||||
override def passwordResetXhtml = {
|
||||
<div id="recover-password" tabindex="-1">
|
||||
<h1>{if(S.queryString.isDefined) Helper.i18n("set.your.password") else S.?("reset.your.password")}</h1>
|
||||
<form action={S.uri} method="post">
|
||||
<div class="form-group">
|
||||
<label for="password">{S.?("enter.your.new.password")}</label> <span><input id="password" class="form-control" type="password" /></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="repeatpassword">{S.?("repeat.your.new.password")}</label> <span><input id="repeatpassword" class="form-control" type="password" /></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="submit" class="btn btn-danger" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the authUsers by author email(authUser and resourceUser are the same).
|
||||
* Only search for the local database.
|
||||
@ -1235,4 +1375,21 @@ def restoreSomeSessions(): Unit = {
|
||||
|
||||
innerSignup
|
||||
}
|
||||
|
||||
def scrambleAuthUser(userPrimaryKey: UserPrimaryKey): Box[Boolean] = tryo {
|
||||
AuthUser.find(By(AuthUser.user, userPrimaryKey.value)) match {
|
||||
case Full(user) =>
|
||||
val scrambledUser = user.firstName(Helpers.randomString(16))
|
||||
.email(Helpers.randomString(10) + "@example.com")
|
||||
.username("DELETED-" + Helpers.randomString(16))
|
||||
.firstName(Helpers.randomString(16))
|
||||
.lastName(Helpers.randomString(16))
|
||||
.password(Helpers.randomString(40))
|
||||
.validated(false)
|
||||
scrambledUser.save()
|
||||
case Empty => true // There is a resource user but no the correlated Auth user
|
||||
case _ => false // Error case
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -75,8 +75,12 @@ class ResourceUser extends LongKeyedMapper[ResourceUser] with User with ManyToMa
|
||||
object providerId extends MappedString(this, 100){
|
||||
override def defaultValue = java.util.UUID.randomUUID.toString
|
||||
}
|
||||
|
||||
object Company extends MappedString(this, 50)
|
||||
object CreatedByConsentId extends MappedString(this, 100)
|
||||
object CreatedByUserInvitationId extends MappedString(this, 100)
|
||||
object IsDeleted extends MappedBoolean(this) {
|
||||
override def defaultValue = false
|
||||
}
|
||||
|
||||
def emailAddress = {
|
||||
val e = email.get
|
||||
@ -90,6 +94,7 @@ class ResourceUser extends LongKeyedMapper[ResourceUser] with User with ManyToMa
|
||||
|
||||
def name : String = name_.get
|
||||
def provider = provider_.get
|
||||
def company: String = Company.get
|
||||
|
||||
def toCaseClass: ResourceUserCaseClass =
|
||||
ResourceUserCaseClass(
|
||||
@ -102,6 +107,8 @@ class ResourceUser extends LongKeyedMapper[ResourceUser] with User with ManyToMa
|
||||
)
|
||||
|
||||
override def createdByConsentId = if(CreatedByConsentId.get == null) None else if (CreatedByConsentId.get.isEmpty) None else Some(CreatedByConsentId.get) //null --> None
|
||||
override def createdByUserInvitationId = if(CreatedByUserInvitationId.get == null) None else if (CreatedByUserInvitationId.get.isEmpty) None else Some(CreatedByUserInvitationId.get) //null --> None
|
||||
override def isDeleted: Option[Boolean] = if(IsDeleted.get == null) None else Some(IsDeleted.get) // null --> false
|
||||
}
|
||||
|
||||
object ResourceUser extends ResourceUser with LongKeyedMetaMapper[ResourceUser]{
|
||||
|
||||
@ -62,7 +62,11 @@ object RemotedataActors extends MdcLoggable {
|
||||
ActorProps[RemotedataCustomerAttributeActor] -> RemotedataCustomerAttribute.actorName,
|
||||
ActorProps[RemotedataTransactionAttributeActor] -> RemotedataTransactionAttribute.actorName,
|
||||
ActorProps[RemotedataRateLimitingActor] -> RemotedataRateLimiting.actorName,
|
||||
ActorProps[RemotedataAttributeDefinitionActor] -> RemotedataAttributeDefinition.actorName
|
||||
ActorProps[RemotedataAttributeDefinitionActor] -> RemotedataAttributeDefinition.actorName,
|
||||
ActorProps[RemotedataUserInvitationActor] -> RemotedataUserInvitation.actorName,
|
||||
ActorProps[RemotedataConsentAuthContextActor] -> RemotedataConsentAuthContext.actorName,
|
||||
ActorProps[RemotedataTransactionRequestAttributeActor] -> RemotedataTransactionRequestAttribute.actorName,
|
||||
ActorProps[RemotedataUserAgreementActor] -> RemotedataUserAgreement.actorName
|
||||
)
|
||||
|
||||
actorsRemotedata.foreach { a => logger.info(actorSystem.actorOf(a._1, name = a._2)) }
|
||||
|
||||
@ -31,8 +31,12 @@ class RemotedataChallengesActor extends Actor with ObpActorHelper with MdcLoggab
|
||||
logger.debug(s"validateChallenge($challengeId, $challengeAnswer, $userId)")
|
||||
sender ! (mapper.validateChallenge(challengeId, challengeAnswer, userId))
|
||||
|
||||
case cc.getChallengesByTransactionRequestId(transactionRequestId: String)=>
|
||||
logger.debug(s"getChallengesByTransactionRequestId($transactionRequestId)")
|
||||
sender ! (mapper.getChallengesByTransactionRequestId(transactionRequestId))
|
||||
|
||||
case cc.getChallengesByConsentId(consentId: String)=>
|
||||
logger.debug(s"validateChallenge($consentId)")
|
||||
logger.debug(s"getChallengesByConsentId($consentId)")
|
||||
sender ! (mapper.getChallengesByConsentId(consentId))
|
||||
|
||||
case message => logger.warn("[AKKA ACTOR ERROR - REQUEST NOT RECOGNIZED] " + message)
|
||||
|
||||
@ -47,8 +47,8 @@ object RemotedataConsumers extends ObpActorInit with ConsumersProvider {
|
||||
def getConsumersFuture(): Future[List[Consumer]] =
|
||||
(actor ? cc.getConsumersFuture()).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): Box[Consumer] = getValueFromFuture(
|
||||
(actor ? cc.createConsumer(key, secret, isActive, name, appType, description, developerEmail, redirectURL, createdByUserId, clientCertificate)).mapTo[Box[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]]
|
||||
)
|
||||
|
||||
def deleteConsumer(consumer: Consumer): Boolean = getValueFromFuture(
|
||||
|
||||
@ -45,9 +45,9 @@ class RemotedataConsumersActor extends Actor with ObpActorHelper with MdcLoggabl
|
||||
logger.debug(s"getConsumersFuture()")
|
||||
sender ! (mapper.getConsumers())
|
||||
|
||||
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]) =>
|
||||
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")})")
|
||||
sender ! (mapper.createConsumer(key, secret, isActive, name, appType, description, developerEmail, redirectURL, createdByUserId, clientCertificate))
|
||||
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")})")
|
||||
sender ! (mapper.createConsumer(key, secret, isActive, name, appType, description, developerEmail, redirectURL, createdByUserId, clientCertificate, company))
|
||||
|
||||
case cc.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]) =>
|
||||
logger.debug(s"updateConsumer($id, *****, *****, ${isActive.getOrElse("None")}, ${name.getOrElse("None")}, ${appType.getOrElse("None")}, ${description.getOrElse("None")}, ${developerEmail.getOrElse("None")}, ${redirectURL.getOrElse("None")}, ${createdByUserId.getOrElse("None")})")
|
||||
|
||||
@ -88,6 +88,10 @@ class RemotedataCounterpartiesActor extends Actor with ObpActorHelper with MdcLo
|
||||
case cc.getCounterpartyByIban(iban: String) =>
|
||||
logger.debug(s"getOrCreateMetadata($iban)")
|
||||
sender ! (mapper.getCounterpartyByIban(iban: String))
|
||||
|
||||
case cc.getCounterpartyByIbanAndBankAccountId(iban: String, bankId: BankId, accountId: AccountId) =>
|
||||
logger.debug(s"getCounterpartyByIbanAndBankAccountId($iban, $bankId, $accountId)")
|
||||
sender ! (mapper.getCounterpartyByIbanAndBankAccountId(iban, bankId, accountId))
|
||||
|
||||
case cc.getPublicAlias(counterpartyId: String) =>
|
||||
logger.debug(s"getPublicAlias($counterpartyId)")
|
||||
|
||||
@ -48,8 +48,8 @@ object RemotedataEntitlements extends ObpActorInit with EntitlementProvider {
|
||||
def getEntitlementsByRoleFuture(roleName: String) : Future[Box[List[Entitlement]]] =
|
||||
(actor ? cc.getEntitlementsByRoleFuture(roleName)).mapTo[Box[List[Entitlement]]]
|
||||
|
||||
def addEntitlement(bankId: String, userId: String, roleName: String) : Box[Entitlement] = getValueFromFuture(
|
||||
(actor ? cc.addEntitlement(bankId, userId, roleName)).mapTo[Box[Entitlement]]
|
||||
def addEntitlement(bankId: String, userId: String, roleName: String, createdByProcess: String="manual") : Box[Entitlement] = getValueFromFuture(
|
||||
(actor ? cc.addEntitlement(bankId, userId, roleName, createdByProcess: String)).mapTo[Box[Entitlement]]
|
||||
)
|
||||
|
||||
override def deleteDynamicEntityEntitlement(entityName: String, bankId:Option[String]): Box[Boolean] = getValueFromFuture(
|
||||
|
||||
@ -55,9 +55,9 @@ class RemotedataEntitlementsActor extends Actor with ObpActorHelper with MdcLogg
|
||||
logger.debug(s"getEntitlementsByRole($role)")
|
||||
sender ! (mapper.getEntitlementsByRole(role))
|
||||
|
||||
case cc.addEntitlement(bankId: String, userId: String, roleName: String) =>
|
||||
logger.debug(s"addEntitlement($bankId, $userId, $roleName)")
|
||||
sender ! (mapper.addEntitlement(bankId, userId, roleName))
|
||||
case cc.addEntitlement(bankId: String, userId: String, roleName: String, createdByProcess: String) =>
|
||||
logger.debug(s"addEntitlement($bankId, $userId, $roleName, $createdByProcess)")
|
||||
sender ! (mapper.addEntitlement(bankId, userId, roleName, createdByProcess: String))
|
||||
|
||||
case cc.deleteDynamicEntityEntitlement(entityName: String, bankId:Option[String]) =>
|
||||
logger.debug(s"deleteDynamicEntityEntitlement($entityName) bankId($bankId)")
|
||||
|
||||
@ -0,0 +1,63 @@
|
||||
package code.remotedata
|
||||
|
||||
import akka.actor.Actor
|
||||
import code.actorsystem.ObpActorHelper
|
||||
import code.transactionRequestAttribute.{MappedTransactionRequestAttributeProvider, RemotedataTransactionRequestAttributeCaseClasses}
|
||||
import code.util.Helper.MdcLoggable
|
||||
import com.openbankproject.commons.model._
|
||||
import com.openbankproject.commons.model.enums.TransactionRequestAttributeType
|
||||
import akka.pattern.pipe
|
||||
import scala.collection.immutable.List
|
||||
import com.openbankproject.commons.ExecutionContext.Implicits.global
|
||||
|
||||
class RemotedataTransactionRequestAttributeActor extends Actor with ObpActorHelper with MdcLoggable {
|
||||
|
||||
val mapper = MappedTransactionRequestAttributeProvider
|
||||
val cc = RemotedataTransactionRequestAttributeCaseClasses
|
||||
|
||||
def receive: PartialFunction[Any, Unit] = {
|
||||
|
||||
case cc.getTransactionRequestAttributesFromProvider(transactionRequestId: TransactionRequestId) =>
|
||||
logger.debug(s"getTransactionRequestAttributesFromProvider($transactionRequestId)")
|
||||
mapper.getTransactionRequestAttributesFromProvider(transactionRequestId) pipeTo sender
|
||||
|
||||
case cc.getTransactionRequestAttributes(bankId : BankId, transactionRequestId: TransactionRequestId) =>
|
||||
logger.debug(s"getTransactionRequestAttributes($bankId, $transactionRequestId)")
|
||||
mapper.getTransactionRequestAttributes(bankId, transactionRequestId) pipeTo sender
|
||||
|
||||
case cc.getTransactionRequestAttributesCanBeSeenOnView(bankId : BankId, transactionRequestId: TransactionRequestId, viewId: ViewId) =>
|
||||
logger.debug(s"getTransactionRequestAttributesCanBeSeenOnView($bankId, $transactionRequestId, $viewId)")
|
||||
mapper.getTransactionRequestAttributesCanBeSeenOnView(bankId, transactionRequestId, viewId) pipeTo sender
|
||||
|
||||
case cc.getTransactionRequestAttributeById(transactionRequestAttributeId: String) =>
|
||||
logger.debug(s"getTransactionRequestAttributeById($transactionRequestAttributeId)")
|
||||
mapper.getTransactionRequestAttributeById(transactionRequestAttributeId) pipeTo sender
|
||||
|
||||
case cc.getTransactionRequestIdsByAttributeNameValues(bankId: BankId, params: Map[String, List[String]]) =>
|
||||
logger.debug(s"getTransactionRequestIdsByAttributeNameValues($bankId, $params)")
|
||||
mapper.getTransactionRequestIdsByAttributeNameValues(bankId, params) pipeTo sender
|
||||
|
||||
case cc.createOrUpdateTransactionRequestAttribute(bankId: BankId,
|
||||
transactionRequestId: TransactionRequestId,
|
||||
transactionRequestAttributeId: Option[String],
|
||||
name: String,
|
||||
attributeType: TransactionRequestAttributeType.Value,
|
||||
value: String) =>
|
||||
logger.debug(s"createOrUpdateTransactionRequestAttribute($bankId, $transactionRequestId, $transactionRequestAttributeId, $name, $attributeType, $value)")
|
||||
mapper.createOrUpdateTransactionRequestAttribute(bankId, transactionRequestId, transactionRequestAttributeId, name, attributeType, value) pipeTo sender
|
||||
|
||||
|
||||
case cc.createTransactionRequestAttributes(bankId : BankId, transactionRequestId: TransactionRequestId, transactionRequestAttributes: List[TransactionRequestAttributeTrait]) =>
|
||||
logger.debug(s"createTransactionRequestAttributes($bankId, $transactionRequestId, $transactionRequestAttributes)")
|
||||
mapper.createTransactionRequestAttributes(bankId, transactionRequestId, transactionRequestAttributes) pipeTo sender
|
||||
|
||||
case cc.deleteTransactionRequestAttribute(transactionRequestAttributeId: String) =>
|
||||
logger.debug(s"deleteTransactionRequestAttribute($transactionRequestAttributeId)")
|
||||
mapper.deleteTransactionRequestAttribute(transactionRequestAttributeId) pipeTo sender
|
||||
|
||||
case message => logger.warn("[AKKA ACTOR ERROR - REQUEST NOT RECOGNIZED] " + message)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
package code.remotedata
|
||||
|
||||
import akka.pattern.ask
|
||||
import code.actorsystem.ObpActorInit
|
||||
import code.users.{RemotedataUserAgreementProviderCaseClass, UserAgreement, UserAgreementProvider}
|
||||
import net.liftweb.common._
|
||||
|
||||
|
||||
object RemotedataUserAgreement extends ObpActorInit with UserAgreementProvider {
|
||||
|
||||
val cc = RemotedataUserAgreementProviderCaseClass
|
||||
|
||||
def createOrUpdateUserAgreement(userId: String, summary: String, agreementText: String, acceptMarketingInfo: Boolean): Box[UserAgreement] = getValueFromFuture(
|
||||
(actor ? cc.createOrUpdateUserAgreement(userId, summary, agreementText, acceptMarketingInfo)).mapTo[Box[UserAgreement]]
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package code.remotedata
|
||||
|
||||
import akka.actor.Actor
|
||||
import code.actorsystem.ObpActorHelper
|
||||
import code.users.{MappedUserAgreementProvider, RemotedataUserAgreementProviderCaseClass}
|
||||
import code.util.Helper.MdcLoggable
|
||||
|
||||
class RemotedataUserAgreementActor extends Actor with ObpActorHelper with MdcLoggable {
|
||||
|
||||
val mapper = MappedUserAgreementProvider
|
||||
val cc = RemotedataUserAgreementProviderCaseClass
|
||||
|
||||
def receive: PartialFunction[Any, Unit] = {
|
||||
|
||||
case cc.createOrUpdateUserAgreement(userId: String, summary: String, agreementText: String, acceptMarketingInfo: Boolean) =>
|
||||
logger.debug(s"createOrUpdateUserAgreement($userId, $summary, $agreementText, $acceptMarketingInfo)")
|
||||
sender ! (mapper.createOrUpdateUserAgreement(userId, summary, agreementText, acceptMarketingInfo))
|
||||
|
||||
case message => logger.warn("[AKKA ACTOR ERROR - REQUEST NOT RECOGNIZED] " + message)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
package code.remotedata
|
||||
|
||||
import akka.pattern.ask
|
||||
import code.actorsystem.ObpActorInit
|
||||
import code.users.{RemotedataUserInvitationProviderCaseClass, UserInvitation, UserInvitationProvider}
|
||||
import com.openbankproject.commons.model.BankId
|
||||
import net.liftweb.common._
|
||||
|
||||
|
||||
object RemotedataUserInvitation extends ObpActorInit with UserInvitationProvider {
|
||||
|
||||
val cc = RemotedataUserInvitationProviderCaseClass
|
||||
|
||||
def createUserInvitation(bankId: BankId, firstName: String, lastName: String, email: String, company: String, country: String, purpose: String): Box[UserInvitation] = getValueFromFuture(
|
||||
(actor ? cc.createUserInvitation(bankId, firstName, lastName, email, company, country, purpose)).mapTo[Box[UserInvitation]]
|
||||
)
|
||||
def getUserInvitationBySecretLink(secretLink: Long): Box[UserInvitation] = getValueFromFuture(
|
||||
(actor ? cc.getUserInvitationBySecretLink(secretLink)).mapTo[Box[UserInvitation]]
|
||||
)
|
||||
def updateStatusOfUserInvitation(userInvitationId: String, status: String): Box[Boolean] = getValueFromFuture(
|
||||
(actor ? cc.updateStatusOfUserInvitation(userInvitationId, status)).mapTo[Box[Boolean]]
|
||||
)
|
||||
def scrambleUserInvitation(userInvitationId: String): Box[Boolean] = getValueFromFuture(
|
||||
(actor ? cc.scrambleUserInvitation(userInvitationId)).mapTo[Box[Boolean]]
|
||||
)
|
||||
def getUserInvitation(bankId: BankId, secretLink: Long): Box[UserInvitation] = getValueFromFuture(
|
||||
(actor ? cc.getUserInvitation(bankId, secretLink)).mapTo[Box[UserInvitation]]
|
||||
)
|
||||
def getUserInvitations(bankId: BankId): Box[List[UserInvitation]] = getValueFromFuture(
|
||||
(actor ? cc.getUserInvitations(bankId)).mapTo[Box[List[UserInvitation]]]
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package code.remotedata
|
||||
|
||||
import akka.actor.Actor
|
||||
import code.actorsystem.ObpActorHelper
|
||||
import code.users.{MappedUserInvitationProvider, RemotedataUserInvitationProviderCaseClass}
|
||||
import code.util.Helper.MdcLoggable
|
||||
import com.openbankproject.commons.model.BankId
|
||||
|
||||
class RemotedataUserInvitationActor extends Actor with ObpActorHelper with MdcLoggable {
|
||||
|
||||
val mapper = MappedUserInvitationProvider
|
||||
val cc = RemotedataUserInvitationProviderCaseClass
|
||||
|
||||
def receive: PartialFunction[Any, Unit] = {
|
||||
|
||||
case cc.createUserInvitation(bankId: BankId, firstName: String, lastName: String, email: String, company: String, country: String, purpose: String) =>
|
||||
logger.debug(s"createUserInvitation($bankId, $firstName, $lastName, $email, $company, $country, $purpose)")
|
||||
sender ! (mapper.createUserInvitation(bankId, firstName, lastName, email, company, country, purpose))
|
||||
|
||||
case cc.getUserInvitationBySecretLink(secretLink: Long) =>
|
||||
logger.debug(s"getUserInvitationBySecretLink($secretLink)")
|
||||
sender ! (mapper.getUserInvitationBySecretLink(secretLink))
|
||||
|
||||
case cc.updateStatusOfUserInvitation(userInvitationId: String, status: String) =>
|
||||
logger.debug(s"updateStatusOfUserInvitation($userInvitationId, $status)")
|
||||
sender ! (mapper.updateStatusOfUserInvitation(userInvitationId, status))
|
||||
|
||||
case cc.scrambleUserInvitation(userInvitationId: String) =>
|
||||
logger.debug(s"scrambleUserInvitation($userInvitationId)")
|
||||
sender ! (mapper.scrambleUserInvitation(userInvitationId))
|
||||
|
||||
case cc.getUserInvitation(bankId: BankId, secretLink: Long) =>
|
||||
logger.debug(s"getUserInvitation($bankId, $secretLink)")
|
||||
sender ! (mapper.getUserInvitation(bankId, secretLink))
|
||||
|
||||
case cc.getUserInvitations(bankId: BankId) =>
|
||||
logger.debug(s"getUserInvitations($bankId)")
|
||||
sender ! (mapper.getUserInvitations(bankId))
|
||||
|
||||
case message => logger.warn("[AKKA ACTOR ERROR - REQUEST NOT RECOGNIZED] " + message)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ import code.api.util.OBPQueryParam
|
||||
import code.entitlement.Entitlement
|
||||
import code.model.dataAccess.ResourceUser
|
||||
import code.users.{RemotedataUsersCaseClasses, Users}
|
||||
import com.openbankproject.commons.model.User
|
||||
import com.openbankproject.commons.model.{User, UserPrimaryKey}
|
||||
import net.liftweb.common.Box
|
||||
|
||||
import scala.collection.immutable.List
|
||||
@ -71,8 +71,8 @@ object RemotedataUsers extends ObpActorInit with Users {
|
||||
res.mapTo[List[(ResourceUser, Box[List[Entitlement]])]]
|
||||
}
|
||||
|
||||
def createResourceUser(provider: String, providerId: Option[String], createdByConsentId: Option[String], name: Option[String], email: Option[String], userId: Option[String]) : Box[ResourceUser] = getValueFromFuture(
|
||||
(actor ? cc.createResourceUser(provider, providerId, createdByConsentId, name, email, userId)).mapTo[Box[ResourceUser]]
|
||||
def createResourceUser(provider: String, providerId: Option[String], createdByConsentId: Option[String], name: Option[String], email: Option[String], userId: Option[String], createdByUserInvitationId: Option[String], company: Option[String]) : Box[ResourceUser] = getValueFromFuture(
|
||||
(actor ? cc.createResourceUser(provider, providerId, createdByConsentId, name, email, userId, createdByUserInvitationId, company)).mapTo[Box[ResourceUser]]
|
||||
)
|
||||
|
||||
def createUnsavedResourceUser(provider: String, providerId: Option[String], name: Option[String], email: Option[String], userId: Option[String]) : Box[ResourceUser] = getValueFromFuture(
|
||||
@ -86,6 +86,10 @@ object RemotedataUsers extends ObpActorInit with Users {
|
||||
def deleteResourceUser(userId: Long) : Box[Boolean] = getValueFromFuture(
|
||||
(actor ? cc.deleteResourceUser(userId)).mapTo[Box[Boolean]]
|
||||
)
|
||||
|
||||
def scrambleDataOfResourceUser(userPrimaryKey: UserPrimaryKey) : Box[Boolean] = getValueFromFuture(
|
||||
(actor ? cc.scrambleDataOfResourceUser(userPrimaryKey)).mapTo[Box[Boolean]]
|
||||
)
|
||||
|
||||
def bulkDeleteAllResourceUsers(): Box[Boolean] = getValueFromFuture(
|
||||
(actor ? cc.bulkDeleteAllResourceUsers()).mapTo[Box[Boolean]]
|
||||
|
||||
@ -10,6 +10,7 @@ import code.util.Helper.MdcLoggable
|
||||
|
||||
import scala.collection.immutable.List
|
||||
import com.openbankproject.commons.ExecutionContext.Implicits.global
|
||||
import com.openbankproject.commons.model.UserPrimaryKey
|
||||
|
||||
class RemotedataUsersActor extends Actor with ObpActorHelper with MdcLoggable {
|
||||
|
||||
@ -76,11 +77,11 @@ class RemotedataUsersActor extends Actor with ObpActorHelper with MdcLoggable {
|
||||
|
||||
case cc.getAllUsersF(queryParams: List[OBPQueryParam]) =>
|
||||
logger.debug(s"getAllUsersF(queryParams: ($queryParams))")
|
||||
sender ! (mapper.getAllUsersFF(queryParams))
|
||||
mapper.getAllUsersF(queryParams) pipeTo sender
|
||||
|
||||
case cc.createResourceUser(provider: String, providerId: Option[String], createdByConsentId: Option[String], name: Option[String], email: Option[String], userId: Option[String]) =>
|
||||
logger.debug("createResourceUser(" + provider + ", " + providerId.getOrElse("None") + ", " + name.getOrElse("None") + ", " + email.getOrElse("None") + ", " + userId.getOrElse("None") + ")")
|
||||
sender ! (mapper.createResourceUser(provider, providerId, createdByConsentId, name, email, userId))
|
||||
case cc.createResourceUser(provider: String, providerId: Option[String], createdByConsentId: Option[String], name: Option[String], email: Option[String], userId: Option[String], createdByUserInvitationId: Option[String], company: Option[String]) =>
|
||||
logger.debug("createResourceUser(" + provider + ", " + providerId.getOrElse("None") + ", " + name.getOrElse("None") + ", " + email.getOrElse("None") + ", " + userId.getOrElse("None") + ", " + createdByUserInvitationId.getOrElse("None") + ", " + company.getOrElse("None") + ")")
|
||||
sender ! (mapper.createResourceUser(provider, providerId, createdByConsentId, name, email, userId, createdByUserInvitationId, company))
|
||||
|
||||
case cc.createUnsavedResourceUser(provider: String, providerId: Option[String], name: Option[String], email: Option[String], userId: Option[String]) =>
|
||||
logger.debug("createUnsavedResourceUser(" + provider + ", " + providerId.getOrElse("None") + ", " + name.getOrElse("None") + ", " + email.getOrElse("None") + ", " + userId.getOrElse("None") + ")")
|
||||
@ -93,6 +94,10 @@ class RemotedataUsersActor extends Actor with ObpActorHelper with MdcLoggable {
|
||||
case cc.deleteResourceUser(id: Long) =>
|
||||
logger.debug("deleteResourceUser(" + id +")")
|
||||
sender ! (mapper.deleteResourceUser(id))
|
||||
|
||||
case cc.scrambleDataOfResourceUser(userPrimaryKey: UserPrimaryKey) =>
|
||||
logger.debug("scrambleDataOfResourceUser(" + userPrimaryKey +")")
|
||||
sender ! (mapper.scrambleDataOfResourceUser(userPrimaryKey))
|
||||
|
||||
case cc.bulkDeleteAllResourceUsers() =>
|
||||
logger.debug("bulkDeleteAllResourceUsers()")
|
||||
|
||||
@ -53,10 +53,11 @@ class ConsumerRegistration extends MdcLoggable {
|
||||
private object redirectionURLVar extends RequestVar("")
|
||||
private object requestUriVar extends RequestVar("")
|
||||
private object authenticationURLVar extends RequestVar("")
|
||||
private object appTypeVar extends RequestVar[AppType](AppType.Web)
|
||||
private object appTypeVar extends RequestVar[AppType](AppType.Confidential)
|
||||
private object descriptionVar extends RequestVar("")
|
||||
private object devEmailVar extends RequestVar("")
|
||||
private object appType extends RequestVar("Web")
|
||||
private object companyVar extends RequestVar("")
|
||||
private object appType extends RequestVar("Unknown")
|
||||
private object clientCertificateVar extends RequestVar("")
|
||||
private object signingAlgVar extends RequestVar("")
|
||||
private object jwksUriVar extends RequestVar("")
|
||||
@ -79,7 +80,7 @@ class ConsumerRegistration extends MdcLoggable {
|
||||
|
||||
def registerForm = {
|
||||
|
||||
val appTypes = List((AppType.Web.toString, AppType.Web.toString), (AppType.Mobile.toString, AppType.Mobile.toString))
|
||||
val appTypes = List((AppType.Confidential.toString, AppType.Confidential.toString), (AppType.Public.toString, AppType.Public.toString))
|
||||
val signingAlgs = List(
|
||||
"ES256", "ES384", "ES512",
|
||||
//Hydra support alg: RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384 and ES512
|
||||
@ -93,9 +94,12 @@ class ConsumerRegistration extends MdcLoggable {
|
||||
def registerWithoutWarnings =
|
||||
register &
|
||||
"#register-consumer-errors" #> ""
|
||||
|
||||
def displayAppType() = if(APIUtil.getPropsAsBoolValue("consumer_registration.display_app_type", true)) "display: block;" else "display: none"
|
||||
|
||||
def register = {
|
||||
"form" #> {
|
||||
"#app-type-div [style] " #> displayAppType() &
|
||||
"#appType" #> SHtml.select(appTypes, Box!! appType.is, appType(_)) &
|
||||
"#appName" #> SHtml.text(nameVar.is, nameVar(_)) &
|
||||
"#redirect_url_label *" #> {
|
||||
@ -103,6 +107,7 @@ class ConsumerRegistration extends MdcLoggable {
|
||||
} &
|
||||
"#appRedirectUrl" #> SHtml.text(redirectionURLVar, redirectionURLVar(_)) &
|
||||
"#appDev" #> SHtml.text(devEmailVar, devEmailVar(_)) &
|
||||
"#company" #> SHtml.text(companyVar, companyVar(_)) &
|
||||
"#appDesc" #> SHtml.textarea(descriptionVar, descriptionVar (_)) &
|
||||
"#appUserAuthenticationUrl" #> SHtml.text(authenticationURLVar.is, authenticationURLVar(_)) & {
|
||||
if(HydraUtil.integrateWithHydra) {
|
||||
@ -306,6 +311,7 @@ class ConsumerRegistration extends MdcLoggable {
|
||||
appTypeVar.set(appTypeSelected.get)
|
||||
descriptionVar.set(descriptionVar.is)
|
||||
devEmailVar.set(devEmailVar.is)
|
||||
companyVar.set(companyVar.is)
|
||||
redirectionURLVar.set(redirectionURLVar.is)
|
||||
|
||||
requestUriVar.set(requestUri)
|
||||
@ -345,7 +351,8 @@ class ConsumerRegistration extends MdcLoggable {
|
||||
Some(devEmailVar.is),
|
||||
Some(redirectionURLVar.is),
|
||||
Some(AuthUser.getCurrentResourceUserUserId),
|
||||
Some(clientCertificate))
|
||||
Some(clientCertificate),
|
||||
company = Some(companyVar.is))
|
||||
logger.debug("consumer: " + consumer)
|
||||
consumer match {
|
||||
case Full(x) =>
|
||||
@ -471,7 +478,7 @@ class ConsumerRegistration extends MdcLoggable {
|
||||
while(matcher.find()) {
|
||||
val userName = matcher.group(1)
|
||||
val password = matcher.group(2)
|
||||
val (code, token) = DirectLogin.createToken(Map(("username", userName), ("password", password), ("consumer_key", consumerKey)))
|
||||
val (code, token, userId) = DirectLogin.createToken(Map(("username", userName), ("password", password), ("consumer_key", consumerKey)))
|
||||
val authHeader = code match {
|
||||
case 200 => (userName, password) -> s"""Authorization: DirectLogin token="$token""""
|
||||
case _ => (userName, password) -> "username or password is invalid, generate token fail"
|
||||
|
||||
201
obp-api/src/main/scala/code/snippet/UserInvitation.scala
Normal file
201
obp-api/src/main/scala/code/snippet/UserInvitation.scala
Normal file
@ -0,0 +1,201 @@
|
||||
/**
|
||||
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.snippet
|
||||
|
||||
import java.time.{Duration, ZoneId, ZoneOffset, ZonedDateTime}
|
||||
|
||||
import code.api.util.APIUtil
|
||||
import code.model.dataAccess.{AuthUser, ResourceUser}
|
||||
import code.users
|
||||
import code.users.{UserAgreementProvider, UserInvitationProvider, Users}
|
||||
import code.util.Helper
|
||||
import code.util.Helper.MdcLoggable
|
||||
import code.webuiprops.MappedWebUiPropsProvider.getWebUiPropsValue
|
||||
import com.openbankproject.commons.model.User
|
||||
import net.liftweb.common.Box
|
||||
import net.liftweb.http.{RequestVar, S, SHtml}
|
||||
import net.liftweb.util.CssSel
|
||||
import net.liftweb.util.Helpers._
|
||||
|
||||
import scala.collection.immutable.List
|
||||
|
||||
class UserInvitation extends MdcLoggable {
|
||||
|
||||
private object firstNameVar extends RequestVar("")
|
||||
private object lastNameVar extends RequestVar("")
|
||||
private object companyVar extends RequestVar("")
|
||||
private object countryVar extends RequestVar("None")
|
||||
private object devEmailVar extends RequestVar("")
|
||||
private object usernameVar extends RequestVar("")
|
||||
private object termsCheckboxVar extends RequestVar(false)
|
||||
private object marketingInfoCheckboxVar extends RequestVar(false)
|
||||
private object privacyCheckboxVar extends RequestVar(false)
|
||||
|
||||
val ttl = APIUtil.getPropsAsLongValue("user_invitation.ttl.seconds", 86400)
|
||||
|
||||
val registrationConsumerButtonValue: String = getWebUiPropsValue("webui_post_user_invitation_submit_button_value", "Register as a Developer")
|
||||
val privacyConditionsValue: String = getWebUiPropsValue("webui_post_user_invitation_privacy_conditions_value", "Privacy conditions..")
|
||||
val termsAndConditionsValue: String = getWebUiPropsValue("webui_post_user_invitation_terms_and_conditions_value", "Terms and Conditions..")
|
||||
val termsAndConditionsCheckboxValue: String = getWebUiPropsValue("webui_post_user_invitation_terms_and_conditions_checkbox_value", "I agree to the above Developer Terms and Conditions")
|
||||
|
||||
def registerForm: CssSel = {
|
||||
|
||||
val secretLink: Box[Long] = tryo {
|
||||
S.param("id").getOrElse("0").toLong
|
||||
}
|
||||
val userInvitation: Box[users.UserInvitation] = UserInvitationProvider.userInvitationProvider.vend.getUserInvitationBySecretLink(secretLink.getOrElse(0))
|
||||
firstNameVar.set(userInvitation.map(_.firstName).getOrElse("None"))
|
||||
lastNameVar.set(userInvitation.map(_.lastName).getOrElse("None"))
|
||||
val email = userInvitation.map(_.email).getOrElse("None")
|
||||
devEmailVar.set(email)
|
||||
companyVar.set(userInvitation.map(_.company).getOrElse("None"))
|
||||
countryVar.set(userInvitation.map(_.country).getOrElse("None"))
|
||||
usernameVar.set(firstNameVar.is.toLowerCase + "." + lastNameVar.is.toLowerCase())
|
||||
|
||||
def submitButtonDefense(): Unit = {
|
||||
val verifyingTime = ZonedDateTime.now(ZoneOffset.UTC)
|
||||
val createdAt = userInvitation.map(_.createdAt.get).getOrElse(time(239932800))
|
||||
val timeOfCreation = ZonedDateTime.ofInstant(createdAt.toInstant(), ZoneId.systemDefault())
|
||||
val timeDifference = Duration.between(verifyingTime, timeOfCreation)
|
||||
logger.debug("User invitation TTL time: " + ttl)
|
||||
logger.debug("User invitation expiration time: " + timeDifference.abs.getSeconds)
|
||||
|
||||
if(secretLink.isEmpty || userInvitation.isEmpty) showErrorsForSecretLink()
|
||||
else if(userInvitation.map(_.status != "CREATED").getOrElse(false)) showErrorsForStatus()
|
||||
else if(timeDifference.abs.getSeconds > ttl) showErrorsForTtl()
|
||||
else if(Users.users.vend.getUserByUserName(usernameVar.is).isDefined) showErrorsForUsername()
|
||||
else if(privacyCheckboxVar.is == false) showErrorsForPrivacyConditions()
|
||||
else if(termsCheckboxVar.is == false) showErrorsForTermsAndConditions()
|
||||
else {
|
||||
// Resource User table
|
||||
createResourceUser(
|
||||
provider = "OBP-User-Invitation",
|
||||
providerId = Some(usernameVar.is),
|
||||
name = Some(firstNameVar.is + " " + lastNameVar.is),
|
||||
email = Some(email),
|
||||
userInvitationId = userInvitation.map(_.userInvitationId).toOption,
|
||||
company = userInvitation.map(_.company).toOption
|
||||
).map{ u =>
|
||||
// AuthUser table
|
||||
createAuthUser(user = u, firstName = firstNameVar.is, lastName = lastNameVar.is, password = "")
|
||||
// Use Agreement table
|
||||
UserAgreementProvider.userAgreementProvider.vend.createOrUpdateUserAgreement(
|
||||
u.userId, privacyConditionsValue, termsAndConditionsValue, marketingInfoCheckboxVar.is)
|
||||
// Set the status of the user invitation to "FINISHED"
|
||||
UserInvitationProvider.userInvitationProvider.vend.updateStatusOfUserInvitation(userInvitation.map(_.userInvitationId).getOrElse(""), "FINISHED")
|
||||
// Set a new password
|
||||
val resetLink = AuthUser.passwordResetUrl(u.idGivenByProvider, u.emailAddress, u.userId) + "?action=set"
|
||||
S.redirectTo(resetLink)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
def showError(usernameError: String) = {
|
||||
S.error("register-consumer-errors", usernameError)
|
||||
register &
|
||||
"#register-consumer-errors *" #> {
|
||||
".error *" #>
|
||||
List(usernameError).map({ e =>
|
||||
".errorContent *" #> e
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
def showErrorsForSecretLink() = {
|
||||
showError(Helper.i18n("your.secret.link.is.not.valid"))
|
||||
}
|
||||
def showErrorsForUsername() = {
|
||||
showError(Helper.i18n("unique.username"))
|
||||
}
|
||||
def showErrorsForStatus() = {
|
||||
showError(Helper.i18n("user.invitation.is.already.finished"))
|
||||
}
|
||||
def showErrorsForTtl() = {
|
||||
showError(Helper.i18n("user.invitation.is.expired"))
|
||||
}
|
||||
def showErrorsForTermsAndConditions() = {
|
||||
showError(Helper.i18n("terms.and.conditions.are.not.selected"))
|
||||
}
|
||||
def showErrorsForPrivacyConditions() = {
|
||||
showError(Helper.i18n("privacy.conditions.are.not.selected"))
|
||||
}
|
||||
|
||||
def register = {
|
||||
"form" #> {
|
||||
"#country" #> SHtml.text(countryVar.is, countryVar(_)) &
|
||||
"#firstName" #> SHtml.text(firstNameVar.is, firstNameVar(_)) &
|
||||
"#lastName" #> SHtml.text(lastNameVar.is, lastNameVar(_)) &
|
||||
"#companyName" #> SHtml.text(companyVar.is, companyVar(_)) &
|
||||
"#devEmail" #> SHtml.text(devEmailVar, devEmailVar(_)) &
|
||||
"#username" #> SHtml.text(usernameVar, usernameVar(_)) &
|
||||
"#privacy" #> SHtml.textarea(privacyConditionsValue, privacyConditionsValue => privacyConditionsValue) &
|
||||
"#privacy_checkbox" #> SHtml.checkbox(privacyCheckboxVar, privacyCheckboxVar(_)) &
|
||||
"#terms" #> SHtml.textarea(termsAndConditionsValue, termsAndConditionsValue => termsAndConditionsValue) &
|
||||
"#terms_checkbox" #> SHtml.checkbox(termsCheckboxVar, termsCheckboxVar(_)) &
|
||||
"#marketing_info_checkbox" #> SHtml.checkbox(marketingInfoCheckboxVar, marketingInfoCheckboxVar(_)) &
|
||||
"type=submit" #> SHtml.submit(s"$registrationConsumerButtonValue", () => submitButtonDefense)
|
||||
} &
|
||||
"#register-consumer-success" #> ""
|
||||
}
|
||||
register
|
||||
}
|
||||
|
||||
private def createAuthUser(user: User, firstName: String, lastName: String, password: String): Box[AuthUser] = tryo {
|
||||
val newUser = AuthUser.create
|
||||
.firstName(firstName)
|
||||
.lastName(lastName)
|
||||
.email(user.emailAddress)
|
||||
.user(user.userPrimaryKey.value)
|
||||
.username(user.idGivenByProvider)
|
||||
.provider(user.provider)
|
||||
.password(password)
|
||||
.validated(true)
|
||||
// Save the user
|
||||
newUser.saveMe()
|
||||
}
|
||||
|
||||
private def createResourceUser(provider: String,
|
||||
providerId: Option[String],
|
||||
name: Option[String],
|
||||
email: Option[String],
|
||||
userInvitationId: Option[String],
|
||||
company: Option[String]
|
||||
): Box[ResourceUser] = {
|
||||
Users.users.vend.createResourceUser(
|
||||
provider = provider,
|
||||
providerId = providerId,
|
||||
createdByConsentId = None,
|
||||
name = name,
|
||||
email = email,
|
||||
userId = None,
|
||||
createdByUserInvitationId = userInvitationId,
|
||||
company = company
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
85
obp-api/src/main/scala/code/snippet/WebUITemplate.scala
Normal file
85
obp-api/src/main/scala/code/snippet/WebUITemplate.scala
Normal file
@ -0,0 +1,85 @@
|
||||
package code.snippet
|
||||
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
case class WebUIDoc(webUiPropsName: String, defaultValue: String, typeOfValue: String, placeholders: List[String])
|
||||
|
||||
object WebUIPlaceholder {
|
||||
val emailRecipient = "@EMAIL_RECIPIENT"
|
||||
val activateYourAccount = "@ACTIVATE_YOUR_ACCOUNT"
|
||||
}
|
||||
|
||||
object WebUITemplate {
|
||||
import WebUIPlaceholder._
|
||||
|
||||
val webUIDoc = ArrayBuffer[WebUIDoc]()
|
||||
|
||||
|
||||
webUIDoc += WebUIDoc(
|
||||
webUiPropsName = "webui_developer_user_invitation_email_text",
|
||||
defaultValue = webUiDeveloperUserInvitationEmailText,
|
||||
typeOfValue = "plain_text",
|
||||
placeholders = List(emailRecipient, activateYourAccount)
|
||||
)
|
||||
val webUiDeveloperUserInvitationEmailText =
|
||||
s"""
|
||||
|Hi ${emailRecipient},
|
||||
|Welcome to the Open Bank Project API. Your account has been registered. Please use the below link to activate it.
|
||||
|
|
||||
|Activate your account: ${activateYourAccount}
|
||||
|
|
||||
|Our operations team has granted you the appropriate access to the OBP-API. If you have any questions, or you need any assistance, please contact our support.
|
||||
|
|
||||
|Thanks,
|
||||
|Your OBP API team
|
||||
|
|
||||
|
|
||||
|
|
||||
|Please do not reply to this email. Should you wish to contact us, please raise a ticket at our support page. We maintain strict security standards and procedures to prevent unauthorised access to information about you. We will never contact you by email or otherwise and ask you to validate personal information such as your user ID, password or account numbers. This e-mail is confidential. It may also be legally privileged. If you are not the addressee you may not copy, forward, disclose or use any part of it. If you have received this message in error, please delete it and all copies from your system. Internet communications cannot be guaranteed to be timely, secure, error or virus-free. The sender does not accept liability for any errors or omissions.
|
||||
|""".stripMargin
|
||||
|
||||
|
||||
webUIDoc += WebUIDoc(
|
||||
webUiPropsName = "webui_developer_user_invitation_email_html_text",
|
||||
defaultValue = webUiDeveloperUserInvitationEmailHtmlText,
|
||||
typeOfValue = "html",
|
||||
placeholders = List(emailRecipient, activateYourAccount)
|
||||
)
|
||||
val webUiDeveloperUserInvitationEmailHtmlText =
|
||||
s"""<!DOCTYPE html>
|
||||
|<html>
|
||||
|<head>
|
||||
|<style>
|
||||
|.a {
|
||||
| border: none;
|
||||
| color: white;
|
||||
| padding: 15px 32px;
|
||||
| text-align: center;
|
||||
| text-decoration: none;
|
||||
| display: inline-block;
|
||||
| font-size: 16px;
|
||||
| margin: 4px 2px;
|
||||
| cursor: pointer;
|
||||
|}
|
||||
|
|
||||
|.a1 {background-color: #4CAF50;} /* Green */
|
||||
|.a2 {background-color: #008CBA;} /* Blue */
|
||||
|</style>
|
||||
|</head>
|
||||
|<body>
|
||||
|<img src="https://static.openbankproject.com/images/OBP_full_web_25pc.png"></img>
|
||||
|<hr></hr><br></br>
|
||||
|<p>Hi ${emailRecipient},<br></br>
|
||||
|Welcome to the Open Bank Project API. Your account has been registered. Please use the below link to activate it.</p>
|
||||
|<a href="${activateYourAccount}" class="a a1">Activate your account</a>
|
||||
|<p>Our operations team has granted you the appropriate access to the OBP-API. If you have any questions, or you need any assistance, please contact our support.</p>
|
||||
|<p>Thanks,<br></br> Your OBP API team</p><br></br>
|
||||
|<hr></hr>
|
||||
|<p>
|
||||
|Please do not reply to this email. Should you wish to contact us, please raise a ticket at our support page. We maintain strict security standards and procedures to prevent unauthorised access to information about you. We will never contact you by email or otherwise and ask you to validate personal information such as your user ID, password or account numbers. This e-mail is confidential. It may also be legally privileged. If you are not the addressee you may not copy, forward, disclose or use any part of it. If you have received this message in error, please delete it and all copies from your system. Internet communications cannot be guaranteed to be timely, secure, error or virus-free. The sender does not accept liability for any errors or omissions.
|
||||
|</p>
|
||||
|</body>
|
||||
|</html>
|
||||
|
|
||||
|""".stripMargin
|
||||
}
|
||||
@ -1,19 +1,19 @@
|
||||
package code.users
|
||||
|
||||
import code.api.util.{OBPLimit, OBPLockedStatus, OBPOffset, OBPQueryParam}
|
||||
import code.api.util._
|
||||
import code.entitlement.Entitlement
|
||||
import code.loginattempts.LoginAttempt.maxBadLoginAttempts
|
||||
import code.loginattempts.MappedBadLoginAttempt
|
||||
import code.model.dataAccess.ResourceUser
|
||||
import code.model.dataAccess.{AuthUser, ResourceUser}
|
||||
import code.util.Helper.MdcLoggable
|
||||
import com.openbankproject.commons.model.User
|
||||
import net.liftweb.common.{Box, Full}
|
||||
import net.liftweb.mapper._
|
||||
|
||||
import scala.collection.immutable.List
|
||||
import com.openbankproject.commons.ExecutionContext.Implicits.global
|
||||
import com.openbankproject.commons.model.{User, UserPrimaryKey}
|
||||
import net.liftweb.common.{Box, Empty, Full}
|
||||
import net.liftweb.mapper._
|
||||
import net.liftweb.util.Helpers
|
||||
|
||||
import scala.collection.immutable
|
||||
import scala.collection.immutable.List
|
||||
import scala.concurrent.Future
|
||||
|
||||
object LiftUsers extends Users with MdcLoggable{
|
||||
@ -58,7 +58,9 @@ object LiftUsers extends Users with MdcLoggable{
|
||||
createdByConsentId = consentId,
|
||||
name = name,
|
||||
email = email,
|
||||
userId = None
|
||||
userId = None,
|
||||
createdByUserInvitationId = None,
|
||||
company = None
|
||||
)
|
||||
(newUser, true)
|
||||
}
|
||||
@ -125,8 +127,16 @@ object LiftUsers extends Users with MdcLoggable{
|
||||
val limit = queryParams.collect { case OBPLimit(value) => MaxRows[ResourceUser](value) }.headOption
|
||||
val offset: Option[StartAt[ResourceUser]] = queryParams.collect { case OBPOffset(value) => StartAt[ResourceUser](value) }.headOption
|
||||
val locked: Option[String] = queryParams.collect { case OBPLockedStatus(value) => value }.headOption
|
||||
val deleted = queryParams.collect {
|
||||
case OBPIsDeleted(value) if value == true => // ?is_deleted=true
|
||||
By(ResourceUser.IsDeleted, true)
|
||||
case OBPIsDeleted(value) if value == false => // ?is_deleted=false
|
||||
By(ResourceUser.IsDeleted, false)
|
||||
}.headOption.orElse(
|
||||
Some(By(ResourceUser.IsDeleted, false)) // There is no query parameter "is_deleted"
|
||||
)
|
||||
|
||||
val optionalParams: Seq[QueryParam[ResourceUser]] = Seq(limit.toSeq, offset.toSeq).flatten
|
||||
val optionalParams: Seq[QueryParam[ResourceUser]] = Seq(limit.toSeq, offset.toSeq, deleted.toSeq).flatten
|
||||
|
||||
def getAllResourceUsers(): List[ResourceUser] = ResourceUser.findAll(optionalParams: _*)
|
||||
val showUsers: List[ResourceUser] = locked.map(_.toLowerCase()) match {
|
||||
@ -155,24 +165,16 @@ object LiftUsers extends Users with MdcLoggable{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def getAllUsersFF(queryParams: List[OBPQueryParam]): List[(ResourceUser, Box[List[Entitlement]])] = {
|
||||
val limit = queryParams.collect { case OBPLimit(value) => MaxRows[ResourceUser](value) }.headOption
|
||||
val offset = queryParams.collect { case OBPOffset(value) => StartAt[ResourceUser](value) }.headOption
|
||||
|
||||
val optionalParams: Seq[QueryParam[ResourceUser]] = Seq(limit.toSeq, offset.toSeq).flatten
|
||||
|
||||
logger.debug(s"getAllUsersFF parameters $optionalParams")
|
||||
val users = ResourceUser.findAll(optionalParams: _*)
|
||||
logger.debug(s"getAllUsersFF response $users")
|
||||
for {
|
||||
user <- users
|
||||
} yield {
|
||||
(user, Entitlement.entitlement.vend.getEntitlementsByUserId(user.userId).map(_.sortWith(_.roleName < _.roleName)))
|
||||
}
|
||||
}
|
||||
|
||||
override def createResourceUser(provider: String, providerId: Option[String], createdByConsentId: Option[String], name: Option[String], email: Option[String], userId: Option[String]): Box[ResourceUser] = {
|
||||
override def createResourceUser(provider: String,
|
||||
providerId: Option[String],
|
||||
createdByConsentId: Option[String],
|
||||
name: Option[String],
|
||||
email: Option[String],
|
||||
userId: Option[String],
|
||||
createdByUserInvitationId: Option[String],
|
||||
company: Option[String]): Box[ResourceUser] = {
|
||||
val ru = ResourceUser.create
|
||||
ru.provider_(provider)
|
||||
providerId match {
|
||||
@ -183,6 +185,10 @@ object LiftUsers extends Users with MdcLoggable{
|
||||
case Some(consentId) => ru.CreatedByConsentId(consentId)
|
||||
case None => ru.CreatedByConsentId(null)
|
||||
}
|
||||
createdByUserInvitationId match {
|
||||
case Some(invitationId) => ru.CreatedByUserInvitationId(invitationId)
|
||||
case None => ru.CreatedByConsentId(null)
|
||||
}
|
||||
name match {
|
||||
case Some(v) => ru.name_(v)
|
||||
case None =>
|
||||
@ -195,6 +201,10 @@ object LiftUsers extends Users with MdcLoggable{
|
||||
case Some(v) => ru.userId_(v)
|
||||
case None =>
|
||||
}
|
||||
company match {
|
||||
case Some(v) => ru.Company(v)
|
||||
case None =>
|
||||
}
|
||||
Full(ru.saveMe())
|
||||
}
|
||||
|
||||
@ -236,5 +246,26 @@ object LiftUsers extends Users with MdcLoggable{
|
||||
u.delete_!
|
||||
}
|
||||
}
|
||||
override def scrambleDataOfResourceUser(userPrimaryKey: UserPrimaryKey): Box[Boolean] = {
|
||||
for {
|
||||
u <- ResourceUser.find(By(ResourceUser.id, userPrimaryKey.value))
|
||||
} yield {
|
||||
AuthUser.find(By(AuthUser.user, userPrimaryKey.value)) match {
|
||||
case Empty =>
|
||||
u
|
||||
.Company(Helpers.randomString(16))
|
||||
.IsDeleted(true)
|
||||
.name_("DELETED-" + Helpers.randomString(16))
|
||||
.email(Helpers.randomString(10) + "@example.com")
|
||||
.providerId(Helpers.randomString(16))
|
||||
.save()
|
||||
case _ =>
|
||||
u
|
||||
.Company(Helpers.randomString(16))
|
||||
.IsDeleted(true)
|
||||
.save()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
63
obp-api/src/main/scala/code/users/UserAgreement.scala
Normal file
63
obp-api/src/main/scala/code/users/UserAgreement.scala
Normal file
@ -0,0 +1,63 @@
|
||||
package code.users
|
||||
|
||||
import java.util.Date
|
||||
import java.util.UUID.randomUUID
|
||||
|
||||
import code.api.util.HashUtil
|
||||
import code.util.UUIDString
|
||||
import net.liftweb.common.{Box, Empty, Full}
|
||||
import net.liftweb.mapper._
|
||||
|
||||
object MappedUserAgreementProvider extends UserAgreementProvider {
|
||||
override def createOrUpdateUserAgreement(userId: String, summary: String, agreementText: String, acceptMarketingInfo: Boolean): Box[UserAgreement] = {
|
||||
UserAgreement.find(By(UserAgreement.UserId, userId)) match {
|
||||
case Full(existingUser) =>
|
||||
Full(
|
||||
existingUser
|
||||
.Summary(summary)
|
||||
.AgreementText(agreementText)
|
||||
.AcceptMarketingInfo(acceptMarketingInfo)
|
||||
.saveMe()
|
||||
)
|
||||
case Empty =>
|
||||
Full(
|
||||
UserAgreement.create
|
||||
.UserId(userId)
|
||||
.Summary(summary)
|
||||
.AgreementText(agreementText)
|
||||
.AcceptMarketingInfo(acceptMarketingInfo)
|
||||
.Date(new Date)
|
||||
.saveMe()
|
||||
)
|
||||
case everythingElse => everythingElse
|
||||
}
|
||||
}
|
||||
}
|
||||
class UserAgreement extends UserAgreementTrait with LongKeyedMapper[UserAgreement] with IdPK with CreatedUpdated {
|
||||
|
||||
def getSingleton = UserAgreement
|
||||
|
||||
object UserAgreementId extends UUIDString(this) {
|
||||
override def defaultValue = randomUUID().toString
|
||||
}
|
||||
object UserId extends MappedString(this, 255)
|
||||
object Date extends MappedDate(this)
|
||||
object Summary extends MappedText(this)
|
||||
object AgreementText extends MappedText(this)
|
||||
object AgreementHash extends MappedString(this, 64) {
|
||||
override def defaultValue: String = HashUtil.Sha256Hash(AgreementText.get)
|
||||
}
|
||||
object AcceptMarketingInfo extends MappedBoolean(this)
|
||||
|
||||
override def userInvitationId: String = UserAgreementId.get
|
||||
override def userId: String = UserId.get
|
||||
override def summary: String = Summary.get
|
||||
override def agreementText: String = AgreementText.get
|
||||
override def agreementHash: String = AgreementHash.get
|
||||
override def acceptMarketingInfo: Boolean = AcceptMarketingInfo.get
|
||||
}
|
||||
|
||||
object UserAgreement extends UserAgreement with LongKeyedMetaMapper[UserAgreement] {
|
||||
override def dbIndexes: List[BaseIndex[UserAgreement]] = UniqueIndex(UserAgreementId) :: super.dbIndexes
|
||||
}
|
||||
|
||||
@ -0,0 +1,39 @@
|
||||
package code.users
|
||||
|
||||
import code.api.util.APIUtil
|
||||
import code.remotedata.{RemotedataUserAgreement, RemotedataUserInvitation}
|
||||
import com.openbankproject.commons.model.BankId
|
||||
import net.liftweb.common.Box
|
||||
import net.liftweb.util.SimpleInjector
|
||||
|
||||
|
||||
object UserAgreementProvider extends SimpleInjector {
|
||||
|
||||
val userAgreementProvider = new Inject(buildOne _) {}
|
||||
|
||||
def buildOne: UserAgreementProvider =
|
||||
APIUtil.getPropsAsBoolValue("use_akka", false) match {
|
||||
case false => MappedUserAgreementProvider
|
||||
case true => RemotedataUserAgreement // We will use Akka as a middleware
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
trait UserAgreementProvider {
|
||||
def createOrUpdateUserAgreement(userId: String, summary: String, agreementText: String, acceptMarketingInfo: Boolean): Box[UserAgreement]
|
||||
}
|
||||
|
||||
class RemotedataUserAgreementProviderCaseClass {
|
||||
case class createOrUpdateUserAgreement(userId: String, summary: String, agreementText: String, acceptMarketingInfo: Boolean)
|
||||
}
|
||||
|
||||
object RemotedataUserAgreementProviderCaseClass extends RemotedataUserAgreementProviderCaseClass
|
||||
|
||||
trait UserAgreementTrait {
|
||||
def userInvitationId: String
|
||||
def userId: String
|
||||
def summary: String
|
||||
def agreementText: String
|
||||
def agreementHash: String
|
||||
def acceptMarketingInfo: Boolean
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
package code.users
|
||||
|
||||
import code.api.util.APIUtil
|
||||
import code.remotedata.RemotedataUserInvitation
|
||||
import com.openbankproject.commons.model.BankId
|
||||
import net.liftweb.common.Box
|
||||
import net.liftweb.util.SimpleInjector
|
||||
|
||||
|
||||
object UserInvitationProvider extends SimpleInjector {
|
||||
|
||||
val userInvitationProvider = new Inject(buildOne _) {}
|
||||
|
||||
def buildOne: UserInvitationProvider =
|
||||
APIUtil.getPropsAsBoolValue("use_akka", false) match {
|
||||
case false => MappedUserInvitationProvider
|
||||
case true => RemotedataUserInvitation // We will use Akka as a middleware
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
trait UserInvitationProvider {
|
||||
def createUserInvitation(bankId: BankId, firstName: String, lastName: String, email: String, company: String, country: String, purpose: String): Box[UserInvitation]
|
||||
def getUserInvitationBySecretLink(secretLink: Long): Box[UserInvitation]
|
||||
def scrambleUserInvitation(userInvitationId: String): Box[Boolean]
|
||||
def updateStatusOfUserInvitation(userInvitationId: String, status: String): Box[Boolean]
|
||||
def getUserInvitation(bankId: BankId, secretLink: Long): Box[UserInvitation]
|
||||
def getUserInvitations(bankId: BankId): Box[List[UserInvitation]]
|
||||
}
|
||||
|
||||
class RemotedataUserInvitationProviderCaseClass {
|
||||
case class createUserInvitation(bankId: BankId, firstName: String, lastName: String, email: String, company: String, country: String, purpose: String)
|
||||
case class getUserInvitationBySecretLink(secretLink: Long)
|
||||
case class updateStatusOfUserInvitation(userInvitationId: String, status: String)
|
||||
case class scrambleUserInvitation(userInvitationId: String)
|
||||
case class getUserInvitation(bankId: BankId, secretLink: Long)
|
||||
case class getUserInvitations(bankId: BankId)
|
||||
}
|
||||
|
||||
object RemotedataUserInvitationProviderCaseClass extends RemotedataUserInvitationProviderCaseClass
|
||||
|
||||
trait UserInvitationTrait {
|
||||
def userInvitationId: String
|
||||
def bankId: String
|
||||
def firstName: String
|
||||
def lastName: String
|
||||
def email: String
|
||||
def company: String
|
||||
def country: String
|
||||
def status: String
|
||||
def purpose: String
|
||||
def secretKey: Long
|
||||
}
|
||||
100
obp-api/src/main/scala/code/users/UserInvitation.scala
Normal file
100
obp-api/src/main/scala/code/users/UserInvitation.scala
Normal file
@ -0,0 +1,100 @@
|
||||
package code.users
|
||||
|
||||
import java.util.UUID.randomUUID
|
||||
|
||||
import code.api.util.SecureRandomUtil
|
||||
import code.util.UUIDString
|
||||
import com.openbankproject.commons.model.BankId
|
||||
import net.liftweb.common.{Box, Full}
|
||||
import net.liftweb.mapper._
|
||||
import net.liftweb.util.Helpers
|
||||
import net.liftweb.util.Helpers.tryo
|
||||
|
||||
object MappedUserInvitationProvider extends UserInvitationProvider {
|
||||
override def createUserInvitation(bankId: BankId, firstName: String, lastName: String, email: String, company: String, country: String, purpose: String): Box[UserInvitation] = tryo {
|
||||
UserInvitation.create
|
||||
.BankId(bankId.value)
|
||||
.FirstName(firstName)
|
||||
.LastName(lastName)
|
||||
.Email(email)
|
||||
.Company(company)
|
||||
.Country(country)
|
||||
.Status("CREATED")
|
||||
.Purpose(purpose)
|
||||
.saveMe()
|
||||
}
|
||||
override def getUserInvitationBySecretLink(secretLink: Long): Box[UserInvitation] = {
|
||||
UserInvitation.find(
|
||||
By(UserInvitation.SecretKey, secretLink)
|
||||
)
|
||||
}
|
||||
override def updateStatusOfUserInvitation(userInvitationId: String, status: String): Box[Boolean] = tryo {
|
||||
UserInvitation.find(
|
||||
By(UserInvitation.UserInvitationId, userInvitationId)
|
||||
) match {
|
||||
case Full(userInvitation) => userInvitation.Status(status).save()
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
override def scrambleUserInvitation(userInvitationId: String): Box[Boolean] = tryo {
|
||||
UserInvitation.find(
|
||||
By(UserInvitation.UserInvitationId, userInvitationId)
|
||||
) match {
|
||||
case Full(userInvitation) =>
|
||||
userInvitation
|
||||
.Email(Helpers.randomString(10) + "@example.com")
|
||||
.FirstName(Helpers.randomString(userInvitation.firstName.length))
|
||||
.LastName(Helpers.randomString(userInvitation.lastName.length))
|
||||
.Company(Helpers.randomString(userInvitation.company.length))
|
||||
.Country(Helpers.randomString(userInvitation.country.length))
|
||||
.Purpose(Helpers.randomString(userInvitation.purpose.length))
|
||||
.Status("DELETED")
|
||||
.save()
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
override def getUserInvitation(bankId: BankId, secretLink: Long): Box[UserInvitation] = {
|
||||
UserInvitation.find(
|
||||
By(UserInvitation.BankId, bankId.value),
|
||||
By(UserInvitation.SecretKey, secretLink)
|
||||
)
|
||||
}
|
||||
override def getUserInvitations(bankId: BankId): Box[List[UserInvitation]] = tryo {
|
||||
UserInvitation.findAll(By(UserInvitation.BankId, bankId.value))
|
||||
}
|
||||
}
|
||||
class UserInvitation extends UserInvitationTrait with LongKeyedMapper[UserInvitation] with IdPK with CreatedUpdated {
|
||||
|
||||
def getSingleton = UserInvitation
|
||||
|
||||
object UserInvitationId extends UUIDString(this) {
|
||||
override def defaultValue = randomUUID().toString
|
||||
}
|
||||
object BankId extends MappedString(this, 255)
|
||||
object FirstName extends MappedString(this, 50)
|
||||
object LastName extends MappedString(this, 50)
|
||||
object Email extends MappedString(this, 50)
|
||||
object Company extends MappedString(this, 50)
|
||||
object Country extends MappedString(this, 50)
|
||||
object Status extends MappedString(this, 50)
|
||||
object Purpose extends MappedString(this, 50)
|
||||
object SecretKey extends MappedLong(this) {
|
||||
override def defaultValue: Long = SecureRandomUtil.csprng.nextLong()
|
||||
}
|
||||
|
||||
override def userInvitationId: String = UserInvitationId.get
|
||||
override def bankId: String = BankId.get
|
||||
override def firstName: String = FirstName.get
|
||||
override def lastName: String = LastName.get
|
||||
override def email: String = Email.get
|
||||
override def company: String = Company.get
|
||||
override def country: String = Country.get
|
||||
override def status: String = Status.get
|
||||
override def purpose: String = Purpose.get
|
||||
override def secretKey: Long = SecretKey.get
|
||||
}
|
||||
|
||||
object UserInvitation extends UserInvitation with LongKeyedMetaMapper[UserInvitation] {
|
||||
override def dbIndexes: List[BaseIndex[UserInvitation]] = UniqueIndex(UserInvitationId) :: super.dbIndexes
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ import code.api.util.{APIUtil, OBPQueryParam}
|
||||
import code.entitlement.Entitlement
|
||||
import code.model.dataAccess.ResourceUser
|
||||
import code.remotedata.RemotedataUsers
|
||||
import com.openbankproject.commons.model.User
|
||||
import com.openbankproject.commons.model.{User, UserPrimaryKey}
|
||||
import net.liftweb.common.Box
|
||||
import net.liftweb.util.SimpleInjector
|
||||
|
||||
@ -51,13 +51,15 @@ trait Users {
|
||||
|
||||
def getAllUsersF(queryParams: List[OBPQueryParam]) : Future[List[(ResourceUser, Box[List[Entitlement]])]]
|
||||
|
||||
def createResourceUser(provider: String, providerId: Option[String], createdByConsentId: Option[String], name: Option[String], email: Option[String], userId: Option[String]) : Box[ResourceUser]
|
||||
def createResourceUser(provider: String, providerId: Option[String], createdByConsentId: Option[String], name: Option[String], email: Option[String], userId: Option[String], createdByUserInvitationId: Option[String], company: Option[String]) : Box[ResourceUser]
|
||||
|
||||
def createUnsavedResourceUser(provider: String, providerId: Option[String], name: Option[String], email: Option[String], userId: Option[String]) : Box[ResourceUser]
|
||||
|
||||
def saveResourceUser(resourceUser: ResourceUser) : Box[ResourceUser]
|
||||
|
||||
def deleteResourceUser(userId: Long) : Box[Boolean]
|
||||
|
||||
def scrambleDataOfResourceUser(userPrimaryKey: UserPrimaryKey) : Box[Boolean]
|
||||
|
||||
def bulkDeleteAllResourceUsers() : Box[Boolean]
|
||||
}
|
||||
@ -78,10 +80,11 @@ class RemotedataUsersCaseClasses {
|
||||
case class getUserByEmailFuture(email : String)
|
||||
case class getAllUsers()
|
||||
case class getAllUsersF(queryParams: List[OBPQueryParam])
|
||||
case class createResourceUser(provider: String, providerId: Option[String],createdByConsentId: Option[String], name: Option[String], email: Option[String], userId: Option[String])
|
||||
case class createResourceUser(provider: String, providerId: Option[String],createdByConsentId: Option[String], name: Option[String], email: Option[String], userId: Option[String], createdByUserInvitationId: Option[String], company: Option[String])
|
||||
case class createUnsavedResourceUser(provider: String, providerId: Option[String], name: Option[String], email: Option[String], userId: Option[String])
|
||||
case class saveResourceUser(resourceUser: ResourceUser)
|
||||
case class deleteResourceUser(userId: Long)
|
||||
case class scrambleDataOfResourceUser(userPrimaryKey: UserPrimaryKey)
|
||||
case class bulkDeleteAllResourceUsers()
|
||||
}
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@ import net.liftweb.json.JsonAST._
|
||||
import net.liftweb.json.{DateFormat, Formats}
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import com.openbankproject.commons.ExecutionContext.Implicits.global
|
||||
import com.openbankproject.commons.model.{AccountBalance, AccountHeld, AccountId, CoreAccount, Customer, CustomerId}
|
||||
import com.openbankproject.commons.model.{AccountBalance, AccountBalances, AccountHeld, AccountId, CoreAccount, Customer, CustomerId}
|
||||
import com.openbankproject.commons.util.{ReflectUtils, RequiredFieldValidation, RequiredInfo}
|
||||
import com.tesobe.CacheKeyFromArguments
|
||||
import net.liftweb.http.S
|
||||
@ -415,6 +415,7 @@ object Helper{
|
||||
(fieldName.equalsIgnoreCase("accountId") && fieldType =:= typeOf[String])||
|
||||
(ownerType <:< typeOf[CoreAccount] && fieldName.equalsIgnoreCase("id") && fieldType =:= typeOf[String])||
|
||||
(ownerType <:< typeOf[AccountBalance] && fieldName.equalsIgnoreCase("id") && fieldType =:= typeOf[String])||
|
||||
(ownerType <:< typeOf[AccountBalances] && fieldName.equalsIgnoreCase("id") && fieldType =:= typeOf[String])||
|
||||
(ownerType <:< typeOf[AccountHeld] && fieldName.equalsIgnoreCase("id") && fieldType =:= typeOf[String])
|
||||
}
|
||||
|
||||
|
||||
@ -53,7 +53,7 @@ object MapperViews extends Views with MdcLoggable {
|
||||
.map(v => v.bank_id(a.bank_id.get).account_id(a.account_id.get))
|
||||
).filter(
|
||||
v =>
|
||||
if (ALLOW_PUBLIC_VIEWS) {
|
||||
if (allowPublicViews) {
|
||||
true // All views
|
||||
} else {
|
||||
v.isPrivate == true // Only private views
|
||||
@ -122,7 +122,7 @@ object MapperViews extends Views with MdcLoggable {
|
||||
|
||||
viewDefinition match {
|
||||
case Full(v) => {
|
||||
if(v.isPublic && !ALLOW_PUBLIC_VIEWS) return Failure(PublicViewsNotAllowedOnThisInstance)
|
||||
if(v.isPublic && !allowPublicViews) return Failure(PublicViewsNotAllowedOnThisInstance)
|
||||
// SQL Select Count ViewPrivileges where
|
||||
// This is idempotent
|
||||
getOrGrantAccessToCustomView(user, v, viewIdBankIdAccountId.bankId.value, viewIdBankIdAccountId.accountId.value) //privilege already exists, no need to create one
|
||||
@ -133,7 +133,7 @@ object MapperViews extends Views with MdcLoggable {
|
||||
}
|
||||
}
|
||||
def grantAccessToSystemView(bankId: BankId, accountId: AccountId, view: View, user: User): Box[View] = {
|
||||
{ view.isPublic && !ALLOW_PUBLIC_VIEWS } match {
|
||||
{ view.isPublic && !allowPublicViews } match {
|
||||
case true => Failure(PublicViewsNotAllowedOnThisInstance)
|
||||
case false => getOrGrantAccessToSystemView(bankId: BankId, accountId: AccountId, user, view)
|
||||
}
|
||||
@ -153,7 +153,7 @@ object MapperViews extends Views with MdcLoggable {
|
||||
//TODO: APIFailures with http response codes belong at a higher level in the code
|
||||
} else {
|
||||
viewDefinitions.foreach(v => {
|
||||
if(v._1.isPublic && !ALLOW_PUBLIC_VIEWS) return Failure(PublicViewsNotAllowedOnThisInstance)
|
||||
if(v._1.isPublic && !allowPublicViews) return Failure(PublicViewsNotAllowedOnThisInstance)
|
||||
val viewDefinition = v._1
|
||||
val viewIdBankIdAccountId = v._2
|
||||
// This is idempotent
|
||||
@ -176,7 +176,7 @@ object MapperViews extends Views with MdcLoggable {
|
||||
//TODO: APIFailures with http response codes belong at a higher level in the code
|
||||
} else {
|
||||
viewDefinitions.foreach(v => {
|
||||
if(v._1.isPublic && !ALLOW_PUBLIC_VIEWS) return Failure(PublicViewsNotAllowedOnThisInstance)
|
||||
if(v._1.isPublic && !allowPublicViews) return Failure(PublicViewsNotAllowedOnThisInstance)
|
||||
val viewDefinition = v._1
|
||||
val viewIdBankIdAccountId = v._2
|
||||
// This is idempotent
|
||||
@ -300,7 +300,7 @@ object MapperViews extends Views with MdcLoggable {
|
||||
|
||||
def customView(viewId : ViewId, account: BankIdAccountId) : Box[View] = {
|
||||
val view = ViewDefinition.findCustomView(account.bankId.value, account.accountId.value, viewId.value)
|
||||
if(view.isDefined && view.openOrThrowException(attemptedToOpenAnEmptyBox).isPublic && !ALLOW_PUBLIC_VIEWS) return Failure(PublicViewsNotAllowedOnThisInstance)
|
||||
if(view.isDefined && view.openOrThrowException(attemptedToOpenAnEmptyBox).isPublic && !allowPublicViews) return Failure(PublicViewsNotAllowedOnThisInstance)
|
||||
|
||||
view
|
||||
}
|
||||
@ -360,7 +360,7 @@ object MapperViews extends Views with MdcLoggable {
|
||||
* */
|
||||
def createView(bankAccountId: BankIdAccountId, view: CreateViewJson): Box[View] = {
|
||||
|
||||
if(view.is_public && !ALLOW_PUBLIC_VIEWS) {
|
||||
if(view.is_public && !allowPublicViews) {
|
||||
return Failure(PublicViewsNotAllowedOnThisInstance)
|
||||
}
|
||||
|
||||
@ -456,7 +456,7 @@ object MapperViews extends Views with MdcLoggable {
|
||||
}
|
||||
|
||||
def publicViews: (List[View], List[AccountAccess]) = {
|
||||
if (APIUtil.ALLOW_PUBLIC_VIEWS) {
|
||||
if (APIUtil.allowPublicViews) {
|
||||
val publicViews = ViewDefinition.findAll(By(ViewDefinition.isPublic_, true)) // Custom and System views
|
||||
val publicAccountAccesses = AccountAccess.findAll(ByList(AccountAccess.view_fk, publicViews.map(_.id)))
|
||||
(publicViews, publicAccountAccesses)
|
||||
@ -466,7 +466,7 @@ object MapperViews extends Views with MdcLoggable {
|
||||
}
|
||||
|
||||
def publicViewsForBank(bankId: BankId): (List[View], List[AccountAccess]) ={
|
||||
if (APIUtil.ALLOW_PUBLIC_VIEWS) {
|
||||
if (APIUtil.allowPublicViews) {
|
||||
val publicViews =
|
||||
ViewDefinition.findAll(By(ViewDefinition.isPublic_, true), By(ViewDefinition.bank_id, bankId.value), By(ViewDefinition.isSystem_, false)) ::: // Custom views
|
||||
ViewDefinition.findAll(By(ViewDefinition.isPublic_, true), By(ViewDefinition.isSystem_, true)) ::: // System views
|
||||
@ -723,7 +723,7 @@ object MapperViews extends Views with MdcLoggable {
|
||||
}
|
||||
|
||||
def createDefaultPublicView(bankId: BankId, accountId: AccountId, name: String): Box[View] = {
|
||||
if(!ALLOW_PUBLIC_VIEWS) {
|
||||
if(!allowPublicViews) {
|
||||
return Failure(PublicViewsNotAllowedOnThisInstance)
|
||||
}
|
||||
createAndSaveDefaultPublicView(bankId, accountId, "Public View")
|
||||
@ -739,12 +739,12 @@ object MapperViews extends Views with MdcLoggable {
|
||||
|
||||
def getExistingView(bankId: BankId, accountId: AccountId, name: String): Box[View] = {
|
||||
val res = ViewDefinition.findCustomView(bankId.value, accountId.value, name)
|
||||
if(res.isDefined && res.openOrThrowException(attemptedToOpenAnEmptyBox).isPublic && !ALLOW_PUBLIC_VIEWS) return Failure(PublicViewsNotAllowedOnThisInstance)
|
||||
if(res.isDefined && res.openOrThrowException(attemptedToOpenAnEmptyBox).isPublic && !allowPublicViews) return Failure(PublicViewsNotAllowedOnThisInstance)
|
||||
res
|
||||
}
|
||||
def getExistingSystemView(name: String): Box[View] = {
|
||||
val res = ViewDefinition.findSystemView(name)
|
||||
if(res.isDefined && res.openOrThrowException(attemptedToOpenAnEmptyBox).isPublic && !ALLOW_PUBLIC_VIEWS) return Failure(PublicViewsNotAllowedOnThisInstance)
|
||||
if(res.isDefined && res.openOrThrowException(attemptedToOpenAnEmptyBox).isPublic && !allowPublicViews) return Failure(PublicViewsNotAllowedOnThisInstance)
|
||||
res
|
||||
}
|
||||
|
||||
@ -1135,7 +1135,7 @@ object MapperViews extends Views with MdcLoggable {
|
||||
}
|
||||
|
||||
def createAndSaveDefaultPublicView(bankId : BankId, accountId: AccountId, description: String) : Box[View] = {
|
||||
if(!ALLOW_PUBLIC_VIEWS) {
|
||||
if(!allowPublicViews) {
|
||||
return Failure(PublicViewsNotAllowedOnThisInstance)
|
||||
}
|
||||
val res = unsavedDefaultPublicView(bankId, accountId, description).saveMe
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package code.views
|
||||
|
||||
import code.api.util.APIUtil
|
||||
import code.api.util.APIUtil.canUseAccountFirehose
|
||||
import code.model.dataAccess.{MappedBankAccount, ViewImpl, ViewPrivileges}
|
||||
import code.remotedata.RemotedataViews
|
||||
import code.views.MapperViews.getPrivateBankAccounts
|
||||
|
||||
@ -45,7 +45,7 @@ Berlin 13359, Germany
|
||||
<form method="post">
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-sm-12">
|
||||
<div class="form-group">
|
||||
<div class="form-group" id="app-type-div">
|
||||
<label for="appType" id="appTypeLabel">Application type</label>
|
||||
<select name="app-type" id="appType" class="form-control js-example-basic-single">
|
||||
<option class="app-type-option"></option>
|
||||
@ -60,6 +60,7 @@ Berlin 13359, Germany
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="appRedirectUrl" id="redirect_url_label">Redirect URL (Optional)</label>
|
||||
<img src="/media/icons/info-square.svg" alt="i" style="padding-left: 5px;" width="16" height="16" data-toggle="tooltip" data-placement="right" title="For use in OAuth flows: This is the URL served by your App that the OBP authorisation server will redirect to with an authorization code or access token as parameters in the URL following successful authorisation.">
|
||||
<input type="text" name="app-redirect-url" id="appRedirectUrl" class="form-control" aria-describedby="consumer-registration-app-redirect-url-error">
|
||||
<div id = "consumer-registration-app-redirect-url-error-div" class="hide">
|
||||
<span data-lift="Msg?id=consumer-registration-app-redirect-url-error"/>
|
||||
@ -79,6 +80,13 @@ Berlin 13359, Germany
|
||||
<span data-lift="Msg?id=consumer-registration-app-description-error"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="company">Company</label>
|
||||
<input type="text" name="app-company" id="company" class="form-control" aria-describedby="consumer-registration-app-company-error">
|
||||
<div id = "consumer-registration-app-company-error-div" class="hide">
|
||||
<span data-lift="Msg?id=consumer-registration-app-company-error"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -19,8 +19,8 @@ time, mark, audio, video {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: 100%;
|
||||
font: inherit;
|
||||
font-size: 100%;
|
||||
vertical-align: baseline;
|
||||
font-family: Roboto-Light;
|
||||
}
|
||||
|
||||
4
obp-api/src/main/webapp/media/icons/info-square.svg
Normal file
4
obp-api/src/main/webapp/media/icons/info-square.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-info-square" viewBox="0 0 16 16">
|
||||
<path d="M14 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h12zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z"/>
|
||||
<path d="m8.93 6.588-2.29.287-.082.38.45.083c.294.07.352.176.288.469l-.738 3.468c-.194.897.105 1.319.808 1.319.545 0 1.178-.252 1.465-.598l.088-.416c-.2.176-.492.246-.686.246-.275 0-.375-.193-.304-.533L8.93 6.588zM9 4.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 548 B |
@ -31,7 +31,7 @@ function showIndicatorCookiePage(id)
|
||||
{
|
||||
var cookieValue = getCookieValue('we-use-cookies-indicator');
|
||||
//if the value of 'we-use-cookies-indicator' is not the same as we set before (set it '1' before).
|
||||
if (cookieValue!= undefined && cookieValue != '1')
|
||||
if (cookieValue!= undefined && cookieValue != '1' && document.getElementById(id) != null)
|
||||
{
|
||||
//show the cookie page
|
||||
document.getElementById(id).style.display="block";
|
||||
|
||||
5
obp-api/src/main/webapp/media/js/popper.min.js
vendored
Normal file
5
obp-api/src/main/webapp/media/js/popper.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -43,20 +43,20 @@
|
||||
<a href="/user_mgt/sign_up" id="authorise-signup" class="btn btn-default pull-right authorise-button" tabindex="0">Register</a>
|
||||
</div>
|
||||
|
||||
<!-- <div class ="login-or"> or Login with OpenID : </div>-->
|
||||
<!-- <hr>-->
|
||||
<!-- <div data-lift="OpenIDConnectSnippet.showFirstButton">-->
|
||||
<!-- <div data-lift="OpenidConnectInvoke.linkButtonFirstProvider">-->
|
||||
<!-- <div class="authorise-button-oidc"><a id="open-id-connect-button-1" data-lift="OpenIDConnectSnippet.getFirstButtonText" class="btn btn-danger">OIDC 1</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div data-lift="OpenIDConnectSnippet.showSecondButton">-->
|
||||
<!-- <div data-lift="OpenidConnectInvoke.linkButtonSecondProvider">-->
|
||||
<!-- <div class="authorise-button-oidc"> <a id="open-id-connect-button-2" data-lift="OpenIDConnectSnippet.getSecondButtonText" class="btn btn-danger">OIDC 2</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<div class ="login-or"> or Login with OpenID : </div>
|
||||
<hr>
|
||||
<div data-lift="OpenIDConnectSnippet.showFirstButton">
|
||||
<div data-lift="OpenidConnectInvoke.linkButtonFirstProvider">
|
||||
<div class="authorise-button-oidc"><a id="open-id-connect-button-1" data-lift="OpenIDConnectSnippet.getFirstButtonText" class="btn btn-danger">OIDC 1</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div data-lift="OpenIDConnectSnippet.showSecondButton">
|
||||
<div data-lift="OpenidConnectInvoke.linkButtonSecondProvider">
|
||||
<div class="authorise-button-oidc"> <a id="open-id-connect-button-2" data-lift="OpenIDConnectSnippet.getSecondButtonText" class="btn btn-danger">OIDC 2</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@ -45,20 +45,20 @@
|
||||
<a href="/user_mgt/sign_up" id="authorise-signup" class="btn btn-default pull-right authorise-button" tabindex="0">Register</a>
|
||||
</div>
|
||||
|
||||
<!-- <div class ="login-or"> or Login with OpenID : </div>-->
|
||||
<!-- <hr>-->
|
||||
<!-- <div data-lift="OpenIDConnectSnippet.showFirstButton">-->
|
||||
<!-- <div data-lift="OpenidConnectInvoke.linkButtonFirstProvider">-->
|
||||
<!-- <div class="authorise-button-oidc"><a id="open-id-connect-button-1" data-lift="OpenIDConnectSnippet.getFirstButtonText" class="btn btn-danger">OIDC 1</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div data-lift="OpenIDConnectSnippet.showSecondButton">-->
|
||||
<!-- <div data-lift="OpenidConnectInvoke.linkButtonSecondProvider">-->
|
||||
<!-- <div class="authorise-button-oidc"> <a id="open-id-connect-button-2" data-lift="OpenIDConnectSnippet.getSecondButtonText" class="btn btn-danger">OIDC 2</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<div class ="login-or"> or Login with OpenID : </div>
|
||||
<hr>
|
||||
<div data-lift="OpenIDConnectSnippet.showFirstButton">
|
||||
<div data-lift="OpenidConnectInvoke.linkButtonFirstProvider">
|
||||
<div class="authorise-button-oidc"><a id="open-id-connect-button-1" data-lift="OpenIDConnectSnippet.getFirstButtonText" class="btn btn-danger">OIDC 1</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div data-lift="OpenIDConnectSnippet.showSecondButton">
|
||||
<div data-lift="OpenidConnectInvoke.linkButtonSecondProvider">
|
||||
<div class="authorise-button-oidc"> <a id="open-id-connect-button-2" data-lift="OpenIDConnectSnippet.getSecondButtonText" class="btn btn-danger">OIDC 2</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -52,6 +52,7 @@ Berlin 13359, Germany
|
||||
<script src="/media/js/cookies-consent.js"></script>
|
||||
<script src="/media/js/moment-with-locales.min.js"></script>
|
||||
<script src="/media/js/bootstrap-datetimepicker.min.js"></script>
|
||||
<script src="/media/js/popper.min.js"></script>
|
||||
</head>
|
||||
<body id="page_init">
|
||||
<div id="cookies-consent" data-lift="WebUI.cookieConsent">
|
||||
@ -182,7 +183,6 @@ Berlin 13359, Germany
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<hr style="margin: 0">
|
||||
<section id="content">
|
||||
<lift:bind name="content"/>
|
||||
The main content gets bound here
|
||||
|
||||
112
obp-api/src/main/webapp/user-invitation.html
Normal file
112
obp-api/src/main/webapp/user-invitation.html
Normal file
@ -0,0 +1,112 @@
|
||||
<!--
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2017, 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
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
-->
|
||||
<div id="register-consumer" data-lift="surround?with=default;at=content" tabindex="-1">
|
||||
<div data-lift="UserInvitation.registerForm">
|
||||
|
||||
<div id="register-consumer-input" tabindex="-1">
|
||||
<div id="register-consumer-explanation">
|
||||
<h1>Complete your user invitation</h1>
|
||||
<p>Please complete the information about the user invitation application below.</p>
|
||||
<p>All fields are required unless marked as 'optional'</p>
|
||||
</div>
|
||||
|
||||
<form method="post">
|
||||
<style>
|
||||
.form-control[disabled],
|
||||
.form-control[readonly],
|
||||
fieldset[disabled] .form-control {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-sm-12">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="firstName">First name</label>
|
||||
<input readonly type="text" name="first-name" id="firstName" class="form-control" aria-describedby="consumer-registration-first-name-error">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="lastName">Last name</label>
|
||||
<input readonly type="text" name="last-name" id="lastName" class="form-control" aria-describedby="consumer-registration-app-name-error">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="devEmail">Developer email</label>
|
||||
<input readonly type="text" name="app-developer" id="devEmail" class="form-control" aria-describedby="consumer-registration-app-developer-error">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="companyName">Company</label>
|
||||
<input readonly type="text" name="app-developer" id="companyName" class="form-control" aria-describedby="consumer-registration-company-error">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="country">Country</label>
|
||||
<input readonly type="text" name="country" id="country" class="form-control" aria-describedby="consumer-registration-company-error">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="username">Username</label>
|
||||
<input type="text" name="username" id="username" class="form-control" aria-describedby="consumer-registration-company-error">
|
||||
</div>
|
||||
<div class="form-group" id="privacy-conditions-div" onclick="myFunction()">
|
||||
<label for="privacy"></label>
|
||||
<textarea readonly rows="4" name="privacy-conditions" id="privacy" class="form-control" aria-describedby="consumer-registration-company-error"></textarea>
|
||||
<input type="checkbox" class="form-check-input" id="privacy_checkbox">
|
||||
<label class="form-check-label" for="privacy_checkbox">I agree to the above privacy conditions</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input onclick="disableBtn()" type="checkbox" class="form-check-input" id="marketing_info_checkbox">
|
||||
<label class="marketing-info-label" for="marketing_info_checkbox">I would like to receive developer related emails from the bank</label>
|
||||
</div>
|
||||
<div class="form-group" id="terms-and-conditions-div" onclick="myFunction()">
|
||||
<label for="terms"></label>
|
||||
<textarea readonly rows="4" name="terms-and-conditions" id="terms" class="form-control" aria-describedby="consumer-registration-company-error"></textarea>
|
||||
<input type="checkbox" class="form-check-input" id="terms_checkbox" >
|
||||
<label id="terms_checkbox_value" class="form-check-label" for="terms_checkbox">I agree to the above Developer Terms and Conditions</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function myFunction() {
|
||||
var checkBox = document.getElementById("terms-and-conditions-div").querySelector("input[type=checkbox]");
|
||||
var checkBox2 = document.getElementById("privacy-conditions-div").querySelector("input[type=checkbox]");
|
||||
var button = document.getElementById("submit-button");
|
||||
if (checkBox.checked == true && checkBox2.checked == true){
|
||||
button.disabled = false;
|
||||
} else {
|
||||
button.disabled = true;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<input disabled="true" id="submit-button" type="submit" value="Submit" class="btn btn-danger" id="register-developer-button" aria-describedby="register-consumer-errors"/>
|
||||
<div id = "register-consumer-errors-div" class="hide">
|
||||
<span data-lift="Msg?id=register-consumer-errors"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -88,7 +88,10 @@ class directloginTest extends ServerSetup with BeforeAndAfter {
|
||||
val invalidConsumerKeyHeader = ("Authorization", ("DirectLogin username=%s, " +
|
||||
"password=%s, consumer_key=%s").format(USERNAME, PASSWORD, "invalid"))
|
||||
|
||||
val validHeader = ("Authorization", "DirectLogin username=%s, password=%s, consumer_key=%s".
|
||||
val validDeprecatedHeader = ("Authorization", "DirectLogin username=%s, password=%s, consumer_key=%s".
|
||||
format(USERNAME, PASSWORD, KEY))
|
||||
|
||||
val validHeader = ("DirectLogin", "username=%s, password=%s, consumer_key=%s".
|
||||
format(USERNAME, PASSWORD, KEY))
|
||||
|
||||
val disabledConsumerValidHeader = ("Authorization", "DirectLogin username=%s, password=%s, consumer_key=%s".
|
||||
@ -103,6 +106,7 @@ class directloginTest extends ServerSetup with BeforeAndAfter {
|
||||
val invalidConsumerKeyHeaders = List(accessControlOriginHeader, invalidConsumerKeyHeader)
|
||||
|
||||
val validHeaders = List(accessControlOriginHeader, validHeader)
|
||||
val validDeprecatedHeaders = List(accessControlOriginHeader, validDeprecatedHeader)
|
||||
|
||||
val disabledConsumerKeyHeaders = List(accessControlOriginHeader, disabledConsumerValidHeader)
|
||||
|
||||
@ -224,6 +228,61 @@ class directloginTest extends ServerSetup with BeforeAndAfter {
|
||||
assertResponse(response, ErrorMessages.InvalidConsumerKey)
|
||||
}
|
||||
|
||||
scenario("Login with correct everything! - Deprecated Header", ApiEndpoint1, ApiEndpoint2) {
|
||||
|
||||
//setupUserAndConsumer
|
||||
|
||||
Given("the app we are testing is registered and active")
|
||||
Then("We should be able to find it")
|
||||
//assert(registeredApplication(KEY) == true)
|
||||
|
||||
When("the header and credentials are good")
|
||||
val request = directLoginRequest
|
||||
val response = makePostRequestAdditionalHeader(request, "", validDeprecatedHeaders)
|
||||
var token = "INVALID"
|
||||
Then("We should get a 201 - OK and a token")
|
||||
response.code should equal(201)
|
||||
response.body match {
|
||||
case JObject(List(JField(name, JString(value)))) =>
|
||||
name should equal("token")
|
||||
value.length should be > 0
|
||||
token = value
|
||||
case _ => fail("Expected a token")
|
||||
}
|
||||
|
||||
// TODO Check that we are logged in. TODO Add an endpoint like /me that returns the currently logged in user.
|
||||
When("when we use the token it should work")
|
||||
val headerWithToken = ("Authorization", "DirectLogin token=%s".format(token))
|
||||
val validHeadersWithToken = List(accessControlOriginHeader, headerWithToken)
|
||||
val request2 = baseRequest / "obp" / "v2.0.0" / "my" / "accounts"
|
||||
val response2 = makeGetRequest(request2, validHeadersWithToken)
|
||||
|
||||
Then("We should get a 200 - OK and an empty list of accounts")
|
||||
response2.code should equal(200)
|
||||
response2.body match {
|
||||
case JArray(List()) =>
|
||||
case _ => fail("Expected empty list of accounts")
|
||||
}
|
||||
|
||||
When("when we use the token to get current user and it should work - New Style")
|
||||
val requestCurrentUserNewStyle = baseRequest / "obp" / "v3.0.0" / "users" / "current"
|
||||
val responseCurrentUserNewStyle = makeGetRequest(requestCurrentUserNewStyle, validHeadersWithToken)
|
||||
And("We should get a 200")
|
||||
responseCurrentUserNewStyle.code should equal(200)
|
||||
val currentUserNewStyle = responseCurrentUserNewStyle.body.extract[UserJsonV300]
|
||||
currentUserNewStyle.username shouldBe USERNAME
|
||||
|
||||
When("when we use the token to get current user and it should work - Old Style")
|
||||
val requestCurrentUserOldStyle = baseRequest / "obp" / "v2.0.0" / "users" / "current"
|
||||
val responseCurrentUserOldStyle = makeGetRequest(requestCurrentUserOldStyle, validHeadersWithToken)
|
||||
And("We should get a 200")
|
||||
responseCurrentUserOldStyle.code should equal(200)
|
||||
val currentUserOldStyle = responseCurrentUserOldStyle.body.extract[UserJsonV300]
|
||||
currentUserOldStyle.username shouldBe USERNAME
|
||||
|
||||
currentUserNewStyle.username shouldBe currentUserOldStyle.username
|
||||
}
|
||||
|
||||
scenario("Login with correct everything!", ApiEndpoint1, ApiEndpoint2) {
|
||||
|
||||
//setupUserAndConsumer
|
||||
|
||||
@ -6471,8 +6471,8 @@ class API1_2_1Test extends ServerSetupWithTestData with DefaultUsers with Privat
|
||||
val transaction = randomTransaction(bankId, bankAccount.id, view)
|
||||
When("the request is sent")
|
||||
val reply = getTheCounterpartyOfOneTransaction(bankId, bankAccount.id, view, transaction.id, None)
|
||||
Then("we should get a 400 code")
|
||||
reply.code should equal (400)
|
||||
Then("we should get a 401 code")
|
||||
reply.code should equal (401)
|
||||
And("we should get an error message")
|
||||
reply.body.extract[ErrorMessage].message.nonEmpty should equal (true)
|
||||
}
|
||||
@ -6485,8 +6485,8 @@ class API1_2_1Test extends ServerSetupWithTestData with DefaultUsers with Privat
|
||||
val transaction = randomTransaction(bankId, bankAccount.id, view)
|
||||
When("the request is sent")
|
||||
val reply = getTheCounterpartyOfOneTransaction(bankId, bankAccount.id, view, transaction.id, user3)
|
||||
Then("we should get a 400 code")
|
||||
reply.code should equal (400)
|
||||
Then("we should get a 403 code")
|
||||
reply.code should equal (403)
|
||||
And("we should get an error message")
|
||||
reply.body.extract[ErrorMessage].message.nonEmpty should equal (true)
|
||||
}
|
||||
@ -6499,10 +6499,10 @@ class API1_2_1Test extends ServerSetupWithTestData with DefaultUsers with Privat
|
||||
val transaction = randomTransaction(bankId, bankAccount.id, view)
|
||||
When("the request is sent")
|
||||
val reply = getTheCounterpartyOfOneTransaction(bankId, bankAccount.id, randomString(5), transaction.id, user1)
|
||||
Then("we should get a 400 code")
|
||||
reply.code should equal (400)
|
||||
Then("we should get a 403 code")
|
||||
reply.code should equal (403)
|
||||
And("we should get an error message")
|
||||
reply.body.extract[ErrorMessage].message should equal (ViewNotFound)
|
||||
reply.body.extract[ErrorMessage].message should equal (UserNoPermissionAccessView)
|
||||
}
|
||||
|
||||
scenario("we will not get get the other bank account of a random transaction because the transaction does not exist", API1_2_1, GetTransactionAccount){
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
package code.api.v1_3_0
|
||||
|
||||
import java.util.Date
|
||||
|
||||
import code.api.util.APIUtil.OAuth._
|
||||
import code.api.util.{CallContext, OBPQueryParam}
|
||||
import code.api.util.{APIUtil, CallContext, OBPQueryParam}
|
||||
import code.bankconnectors.Connector
|
||||
import code.setup.{DefaultConnectorTestSetup, DefaultUsers, ServerSetup}
|
||||
import code.util.Helper.MdcLoggable
|
||||
@ -14,7 +13,7 @@ class PhysicalCardsTest extends ServerSetup with DefaultUsers with DefaultConne
|
||||
|
||||
def v1_3Request = baseRequest / "obp" / "v1.3.0"
|
||||
|
||||
lazy val bank = createBank("a-bank")
|
||||
lazy val bank = createBank(APIUtil.defaultBankId)
|
||||
lazy val accId = "a-account"
|
||||
lazy val accountCurrency = "EUR"
|
||||
lazy val account = createAccount(bank.bankId, AccountId(accId), accountCurrency)
|
||||
@ -94,6 +93,7 @@ class PhysicalCardsTest extends ServerSetup with DefaultUsers with DefaultConne
|
||||
super.afterAll()
|
||||
//reset the default connector
|
||||
Connector.connector.default.set(Connector.buildOne)
|
||||
wipeTestData()
|
||||
}
|
||||
|
||||
feature("Getting details of physical cards") {
|
||||
|
||||
@ -51,9 +51,9 @@ class AccountTest extends V300ServerSetup {
|
||||
val requestGet = (v3_0Request / "banks" / "BANK_ID" / "firehose" / "accounts" / "views" / "VIEW_ID").GET <@ (user1)
|
||||
val responseGet = makeGetRequest(requestGet)
|
||||
|
||||
And("We should get a 403")
|
||||
responseGet.code should equal(403)
|
||||
responseGet.body.extract[ErrorMessage].message should equal(AccountFirehoseNotAllowedOnThisInstance +" or " + UserHasMissingRoles + CanUseAccountFirehoseAtAnyBank )
|
||||
And("We should get a 400")
|
||||
responseGet.code should equal(400)
|
||||
responseGet.body.extract[ErrorMessage].message contains AccountFirehoseNotAllowedOnThisInstance should be (true)
|
||||
}}
|
||||
|
||||
|
||||
|
||||
119
obp-api/src/test/scala/code/api/v3_0_0/FirehoseTest.scala
Normal file
119
obp-api/src/test/scala/code/api/v3_0_0/FirehoseTest.scala
Normal file
@ -0,0 +1,119 @@
|
||||
package code.api.v3_0_0
|
||||
|
||||
import code.api.util.APIUtil.OAuth._
|
||||
import code.api.util.ApiRole
|
||||
import code.api.util.ApiRole.CanUseAccountFirehoseAtAnyBank
|
||||
import code.api.util.ErrorMessages.AccountFirehoseNotAllowedOnThisInstance
|
||||
import code.api.v3_0_0.OBPAPI3_0_0.Implementations3_0_0
|
||||
import code.entitlement.Entitlement
|
||||
import code.setup.PropsReset
|
||||
import com.github.dwickern.macros.NameOf.nameOf
|
||||
import com.openbankproject.commons.util.ApiVersion
|
||||
import org.scalatest.Tag
|
||||
|
||||
class FirehoseTest extends V300ServerSetup with PropsReset{
|
||||
/**
|
||||
* 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.v3_0_0.toString)
|
||||
object ApiEndpoint2 extends Tag(nameOf(Implementations3_0_0.getFirehoseAccountsAtOneBank))
|
||||
object ApiEndpoint4 extends Tag(nameOf(Implementations3_0_0.getFirehoseTransactionsForBankAccount))
|
||||
|
||||
|
||||
feature(s"test ${ApiEndpoint2}") {
|
||||
|
||||
scenario("We will call the endpoint with user credentials", VersionOfApi, ApiEndpoint2) {
|
||||
setPropsValues("allow_account_firehose" -> "true")
|
||||
setPropsValues("enable.force_error"->"true")
|
||||
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanUseAccountFirehoseAtAnyBank.toString)
|
||||
When("We send the request")
|
||||
val request = (v3_0Request / "banks" / testBankId1.value /"firehose" / "accounts" / "views"/"owner").GET <@ (user1)
|
||||
val response = makeGetRequest(request)
|
||||
Then("We should get a 200 and check the response body")
|
||||
response.code should equal(200)
|
||||
response.body.extract[ModeratedCoreAccountsJsonV300]
|
||||
}
|
||||
scenario("We will call the endpoint with user credentials, props alias", VersionOfApi, ApiEndpoint2) {
|
||||
setPropsValues("allow_firehose_views" -> "true")
|
||||
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanUseAccountFirehoseAtAnyBank.toString)
|
||||
When("We send the request")
|
||||
val request = (v3_0Request / "banks" / testBankId1.value /"firehose" / "accounts" / "views"/"owner").GET <@ (user1)
|
||||
val response = makeGetRequest(request)
|
||||
Then("We should get a 200 and check the response body")
|
||||
response.code should equal(200)
|
||||
response.body.extract[ModeratedCoreAccountsJsonV300]
|
||||
}
|
||||
|
||||
|
||||
scenario("We will call the endpoint missing role", VersionOfApi, ApiEndpoint2) {
|
||||
setPropsValues("allow_account_firehose" -> "true")
|
||||
When("We send the request")
|
||||
val request = (v3_0Request / "banks" / testBankId1.value / "firehose" / "accounts" / "views" / "owner").GET <@ (user1)
|
||||
val response = makeGetRequest(request)
|
||||
Then("We should get a 403 and check the response body")
|
||||
response.code should equal(403)
|
||||
response.body.toString contains (CanUseAccountFirehoseAtAnyBank.toString()) should be(true)
|
||||
}
|
||||
|
||||
scenario("We will call the endpoint missing props ", VersionOfApi, ApiEndpoint2) {
|
||||
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanUseAccountFirehoseAtAnyBank.toString)
|
||||
When("We send the request")
|
||||
val request = (v3_0Request / "banks" / testBankId1.value /"firehose" / "accounts" / "views"/"owner").GET <@ (user1)
|
||||
val response = makeGetRequest(request)
|
||||
Then("We should get a 400 and check the response body")
|
||||
response.code should equal(400)
|
||||
response.body.toString contains (AccountFirehoseNotAllowedOnThisInstance) should be (true)
|
||||
}
|
||||
}
|
||||
|
||||
feature(s"test ${ApiEndpoint4.name}") {
|
||||
|
||||
scenario("We will call the endpoint with user credentials", VersionOfApi, ApiEndpoint4) {
|
||||
setPropsValues("allow_account_firehose" -> "true")
|
||||
setPropsValues("enable.force_error"->"true")
|
||||
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanUseAccountFirehoseAtAnyBank.toString)
|
||||
When("We send the request")
|
||||
val request = (v3_0Request / "banks" / testBankId1.value /"firehose" / "accounts" / testAccountId1.value / "views"/"owner"/"transactions").GET <@ (user1)
|
||||
val response = makeGetRequest(request)
|
||||
Then("We should get a 200 and check the response body")
|
||||
response.code should equal(200)
|
||||
response.body.extract[ModeratedCoreAccountsJsonV300]
|
||||
}
|
||||
|
||||
scenario("We will call the endpoint with user credentials, props alias", VersionOfApi, ApiEndpoint4) {
|
||||
setPropsValues("allow_firehose_views" -> "true")
|
||||
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanUseAccountFirehoseAtAnyBank.toString)
|
||||
When("We send the request")
|
||||
val request = (v3_0Request / "banks" / testBankId1.value /"firehose" / "accounts" / testAccountId1.value / "views"/"owner"/"transactions").GET <@ (user1)
|
||||
val response = makeGetRequest(request)
|
||||
Then("We should get a 200 and check the response body")
|
||||
response.code should equal(200)
|
||||
response.body.extract[ModeratedCoreAccountsJsonV300]
|
||||
}
|
||||
|
||||
|
||||
scenario("We will call the endpoint missing role", VersionOfApi, ApiEndpoint4) {
|
||||
setPropsValues("allow_account_firehose" -> "true")
|
||||
When("We send the request")
|
||||
val request = (v3_0Request / "banks" / testBankId1.value / "firehose" / "accounts" / testAccountId1.value /"views" / "owner"/"transactions").GET <@ (user1)
|
||||
val response = makeGetRequest(request)
|
||||
Then("We should get a 403 and check the response body")
|
||||
response.code should equal(403)
|
||||
response.body.toString contains (CanUseAccountFirehoseAtAnyBank.toString()) should be(true)
|
||||
}
|
||||
|
||||
scenario("We will call the endpoint missing props ", VersionOfApi, ApiEndpoint4) {
|
||||
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanUseAccountFirehoseAtAnyBank.toString)
|
||||
When("We send the request")
|
||||
val request = (v3_0Request / "banks" / testBankId1.value /"firehose" / "accounts" / testAccountId1.value / "views"/"owner"/"transactions").GET <@ (user1)
|
||||
val response = makeGetRequest(request)
|
||||
Then("We should get a 400 and check the response body")
|
||||
response.code should equal(400)
|
||||
response.body.toString contains (AccountFirehoseNotAllowedOnThisInstance) should be (true)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -401,9 +401,9 @@ class TransactionsTest extends V300ServerSetup {
|
||||
val requestGet = (v3_0Request / "banks" / "BANK_ID" / "firehose" / "accounts" / "AccountId(accountId)" / "views" / "ViewId(viewId)" / "transactions").GET <@ (user1)
|
||||
val responseGet = makeGetRequest(requestGet)
|
||||
|
||||
And("We should get a 403")
|
||||
responseGet.code should equal(403)
|
||||
responseGet.body.extract[ErrorMessage].message should equal(AccountFirehoseNotAllowedOnThisInstance +" or " + UserHasMissingRoles + CanUseAccountFirehoseAtAnyBank )
|
||||
And("We should get a 400")
|
||||
responseGet.code should equal(400)
|
||||
responseGet.body.extract[ErrorMessage].message contains AccountFirehoseNotAllowedOnThisInstance should be (true)
|
||||
}}
|
||||
|
||||
|
||||
|
||||
@ -131,7 +131,10 @@ class AccountAttributeTest extends V310ServerSetup {
|
||||
response310.code should equal(403)
|
||||
val errorMessageText = UserHasMissingRoles + canCreateAccountAttributeAtOneBank
|
||||
And("error should be " + errorMessageText)
|
||||
response310.body.extract[ErrorMessage].message should equal (errorMessageText)
|
||||
val errorMessage = response310.body.extract[ErrorMessage].message
|
||||
errorMessage contains (UserHasMissingRoles) should be (true)
|
||||
errorMessage contains (canCreateAccountAttributeAtOneBank.toString()) should be (true)
|
||||
|
||||
}
|
||||
scenario("We will call the Create endpoint but wrong `type` ", ApiEndpoint1, VersionOfApi) {
|
||||
When(s"We make a request $VersionOfApi")
|
||||
@ -163,7 +166,9 @@ class AccountAttributeTest extends V310ServerSetup {
|
||||
response310.code should equal(403)
|
||||
val errorMessageText = UserHasMissingRoles + canUpdateAccountAttribute
|
||||
And("error should be " + errorMessageText)
|
||||
response310.body.extract[ErrorMessage].message should equal (errorMessageText)
|
||||
val errorMessage = response310.body.extract[ErrorMessage].message
|
||||
errorMessage contains (UserHasMissingRoles) should be (true)
|
||||
errorMessage contains (canUpdateAccountAttribute.toString()) should be (true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -88,7 +88,9 @@ class CustomerAddressTest extends V310ServerSetup {
|
||||
Then("We should get a 403")
|
||||
response310.code should equal(403)
|
||||
And("error should be " + UserHasMissingRoles + CanCreateCustomerAddress)
|
||||
response310.body.extract[ErrorMessage].message should equal (UserHasMissingRoles + CanCreateCustomerAddress)
|
||||
val errorMessage = response310.body.extract[ErrorMessage].message
|
||||
errorMessage contains (UserHasMissingRoles) should be (true)
|
||||
errorMessage contains (CanCreateCustomerAddress.toString()) should be (true)
|
||||
}
|
||||
|
||||
scenario("We will call the Get endpoint without a user credentials", ApiEndpoint2, VersionOfApi) {
|
||||
@ -107,7 +109,9 @@ class CustomerAddressTest extends V310ServerSetup {
|
||||
Then("We should get a 403")
|
||||
response310.code should equal(403)
|
||||
And("error should be " + UserHasMissingRoles + CanGetCustomerAddress)
|
||||
response310.body.extract[ErrorMessage].message should equal (UserHasMissingRoles + CanGetCustomerAddress)
|
||||
val errorMessage = response310.body.extract[ErrorMessage].message
|
||||
errorMessage contains (UserHasMissingRoles) should be (true)
|
||||
errorMessage contains (CanGetCustomerAddress.toString()) should be (true)
|
||||
}
|
||||
|
||||
scenario("We will call the Delete endpoint without a user credentials", ApiEndpoint3, VersionOfApi) {
|
||||
@ -126,7 +130,9 @@ class CustomerAddressTest extends V310ServerSetup {
|
||||
Then("We should get a 403")
|
||||
response310.code should equal(403)
|
||||
And("error should be " + UserHasMissingRoles + CanDeleteCustomerAddress)
|
||||
response310.body.extract[ErrorMessage].message should equal (UserHasMissingRoles + CanDeleteCustomerAddress)
|
||||
val errorMessage = response310.body.extract[ErrorMessage].message
|
||||
errorMessage contains (UserHasMissingRoles) should be (true)
|
||||
errorMessage contains (CanDeleteCustomerAddress.toString()) should be (true)
|
||||
}
|
||||
|
||||
scenario("We will call the Add, Get and Delete endpoints with user credentials and role", ApiEndpoint1, ApiEndpoint2, ApiEndpoint3, ApiEndpoint4, VersionOfApi) {
|
||||
|
||||
@ -28,18 +28,21 @@ package code.api.v3_1_0
|
||||
import com.openbankproject.commons.model.ErrorMessage
|
||||
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON
|
||||
import code.api.util.APIUtil.OAuth._
|
||||
import code.api.util.ApiRole
|
||||
import code.api.util.ApiRole._
|
||||
import com.openbankproject.commons.util.ApiVersion
|
||||
import code.api.util.ErrorMessages._
|
||||
import code.api.v3_0_0.ModeratedCoreAccountsJsonV300
|
||||
import code.api.v3_1_0.OBPAPI3_1_0.Implementations3_1_0
|
||||
import code.customer.CustomerX
|
||||
import code.entitlement.Entitlement
|
||||
import code.setup.PropsReset
|
||||
import code.usercustomerlinks.UserCustomerLink
|
||||
import com.github.dwickern.macros.NameOf.nameOf
|
||||
import net.liftweb.json.Serialization.write
|
||||
import org.scalatest.Tag
|
||||
|
||||
class CustomerTest extends V310ServerSetup {
|
||||
class CustomerTest extends V310ServerSetup with PropsReset{
|
||||
|
||||
override def beforeAll(): Unit = {
|
||||
super.beforeAll()
|
||||
@ -70,6 +73,7 @@ class CustomerTest extends V310ServerSetup {
|
||||
object ApiEndpoint9 extends Tag(nameOf(Implementations3_1_0.updateCustomerBranch))
|
||||
object ApiEndpoint10 extends Tag(nameOf(Implementations3_1_0.updateCustomerData))
|
||||
object ApiEndpoint11 extends Tag(nameOf(Implementations3_1_0.updateCustomerNumber))
|
||||
object ApiEndpoint12 extends Tag(nameOf(Implementations3_1_0.getFirehoseCustomers))
|
||||
|
||||
val customerNumberJson = PostCustomerNumberJsonV310(customer_number = "123")
|
||||
val postCustomerJson = SwaggerDefinitionsJSON.postCustomerJsonV310
|
||||
@ -104,7 +108,9 @@ class CustomerTest extends V310ServerSetup {
|
||||
response310.code should equal(403)
|
||||
val errorMsg = UserHasMissingRoles + canCreateCustomer + " or " + canCreateCustomerAtAnyBank
|
||||
And("error should be " + errorMsg)
|
||||
response310.body.extract[ErrorMessage].message should equal (errorMsg)
|
||||
val errorMessage = response310.body.extract[ErrorMessage].message
|
||||
errorMessage contains (UserHasMissingRoles) should be (true)
|
||||
errorMessage contains (canCreateCustomerAtAnyBank.toString()) should be (true)
|
||||
}
|
||||
scenario("We will call the endpoint with a user credentials and a proper role", ApiEndpoint3, VersionOfApi) {
|
||||
Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, CanCreateCustomer.toString)
|
||||
@ -137,7 +143,9 @@ class CustomerTest extends V310ServerSetup {
|
||||
Then("We should get a 403")
|
||||
response310.code should equal(403)
|
||||
And("error should be " + UserHasMissingRoles + CanGetCustomer)
|
||||
response310.body.extract[ErrorMessage].message should equal (UserHasMissingRoles + CanGetCustomer)
|
||||
val errorMessage = response310.body.extract[ErrorMessage].message
|
||||
errorMessage contains (UserHasMissingRoles) should be (true)
|
||||
errorMessage contains (CanGetCustomer.toString()) should be (true)
|
||||
}
|
||||
scenario("We will call the endpoint with the proper Role " + canGetCustomer, ApiEndpoint1, VersionOfApi) {
|
||||
Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, CanGetCustomer.toString)
|
||||
@ -171,7 +179,9 @@ class CustomerTest extends V310ServerSetup {
|
||||
Then("We should get a 403")
|
||||
response310.code should equal(403)
|
||||
And("error should be " + UserHasMissingRoles + CanGetCustomer)
|
||||
response310.body.extract[ErrorMessage].message should equal (UserHasMissingRoles + CanGetCustomer)
|
||||
val errorMessage = response310.body.extract[ErrorMessage].message
|
||||
errorMessage contains (UserHasMissingRoles) should be (true)
|
||||
errorMessage contains (CanGetCustomer.toString()) should be (true)
|
||||
}
|
||||
scenario("We will call the endpoint with the proper Role " + canGetCustomer, ApiEndpoint2, VersionOfApi) {
|
||||
Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, CanGetCustomer.toString)
|
||||
@ -205,7 +215,9 @@ class CustomerTest extends V310ServerSetup {
|
||||
response310.code should equal(403)
|
||||
val errorMsg = UserHasMissingRoles + canUpdateCustomerEmail
|
||||
And("error should be " + errorMsg)
|
||||
response310.body.extract[ErrorMessage].message should equal (errorMsg)
|
||||
val errorMessage = response310.body.extract[ErrorMessage].message
|
||||
errorMessage contains (UserHasMissingRoles) should be (true)
|
||||
errorMessage contains (canUpdateCustomerEmail.toString()) should be (true)
|
||||
}
|
||||
scenario("We will call the endpoint with user credentials and the proper role", ApiEndpoint3, ApiEndpoint4, VersionOfApi) {
|
||||
Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, CanCreateCustomer.toString)
|
||||
@ -248,7 +260,9 @@ class CustomerTest extends V310ServerSetup {
|
||||
response310.code should equal(403)
|
||||
val errorMsg = UserHasMissingRoles + canUpdateCustomerMobilePhoneNumber
|
||||
And("error should be " + errorMsg)
|
||||
response310.body.extract[ErrorMessage].message should equal (errorMsg)
|
||||
val errorMessage = response310.body.extract[ErrorMessage].message
|
||||
errorMessage contains (UserHasMissingRoles) should be (true)
|
||||
errorMessage contains (canUpdateCustomerMobilePhoneNumber.toString()) should be (true)
|
||||
}
|
||||
scenario("We will call the endpoint with user credentials and the proper role", ApiEndpoint5, VersionOfApi) {
|
||||
Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, CanCreateCustomer.toString)
|
||||
@ -292,7 +306,9 @@ class CustomerTest extends V310ServerSetup {
|
||||
response310.code should equal(403)
|
||||
val errorMsg = UserHasMissingRoles + canUpdateCustomerIdentity
|
||||
And("error should be " + errorMsg)
|
||||
response310.body.extract[ErrorMessage].message should equal (errorMsg)
|
||||
val errorMessage = response310.body.extract[ErrorMessage].message
|
||||
errorMessage contains (UserHasMissingRoles) should be (true)
|
||||
errorMessage contains (canUpdateCustomerIdentity.toString()) should be (true)
|
||||
}
|
||||
scenario("We will call the endpoint with user credentials and the proper role", ApiEndpoint6, VersionOfApi) {
|
||||
Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, CanCreateCustomer.toString)
|
||||
@ -339,7 +355,9 @@ class CustomerTest extends V310ServerSetup {
|
||||
response310.code should equal(403)
|
||||
val errorMsg = UserHasMissingRoles + canUpdateCustomerCreditLimit
|
||||
And("error should be " + errorMsg)
|
||||
response310.body.extract[ErrorMessage].message should equal (errorMsg)
|
||||
val errorMessage = response310.body.extract[ErrorMessage].message
|
||||
errorMessage contains (UserHasMissingRoles) should be (true)
|
||||
errorMessage contains (canUpdateCustomerCreditLimit.toString()) should be (true)
|
||||
}
|
||||
scenario("We will call the endpoint with user credentials and the proper role", ApiEndpoint7, VersionOfApi) {
|
||||
Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, CanCreateCustomer.toString)
|
||||
@ -384,7 +402,9 @@ class CustomerTest extends V310ServerSetup {
|
||||
response310.code should equal(403)
|
||||
val errorMsg = UserHasMissingRoles + canUpdateCustomerCreditRatingAndSource
|
||||
And("error should be " + errorMsg)
|
||||
response310.body.extract[ErrorMessage].message should equal (errorMsg)
|
||||
val errorMessage = response310.body.extract[ErrorMessage].message
|
||||
errorMessage contains (UserHasMissingRoles) should be (true)
|
||||
errorMessage contains (canUpdateCustomerCreditRatingAndSource.toString()) should be (true)
|
||||
}
|
||||
scenario("We will call the endpoint with user credentials and the proper role", ApiEndpoint8, VersionOfApi) {
|
||||
Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, CanCreateCustomer.toString)
|
||||
@ -429,7 +449,9 @@ class CustomerTest extends V310ServerSetup {
|
||||
response310.code should equal(403)
|
||||
val errorMsg = UserHasMissingRoles + canUpdateCustomerBranch
|
||||
And("error should be " + errorMsg)
|
||||
response310.body.extract[ErrorMessage].message should equal (errorMsg)
|
||||
val errorMessage = response310.body.extract[ErrorMessage].message
|
||||
errorMessage contains (UserHasMissingRoles) should be (true)
|
||||
errorMessage contains (canUpdateCustomerBranch.toString()) should be (true)
|
||||
}
|
||||
scenario("We will call the endpoint with user credentials and the proper role", ApiEndpoint9, VersionOfApi) {
|
||||
Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, CanCreateCustomer.toString)
|
||||
@ -473,7 +495,9 @@ class CustomerTest extends V310ServerSetup {
|
||||
response310.code should equal(403)
|
||||
val errorMsg = UserHasMissingRoles + canUpdateCustomerData
|
||||
And("error should be " + errorMsg)
|
||||
response310.body.extract[ErrorMessage].message should equal (errorMsg)
|
||||
val errorMessage = response310.body.extract[ErrorMessage].message
|
||||
errorMessage contains (UserHasMissingRoles) should be (true)
|
||||
errorMessage contains (canUpdateCustomerData.toString()) should be (true)
|
||||
}
|
||||
scenario("We will call the endpoint with user credentials and the proper role", ApiEndpoint10, VersionOfApi) {
|
||||
Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, CanCreateCustomer.toString)
|
||||
@ -521,7 +545,9 @@ class CustomerTest extends V310ServerSetup {
|
||||
response310.code should equal(403)
|
||||
val errorMsg = UserHasMissingRoles + canUpdateCustomerNumber
|
||||
And("error should be " + errorMsg)
|
||||
response310.body.extract[ErrorMessage].message should equal (errorMsg)
|
||||
val errorMessage = response310.body.extract[ErrorMessage].message
|
||||
errorMessage contains (UserHasMissingRoles) should be (true)
|
||||
errorMessage contains (canUpdateCustomerNumber.toString()) should be (true)
|
||||
}
|
||||
scenario("We will call the endpoint with user credentials and the proper role", ApiEndpoint3, ApiEndpoint11, VersionOfApi) {
|
||||
Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, CanCreateCustomer.toString)
|
||||
@ -560,4 +586,61 @@ class CustomerTest extends V310ServerSetup {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
feature(s" $ApiEndpoint12- Authorized access") {
|
||||
|
||||
//first we create the customers:
|
||||
Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, CanCreateCustomer.toString)
|
||||
When("We make a request v3.1.0")
|
||||
val request310 = (v3_1_0_Request / "banks" / bankId / "customers").POST <@(user1)
|
||||
val response310 = makePostRequest(request310, write(postCustomerJson))
|
||||
Then("We should get a 201")
|
||||
response310.code should equal(201)
|
||||
|
||||
scenario("We will call the endpoint with user credentials", VersionOfApi, ApiEndpoint4) {
|
||||
setPropsValues("allow_customer_firehose" -> "true")
|
||||
setPropsValues("enable.force_error"->"true")
|
||||
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanUseCustomerFirehoseAtAnyBank.toString)
|
||||
When("We send the request")
|
||||
val request = (v3_1_0_Request / "banks" / testBankId1.value /"firehose" / "customers" ).GET <@ (user1)
|
||||
val response = makeGetRequest(request)
|
||||
Then("We should get a 200 and check the response body")
|
||||
response.code should equal(200)
|
||||
response.body.extract[ModeratedCoreAccountsJsonV300]
|
||||
}
|
||||
|
||||
scenario("We will call the endpoint with user credentials, props alias", VersionOfApi, ApiEndpoint4) {
|
||||
setPropsValues("allow_firehose_views" -> "true")
|
||||
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanUseCustomerFirehoseAtAnyBank.toString)
|
||||
When("We send the request")
|
||||
val request = (v3_1_0_Request / "banks" / testBankId1.value /"firehose" / "customers" ).GET <@ (user1)
|
||||
val response = makeGetRequest(request)
|
||||
Then("We should get a 200 and check the response body")
|
||||
response.code should equal(200)
|
||||
response.body.extract[ModeratedCoreAccountsJsonV300]
|
||||
}
|
||||
|
||||
|
||||
scenario("We will call the endpoint missing role", VersionOfApi, ApiEndpoint4) {
|
||||
setPropsValues("allow_customer_firehose" -> "true")
|
||||
When("We send the request")
|
||||
val request = (v3_1_0_Request / "banks" / testBankId1.value / "firehose" / "customers").GET <@ (user1)
|
||||
val response = makeGetRequest(request)
|
||||
Then("We should get a 403 and check the response body")
|
||||
response.code should equal(403)
|
||||
response.body.toString contains (CanUseCustomerFirehoseAtAnyBank.toString()) should be(true)
|
||||
}
|
||||
|
||||
scenario("We will call the endpoint missing props ", VersionOfApi, ApiEndpoint4) {
|
||||
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanUseCustomerFirehoseAtAnyBank.toString)
|
||||
When("We send the request")
|
||||
val request = (v3_1_0_Request / "banks" / testBankId1.value /"firehose" / "customers" ).GET <@ (user1)
|
||||
val response = makeGetRequest(request)
|
||||
Then("We should get a 400 and check the response body")
|
||||
response.code should equal(400)
|
||||
response.body.toString contains (AccountFirehoseNotAllowedOnThisInstance) should be (true)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -144,7 +144,9 @@ class ProductAttributeTest extends V310ServerSetup {
|
||||
response310.code should equal(403)
|
||||
val createProductEntitlementsRequiredText = UserHasMissingRoles + canCreateProductAttribute
|
||||
And("error should be " + createProductEntitlementsRequiredText)
|
||||
response310.body.extract[ErrorMessage].message should equal (createProductEntitlementsRequiredText)
|
||||
val errorMessage = response310.body.extract[ErrorMessage].message
|
||||
errorMessage contains (UserHasMissingRoles) should be (true)
|
||||
errorMessage contains (createProductEntitlementsRequiredText.toString()) should be (true)
|
||||
}
|
||||
scenario("We will call the Create endpoint but wrong `type` ", ApiEndpoint1, VersionOfApi) {
|
||||
When("We make a request v3.1.0")
|
||||
@ -176,7 +178,9 @@ class ProductAttributeTest extends V310ServerSetup {
|
||||
response310.code should equal(403)
|
||||
val createProductEntitlementsRequiredText = UserHasMissingRoles + canGetProductAttribute
|
||||
And("error should be " + createProductEntitlementsRequiredText)
|
||||
response310.body.extract[ErrorMessage].message should equal (createProductEntitlementsRequiredText)
|
||||
val errorMessage = response310.body.extract[ErrorMessage].message
|
||||
errorMessage contains (UserHasMissingRoles) should be (true)
|
||||
errorMessage contains (canGetProductAttribute.toString()) should be (true)
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,7 +202,9 @@ class ProductAttributeTest extends V310ServerSetup {
|
||||
response310.code should equal(403)
|
||||
val createProductEntitlementsRequiredText = UserHasMissingRoles + canUpdateProductAttribute
|
||||
And("error should be " + createProductEntitlementsRequiredText)
|
||||
response310.body.extract[ErrorMessage].message should equal (createProductEntitlementsRequiredText)
|
||||
val errorMessage = response310.body.extract[ErrorMessage].message
|
||||
errorMessage contains (UserHasMissingRoles) should be (true)
|
||||
errorMessage contains (createProductEntitlementsRequiredText.toString()) should be (true)
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,7 +226,9 @@ class ProductAttributeTest extends V310ServerSetup {
|
||||
response310.code should equal(403)
|
||||
val createProductEntitlementsRequiredText = UserHasMissingRoles + canDeleteProductAttribute
|
||||
And("error should be " + createProductEntitlementsRequiredText)
|
||||
response310.body.extract[ErrorMessage].message should equal (createProductEntitlementsRequiredText)
|
||||
val errorMessage = response310.body.extract[ErrorMessage].message
|
||||
errorMessage contains (UserHasMissingRoles) should be (true)
|
||||
errorMessage contains (canDeleteProductAttribute.toString()) should be (true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user