mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 18:46:46 +00:00
Merge branch 'develop' of github.com:OpenBankProject/OBP-API into feature/dynamic_message_doc_js
This commit is contained in:
commit
ff67f6493f
5
.github/Dockerfile_PreBuild
vendored
Normal file
5
.github/Dockerfile_PreBuild
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
FROM jetty:9.4-jre11-slim
|
||||
|
||||
# Copy OBP source code
|
||||
# Copy build artifact (.war file) into jetty from 'maven' stage.
|
||||
COPY /obp-api/target/obp-api-1.*.war /var/lib/jetty/webapps/ROOT.war
|
||||
70
.github/workflows/build_package.yml
vendored
Normal file
70
.github/workflows/build_package.yml
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
name: build and publish container
|
||||
|
||||
on: [push]
|
||||
env:
|
||||
## Sets environment variable
|
||||
DOCKER_HUB_ORGANIZATION: openbankproject
|
||||
DOCKER_HUB_REPOSITORY: obp-api
|
||||
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
cache: maven
|
||||
- name: Build with Maven
|
||||
run: |
|
||||
echo connector=mapped > obp-api/src/main/resources/props/test.default.props
|
||||
echo hostname=http://localhost:8016 >> obp-api/src/main/resources/props/test.default.props
|
||||
echo tests.port=8016 >> obp-api/src/main/resources/props/test.default.props
|
||||
echo End of minimum settings >> obp-api/src/main/resources/props/test.default.props
|
||||
echo payments_enabled=false >> obp-api/src/main/resources/props/test.default.props
|
||||
echo importer_secret=change_me >> obp-api/src/main/resources/props/test.default.props
|
||||
echo messageQueue.updateBankAccountsTransaction=false >> obp-api/src/main/resources/props/test.default.props
|
||||
echo messageQueue.createBankAccounts=false >> obp-api/src/main/resources/props/test.default.props
|
||||
echo allow_sandbox_account_creation=true >> obp-api/src/main/resources/props/test.default.props
|
||||
echo allow_sandbox_data_import=true >> obp-api/src/main/resources/props/test.default.props
|
||||
echo sandbox_data_import_secret=change_me >> obp-api/src/main/resources/props/test.default.props
|
||||
echo allow_account_deletion=true >> obp-api/src/main/resources/props/test.default.props
|
||||
echo allowed_internal_redirect_urls = /,/oauth/authorize >> obp-api/src/main/resources/props/test.default.props
|
||||
echo transactionRequests_enabled=true >> obp-api/src/main/resources/props/test.default.props
|
||||
echo transactionRequests_supported_types=SEPA,SANDBOX_TAN,FREE_FORM,COUNTERPARTY,ACCOUNT,SIMPLE >> obp-api/src/main/resources/props/test.default.props
|
||||
echo SIMPLE_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
echo openredirects.hostname.whitlelist=http://127.0.0.1,http://localhost >> obp-api/src/main/resources/props/test.default.props
|
||||
echo remotedata.secret = foobarbaz >> obp-api/src/main/resources/props/test.default.props
|
||||
echo allow_public_views=true >> obp-api/src/main/resources/props/test.default.props
|
||||
|
||||
echo SIMPLE_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
echo ACCOUNT_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
echo SEPA_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
echo FREE_FORM_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
echo COUNTERPARTY_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
echo SEPA_CREDIT_TRANSFERS_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
|
||||
echo kafka.akka.timeout = 9 >> obp-api/src/main/resources/props/test.default.props
|
||||
echo remotedata.timeout = 10 >> obp-api/src/main/resources/props/test.default.props
|
||||
|
||||
echo allow_oauth2_login=true >> obp-api/src/main/resources/props/test.default.props
|
||||
echo oauth2.jwk_set.url=https://www.googleapis.com/oauth2/v3/certs >> obp-api/src/main/resources/props/test.default.props
|
||||
|
||||
echo ResetPasswordUrlEnabled=true >> obp-api/src/main/resources/props/test.default.props
|
||||
|
||||
echo consents.allowed=true >> obp-api/src/main/resources/props/test.default.props
|
||||
MAVEN_OPTS="-Xmx3G -Xss2m" mvn package
|
||||
- name: Build the Docker image
|
||||
run: |
|
||||
echo "${{ secrets.DOCKER_HUB_TOKEN }}" | docker login -u "${{ secrets.DOCKER_HUB_USERNAME }}" --password-stdin docker.io
|
||||
docker build . --file .github/Dockerfile_PreBuild --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:$GITHUB_SHA --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop
|
||||
docker push docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }} --all-tags
|
||||
echo docker done
|
||||
|
||||
|
||||
|
||||
|
||||
@ -106,7 +106,7 @@ To compile and run jetty, install Maven 3, create your configuration in obp-api/
|
||||
|
||||
Set memory options
|
||||
|
||||
export MAVEN_OPTS="-Xmx3000m -XX:MaxPermSize=512m"
|
||||
export MAVEN_OPTS="-Xmx3000m -Xss2m"
|
||||
|
||||
Run one test
|
||||
|
||||
|
||||
@ -639,7 +639,7 @@
|
||||
<plugin>
|
||||
<groupId>pl.project13.maven</groupId>
|
||||
<artifactId>git-commit-id-plugin</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<version>4.9.10</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
|
||||
387
obp-api/src/main/resources/i18n/lift-core_es_ES.properties
Normal file
387
obp-api/src/main/resources/i18n/lift-core_es_ES.properties
Normal file
@ -0,0 +1,387 @@
|
||||
api.explorer = Explorador API
|
||||
introduction = Introducción
|
||||
support = Soporte
|
||||
register = Registrarse
|
||||
logon = Ingresar
|
||||
|
||||
invalid.email.address = Invalid email address
|
||||
password.must.be.set = Password must be set
|
||||
password.too.short = Password too short
|
||||
passwords.do.not.match = Passwords do not match
|
||||
number.required = A numeric value must be provided
|
||||
ajax.error=The server cannot be contacted at this time
|
||||
invalid.zip.code = Invalid ZIP code
|
||||
invalid.postal.code = Invalid postal code
|
||||
unique.email.address = The email address must be unique
|
||||
must.be.logged.in = You must be logged in
|
||||
already.logged.in = already logged in. Please logout first.
|
||||
login = Login
|
||||
logout = Logout
|
||||
log.in = Log In
|
||||
log.out = Log Out
|
||||
sign.up = Sign Up
|
||||
logged.in = Logged In
|
||||
logout.first = Please logout first.
|
||||
lost.password = Lost Password
|
||||
reset.password = Reset Password
|
||||
change.password = Change Password
|
||||
password.changed = Password Changed
|
||||
edit.user = Edit User
|
||||
validate.user = Validate User
|
||||
edit.profile = Edit Profile
|
||||
sign.up.confirmation = Sign up confirmation
|
||||
sign.up.message = You have signed up. A validation email message will be sent to you.
|
||||
sign.up.validation.link=Click on this link to complete signup:
|
||||
welcome = Welcome
|
||||
account.validated = Account Validated
|
||||
invalid.validation.link = Validation link invalid
|
||||
account.validation.error = Your account has not been validated. Please check your email for a validation link.
|
||||
invalid.credentials = Invalid Username/Password
|
||||
enter.email = Enter your email address and we'll email you a link to reset your password
|
||||
email.address = Email address
|
||||
reset.password.confirmation = Reset Password Confirmation
|
||||
dear = Dear
|
||||
click.reset.link = Click on this link to reset your password
|
||||
thank.you = Thank you
|
||||
reset.password.request = Reset Password Request
|
||||
password.reset.email.sent = Password Reset Email sent
|
||||
account.validation.resent = Account Validation Re-sent
|
||||
email.address.not.found = Email address not found
|
||||
send.it = Send It
|
||||
reset.your.password = Reset your password
|
||||
enter.your.new.password = Enter your new password
|
||||
repeat.your.new.password = Enter your new password (repeat)
|
||||
set.password = Set Password
|
||||
password.link.invalid = Password reset link invalid
|
||||
wrong.old.password = Wrong old password
|
||||
old.password = Old password
|
||||
new.password = New password
|
||||
repeat.password = New password (repeat)
|
||||
repeat = Repeat
|
||||
edit = Edit
|
||||
cancel = Cancel
|
||||
ok = OK
|
||||
change = Change
|
||||
password = Password
|
||||
recover.password = Recover Password
|
||||
profile.updated = You have updated your profile
|
||||
male = Male
|
||||
female = Female
|
||||
first.name = First name
|
||||
last.name = Last name
|
||||
locale = Locale
|
||||
time.zone = Time Zone
|
||||
msg.notice = Notice
|
||||
msg.warning = Warning
|
||||
msg.error = Error
|
||||
crudify.menu.view.displayName=View %s
|
||||
crudify.menu.edit.displayName=Edit %s
|
||||
crudify.menu.delete.displayName=Delete %s
|
||||
paginator.norecords = There are no records to display
|
||||
paginator.displayingrecords = Displaying %s-%s of %s
|
||||
# Country names
|
||||
country_1 = United States
|
||||
country_2 = Afghanistan
|
||||
country_3 = Albania
|
||||
country_4 = Algeria
|
||||
country_5 = Andorra
|
||||
country_6 = Angola
|
||||
country_7 = Antigua and Barbuda
|
||||
country_8 = Argentina
|
||||
country_9 = Armenia
|
||||
country_10 = Australia
|
||||
country_11 = Austria
|
||||
country_12 = Azerbaijan
|
||||
country_13 = Bahamas, The
|
||||
country_14 = Bahrain
|
||||
country_15 = Bangladesh
|
||||
country_16 = Barbados
|
||||
country_17 = Belarus
|
||||
country_18 = Belgium
|
||||
country_19 = Belize
|
||||
country_20 = Benin
|
||||
country_21 = Bhutan
|
||||
country_22 = Bolivia
|
||||
country_23 = Bosnia and Herzegovina
|
||||
country_24 = Botswana
|
||||
country_25 = Brazil
|
||||
country_26 = Brunei
|
||||
country_27 = Bulgaria
|
||||
country_28 = Burkina Faso
|
||||
country_29 = Burundi
|
||||
country_30 = Cambodia
|
||||
country_31 = Cameroon
|
||||
country_32 = Canada
|
||||
country_33 = Cape Verde
|
||||
country_34 = Central African Republic
|
||||
country_35 = Chad
|
||||
country_36 = Chile
|
||||
country_37 = China, People's Republic of
|
||||
country_38 = Colombia
|
||||
country_39 = Comoros
|
||||
country_40 = Congo, Democratic Republic of the (Congo Kinshasa)
|
||||
country_41 = Congo, Republic of the (Congo Brazzaville)
|
||||
country_42 = Costa Rica
|
||||
country_43 = Cote d'Ivoire (Ivory Coast)
|
||||
country_44 = Croatia
|
||||
country_45 = Cuba
|
||||
country_46 = Cyprus
|
||||
country_47 = Czech Republic
|
||||
country_48 = Denmark
|
||||
country_49 = Djibouti
|
||||
country_50 = Dominica
|
||||
country_51 = Dominican Republic
|
||||
country_52 = Ecuador
|
||||
country_53 = Egypt
|
||||
country_54 = El Salvador
|
||||
country_55 = Equatorial Guinea
|
||||
country_56 = Eritrea
|
||||
country_57 = Estonia
|
||||
country_58 = Ethiopia
|
||||
country_59 = Fiji
|
||||
country_60 = Finland
|
||||
country_61 = France
|
||||
country_62 = Gabon
|
||||
country_63 = Gambia, The
|
||||
country_64 = Georgia
|
||||
country_65 = Germany
|
||||
country_66 = Ghana
|
||||
country_67 = Greece
|
||||
country_68 = Grenada
|
||||
country_69 = Guatemala
|
||||
country_70 = Guinea
|
||||
country_71 = Guinea-Bissau
|
||||
country_72 = Guyana
|
||||
country_73 = Haiti
|
||||
country_74 = Honduras
|
||||
country_75 = Hungary
|
||||
country_76 = Iceland
|
||||
country_77 = India
|
||||
country_78 = Indonesia
|
||||
country_79 = Iran
|
||||
country_80 = Iraq
|
||||
country_81 = Ireland
|
||||
country_82 = Israel
|
||||
country_83 = Italy
|
||||
country_84 = Jamaica
|
||||
country_85 = Japan
|
||||
country_86 = Jordan
|
||||
country_87 = Kazakhstan
|
||||
country_88 = Kenya
|
||||
country_89 = Kiribati
|
||||
country_90 = Korea, Democratic People's Republic of (North Korea)
|
||||
country_91 = Korea, Republic of (South Korea)
|
||||
country_92 = Kuwait
|
||||
country_93 = Kyrgyzstan
|
||||
country_94 = Laos
|
||||
country_95 = Latvia
|
||||
country_96 = Lebanon
|
||||
country_97 = Lesotho
|
||||
country_98 = Liberia
|
||||
country_99 = Libya
|
||||
country_100 = Liechtenstein
|
||||
country_101 = Lithuania
|
||||
country_102 = Luxembourg
|
||||
country_103 = Macedonia
|
||||
country_104 = Madagascar
|
||||
country_105 = Malawi
|
||||
country_106 = Malaysia
|
||||
country_107 = Maldives
|
||||
country_108 = Mali
|
||||
country_109 = Malta
|
||||
country_110 = Marshall Islands
|
||||
country_111 = Mauritania
|
||||
country_112 = Mauritius
|
||||
country_113 = Mexico
|
||||
country_114 = Micronesia
|
||||
country_115 = Moldova
|
||||
country_116 = Monaco
|
||||
country_117 = Mongolia
|
||||
country_118 = Montenegro
|
||||
country_119 = Morocco
|
||||
country_120 = Mozambique
|
||||
country_121 = Myanmar (Burma)
|
||||
country_122 = Namibia
|
||||
country_123 = Nauru
|
||||
country_124 = Nepal
|
||||
country_125 = Netherlands
|
||||
country_126 = New Zealand
|
||||
country_127 = Nicaragua
|
||||
country_128 = Niger
|
||||
country_129 = Nigeria
|
||||
country_130 = Norway
|
||||
country_131 = Oman
|
||||
country_132 = Pakistan
|
||||
country_133 = Palau
|
||||
country_134 = Panama
|
||||
country_135 = Papua New Guinea
|
||||
country_136 = Paraguay
|
||||
country_137 = Peru
|
||||
country_138 = Philippines
|
||||
country_139 = Poland
|
||||
country_140 = Portugal
|
||||
country_141 = Qatar
|
||||
country_142 = Romania
|
||||
country_143 = Russia
|
||||
country_144 = Rwanda
|
||||
country_145 = Saint Kitts and Nevis
|
||||
country_146 = Saint Lucia
|
||||
country_147 = Saint Vincent and the Grenadines
|
||||
country_148 = Samoa
|
||||
country_149 = San Marino
|
||||
country_150 = Sao Tome and Principe
|
||||
country_151 = Saudi Arabia
|
||||
country_152 = Senegal
|
||||
country_153 = Serbia
|
||||
country_154 = Seychelles
|
||||
country_155 = Sierra Leone
|
||||
country_156 = Singapore
|
||||
country_157 = Slovakia
|
||||
country_158 = Slovenia
|
||||
country_159 = Solomon Islands
|
||||
country_160 = Somalia
|
||||
country_161 = South Africa
|
||||
country_162 = Spain
|
||||
country_163 = Sri Lanka
|
||||
country_164 = Sudan
|
||||
country_165 = Suriname
|
||||
country_166 = Swaziland
|
||||
country_167 = Sweden
|
||||
country_168 = Switzerland
|
||||
country_169 = Syria
|
||||
country_170 = Tajikistan
|
||||
country_171 = Tanzania
|
||||
country_172 = Thailand
|
||||
country_173 = Timor-Leste (East Timor)
|
||||
country_174 = Togo
|
||||
country_175 = Tonga
|
||||
country_176 = Trinidad and Tobago
|
||||
country_177 = Tunisia
|
||||
country_178 = Turkey
|
||||
country_179 = Turkmenistan
|
||||
country_180 = Tuvalu
|
||||
country_181 = Uganda
|
||||
country_182 = Ukraine
|
||||
country_183 = United Arab Emirates
|
||||
country_184 = United Kingdom
|
||||
country_185 = Uruguay
|
||||
country_186 = Uzbekistan
|
||||
country_187 = Vanuatu
|
||||
country_188 = Vatican City
|
||||
country_189 = Venezuela
|
||||
country_190 = Vietnam
|
||||
country_191 = Yemen
|
||||
country_192 = Zambia
|
||||
country_193 = Zimbabwe
|
||||
country_194 = Abkhazia
|
||||
country_195 = China, Republic of (Taiwan)
|
||||
country_196 = Nagorno-Karabakh
|
||||
country_197 = Northern Cyprus
|
||||
country_198 = Pridnestrovie (Transnistria)
|
||||
country_199 = Somaliland
|
||||
country_200 = South Ossetia
|
||||
country_201 = Ashmore and Cartier Islands
|
||||
country_202 = Christmas Island
|
||||
country_203 = Cocos (Keeling) Islands
|
||||
country_204 = Coral Sea Islands
|
||||
country_205 = Heard Island and McDonald Islands
|
||||
country_206 = Norfolk Island
|
||||
country_207 = New Caledonia
|
||||
country_208 = French Polynesia
|
||||
country_209 = Mayotte
|
||||
country_210 = Saint Barthelemy
|
||||
country_211 = Saint Martin
|
||||
country_212 = Saint Pierre and Miquelon
|
||||
country_213 = Wallis and Futuna
|
||||
country_214 = French Southern and Antarctic Lands
|
||||
country_215 = Clipperton Island
|
||||
country_216 = Bouvet Island
|
||||
country_217 = Cook Islands
|
||||
country_218 = Niue
|
||||
country_219 = Tokelau
|
||||
country_220 = Guernsey
|
||||
country_221 = Isle of Man
|
||||
country_222 = Jersey
|
||||
country_223 = Anguilla
|
||||
country_224 = Bermuda
|
||||
country_225 = British Indian Ocean Territory
|
||||
country_226 = British Sovereign Base Areas
|
||||
country_227 = British Virgin Islands
|
||||
country_228 = Cayman Islands
|
||||
country_229 = Falkland Islands (Islas Malvinas)
|
||||
country_230 = Gibraltar
|
||||
country_231 = Montserrat
|
||||
country_232 = Pitcairn Islands
|
||||
country_233 = Saint Helena
|
||||
country_234 = South Georgia and the South Sandwich Islands
|
||||
country_235 = Turks and Caicos Islands
|
||||
country_236 = Northern Mariana Islands
|
||||
country_237 = Puerto Rico
|
||||
country_238 = American Samoa
|
||||
country_239 = Baker Island
|
||||
country_240 = Guam
|
||||
country_241 = Howland Island
|
||||
country_242 = Jarvis Island
|
||||
country_243 = Johnston Atoll
|
||||
country_244 = Kingman Reef
|
||||
country_245 = Midway Islands
|
||||
country_246 = Navassa Island
|
||||
country_247 = Palmyra Atoll
|
||||
country_248 = U.S. Virgin Islands
|
||||
country_249 = Wake Island
|
||||
country_250 = Hong Kong
|
||||
country_251 = Macau
|
||||
country_252 = Faroe Islands
|
||||
country_253 = Greenland
|
||||
country_254 = French Guiana
|
||||
country_255 = Guadeloupe
|
||||
country_256 = Martinique
|
||||
country_257 = Reunion
|
||||
country_258 = Aland
|
||||
country_259 = Aruba
|
||||
country_260 = Netherlands Antilles
|
||||
country_261 = Svalbard
|
||||
country_262 = Ascension
|
||||
country_263 = Tristan da Cunha
|
||||
country_264 = Antarctica
|
||||
country_265 = Kosovo
|
||||
country_266 = Palestinian Territories (Gaza Strip and West Bank)
|
||||
country_267 = Western Sahara
|
||||
country_268 = Australian Antarctic Territory
|
||||
country_269 = Ross Dependency
|
||||
country_270 = Peter I Island
|
||||
country_271 = Queen Maud Land
|
||||
country_272 = British Antarctic Territory
|
||||
# LiftScreen + Wizard
|
||||
Next = Next
|
||||
Previous = Previous
|
||||
Finish = Finish
|
||||
Cancel = Cancel
|
||||
|
||||
# Crudify
|
||||
Create = Create
|
||||
Save = Save
|
||||
Edit = Edit
|
||||
Delete = Delete
|
||||
delete = delete
|
||||
View = View
|
||||
List = List %s
|
||||
Created = Created
|
||||
Edited = Edited
|
||||
Deleted = Deleted
|
||||
|
||||
#OBP specific fields
|
||||
consumer.registration.nav.name=Obtener llave API
|
||||
invalid.login.credentials=Invalid Login Credentials
|
||||
invalid.username=Invalid Username: \
|
||||
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 . \
|
||||
6) Any valid email address is allowed as the Username
|
||||
|
||||
your.username.is.not.unique = Your username is not unique. Please enter a different one.
|
||||
# Those 2 messages must have the same output in order to prevent leakage of information
|
||||
user.invitation.is.already.finished = Looks like the invitation link is invalid. Still need help? Please send us a message using API Playground Support.
|
||||
your.secret.link.is.not.valid = Looks like the invitation link is invalid. Still need help? Please send us a message using API Playground Support.
|
||||
@ -131,7 +131,9 @@ jwt.use.ssl=false
|
||||
|
||||
# Paths to the SSL keystore files - has to be jks
|
||||
#keystore.path=/path/to/api.keystore.jks
|
||||
#keystore password
|
||||
#keystore.password = redf1234
|
||||
# private key password
|
||||
#keystore.passphrase = redf1234
|
||||
#keystore.alias = localhost
|
||||
#truststore.path=/path/to/api.truststore.jks
|
||||
@ -239,6 +241,9 @@ mail.smtp.port=25
|
||||
## Oauth token timeout
|
||||
token_expiration_weeks=4
|
||||
|
||||
## payment challenge answer timeout,default is 600 seconds/10 minutes
|
||||
transaction_request_challenge_ttl=600
|
||||
|
||||
|
||||
|
||||
### Sandbox
|
||||
@ -264,7 +269,7 @@ transactionRequests_enabled=true
|
||||
transactionRequests_connector=mapped
|
||||
|
||||
## Transaction Request Types that are supported on this server. Possible values might include SANDBOX_TAN, COUNTERPARTY, SEPA, FREE_FORM
|
||||
transactionRequests_supported_types=SANDBOX_TAN,COUNTERPARTY,SEPA,ACCOUNT_OTP,ACCOUNT
|
||||
transactionRequests_supported_types=SANDBOX_TAN,COUNTERPARTY,SEPA,ACCOUNT_OTP,ACCOUNT,SIMPLE
|
||||
|
||||
## Transaction request challenge threshold. Level at which challenge is created and needs to be answered.
|
||||
## The Currency is EUR unless set with transactionRequests_challenge_currency.
|
||||
@ -540,6 +545,12 @@ webui_register_consumer_success_message_email = Thank you for registering to use
|
||||
## End of webui_ section ########
|
||||
|
||||
|
||||
|
||||
# Set Locale
|
||||
language_tag = en-GB
|
||||
|
||||
|
||||
|
||||
## API Options
|
||||
apiOptions.getBranchesIsPublic = true
|
||||
apiOptions.getAtmsIsPublic = true
|
||||
@ -859,6 +870,7 @@ database_messages_scheduler_interval=3600
|
||||
|
||||
# -- SCA (Strong Customer Authentication) method for OTP challenge-------
|
||||
# ACCOUNT_OTP_INSTRUCTION_TRANSPORT=DUMMY
|
||||
# SIMPLE_OTP_INSTRUCTION_TRANSPORT=DUMMY
|
||||
# SEPA_OTP_INSTRUCTION_TRANSPORT=DUMMY
|
||||
# FREE_FORM_OTP_INSTRUCTION_TRANSPORT=DUMMY
|
||||
# COUNTERPARTY_OTP_INSTRUCTION_TRANSPORT=DUMMY
|
||||
@ -920,8 +932,8 @@ stored_procedure_connector.poolFactoryName=commons-dbcp2
|
||||
# Set whether DynamicEntity display name starts with underscore, default is true
|
||||
dynamic_entities_have_prefix=true
|
||||
|
||||
# Url prefix of dynamic endpoints, default is dynamic. e.g if set to foobar, one url can be /obp/v4.0.0/foobar/Address
|
||||
dynamic_endpoints_url_prefix=dynamic
|
||||
# Url prefix of dynamic endpoints, default is empty. e.g if set to foobar, one url can be /obp/dynamic-endpoint/foobar/Address
|
||||
dynamic_endpoints_url_prefix=
|
||||
|
||||
# --- Locking a user due to consecutively failed login attempts ------
|
||||
# Defines consecutively failed login attempts before a user is locked
|
||||
@ -1154,4 +1166,12 @@ dynamic_code_compile_validate_dependencies=[\
|
||||
]
|
||||
|
||||
## when api response json field value in the json array, the field will be excluded
|
||||
excluded.response.field.values=["String", "", null, []]
|
||||
excluded.response.field.values=["String", "", null, []]
|
||||
|
||||
# If you want to make the Lift inactivity timeout shorter than
|
||||
# the container inactivity timeout, set the inactivity timeout here
|
||||
session_inactivity_timeout_in_minutes = 30
|
||||
|
||||
# Defines redirect URL after user account is validated
|
||||
# In case is not defined default value is the home page of this application
|
||||
user_account_validated_redirect_url =
|
||||
@ -1,9 +1,13 @@
|
||||
#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
|
||||
|
||||
## REQUIREMENTS
|
||||
# see https://github.com/OpenBankProject/OBP-API#from-the-command-line for minimal memory requirements
|
||||
|
||||
####################################
|
||||
## Minimum Settings
|
||||
|
||||
|
||||
### Log level
|
||||
#logger.loglevel=INFO
|
||||
|
||||
@ -104,7 +108,14 @@ sandbox_data_import_secret=change_me
|
||||
allow_account_deletion=true
|
||||
|
||||
# This needs to be a list all the types of transaction_requests that we have tests for. Else those tests will fail
|
||||
transactionRequests_supported_types=SANDBOX_TAN,FREE_FORM,SEPA,COUNTERPARTY,TRANSFER_TO_PHONE
|
||||
transactionRequests_supported_types=SANDBOX_TAN,COUNTERPARTY,SEPA,ACCOUNT_OTP,ACCOUNT,SIMPLE
|
||||
ACCOUNT_OTP_INSTRUCTION_TRANSPORT=dummy
|
||||
SIMPLE_OTP_INSTRUCTION_TRANSPORT=dummy
|
||||
SEPA_OTP_INSTRUCTION_TRANSPORT=dummy
|
||||
FREE_FORM_OTP_INSTRUCTION_TRANSPORT=dummy
|
||||
COUNTERPARTY_OTP_INSTRUCTION_TRANSPORT=dummy
|
||||
SEPA_CREDIT_TRANSFERS_OTP_INSTRUCTION_TRANSPORT=dummy
|
||||
|
||||
|
||||
# control the create and access to public views.
|
||||
allow_public_views =true
|
||||
|
||||
@ -127,7 +127,7 @@ import code.util.{Helper, HydraUtil}
|
||||
import code.validation.JsonSchemaValidation
|
||||
import code.views.Views
|
||||
import code.views.system.{AccountAccess, ViewDefinition}
|
||||
import code.webhook.{MappedAccountWebhook, WebhookHelperActors}
|
||||
import code.webhook.{BankAccountNotificationWebhook, MappedAccountWebhook, SystemAccountNotificationWebhook, WebhookHelperActors}
|
||||
import code.webuiprops.WebUiProps
|
||||
import com.openbankproject.commons.model.ErrorMessage
|
||||
import com.openbankproject.commons.util.Functions.Implicits._
|
||||
@ -138,6 +138,7 @@ import net.liftweb.common._
|
||||
import net.liftweb.db.DBLogEntry
|
||||
import net.liftweb.http.LiftRules.DispatchPF
|
||||
import net.liftweb.http._
|
||||
import net.liftweb.http.provider.HTTPCookie
|
||||
import net.liftweb.json.Extraction
|
||||
import net.liftweb.mapper._
|
||||
import net.liftweb.sitemap.Loc._
|
||||
@ -407,7 +408,10 @@ class Boot extends MdcLoggable {
|
||||
enableVersionIfAllowed(ApiVersion.v3_0_0)
|
||||
enableVersionIfAllowed(ApiVersion.v3_1_0)
|
||||
enableVersionIfAllowed(ApiVersion.v4_0_0)
|
||||
enableVersionIfAllowed(ApiVersion.v5_0_0)
|
||||
enableVersionIfAllowed(ApiVersion.b1)
|
||||
enableVersionIfAllowed(ApiVersion.`dynamic-endpoint`)
|
||||
enableVersionIfAllowed(ApiVersion.`dynamic-entity`)
|
||||
|
||||
def enableOpenIdConnectApis = {
|
||||
// OpenIdConnect endpoint and validator
|
||||
@ -584,14 +588,33 @@ class Boot extends MdcLoggable {
|
||||
|
||||
LiftRules.explicitlyParsedSuffixes = Helpers.knownSuffixes &~ (Set("com"))
|
||||
|
||||
//set base localization to english (instead of computer default)
|
||||
Locale.setDefault(Locale.ENGLISH)
|
||||
logger.info("Current Project Locale is :" +Locale.getDefault)
|
||||
|
||||
//override locale calculated from client request with default (until we have translations)
|
||||
val locale = I18NUtil.getLocale()
|
||||
Locale.setDefault(locale)
|
||||
logger.info("Default Project Locale is :" + locale)
|
||||
|
||||
// Cookie name
|
||||
val localeCookieName = "SELECTED_LOCALE"
|
||||
LiftRules.localeCalculator = {
|
||||
case fullReq @ Full(req) => Locale.ENGLISH
|
||||
case _ => Locale.ENGLISH
|
||||
case fullReq @ Full(req) => {
|
||||
// Check against a set cookie, or the locale sent in the request
|
||||
def currentLocale : Locale = {
|
||||
S.findCookie(localeCookieName).flatMap {
|
||||
cookie => cookie.value.map(I18NUtil.computeLocale)
|
||||
} openOr locale
|
||||
}
|
||||
|
||||
// Check to see if the user explicitly requests a new locale
|
||||
// In case it's true we use that value to set up a new cookie value
|
||||
S.param("locale") match {
|
||||
case Full(requestedLocale) if requestedLocale != null => {
|
||||
val computedLocale = I18NUtil.computeLocale(requestedLocale)
|
||||
S.addCookie(HTTPCookie(localeCookieName, requestedLocale))
|
||||
computedLocale
|
||||
}
|
||||
case _ => currentLocale
|
||||
}
|
||||
}
|
||||
case _ => locale
|
||||
}
|
||||
|
||||
//for XSS vulnerability, set X-Frame-Options header as DENY
|
||||
@ -700,6 +723,7 @@ class Boot extends MdcLoggable {
|
||||
val owner = Views.views.vend.getOrCreateSystemView(SYSTEM_OWNER_VIEW_ID).isDefined
|
||||
val auditor = Views.views.vend.getOrCreateSystemView(SYSTEM_AUDITOR_VIEW_ID).isDefined
|
||||
val accountant = Views.views.vend.getOrCreateSystemView(SYSTEM_ACCOUNTANT_VIEW_ID).isDefined
|
||||
val smallPaymentVerified = Views.views.vend.getOrCreateSystemView(SYSTEM_SMALL_PAYMENT_VERIFIED_VIEW_ID).isDefined
|
||||
// Only create Firehose view if they are enabled at instance.
|
||||
val accountFirehose = if (ApiPropsWithAlias.allowAccountFirehose)
|
||||
Views.views.vend.getOrCreateSystemView(SYSTEM_FIREHOSE_VIEW_ID).isDefined
|
||||
@ -711,6 +735,7 @@ class Boot extends MdcLoggable {
|
||||
|System view ${SYSTEM_AUDITOR_VIEW_ID} exists/created at the instance: ${auditor}
|
||||
|System view ${SYSTEM_ACCOUNTANT_VIEW_ID} exists/created at the instance: ${accountant}
|
||||
|System view ${SYSTEM_FIREHOSE_VIEW_ID} exists/created at the instance: ${accountFirehose}
|
||||
|System view ${SYSTEM_SMALL_PAYMENT_VERIFIED_VIEW_ID} exists/created at the instance: ${smallPaymentVerified}
|
||||
|""".stripMargin
|
||||
logger.info(comment)
|
||||
|
||||
@ -746,6 +771,14 @@ class Boot extends MdcLoggable {
|
||||
if(HydraUtil.mirrorConsumerInHydra) {
|
||||
createHydraClients()
|
||||
}
|
||||
|
||||
Props.get("session_inactivity_timeout_in_minutes") match {
|
||||
case Full(x) if tryo(x.toLong).isDefined =>
|
||||
LiftRules.sessionInactivityTimeout.default.set(Full((x.toLong.minutes): Long))
|
||||
case _ =>
|
||||
// Do not change default value
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private def sanityCheckOPropertiesRegardingScopes() = {
|
||||
@ -835,8 +868,8 @@ class Boot extends MdcLoggable {
|
||||
*/
|
||||
private def createDefaultBankAndDefaultAccountsIfNotExisting() ={
|
||||
val defaultBankId= APIUtil.defaultBankId
|
||||
val incomingAccountId= INCOMING_ACCOUNT_ID
|
||||
val outgoingAccountId= OUTGOING_ACCOUNT_ID
|
||||
val incomingAccountId= INCOMING_SETTLEMENT_ACCOUNT_ID
|
||||
val outgoingAccountId= OUTGOING_SETTLEMENT_ACCOUNT_ID
|
||||
|
||||
MappedBank.find(By(MappedBank.permalink, defaultBankId)) match {
|
||||
case Full(b) =>
|
||||
@ -965,6 +998,8 @@ object ToSchemify {
|
||||
MappedCurrency,
|
||||
MappedTransactionRequestTypeCharge,
|
||||
MappedAccountWebhook,
|
||||
SystemAccountNotificationWebhook,
|
||||
BankAccountNotificationWebhook,
|
||||
MappedCustomerIdMapping,
|
||||
MappedProductAttribute,
|
||||
MappedConsent,
|
||||
|
||||
@ -4,6 +4,7 @@ import java.util.UUID.randomUUID
|
||||
import code.api.OBPRestHelper
|
||||
import code.api.builder.OBP_APIBuilder
|
||||
import code.api.cache.Caching
|
||||
import code.api.dynamic.endpoint.helper.{DynamicEndpointHelper, DynamicEndpoints, DynamicEntityHelper}
|
||||
import code.api.util.APIUtil._
|
||||
import code.api.util.ApiRole.{canReadDynamicResourceDocsAtOneBank, canReadResourceDoc, canReadStaticResourceDoc}
|
||||
import code.api.util.ApiTag._
|
||||
@ -14,7 +15,6 @@ import code.api.v1_4_0.{APIMethods140, JSONFactory1_4_0, OBPAPI1_4_0}
|
||||
import code.api.v2_2_0.{APIMethods220, OBPAPI2_2_0}
|
||||
import code.api.v3_0_0.OBPAPI3_0_0
|
||||
import code.api.v3_1_0.OBPAPI3_1_0
|
||||
import code.api.v4_0_0.dynamic.{DynamicEndpointHelper, DynamicEndpoints, DynamicEntityHelper}
|
||||
import code.api.v4_0_0.{APIMethods400, OBPAPI4_0_0}
|
||||
import code.apicollectionendpoint.MappedApiCollectionEndpointsProvider
|
||||
import code.util.Helper.MdcLoggable
|
||||
@ -33,8 +33,10 @@ 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.api.v5_0_0.OBPAPI5_0_0
|
||||
import code.util.Helper
|
||||
|
||||
import scala.collection.immutable.{List, Nil}
|
||||
@ -117,6 +119,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
|
||||
|
||||
val resourceDocs = requestedApiVersion match {
|
||||
case ApiVersion.`b1` => OBP_APIBuilder.allResourceDocs
|
||||
case ApiVersion.v5_0_0 => OBPAPI5_0_0.allResourceDocs
|
||||
case ApiVersion.v4_0_0 => OBPAPI4_0_0.allResourceDocs
|
||||
case ApiVersion.v3_1_0 => OBPAPI3_1_0.allResourceDocs
|
||||
case ApiVersion.v3_0_0 => OBPAPI3_0_0.allResourceDocs
|
||||
@ -134,6 +137,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
|
||||
|
||||
val versionRoutes = requestedApiVersion match {
|
||||
case ApiVersion.`b1` => OBP_APIBuilder.routes
|
||||
case ApiVersion.v5_0_0 => OBPAPI5_0_0.routes
|
||||
case ApiVersion.v4_0_0 => OBPAPI4_0_0.routes
|
||||
case ApiVersion.v3_1_0 => OBPAPI3_1_0.routes
|
||||
case ApiVersion.v3_0_0 => OBPAPI3_0_0.routes
|
||||
@ -311,7 +315,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
|
||||
}
|
||||
}
|
||||
|
||||
private def getResourceDocsObpDynamicCached(requestedApiVersion : ApiVersion,
|
||||
private def getResourceDocsObpDynamicCached(
|
||||
resourceDocTags: Option[List[ResourceDocTag]],
|
||||
partialFunctionNames: Option[List[String]],
|
||||
languageParam: Option[LanguageParam],
|
||||
@ -325,11 +329,10 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
|
||||
Caching.memoizeSyncWithProvider (Some(cacheKey.toString())) (getDynamicResourceDocsTTL second) {
|
||||
val dynamicDocs = (DynamicEntityHelper.doc ++ DynamicEndpointHelper.doc ++ DynamicEndpoints.dynamicResourceDocs)
|
||||
.filter(rd => if (bankId.isDefined) rd.createdByBankId == bankId else true)
|
||||
.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.specifiedUrl = if(it.partialFunctionName.startsWith("dynamicEntity"))Some(s"/${it.implementedInApiVersion.urlPrefix}/${ApiVersion.`dynamic-entity`}${it.requestUrl}") else Some(s"/${it.implementedInApiVersion.urlPrefix}/${ApiVersion.`dynamic-endpoint`}${it.requestUrl}")
|
||||
it
|
||||
})
|
||||
.toList
|
||||
@ -581,7 +584,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
|
||||
case _ =>
|
||||
contentParam match {
|
||||
case Some(DYNAMIC) =>
|
||||
val dynamicDocs: Box[JValue] = getResourceDocsObpDynamicCached(requestedApiVersion, tags, partialFunctions, languageParam, contentParam, cacheModifierParam, None, isVersion4OrHigher)
|
||||
val dynamicDocs: Box[JValue] = getResourceDocsObpDynamicCached(tags, partialFunctions, languageParam, contentParam, cacheModifierParam, None, isVersion4OrHigher)
|
||||
Future(dynamicDocs.map(successJsonResponse(_)))
|
||||
case Some(STATIC) =>
|
||||
val staticDocs: Box[JValue] = getStaticResourceDocsObpCached(requestedApiVersion, tags, partialFunctions, languageParam, contentParam, cacheModifierParam, isVersion4OrHigher)
|
||||
@ -632,7 +635,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
|
||||
}
|
||||
requestedApiVersion <- NewStyle.function.tryons(s"$InvalidApiVersionString $requestedApiVersionString", 400, callContext) {ApiVersionUtils.valueOf(requestedApiVersionString)}
|
||||
json <- NewStyle.function.tryons(s"$UnknownError Can not create dynamic resource docs.", 400, callContext) {
|
||||
getResourceDocsObpDynamicCached(requestedApiVersion, tags, partialFunctions, languageParam, contentParam, cacheModifierParam, Some(bankId), false).map(successJsonResponse(_)).get
|
||||
getResourceDocsObpDynamicCached(tags, partialFunctions, languageParam, contentParam, cacheModifierParam, Some(bankId), false).map(successJsonResponse(_)).get
|
||||
}
|
||||
} yield {
|
||||
(Full(json), HttpCode.`200`(callContext))
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
package code.api.ResourceDocs1_4_0
|
||||
|
||||
import java.util.Date
|
||||
|
||||
import code.api.Constant._
|
||||
import code.api.Constant
|
||||
import code.api.UKOpenBanking.v2_0_0.JSONFactory_UKOpenBanking_200
|
||||
import code.api.UKOpenBanking.v2_0_0.JSONFactory_UKOpenBanking_200.{Account, AccountBalancesUKV200, AccountInner, AccountList, Accounts, BalanceJsonUKV200, BalanceUKOpenBankingJson, BankTransactionCodeJson, CreditLineJson, DataJsonUKV200, Links, MetaBisJson, MetaInnerJson, TransactionCodeJson, TransactionInnerJson, TransactionsInnerJson, TransactionsJsonUKV200}
|
||||
import code.api.berlin.group.v1.JSONFactory_BERLIN_GROUP_1.{AccountBalanceV1, AccountBalances, AmountOfMoneyV1, ClosingBookedBody, ExpectedBody, TransactionJsonV1, TransactionsJsonV1, ViewAccount}
|
||||
import code.api.dynamic.endpoint.helper.practise.PractiseEndpoint
|
||||
import code.api.util.APIUtil.{defaultJValue, _}
|
||||
import code.api.util.ApiRole._
|
||||
import code.api.util.ExampleValue._
|
||||
@ -18,6 +18,7 @@ import code.api.v3_0_0.{LobbyJsonV330, _}
|
||||
import code.api.v3_1_0.{AccountBalanceV310, AccountsBalancesV310Json, BadLoginStatusJson, ContactDetailsJson, CustomerWithAttributesJsonV310, InviteeJson, ObpApiLoopbackJson, PhysicalCardWithAttributesJsonV310, PutUpdateCustomerEmailJsonV310, _}
|
||||
import code.api.v4_0_0.{AccountMinimalJson400, BankAttributeBankResponseJsonV400, CustomerMinimalJsonV400, FastFirehoseAccountsJsonV400, PostHistoricalTransactionAtBankJson, _}
|
||||
import code.api.v3_1_0.{AccountBalanceV310, AccountsBalancesV310Json, BadLoginStatusJson, ContactDetailsJson, InviteeJson, ObpApiLoopbackJson, PhysicalCardWithAttributesJsonV310, PutUpdateCustomerEmailJsonV310, _}
|
||||
import code.api.v5_0_0._
|
||||
import code.branches.Branches.{Branch, DriveUpString, LobbyString}
|
||||
import code.consent.ConsentStatus
|
||||
import code.connectormethod.{JsonConnectorMethod, JsonConnectorMethodMethodBody}
|
||||
@ -33,8 +34,8 @@ import com.openbankproject.commons.model.enums.{AttributeCategory, CardAttribute
|
||||
import com.openbankproject.commons.model.{UserAuthContextUpdateStatus, ViewBasic, _}
|
||||
import com.openbankproject.commons.util.{ApiVersion, FieldNameApiVersions, ReflectUtils, RequiredArgs, RequiredInfo}
|
||||
import net.liftweb.json
|
||||
import java.net.URLEncoder
|
||||
|
||||
import java.net.URLEncoder
|
||||
import code.endpointMapping.EndpointMappingCommons
|
||||
|
||||
import scala.collection.immutable.List
|
||||
@ -178,7 +179,10 @@ object SwaggerDefinitionsJSON {
|
||||
"can_see_bank_account_credit_limit",
|
||||
//v400
|
||||
"can_create_direct_debit",
|
||||
"can_create_standing_order"
|
||||
"can_create_standing_order",
|
||||
|
||||
//payments
|
||||
"can_add_transaction_request_to_any_account"
|
||||
)
|
||||
)
|
||||
|
||||
@ -435,7 +439,7 @@ object SwaggerDefinitionsJSON {
|
||||
other_bank_routing_scheme= counterpartyOtherBankRoutingSchemeExample.value,
|
||||
other_bank_routing_address= counterpartyOtherBankRoutingAddressExample.value,
|
||||
is_beneficiary= true,
|
||||
future_date = Some("20881230")
|
||||
future_date = Some(futureDateExample.value)
|
||||
)
|
||||
|
||||
val adapterImplementationJson = AdapterImplementationJson("CORE",3)
|
||||
@ -1168,7 +1172,7 @@ object SwaggerDefinitionsJSON {
|
||||
end_date = DateWithDayExampleObject,
|
||||
challenge = transactionRequestChallenge,
|
||||
charge = transactionRequestCharge,
|
||||
charge_policy = "String",
|
||||
charge_policy = chargePolicyExample.value,
|
||||
counterparty_id = counterpartyIdSwagger,
|
||||
name = counterpartyNameExample.value,
|
||||
this_bank_id = bankIdSwagger,
|
||||
@ -2066,23 +2070,23 @@ object SwaggerDefinitionsJSON {
|
||||
counterpartyIdJson,
|
||||
amountOfMoneyJsonV121,
|
||||
"A description for the transaction to the counterparty",
|
||||
"SHARED",
|
||||
Some("20881230")
|
||||
chargePolicyExample.value,
|
||||
Some(futureDateExample.value)
|
||||
)
|
||||
|
||||
val transactionRequestBodySEPAJSON = TransactionRequestBodySEPAJSON(
|
||||
amountOfMoneyJsonV121,
|
||||
ibanJson,
|
||||
"This is a SEPA Transaction Request",
|
||||
"SHARED",
|
||||
Some("20881230")
|
||||
chargePolicyExample.value,
|
||||
Some(futureDateExample.value)
|
||||
)
|
||||
val transactionRequestBodySEPAJsonV400 = TransactionRequestBodySEPAJsonV400(
|
||||
amountOfMoneyJsonV121,
|
||||
ibanJson,
|
||||
description = "This is a SEPA Transaction Request",
|
||||
charge_policy = "SHARED",
|
||||
future_date = Some("20881230"),
|
||||
charge_policy = chargePolicyExample.value,
|
||||
future_date = Some(futureDateExample.value),
|
||||
reasons = Some(List(
|
||||
TransactionRequestReasonJsonV400(
|
||||
code = "410",
|
||||
@ -3505,13 +3509,15 @@ object SwaggerDefinitionsJSON {
|
||||
key = "CUSTOMER_NUMBER",
|
||||
value = "78987432"
|
||||
)
|
||||
|
||||
val postUserAuthContextUpdateJsonV310 = PostUserAuthContextUpdateJsonV310(answer = "123")
|
||||
|
||||
val userAuthContextJson = UserAuthContextJson(
|
||||
user_auth_context_id = "613c83ea-80f9-4560-8404-b9cd4ec42a7f",
|
||||
user_id = ExampleValue.userIdExample.value,
|
||||
key = "CUSTOMER_NUMBER",
|
||||
value = "78987432",
|
||||
timeStamp = parseDate(timeStampExample.value).getOrElse(sys.error("timeStampExample.value is not validate date format."))
|
||||
time_stamp = parseDate(timeStampExample.value).getOrElse(sys.error("timeStampExample.value is not validate date format."))
|
||||
)
|
||||
|
||||
val userAuthContextUpdateJson = UserAuthContextUpdateJson(
|
||||
@ -3896,7 +3902,7 @@ object SwaggerDefinitionsJSON {
|
||||
posted = DateWithSecondsExampleString,
|
||||
completed= DateWithSecondsExampleString,
|
||||
`type`= SANDBOX_TAN.toString,
|
||||
charge_policy= "SHARED"
|
||||
charge_policy= chargePolicyExample.value
|
||||
)
|
||||
val postHistoricalTransactionAtBankJson = PostHistoricalTransactionAtBankJson(
|
||||
from_account_id = "",
|
||||
@ -3906,7 +3912,7 @@ object SwaggerDefinitionsJSON {
|
||||
posted = DateWithSecondsExampleString,
|
||||
completed= DateWithSecondsExampleString,
|
||||
`type`= SANDBOX_TAN.toString,
|
||||
charge_policy= "SHARED"
|
||||
charge_policy = chargePolicyExample.value
|
||||
)
|
||||
|
||||
val postHistoricalTransactionResponseJson = PostHistoricalTransactionResponseJson(
|
||||
@ -3918,7 +3924,7 @@ object SwaggerDefinitionsJSON {
|
||||
posted = DateWithMsExampleObject,
|
||||
completed= DateWithMsExampleObject,
|
||||
transaction_request_type= SANDBOX_TAN.toString,
|
||||
charge_policy= "SHARED"
|
||||
charge_policy = chargePolicyExample.value
|
||||
)
|
||||
|
||||
val viewBasicCommons = ViewBasic(
|
||||
@ -4318,6 +4324,27 @@ object SwaggerDefinitionsJSON {
|
||||
challenges = List(challengeJsonV400),
|
||||
charge = transactionRequestChargeJsonV200
|
||||
)
|
||||
|
||||
val postSimpleCounterpartyJson400 = PostSimpleCounterpartyJson400(
|
||||
name = counterpartyNameExample.value,
|
||||
description = transactionDescriptionExample.value,
|
||||
other_account_routing_scheme = counterpartyOtherAccountRoutingSchemeExample.value,
|
||||
other_account_routing_address = counterpartyOtherAccountRoutingAddressExample.value,
|
||||
other_account_secondary_routing_scheme = counterpartyOtherAccountSecondaryRoutingSchemeExample.value,
|
||||
other_account_secondary_routing_address = counterpartyOtherAccountSecondaryRoutingAddressExample.value,
|
||||
other_bank_routing_scheme = counterpartyOtherBankRoutingSchemeExample.value,
|
||||
other_bank_routing_address = counterpartyOtherBankRoutingAddressExample.value,
|
||||
other_branch_routing_scheme = counterpartyOtherBranchRoutingSchemeExample.value,
|
||||
other_branch_routing_address = counterpartyOtherBranchRoutingAddressExample.value
|
||||
)
|
||||
|
||||
val transactionRequestBodySimpleJsonV400 = TransactionRequestBodySimpleJsonV400(
|
||||
to= postSimpleCounterpartyJson400,
|
||||
amountOfMoneyJsonV121,
|
||||
descriptionExample.value,
|
||||
chargePolicyExample.value,
|
||||
Some(futureDateExample.value)
|
||||
)
|
||||
|
||||
val postApiCollectionJson400 = PostApiCollectionJson400(apiCollectionNameExample.value, true, Some(descriptionExample.value))
|
||||
|
||||
@ -4570,7 +4597,7 @@ object SwaggerDefinitionsJSON {
|
||||
messages = List(customerMessageJsonV400)
|
||||
)
|
||||
|
||||
val requestRootJsonClass = dynamic.practise.PractiseEndpoint.RequestRootJsonClass(name = nameExample.value, age=ageExample.value.toLong, Nil)
|
||||
val requestRootJsonClass = PractiseEndpoint.RequestRootJsonClass(name = nameExample.value, age=ageExample.value.toLong, Nil)
|
||||
|
||||
val entitlementJsonV400 = EntitlementJsonV400(
|
||||
entitlement_id = entitlementIdExample.value,
|
||||
@ -4583,6 +4610,48 @@ object SwaggerDefinitionsJSON {
|
||||
list = List(entitlementJsonV400)
|
||||
)
|
||||
|
||||
val accountNotificationWebhookPostJson = AccountNotificationWebhookPostJson(
|
||||
url = "https://localhost.openbankproject.com",
|
||||
http_method = "POST",
|
||||
http_protocol = "HTTP/1.1"
|
||||
)
|
||||
|
||||
val systemAccountNotificationWebhookJson = SystemAccountNotificationWebhookJson(
|
||||
webhook_id = "fc23a7e2-7dd2-4bdf-a0b4-ae31232a4762",
|
||||
trigger_name = ApiTrigger.onCreateTransaction.toString(),
|
||||
url = "https://localhost.openbankproject.com",
|
||||
http_method = "POST",
|
||||
http_protocol = "HTTP/1.1",
|
||||
created_by_user_id = ExampleValue.userIdExample.value
|
||||
)
|
||||
|
||||
val bankAccountNotificationWebhookJson = BankAccountNotificationWebhookJson(
|
||||
webhook_id = "fc23a7e2-7dd2-4bdf-a0b4-ae31232a4762",
|
||||
bank_id = bankIdExample.value,
|
||||
trigger_name = ApiTrigger.onCreateTransaction.toString(),
|
||||
url = "https://localhost.openbankproject.com",
|
||||
http_method = "POST",
|
||||
http_protocol = "HTTP/1.1",
|
||||
created_by_user_id = ExampleValue.userIdExample.value
|
||||
)
|
||||
|
||||
val userAuthContextJsonV500 = UserAuthContextJsonV500(
|
||||
user_auth_context_id = "613c83ea-80f9-4560-8404-b9cd4ec42a7f",
|
||||
user_id = ExampleValue.userIdExample.value,
|
||||
key = "CUSTOMER_NUMBER",
|
||||
value = "78987432",
|
||||
time_stamp = parseDate(timeStampExample.value).getOrElse(sys.error("timeStampExample.value is not validate date format.")),
|
||||
consumer_id = consumerIdExample.value
|
||||
)
|
||||
|
||||
val userAuthContextUpdateJsonV500 = UserAuthContextUpdateJsonV500(
|
||||
user_auth_context_update_id = "613c83ea-80f9-4560-8404-b9cd4ec42a7f",
|
||||
user_id = ExampleValue.userIdExample.value,
|
||||
key = "CUSTOMER_NUMBER",
|
||||
value = "78987432",
|
||||
status = UserAuthContextUpdateStatus.INITIATED.toString,
|
||||
consumer_id = consumerIdExample.value
|
||||
)
|
||||
|
||||
//The common error or success format.
|
||||
//Just some helper format to use in Json
|
||||
|
||||
@ -26,6 +26,7 @@ object Constant extends MdcLoggable {
|
||||
final val SYSTEM_AUDITOR_VIEW_ID = "auditor"
|
||||
final val SYSTEM_ACCOUNTANT_VIEW_ID = "accountant"
|
||||
final val SYSTEM_FIREHOSE_VIEW_ID = "firehose"
|
||||
final val SYSTEM_SMALL_PAYMENT_VERIFIED_VIEW_ID = "SmallPaymentVerified"
|
||||
final val SYSTEM_READ_ACCOUNTS_BASIC_VIEW_ID = "ReadAccountsBasic"
|
||||
final val SYSTEM_READ_ACCOUNTS_DETAIL_VIEW_ID = "ReadAccountsDetail"
|
||||
final val SYSTEM_READ_BALANCES_VIEW_ID = "ReadBalances"
|
||||
@ -38,8 +39,9 @@ object Constant extends MdcLoggable {
|
||||
final val SYSTEM_READ_TRANSACTIONS_BERLIN_GROUP_VIEW_ID = "ReadTransactionsBerlinGroup"
|
||||
|
||||
//These are the default incoming and outgoing account ids. we will create both during the boot.scala.
|
||||
final val INCOMING_ACCOUNT_ID= "OBP_DEFAULT_INCOMING_ACCOUNT_ID"
|
||||
final val OUTGOING_ACCOUNT_ID= "OBP_DEFAULT_OUTGOING_ACCOUNT_ID"
|
||||
final val INCOMING_SETTLEMENT_ACCOUNT_ID = "OBP-INCOMING-SETTLEMENT-ACCOUNT"
|
||||
final val OUTGOING_SETTLEMENT_ACCOUNT_ID = "OBP-OUTGOING-SETTLEMENT-ACCOUNT"
|
||||
final val ALL_CONSUMERS = "ALL_CONSUMERS"
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,211 @@
|
||||
package code.api.dynamic.endpoint
|
||||
|
||||
import code.DynamicData.{DynamicData, DynamicDataProvider}
|
||||
import code.api.dynamic.endpoint.helper.{DynamicEndpointHelper, DynamicEntityHelper, DynamicEntityInfo, EntityName, MockResponseHolder}
|
||||
import code.api.dynamic.endpoint.helper.DynamicEndpointHelper.DynamicReq
|
||||
import code.api.dynamic.endpoint.helper.MockResponseHolder
|
||||
import code.api.util.APIUtil.{fullBoxOrException, _}
|
||||
import code.api.util.ErrorMessages._
|
||||
import code.api.util.NewStyle.HttpCode
|
||||
import code.api.util._
|
||||
import code.endpointMapping.EndpointMappingCommons
|
||||
import code.transactionrequests.TransactionRequests.TransactionRequestTypes.{apply => _}
|
||||
import code.util.Helper
|
||||
import com.openbankproject.commons.ExecutionContext.Implicits.global
|
||||
import com.openbankproject.commons.model._
|
||||
import com.openbankproject.commons.model.enums.DynamicEntityOperation._
|
||||
import com.openbankproject.commons.model.enums._
|
||||
import com.openbankproject.commons.util.{ApiVersion, JsonUtils}
|
||||
import net.liftweb.common._
|
||||
import net.liftweb.http.rest.RestHelper
|
||||
import net.liftweb.http.{JsonResponse, Req}
|
||||
import net.liftweb.json.JsonAST.JValue
|
||||
import net.liftweb.json.JsonDSL._
|
||||
import net.liftweb.json._
|
||||
import net.liftweb.util.StringHelpers
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
|
||||
import scala.collection.immutable.List
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.concurrent.Future
|
||||
|
||||
trait APIMethodsDynamicEndpoint {
|
||||
self: RestHelper =>
|
||||
|
||||
val ImplementationsDynamicEndpoint = new ImplementationsDynamicEndpoint()
|
||||
|
||||
class ImplementationsDynamicEndpoint {
|
||||
|
||||
val implementedInApiVersion = ApiVersion.`dynamic-endpoint`
|
||||
|
||||
private val staticResourceDocs = ArrayBuffer[ResourceDoc]()
|
||||
|
||||
// createDynamicEntityDoc and updateDynamicEntityDoc are dynamic, So here dynamic create resourceDocs
|
||||
def resourceDocs = staticResourceDocs
|
||||
|
||||
val apiRelations = ArrayBuffer[ApiRelation]()
|
||||
val codeContext = CodeContext(staticResourceDocs, apiRelations)
|
||||
|
||||
private def unboxResult[T: Manifest](box: Box[T], entityName: String): T = {
|
||||
if (box.isInstanceOf[Failure]) {
|
||||
val failure = box.asInstanceOf[Failure]
|
||||
// change the internal db column name 'dynamicdataid' to entity's id name
|
||||
val msg = failure.msg.replace(DynamicData.DynamicDataId.dbColumnName, StringUtils.uncapitalize(entityName) + "Id")
|
||||
val changedMsgFailure = failure.copy(msg = s"$InternalServerError $msg")
|
||||
fullBoxOrException[T](changedMsgFailure)
|
||||
}
|
||||
|
||||
box.openOrThrowException("impossible error")
|
||||
}
|
||||
|
||||
lazy val dynamicEndpoint: OBPEndpoint = {
|
||||
case DynamicReq(url, json, method, params, pathParams, role, operationId, mockResponse, bankId) => { cc =>
|
||||
// process before authentication interceptor, get intercept result
|
||||
val resourceDoc = DynamicEndpointHelper.doc.find(_.operationId == operationId)
|
||||
val callContext = cc.copy(operationId = Some(operationId), resourceDocument = resourceDoc)
|
||||
val beforeInterceptResult: Box[JsonResponse] = beforeAuthenticateInterceptResult(Option(callContext), operationId)
|
||||
if (beforeInterceptResult.isDefined) beforeInterceptResult
|
||||
else for {
|
||||
(Full(u), callContext) <- authenticatedAccess(callContext) // Inject operationId into Call Context. It's used by Rate Limiting.
|
||||
_ <- NewStyle.function.hasEntitlement(bankId.getOrElse(""), u.userId, role, callContext)
|
||||
|
||||
// validate request json payload
|
||||
httpRequestMethod = cc.verb
|
||||
path = StringUtils.substringAfter(cc.url, DynamicEndpointHelper.urlPrefix)
|
||||
|
||||
// process after authentication interceptor, get intercept result
|
||||
jsonResponse: Box[ErrorMessage] = afterAuthenticateInterceptResult(callContext, operationId).collect({
|
||||
case JsonResponseExtractor(message, code) => ErrorMessage(code, message)
|
||||
})
|
||||
_ <- Helper.booleanToFuture(failMsg = jsonResponse.map(_.message).orNull, failCode = jsonResponse.map(_.code).openOr(400), cc = callContext) {
|
||||
jsonResponse.isEmpty
|
||||
}
|
||||
(box, callContext) <- if (DynamicEndpointHelper.isDynamicEntityResponse(url)) {
|
||||
for {
|
||||
(endpointMapping, callContext) <- if (DynamicEndpointHelper.isDynamicEntityResponse(url)) {
|
||||
NewStyle.function.getEndpointMappingByOperationId(bankId, operationId, cc.callContext)
|
||||
} else {
|
||||
Future.successful((EndpointMappingCommons(None, "", "", "", None), callContext))
|
||||
}
|
||||
requestMappingString = endpointMapping.requestMapping
|
||||
requestMappingJvalue = net.liftweb.json.parse(requestMappingString)
|
||||
responseMappingString = endpointMapping.responseMapping
|
||||
responseMappingJvalue = net.liftweb.json.parse(responseMappingString)
|
||||
|
||||
responseBody <- if (method.value.equalsIgnoreCase("get")) {
|
||||
for {
|
||||
(entityName, entityIdKey, entityIdValueFromUrl) <- NewStyle.function.tryons(s"$InvalidEndpointMapping `response_mapping` must be linked to at least one valid dynamic entity!", 400, cc.callContext) {
|
||||
DynamicEndpointHelper.getEntityNameKeyAndValue(responseMappingString, pathParams)
|
||||
}
|
||||
dynamicData <- Future {
|
||||
DynamicDataProvider.connectorMethodProvider.vend.getAll(entityName)
|
||||
}
|
||||
dynamicJsonData = JArray(dynamicData.map(it => net.liftweb.json.parse(it.dataJson)).map(_.asInstanceOf[JObject]))
|
||||
// //We only get the value, but not sure the field name of it.
|
||||
// // we can get the field name from the mapping: `primary_query_key`
|
||||
// //requestBodyMapping --> Convert `RequestJson` --> `DynamicEntity Model.`
|
||||
// targetRequestBody = JsonUtils.buildJson(json, requestBodySchemeJvalue)
|
||||
// requestBody = targetRequestBody match {
|
||||
// case j@JObject(_) => Some(j)
|
||||
// case _ => None
|
||||
// }
|
||||
result = if (method.value.equalsIgnoreCase("get") && entityIdValueFromUrl.isDefined) {
|
||||
DynamicEndpointHelper.getObjectByKeyValuePair(dynamicJsonData, entityIdKey, entityIdValueFromUrl.get)
|
||||
} else {
|
||||
val newParams = DynamicEndpointHelper.convertToMappingQueryParams(responseMappingJvalue, params)
|
||||
DynamicEndpointHelper.getObjectsByParams(dynamicJsonData, newParams)
|
||||
}
|
||||
responseBodyScheme = DynamicEndpointHelper.prepareMappingFields(responseMappingJvalue)
|
||||
responseBody = JsonUtils.buildJson(result, responseBodyScheme)
|
||||
} yield {
|
||||
responseBody
|
||||
}
|
||||
} else if (method.value.equalsIgnoreCase("post")) {
|
||||
for {
|
||||
(entityName, entityIdKey, entityIdValueFromUrl) <- NewStyle.function.tryons(s"$InvalidEndpointMapping `response_mapping` must be linked to at least one valid dynamic entity!", 400, cc.callContext) {
|
||||
DynamicEndpointHelper.getEntityNameKeyAndValue(responseMappingString, pathParams)
|
||||
}
|
||||
//build the entity body according to the request json and mapping
|
||||
entityBody = JsonUtils.buildJson(json, requestMappingJvalue)
|
||||
(box, _) <- NewStyle.function.invokeDynamicConnector(CREATE, entityName, Some(entityBody.asInstanceOf[JObject]), None, None, None, Some(cc))
|
||||
singleObject: JValue = unboxResult(box.asInstanceOf[Box[JValue]], entityName)
|
||||
responseBodyScheme = DynamicEndpointHelper.prepareMappingFields(responseMappingJvalue)
|
||||
responseBody = JsonUtils.buildJson(singleObject, responseBodyScheme)
|
||||
} yield {
|
||||
responseBody
|
||||
}
|
||||
} else if (method.value.equalsIgnoreCase("delete")) {
|
||||
for {
|
||||
(entityName, entityIdKey, entityIdValueFromUrl) <- NewStyle.function.tryons(s"$InvalidEndpointMapping `response_mapping` must be linked to at least one valid dynamic entity!", 400, cc.callContext) {
|
||||
DynamicEndpointHelper.getEntityNameKeyAndValue(responseMappingString, pathParams)
|
||||
}
|
||||
dynamicData = DynamicDataProvider.connectorMethodProvider.vend.getAll(entityName)
|
||||
dynamicJsonData = JArray(dynamicData.map(it => net.liftweb.json.parse(it.dataJson)).map(_.asInstanceOf[JObject]))
|
||||
entityObject = DynamicEndpointHelper.getObjectByKeyValuePair(dynamicJsonData, entityIdKey, entityIdValueFromUrl.get)
|
||||
isDeleted <- NewStyle.function.tryons(s"$InvalidEndpointMapping `response_mapping` must be linked to at least one valid dynamic entity!", 400, cc.callContext) {
|
||||
val entityIdName = DynamicEntityHelper.createEntityId(entityName)
|
||||
val entityIdValue = (entityObject \ entityIdName).asInstanceOf[JString].s
|
||||
DynamicDataProvider.connectorMethodProvider.vend.delete(entityName, entityIdValue).head
|
||||
}
|
||||
} yield {
|
||||
JBool(isDeleted)
|
||||
}
|
||||
} else if (method.value.equalsIgnoreCase("put")) {
|
||||
for {
|
||||
(entityName, entityIdKey, entityIdValueFromUrl) <- NewStyle.function.tryons(s"$InvalidEndpointMapping `response_mapping` must be linked to at least one valid dynamic entity!", 400, cc.callContext) {
|
||||
DynamicEndpointHelper.getEntityNameKeyAndValue(responseMappingString, pathParams)
|
||||
}
|
||||
dynamicData = DynamicDataProvider.connectorMethodProvider.vend.getAll(entityName)
|
||||
dynamicJsonData = JArray(dynamicData.map(it => net.liftweb.json.parse(it.dataJson)).map(_.asInstanceOf[JObject]))
|
||||
entityObject = DynamicEndpointHelper.getObjectByKeyValuePair(dynamicJsonData, entityIdKey, entityIdValueFromUrl.get)
|
||||
_ <- NewStyle.function.tryons(s"$InvalidEndpointMapping `response_mapping` must be linked to at least one valid dynamic entity!", 400, cc.callContext) {
|
||||
val entityIdName = DynamicEntityHelper.createEntityId(entityName)
|
||||
val entityIdValue = (entityObject \ entityIdName).asInstanceOf[JString].s
|
||||
DynamicDataProvider.connectorMethodProvider.vend.delete(entityName, entityIdValue).head
|
||||
}
|
||||
entityBody = JsonUtils.buildJson(json, requestMappingJvalue)
|
||||
(box, _) <- NewStyle.function.invokeDynamicConnector(CREATE, entityName, Some(entityBody.asInstanceOf[JObject]), None, None, None, Some(cc))
|
||||
singleObject: JValue = unboxResult(box.asInstanceOf[Box[JValue]], entityName)
|
||||
responseBodyScheme = DynamicEndpointHelper.prepareMappingFields(responseMappingJvalue)
|
||||
responseBody = JsonUtils.buildJson(singleObject, responseBodyScheme)
|
||||
} yield {
|
||||
responseBody
|
||||
}
|
||||
} else {
|
||||
NewStyle.function.tryons(s"$InvalidEndpointMapping `request_mapping` must be linked to at least one valid dynamic entity!", 400, cc.callContext) {
|
||||
DynamicEndpointHelper.getEntityNameKeyAndValue(responseMappingString, pathParams)
|
||||
}
|
||||
throw new RuntimeException(s"$NotImplemented We only support the Http Methods GET and POST . The current method is: ${method.value}")
|
||||
}
|
||||
} yield {
|
||||
(Full(("code", 200) ~ ("value", responseBody)), callContext)
|
||||
}
|
||||
} else {
|
||||
MockResponseHolder.init(mockResponse) { // if target url domain is `obp_mock`, set mock response to current thread
|
||||
NewStyle.function.dynamicEndpointProcess(url, json, method, params, pathParams, callContext)
|
||||
}
|
||||
}
|
||||
} yield {
|
||||
box match {
|
||||
case Full(v) =>
|
||||
val code = (v \ "code").asInstanceOf[JInt].num.toInt
|
||||
(v \ "value", callContext.map(_.copy(httpCode = Some(code))))
|
||||
|
||||
case e: Failure =>
|
||||
val changedMsgFailure = e.copy(msg = s"$InternalServerError ${e.msg}")
|
||||
fullBoxOrException[JValue](changedMsgFailure)
|
||||
??? // will not execute to here, Because the failure message is thrown by upper line.
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object APIMethodsDynamicEndpoint extends RestHelper with APIMethodsDynamicEndpoint {
|
||||
lazy val newStyleEndpoints: List[(String, String)] = ImplementationsDynamicEndpoint.resourceDocs.map {
|
||||
rd => (rd.partialFunctionName, rd.implementedInApiVersion.toString())
|
||||
}.toList
|
||||
}
|
||||
|
||||
@ -0,0 +1,89 @@
|
||||
/**
|
||||
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.api.dynamic.endpoint
|
||||
|
||||
import APIMethodsDynamicEndpoint.ImplementationsDynamicEndpoint
|
||||
import code.api.OBPRestHelper
|
||||
import code.api.dynamic.endpoint.helper.DynamicEndpoints
|
||||
import code.api.util.APIUtil.OBPEndpoint
|
||||
import code.api.util.{APIUtil, VersionedOBPApis}
|
||||
import code.api.v5_0_0.OBPAPI5_0_0.{allResourceDocs, apiPrefix, registerRoutes, routes}
|
||||
import code.util.Helper.MdcLoggable
|
||||
import com.openbankproject.commons.util.ApiVersion
|
||||
import net.liftweb.common.{Box, Full}
|
||||
import net.liftweb.http.{LiftResponse, PlainTextResponse}
|
||||
import org.apache.http.HttpStatus
|
||||
|
||||
/*
|
||||
This file defines which endpoints from all the versions are available in v4.0.0
|
||||
*/
|
||||
object OBPAPIDynamicEndpoint extends OBPRestHelper with MdcLoggable with VersionedOBPApis{
|
||||
|
||||
val version : ApiVersion = ApiVersion.`dynamic-endpoint`
|
||||
|
||||
val versionStatus = "BLEEDING-EDGE" // TODO this should be a property of ApiVersion.
|
||||
|
||||
// if old version ResourceDoc objects have the same name endpoint with new version, omit old version ResourceDoc.
|
||||
def allResourceDocs = collectResourceDocs(ImplementationsDynamicEndpoint.resourceDocs)
|
||||
|
||||
val routes : List[OBPEndpoint] = List(APIUtil.dynamicEndpointStub,
|
||||
//This is for the dynamic endpoints which are created by dynamic swagger files
|
||||
ImplementationsDynamicEndpoint.dynamicEndpoint,
|
||||
/**
|
||||
* Here is the place where we register the dynamicEndpoint, all the dynamic resource docs endpoints are here.
|
||||
* Actually, we only register one endpoint for all the dynamic resource docs endpoints.
|
||||
* For Liftweb, it just need to handle one endpoint,
|
||||
* all the router functionalities are in OBP code.
|
||||
* details: please also check code/api/vDynamic/dynamic/DynamicEndpoints.findEndpoint method
|
||||
* NOTE: this must be the last one endpoint to register into Liftweb
|
||||
* Because firstly, Liftweb should look for the static endpoints --> then the dynamic ones.
|
||||
* This is for the dynamic endpoints which are createdy by dynamic resourceDocs
|
||||
*/
|
||||
DynamicEndpoints.dynamicEndpoint
|
||||
)
|
||||
|
||||
routes.map(endpoint => oauthServe(apiPrefix{endpoint}, None))
|
||||
|
||||
logger.info(s"version $version has been run! There are ${routes.length} routes.")
|
||||
// specified response for OPTIONS request.
|
||||
private val corsResponse: Box[LiftResponse] = Full{
|
||||
val corsHeaders = List(
|
||||
"Access-Control-Allow-Origin" -> "*",
|
||||
"Access-Control-Allow-Methods" -> "GET, POST, OPTIONS, PUT, PATCH, DELETE",
|
||||
"Access-Control-Allow-Headers" -> "*",
|
||||
"Access-Control-Allow-Credentials" -> "true",
|
||||
"Access-Control-Max-Age" -> "1728000" //Tell client that this pre-flight info is valid for 20 days
|
||||
)
|
||||
PlainTextResponse("", corsHeaders, HttpStatus.SC_NO_CONTENT)
|
||||
}
|
||||
/*
|
||||
* process OPTIONS http request, just return no content and status is 204
|
||||
*/
|
||||
this.serve({
|
||||
case req if req.requestType.method == "OPTIONS" => corsResponse
|
||||
})
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package code.api.v4_0_0.dynamic
|
||||
package code.api.dynamic.endpoint.helper
|
||||
|
||||
import code.api.util.APIUtil.{OBPEndpoint, OBPReturnType, futureToBoxedResponse, scalaFutureToLaFuture}
|
||||
import code.api.util.DynamicUtil.{Sandbox, Validation}
|
||||
@ -1,4 +1,4 @@
|
||||
package code.api.v4_0_0.dynamic
|
||||
package code.api.dynamic.endpoint.helper
|
||||
|
||||
import akka.http.scaladsl.model.{HttpMethods, HttpMethod => AkkaHttpMethod}
|
||||
import code.DynamicData.{DynamicDataProvider, DynamicDataT}
|
||||
@ -7,7 +7,7 @@ import code.api.util.APIUtil.{BigDecimalBody, BigIntBody, BooleanBody, DoubleBod
|
||||
import code.api.util.ApiTag._
|
||||
import code.api.util.ErrorMessages.{DynamicDataNotFound, InvalidUrlParameters, UnknownError, UserHasMissingRoles, UserNotLoggedIn}
|
||||
import code.api.util.{APIUtil, ApiRole, ApiTag, CustomJsonFormats, NewStyle}
|
||||
import com.openbankproject.commons.util.ApiVersion
|
||||
import com.openbankproject.commons.util.{ApiShortVersions, ApiStandards, ApiVersion}
|
||||
import com.openbankproject.commons.util.Functions.Memo
|
||||
import io.swagger.v3.oas.models.PathItem.HttpMethod
|
||||
import io.swagger.v3.oas.models.media._
|
||||
@ -50,7 +50,7 @@ object DynamicEndpointHelper extends RestHelper {
|
||||
/**
|
||||
* dynamic endpoints url prefix
|
||||
*/
|
||||
val urlPrefix = APIUtil.getPropsValue("dynamic_endpoints_url_prefix", "dynamic")
|
||||
val urlPrefix = APIUtil.getPropsValue("dynamic_endpoints_url_prefix", "")
|
||||
private val implementedInApiVersion = ApiVersion.v4_0_0
|
||||
private val IsDynamicEntityUrl = """.*dynamic_entity.*"""
|
||||
private val IsMockUrlString = """.*obp_mock(?::\d+)?.*"""
|
||||
@ -173,15 +173,18 @@ object DynamicEndpointHelper extends RestHelper {
|
||||
* @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)], 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`
|
||||
|
||||
val requestUri = r.request.uri //eg: `/obp/dynamic-endpoint/fashion-brand-list/BRAND_ID`
|
||||
val partPath = r.path.partPath //eg: List("fashion-brand-list","BRAND_ID"), the dynamic is from OBP URL, not in the partPath now.
|
||||
|
||||
if (!testResponse_?(r) || !requestUri.startsWith(s"/${ApiStandards.obp.toString}/${ApiShortVersions.`dynamic-endpoint`.toString}"+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.
|
||||
else {
|
||||
val akkaHttpMethod = HttpMethods.getForKeyCaseInsensitive(r.requestType.method).get
|
||||
val httpMethod = HttpMethod.valueOf(r.requestType.method)
|
||||
val urlQueryParameters = r.params
|
||||
// url that match original swagger endpoint.
|
||||
val url = partPath.tail.mkString("/", "/", "") // eg: --> /feature-test
|
||||
val url = partPath.mkString("/", "/", "") // eg: --> /feature-test
|
||||
val foundDynamicEndpoint: Option[(String, String, Int, ResourceDoc, Option[String])] = dynamicEndpointInfos
|
||||
.map(_.findDynamicEndpoint(httpMethod, url))
|
||||
.collectFirst {
|
||||
@ -195,7 +198,7 @@ object DynamicEndpointHelper extends RestHelper {
|
||||
val pathParams: Map[String, String] = if(endpointUrl == url) {
|
||||
Map.empty[String, String]
|
||||
} else {
|
||||
val tuples: Array[(String, String)] = StringUtils.split(endpointUrl, "/").zip(partPath.tail)
|
||||
val tuples: Array[(String, String)] = StringUtils.split(endpointUrl, "/").zip(partPath)
|
||||
tuples.collect {
|
||||
case (ExpressionRegx(name), value) => name->value
|
||||
}.toMap
|
||||
@ -1,8 +1,10 @@
|
||||
package code.api.v4_0_0.dynamic
|
||||
package code.api.dynamic.endpoint.helper
|
||||
|
||||
import code.api.dynamic.endpoint.helper.practise.{DynamicEndpointCodeGenerator, PractiseEndpointGroup}
|
||||
import code.api.dynamic.endpoint.helper.practise.PractiseEndpointGroup
|
||||
import code.api.util.DynamicUtil.{Sandbox, Validation}
|
||||
import code.api.util.APIUtil.{BooleanBody, DoubleBody, EmptyBody, LongBody, OBPEndpoint, PrimaryDataBody, ResourceDoc, StringBody, getDisabledEndpointOperationIds}
|
||||
import code.api.util.{CallContext, DynamicUtil}
|
||||
import code.api.v4_0_0.dynamic.practise.{DynamicEndpointCodeGenerator, PractiseEndpointGroup}
|
||||
import net.liftweb.common.{Box, Failure, Full}
|
||||
import net.liftweb.http.{JsonResponse, Req}
|
||||
import net.liftweb.json.{JNothing, JValue}
|
||||
@ -0,0 +1,443 @@
|
||||
package code.api.dynamic.endpoint.helper
|
||||
|
||||
import code.api.util.APIUtil.{EmptyBody, ResourceDoc, authenticationRequiredMessage, generateUUID}
|
||||
import code.api.util.ApiRole.getOrCreateDynamicApiRole
|
||||
import code.api.util.ApiTag._
|
||||
import code.api.util.ErrorMessages.{InvalidJsonFormat, UnknownError, UserHasMissingRoles, UserNotLoggedIn}
|
||||
import code.api.util._
|
||||
import com.openbankproject.commons.model.enums.{DynamicEntityFieldType, DynamicEntityOperation}
|
||||
import com.openbankproject.commons.util.ApiVersion
|
||||
import net.liftweb.json.JsonDSL._
|
||||
import net.liftweb.json._
|
||||
import net.liftweb.util.StringHelpers
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
|
||||
import scala.collection.immutable.{List, Nil}
|
||||
import scala.collection.mutable
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
|
||||
object EntityName {
|
||||
// unapply result structure: (BankId, entityName, id)
|
||||
def unapply(url: List[String]): Option[(Option[String], String, String)] = url match {
|
||||
//no bank:
|
||||
//eg: /FooBar21
|
||||
case entityName :: Nil =>
|
||||
DynamicEntityHelper.definitionsMap.find(definitionMap => definitionMap._1 == entityName && definitionMap._2.bankId.isEmpty)
|
||||
.map(_ => (None, entityName, ""))
|
||||
//eg: /FooBar21/FOO_BAR21_ID
|
||||
case entityName :: id :: Nil =>
|
||||
DynamicEntityHelper.definitionsMap.find(definitionMap => definitionMap._1 == entityName && definitionMap._2.bankId.isEmpty)
|
||||
.map(_ => (None, entityName, id))
|
||||
|
||||
//contains Bank:
|
||||
//eg: /Banks/BANK_ID/FooBar21
|
||||
case "banks" :: bankId :: entityName :: Nil =>
|
||||
DynamicEntityHelper.definitionsMap.find(definitionMap => definitionMap._1 == entityName && definitionMap._2.bankId == Some(bankId))
|
||||
.map(_ => (Some(bankId), entityName, ""))
|
||||
//eg: /Banks/BANK_ID/FooBar21/FOO_BAR21_ID
|
||||
case "banks" :: bankId :: entityName :: id :: Nil =>
|
||||
DynamicEntityHelper.definitionsMap.find(definitionMap => definitionMap._1 == entityName && definitionMap._2.bankId == Some(bankId))
|
||||
.map(_ => (Some(bankId),entityName, id))
|
||||
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
|
||||
object DynamicEntityHelper {
|
||||
private val implementedInApiVersion = ApiVersion.v4_0_0
|
||||
|
||||
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(None).flatMap(dEntity => DynamicEntityInfo.roleNames(dEntity.entityName, dEntity.bankId))
|
||||
|
||||
def doc: ArrayBuffer[ResourceDoc] = {
|
||||
val docs = operationToResourceDoc.values.toList
|
||||
collection.mutable.ArrayBuffer(docs:_*)
|
||||
}
|
||||
|
||||
def createEntityId(entityName: String) = {
|
||||
// (?<=[a-z0-9])(?=[A-Z]) --> mean `Positive Lookbehind (?<=[a-z0-9])` && Positive Lookahead (?=[A-Z]) --> So we can find the space to replace to `_`
|
||||
val regexPattern = "(?<=[a-z0-9])(?=[A-Z])|-"
|
||||
// eg: entityName = PetEntity => entityIdName = pet_entity_id
|
||||
s"${entityName}_Id".replaceAll(regexPattern, "_").toLowerCase
|
||||
}
|
||||
|
||||
def operationToResourceDoc: Map[(DynamicEntityOperation, String), ResourceDoc] = {
|
||||
val addPrefix = APIUtil.getPropsAsBoolValue("dynamic_entities_have_prefix", true)
|
||||
|
||||
// record exists tag names, to avoid duplicated dynamic tag name.
|
||||
var existsTagNames = ApiTag.staticTagNames
|
||||
// match string that start with _, e.g: "_abc"
|
||||
val Regex = "(_+)(.+)".r
|
||||
|
||||
|
||||
//convert entity name to tag name, example:
|
||||
// Csem-case -> Csem Case
|
||||
// _Csem-case -> _Csem Case
|
||||
// Csem_case -> Csem Case
|
||||
// _Csem_case -> _Csem Case
|
||||
// csem-case -> Csem Case
|
||||
def prettyTagName(s: String) = s.capitalize.split("(?<=[^-_])[-_]+").reduceLeft(_ + " " + _.capitalize)
|
||||
|
||||
def apiTag(entityName: String, singularName: String): ResourceDocTag = {
|
||||
|
||||
val existsSameStaticEntity: Boolean = existsTagNames
|
||||
.exists(it => it.equalsIgnoreCase(singularName) || it.equalsIgnoreCase(entityName))
|
||||
|
||||
|
||||
val tagName = if(addPrefix || existsSameStaticEntity) {
|
||||
var name = singularName match {
|
||||
case Regex(a,b) => s"$a${b.capitalize}"
|
||||
case v => s"_${v.capitalize}"
|
||||
}
|
||||
|
||||
while(existsTagNames.exists(it => it.equalsIgnoreCase(name))) {
|
||||
name = s"_$name"
|
||||
}
|
||||
prettyTagName(name)
|
||||
} else {
|
||||
prettyTagName(singularName.capitalize)
|
||||
}
|
||||
|
||||
existsTagNames += tagName
|
||||
ApiTag(tagName)
|
||||
}
|
||||
val fun: DynamicEntityInfo => mutable.Map[(DynamicEntityOperation, String), ResourceDoc] = createDocs(apiTag)
|
||||
val docs: Iterable[((DynamicEntityOperation, String), ResourceDoc)] = definitionsMap.values.flatMap(fun)
|
||||
docs.toMap
|
||||
}
|
||||
|
||||
// TODO the requestBody and responseBody is not correct ref type
|
||||
/**
|
||||
*
|
||||
* @param fun (singularName, entityName) => ResourceDocTag
|
||||
* @param dynamicEntityInfo dynamicEntityInfo
|
||||
* @return all ResourceDoc of given dynamicEntity
|
||||
*/
|
||||
private def createDocs(fun: (String, String) => ResourceDocTag)
|
||||
(dynamicEntityInfo: DynamicEntityInfo): mutable.Map[(DynamicEntityOperation, String), ResourceDoc] = {
|
||||
val entityName = dynamicEntityInfo.entityName
|
||||
// e.g: "someMultiple-part_Name" -> ["Some", "Multiple", "Part", "Name"]
|
||||
val capitalizedNameParts = entityName.split("(?<=[a-z0-9])(?=[A-Z])|-|_").map(_.capitalize).filterNot(_.trim.isEmpty)
|
||||
val splitName = capitalizedNameParts.mkString(" ")
|
||||
|
||||
val idNameInUrl = StringHelpers.snakify(dynamicEntityInfo.idName).toUpperCase()
|
||||
val listName = dynamicEntityInfo.listName
|
||||
val bankId = dynamicEntityInfo.bankId
|
||||
val resourceDocUrl = if(bankId.isDefined) s"/banks/BANK_ID/$entityName" else s"/$entityName"
|
||||
|
||||
val endPoint = APIUtil.dynamicEndpointStub
|
||||
|
||||
// (operationType, entityName) -> ResourceDoc
|
||||
val resourceDocs = scala.collection.mutable.Map[(DynamicEntityOperation, String),ResourceDoc]()
|
||||
val apiTag: ResourceDocTag = fun(splitName, entityName)
|
||||
|
||||
resourceDocs += (DynamicEntityOperation.GET_ALL, entityName) -> ResourceDoc(
|
||||
endPoint,
|
||||
implementedInApiVersion,
|
||||
buildGetAllFunctionName(entityName),
|
||||
"GET",
|
||||
s"$resourceDocUrl",
|
||||
s"Get $splitName List",
|
||||
s"""Get $splitName List.
|
||||
|${dynamicEntityInfo.description}
|
||||
|
|
||||
|${dynamicEntityInfo.fieldsDescription}
|
||||
|
|
||||
|${methodRoutingExample(entityName)}
|
||||
|
|
||||
|${authenticationRequiredMessage(true)}
|
||||
|
|
||||
|Can do filter on the fields
|
||||
|e.g: /${entityName}?name=James%20Brown&number=123.456&number=11.11
|
||||
|Will do filter by this rule: name == "James Brown" && (number==123.456 || number=11.11)
|
||||
|""".stripMargin,
|
||||
EmptyBody,
|
||||
dynamicEntityInfo.getExampleList,
|
||||
List(
|
||||
UserNotLoggedIn,
|
||||
UserHasMissingRoles,
|
||||
UnknownError
|
||||
),
|
||||
List(apiTag, apiTagNewStyle, apiTagDynamicEntity, apiTagDynamic),
|
||||
Some(List(dynamicEntityInfo.canGetRole)),
|
||||
createdByBankId= dynamicEntityInfo.bankId
|
||||
)
|
||||
resourceDocs += (DynamicEntityOperation.GET_ONE, entityName) -> ResourceDoc(
|
||||
endPoint,
|
||||
implementedInApiVersion,
|
||||
buildGetOneFunctionName(entityName),
|
||||
"GET",
|
||||
s"$resourceDocUrl/$idNameInUrl",
|
||||
s"Get $splitName by id",
|
||||
s"""Get $splitName by id.
|
||||
|${dynamicEntityInfo.description}
|
||||
|
|
||||
|${dynamicEntityInfo.fieldsDescription}
|
||||
|
|
||||
|${methodRoutingExample(entityName)}
|
||||
|
|
||||
|${authenticationRequiredMessage(true)}
|
||||
|""".stripMargin,
|
||||
EmptyBody,
|
||||
dynamicEntityInfo.getSingleExample,
|
||||
List(
|
||||
UserNotLoggedIn,
|
||||
UserHasMissingRoles,
|
||||
UnknownError
|
||||
),
|
||||
List(apiTag, apiTagNewStyle, apiTagDynamicEntity, apiTagDynamic),
|
||||
Some(List(dynamicEntityInfo.canGetRole)),
|
||||
createdByBankId= dynamicEntityInfo.bankId
|
||||
)
|
||||
|
||||
resourceDocs += (DynamicEntityOperation.CREATE, entityName) -> ResourceDoc(
|
||||
endPoint,
|
||||
implementedInApiVersion,
|
||||
buildCreateFunctionName(entityName),
|
||||
"POST",
|
||||
s"$resourceDocUrl",
|
||||
s"Create new $splitName",
|
||||
s"""Create new $splitName.
|
||||
|${dynamicEntityInfo.description}
|
||||
|
|
||||
|${dynamicEntityInfo.fieldsDescription}
|
||||
|
|
||||
|${methodRoutingExample(entityName)}
|
||||
|
|
||||
|${authenticationRequiredMessage(true)}
|
||||
|
|
||||
|""",
|
||||
dynamicEntityInfo.getSingleExampleWithoutId,
|
||||
dynamicEntityInfo.getSingleExample,
|
||||
List(
|
||||
UserNotLoggedIn,
|
||||
UserHasMissingRoles,
|
||||
InvalidJsonFormat,
|
||||
UnknownError
|
||||
),
|
||||
List(apiTag, apiTagNewStyle, apiTagDynamicEntity, apiTagDynamic),
|
||||
Some(List(dynamicEntityInfo.canCreateRole)),
|
||||
createdByBankId= dynamicEntityInfo.bankId
|
||||
)
|
||||
|
||||
resourceDocs += (DynamicEntityOperation.UPDATE, entityName) -> ResourceDoc(
|
||||
endPoint,
|
||||
implementedInApiVersion,
|
||||
buildUpdateFunctionName(entityName),
|
||||
"PUT",
|
||||
s"$resourceDocUrl/$idNameInUrl",
|
||||
s"Update $splitName",
|
||||
s"""Update $splitName.
|
||||
|${dynamicEntityInfo.description}
|
||||
|
|
||||
|${dynamicEntityInfo.fieldsDescription}
|
||||
|
|
||||
|${methodRoutingExample(entityName)}
|
||||
|
|
||||
|${authenticationRequiredMessage(true)}
|
||||
|
|
||||
|""",
|
||||
dynamicEntityInfo.getSingleExampleWithoutId,
|
||||
dynamicEntityInfo.getSingleExample,
|
||||
List(
|
||||
UserNotLoggedIn,
|
||||
UserHasMissingRoles,
|
||||
InvalidJsonFormat,
|
||||
UnknownError
|
||||
),
|
||||
List(apiTag, apiTagNewStyle, apiTagDynamicEntity, apiTagDynamic),
|
||||
Some(List(dynamicEntityInfo.canUpdateRole)),
|
||||
createdByBankId= dynamicEntityInfo.bankId
|
||||
)
|
||||
|
||||
resourceDocs += (DynamicEntityOperation.DELETE, entityName) -> ResourceDoc(
|
||||
endPoint,
|
||||
implementedInApiVersion,
|
||||
buildDeleteFunctionName(entityName),
|
||||
"DELETE",
|
||||
s"$resourceDocUrl/$idNameInUrl",
|
||||
s"Delete $splitName by id",
|
||||
s"""Delete $splitName by id
|
||||
|
|
||||
|${methodRoutingExample(entityName)}
|
||||
|
|
||||
|${authenticationRequiredMessage(true)}
|
||||
|
|
||||
|""",
|
||||
dynamicEntityInfo.getSingleExampleWithoutId,
|
||||
dynamicEntityInfo.getSingleExample,
|
||||
List(
|
||||
UserNotLoggedIn,
|
||||
UserHasMissingRoles,
|
||||
InvalidJsonFormat,
|
||||
UnknownError
|
||||
),
|
||||
List(apiTag, apiTagNewStyle, apiTagDynamicEntity, apiTagDynamic),
|
||||
Some(List(dynamicEntityInfo.canDeleteRole)),
|
||||
createdByBankId= dynamicEntityInfo.bankId
|
||||
)
|
||||
|
||||
resourceDocs
|
||||
}
|
||||
|
||||
private def buildCreateFunctionName(entityName: String) = s"dynamicEntity_create$entityName"
|
||||
private def buildUpdateFunctionName(entityName: String) = s"dynamicEntity_update$entityName"
|
||||
private def buildDeleteFunctionName(entityName: String) = s"dynamicEntity_delete$entityName"
|
||||
private def buildGetOneFunctionName(entityName: String) = s"dynamicEntity_getSingle$entityName"
|
||||
private def buildGetAllFunctionName(entityName: String) = s"dynamicEntity_get${entityName}List"
|
||||
|
||||
@inline
|
||||
private def buildOperationId(entityName: String, fun: String => String): String = {
|
||||
APIUtil.buildOperationId(implementedInApiVersion, fun(entityName))
|
||||
}
|
||||
|
||||
def buildCreateOperationId(entityName: String) = buildOperationId(entityName, buildCreateFunctionName)
|
||||
def buildUpdateOperationId(entityName: String) = buildOperationId(entityName, buildUpdateFunctionName)
|
||||
def buildDeleteOperationId(entityName: String) = buildOperationId(entityName, buildDeleteFunctionName)
|
||||
def buildGetOneOperationId(entityName: String) = buildOperationId(entityName, buildGetOneFunctionName)
|
||||
def buildGetAllOperationId(entityName: String) = buildOperationId(entityName, buildGetAllFunctionName)
|
||||
|
||||
private def methodRoutingExample(entityName: String) =
|
||||
s"""
|
||||
|MethodRouting settings example:
|
||||
|
|
||||
|<details>
|
||||
|
|
||||
|```
|
||||
|{
|
||||
| "is_bank_id_exact_match":false,
|
||||
| "method_name":"dynamicEntityProcess",
|
||||
| "connector_name":"rest_vMar2019",
|
||||
| "bank_id_pattern":".*",
|
||||
| "parameters":[
|
||||
| {
|
||||
| "key":"entityName",
|
||||
| "value":"$entityName"
|
||||
| }
|
||||
| {
|
||||
| "key":"url",
|
||||
| "value":"http://mydomain.com/xxx"
|
||||
| }
|
||||
| ]
|
||||
|}
|
||||
|```
|
||||
|
|
||||
|</details>
|
||||
|""".stripMargin
|
||||
|
||||
}
|
||||
case class DynamicEntityInfo(definition: String, entityName: String, bankId: Option[String]) {
|
||||
|
||||
import net.liftweb.json
|
||||
|
||||
val subEntities: List[DynamicEntityInfo] = Nil
|
||||
|
||||
val idName = StringUtils.uncapitalize(entityName) + "Id"
|
||||
|
||||
val listName = StringHelpers.snakify(entityName).replaceFirst("[-_]*$", "_list")
|
||||
|
||||
val singleName = StringHelpers.snakify(entityName).replaceFirst("[-_]*$", "")
|
||||
|
||||
val jsonTypeMap: Map[String, Class[_]] = DynamicEntityFieldType.nameToValue.mapValues(_.jValueType)
|
||||
|
||||
val definitionJson = json.parse(definition).asInstanceOf[JObject]
|
||||
val entity = (definitionJson \ entityName).asInstanceOf[JObject]
|
||||
|
||||
val description = entity \ "description" match {
|
||||
case JString(s) if StringUtils.isNotBlank(s) =>
|
||||
s"""
|
||||
|${s.capitalize}
|
||||
|""".stripMargin
|
||||
case _ => ""
|
||||
}
|
||||
|
||||
val fieldsDescription = {
|
||||
val descriptions = (entity \ "properties")
|
||||
.asInstanceOf[JObject]
|
||||
.obj
|
||||
.filter(field =>
|
||||
field.value \ "description" match {
|
||||
case JString(s) if StringUtils.isNotBlank(s) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
if(descriptions.nonEmpty) {
|
||||
descriptions
|
||||
.map(field => s"""* ${field.name}: ${(field.value \ "description").asInstanceOf[JString].s}""")
|
||||
.mkString("**Property List:** \n\n", "\n", "")
|
||||
} else {
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
def toResponse(result: JObject, id: Option[String]): JObject = {
|
||||
|
||||
val fieldNameToTypeName: Map[String, String] = (entity \ "properties")
|
||||
.asInstanceOf[JObject]
|
||||
.obj
|
||||
.map(field => (field.name, (field.value \ "type").asInstanceOf[JString].s))
|
||||
.toMap
|
||||
|
||||
val fieldNameToType: Map[String, Class[_]] = fieldNameToTypeName
|
||||
.mapValues(jsonTypeMap(_))
|
||||
|
||||
val fields = result.obj.filter(it => fieldNameToType.keySet.contains(it.name))
|
||||
|
||||
(id, fields.exists(_.name == idName)) match {
|
||||
case (Some(idValue), false) => JObject(JField(idName, JString(idValue)) :: fields)
|
||||
case _ => JObject(fields)
|
||||
}
|
||||
}
|
||||
|
||||
def getSingleExampleWithoutId: JObject = {
|
||||
val fields = (entity \ "properties").asInstanceOf[JObject].obj
|
||||
|
||||
def extractExample(typeAndExample: JValue): JValue = {
|
||||
val example = typeAndExample \ "example"
|
||||
(example, (typeAndExample \ "type")) match {
|
||||
case (JString(s), JString("boolean")) => JBool(s.toLowerCase().toBoolean)
|
||||
case (JString(s), JString("integer")) => JInt(s.toLong)
|
||||
case (JString(s), JString("number")) => JDouble(s.toDouble)
|
||||
case _ => example
|
||||
}
|
||||
}
|
||||
val exampleFields = fields.map(field => JField(field.name, extractExample(field.value)))
|
||||
JObject(exampleFields)
|
||||
}
|
||||
val bankIdJObject: JObject = ("bank-id" -> ExampleValue.bankIdExample.value)
|
||||
|
||||
def getSingleExample: JObject = if (bankId.isDefined){
|
||||
val SingleObject: JObject = (singleName -> (JObject(JField(idName, JString(generateUUID())) :: getSingleExampleWithoutId.obj)))
|
||||
bankIdJObject merge SingleObject
|
||||
} else{
|
||||
(singleName -> (JObject(JField(idName, JString(generateUUID())) :: getSingleExampleWithoutId.obj)))
|
||||
}
|
||||
|
||||
def getExampleList: JObject = if (bankId.isDefined){
|
||||
val objectList: JObject = (listName -> JArray(List(getSingleExample)))
|
||||
bankIdJObject merge objectList
|
||||
} else{
|
||||
(listName -> JArray(List(getSingleExample)))
|
||||
}
|
||||
|
||||
val canCreateRole: ApiRole = DynamicEntityInfo.canCreateRole(entityName, bankId)
|
||||
val canUpdateRole: ApiRole = DynamicEntityInfo.canUpdateRole(entityName, bankId)
|
||||
val canGetRole: ApiRole = DynamicEntityInfo.canGetRole(entityName, bankId)
|
||||
val canDeleteRole: ApiRole = DynamicEntityInfo.canDeleteRole(entityName, bankId)
|
||||
}
|
||||
|
||||
object DynamicEntityInfo {
|
||||
def canCreateRole(entityName: String, bankId:Option[String]): ApiRole = getOrCreateDynamicApiRole("CanCreateDynamicEntity_" + entityName, bankId.isDefined)
|
||||
def canUpdateRole(entityName: String, bankId:Option[String]): ApiRole = getOrCreateDynamicApiRole("CanUpdateDynamicEntity_" + entityName, bankId.isDefined)
|
||||
def canGetRole(entityName: String, bankId:Option[String]): ApiRole = getOrCreateDynamicApiRole("CanGetDynamicEntity_" + entityName, bankId.isDefined)
|
||||
def canDeleteRole(entityName: String, bankId:Option[String]): ApiRole = getOrCreateDynamicApiRole("CanDeleteDynamicEntity_" + entityName, bankId.isDefined)
|
||||
|
||||
def roleNames(entityName: String, bankId:Option[String]): List[String] = List(
|
||||
canCreateRole(entityName, bankId),
|
||||
canUpdateRole(entityName, bankId),
|
||||
canGetRole(entityName, bankId),
|
||||
canDeleteRole(entityName, bankId)
|
||||
).map(_.toString())
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package code.api.v4_0_0.dynamic
|
||||
package code.api.dynamic.endpoint.helper
|
||||
|
||||
import code.api.util.APIUtil.ResourceDoc
|
||||
import code.api.util.{APIUtil, ApiRole, ApiTag}
|
||||
@ -1,4 +1,4 @@
|
||||
package code.api.v4_0_0.dynamic.practise
|
||||
package code.api.dynamic.endpoint.helper.practise
|
||||
|
||||
import code.api.util.APIUtil.ResourceDoc
|
||||
import code.api.v4_0_0.ResourceDocFragment
|
||||
@ -1,4 +1,6 @@
|
||||
package code.api.v4_0_0.dynamic.practise
|
||||
package code.api.dynamic.endpoint.helper.practise
|
||||
|
||||
import code.api.dynamic.endpoint.helper.DynamicCompileEndpoint
|
||||
|
||||
// any import statement here need be moved into the process method body
|
||||
|
||||
@ -13,7 +15,7 @@ package code.api.v4_0_0.dynamic.practise
|
||||
*
|
||||
*
|
||||
*/
|
||||
object PractiseEndpoint extends code.api.v4_0_0.dynamic.DynamicCompileEndpoint {
|
||||
object PractiseEndpoint extends DynamicCompileEndpoint {
|
||||
// don't modify these import statement
|
||||
import code.api.util.CallContext
|
||||
import code.api.util.ErrorMessages.{InvalidJsonFormat, InvalidRequestPayload}
|
||||
@ -26,7 +28,7 @@ object PractiseEndpoint extends code.api.v4_0_0.dynamic.DynamicCompileEndpoint {
|
||||
|
||||
import scala.concurrent.Future
|
||||
import com.openbankproject.commons.ExecutionContext.Implicits.global
|
||||
import code.api.v4_0_0.dynamic.DynamicCompileEndpoint._
|
||||
import code.api.dynamic.endpoint.helper.DynamicCompileEndpoint._
|
||||
|
||||
|
||||
// request method
|
||||
@ -1,11 +1,11 @@
|
||||
package code.api.v4_0_0.dynamic.practise
|
||||
package code.api.dynamic.endpoint.helper.practise
|
||||
|
||||
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.requestRootJsonClass
|
||||
import code.api.dynamic.endpoint.helper.EndpointGroup
|
||||
import code.api.util.APIUtil
|
||||
import code.api.util.APIUtil.{ResourceDoc, StringBody}
|
||||
import code.api.util.ApiTag.{apiTagDynamicResourceDoc, apiTagNewStyle}
|
||||
import code.api.util.ErrorMessages.UnknownError
|
||||
import code.api.v4_0_0.dynamic.EndpointGroup
|
||||
import com.openbankproject.commons.util.ApiVersion
|
||||
|
||||
import scala.collection.immutable.List
|
||||
@ -31,7 +31,7 @@ object PractiseEndpointGroup extends EndpointGroup{
|
||||
|* [Dynamic resourceDoc version1](https://vimeo.com/623381607)
|
||||
|
|
||||
|The endpoint return the response from PractiseEndpoint code.
|
||||
|Here, code.api.v4_0_0.dynamic.practise.PractiseEndpoint.process
|
||||
|Here, code.api.DynamicEndpoints.dynamic.practise.PractiseEndpoint.process
|
||||
|You can test the method body grammar, and try the business logic, but need to restart the OBP-API code .
|
||||
|
|
||||
|""",
|
||||
@ -0,0 +1,281 @@
|
||||
package code.api.dynamic.entity
|
||||
|
||||
import code.DynamicData.{DynamicData, DynamicDataProvider}
|
||||
import code.api.dynamic.endpoint.helper.{DynamicEndpointHelper, DynamicEntityHelper, DynamicEntityInfo, EntityName, MockResponseHolder}
|
||||
import code.api.dynamic.endpoint.helper.DynamicEndpointHelper.DynamicReq
|
||||
import code.api.dynamic.endpoint.helper.MockResponseHolder
|
||||
import code.api.util.APIUtil.{fullBoxOrException, _}
|
||||
import code.api.util.ErrorMessages._
|
||||
import code.api.util.NewStyle.HttpCode
|
||||
import code.api.util._
|
||||
import code.endpointMapping.EndpointMappingCommons
|
||||
import code.transactionrequests.TransactionRequests.TransactionRequestTypes.{apply => _}
|
||||
import code.util.Helper
|
||||
import com.openbankproject.commons.ExecutionContext.Implicits.global
|
||||
import com.openbankproject.commons.model._
|
||||
import com.openbankproject.commons.model.enums.DynamicEntityOperation._
|
||||
import com.openbankproject.commons.model.enums._
|
||||
import com.openbankproject.commons.util.{ApiVersion, JsonUtils}
|
||||
import net.liftweb.common._
|
||||
import net.liftweb.http.rest.RestHelper
|
||||
import net.liftweb.http.{JsonResponse, Req}
|
||||
import net.liftweb.json.JsonAST.JValue
|
||||
import net.liftweb.json.JsonDSL._
|
||||
import net.liftweb.json._
|
||||
import net.liftweb.util.StringHelpers
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
|
||||
import scala.collection.immutable.List
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.concurrent.Future
|
||||
|
||||
trait APIMethodsDynamicEntity {
|
||||
self: RestHelper =>
|
||||
|
||||
val ImplementationsDynamicEntity = new ImplementationsDynamicEntity()
|
||||
|
||||
class ImplementationsDynamicEntity {
|
||||
|
||||
val implementedInApiVersion = ApiVersion.`dynamic-entity`
|
||||
|
||||
private val staticResourceDocs = ArrayBuffer[ResourceDoc]()
|
||||
|
||||
// createDynamicEntityDoc and updateDynamicEntityDoc are dynamic, So here dynamic create resourceDocs
|
||||
def resourceDocs = staticResourceDocs
|
||||
|
||||
val apiRelations = ArrayBuffer[ApiRelation]()
|
||||
val codeContext = CodeContext(staticResourceDocs, apiRelations)
|
||||
|
||||
private def unboxResult[T: Manifest](box: Box[T], entityName: String): T = {
|
||||
if (box.isInstanceOf[Failure]) {
|
||||
val failure = box.asInstanceOf[Failure]
|
||||
// change the internal db column name 'dynamicdataid' to entity's id name
|
||||
val msg = failure.msg.replace(DynamicData.DynamicDataId.dbColumnName, StringUtils.uncapitalize(entityName) + "Id")
|
||||
val changedMsgFailure = failure.copy(msg = s"$InternalServerError $msg")
|
||||
fullBoxOrException[T](changedMsgFailure)
|
||||
}
|
||||
|
||||
box.openOrThrowException("impossible error")
|
||||
}
|
||||
|
||||
//TODO temp solution to support query by field name and value
|
||||
private def filterDynamicObjects(resultList: JArray, req: Req): JArray = {
|
||||
req.params match {
|
||||
case map if map.isEmpty => resultList
|
||||
case params =>
|
||||
val filteredWithFieldValue = resultList.arr.filter { jValue =>
|
||||
params.forall { kv =>
|
||||
val (path, values) = kv
|
||||
values.exists(JsonUtils.isFieldEquals(jValue, path, _))
|
||||
}
|
||||
}
|
||||
|
||||
JArray(filteredWithFieldValue)
|
||||
}
|
||||
}
|
||||
|
||||
lazy val genericEndpoint: OBPEndpoint = {
|
||||
case EntityName(bankId, entityName, id) JsonGet req => { cc =>
|
||||
val listName = StringHelpers.snakify(entityName).replaceFirst("[-_]*$", "_list")
|
||||
val singleName = StringHelpers.snakify(entityName).replaceFirst("[-_]*$", "")
|
||||
val isGetAll = StringUtils.isBlank(id)
|
||||
|
||||
val operation: DynamicEntityOperation = if (StringUtils.isBlank(id)) GET_ALL else GET_ONE
|
||||
val resourceDoc = DynamicEntityHelper.operationToResourceDoc.get(operation -> entityName)
|
||||
val operationId = resourceDoc.map(_.operationId).orNull
|
||||
val callContext = cc.copy(operationId = Some(operationId), resourceDocument = resourceDoc)
|
||||
// process before authentication interceptor, get intercept result
|
||||
val beforeInterceptResult: Box[JsonResponse] = beforeAuthenticateInterceptResult(Option(callContext), operationId)
|
||||
if (beforeInterceptResult.isDefined) beforeInterceptResult
|
||||
else for {
|
||||
(Full(u), callContext) <- authenticatedAccess(callContext) // Inject operationId into Call Context. It's used by Rate Limiting.
|
||||
|
||||
(_, callContext) <-
|
||||
if (bankId.isDefined) { //if it is the bank level entity, we need to check the bankId
|
||||
NewStyle.function.getBank(bankId.map(BankId(_)).orNull, callContext)
|
||||
} else {
|
||||
Future.successful {
|
||||
("", callContext)
|
||||
}
|
||||
}
|
||||
|
||||
_ <- NewStyle.function.hasEntitlement(bankId.getOrElse(""), u.userId, DynamicEntityInfo.canGetRole(entityName, bankId), callContext)
|
||||
|
||||
// process after authentication interceptor, get intercept result
|
||||
jsonResponse: Box[ErrorMessage] = afterAuthenticateInterceptResult(callContext, operationId).collect({
|
||||
case JsonResponseExtractor(message, code) => ErrorMessage(code, message)
|
||||
})
|
||||
_ <- Helper.booleanToFuture(failMsg = jsonResponse.map(_.message).orNull, failCode = jsonResponse.map(_.code).openOr(400), cc = callContext) {
|
||||
jsonResponse.isEmpty
|
||||
}
|
||||
|
||||
(box, _) <- NewStyle.function.invokeDynamicConnector(operation, entityName, None, Option(id).filter(StringUtils.isNotBlank), bankId, None, Some(cc))
|
||||
|
||||
_ <- Helper.booleanToFuture(EntityNotFoundByEntityId, 404, cc = callContext) {
|
||||
box.isDefined
|
||||
}
|
||||
} yield {
|
||||
val jValue = if (isGetAll) {
|
||||
val resultList: JArray = unboxResult(box.asInstanceOf[Box[JArray]], entityName)
|
||||
if (bankId.isDefined) {
|
||||
val bankIdJobject: JObject = ("bank_id" -> bankId.getOrElse(""))
|
||||
val result: JObject = (listName -> filterDynamicObjects(resultList, req))
|
||||
bankIdJobject merge result
|
||||
} else {
|
||||
val result: JObject = (listName -> filterDynamicObjects(resultList, req))
|
||||
result
|
||||
}
|
||||
} else {
|
||||
val singleObject: JValue = unboxResult(box.asInstanceOf[Box[JValue]], entityName)
|
||||
if (bankId.isDefined) {
|
||||
val bankIdJobject: JObject = ("bank_id" -> bankId.getOrElse(""))
|
||||
val result: JObject = (singleName -> singleObject)
|
||||
bankIdJobject merge result
|
||||
} else {
|
||||
val result: JObject = (singleName -> singleObject)
|
||||
result
|
||||
}
|
||||
}
|
||||
(jValue, HttpCode.`200`(Some(cc)))
|
||||
}
|
||||
}
|
||||
|
||||
case EntityName(bankId, entityName, _) JsonPost json -> _ => { cc =>
|
||||
val singleName = StringHelpers.snakify(entityName).replaceFirst("[-_]*$", "")
|
||||
val operation: DynamicEntityOperation = CREATE
|
||||
val resourceDoc = DynamicEntityHelper.operationToResourceDoc.get(operation -> entityName)
|
||||
val operationId = resourceDoc.map(_.operationId).orNull
|
||||
val callContext = cc.copy(operationId = Some(operationId), resourceDocument = resourceDoc)
|
||||
|
||||
// process before authentication interceptor, get intercept result
|
||||
val beforeInterceptResult: Box[JsonResponse] = beforeAuthenticateInterceptResult(Option(callContext), operationId)
|
||||
if (beforeInterceptResult.isDefined) beforeInterceptResult
|
||||
else for {
|
||||
(Full(u), callContext) <- authenticatedAccess(callContext) // Inject operationId into Call Context. It's used by Rate Limiting.
|
||||
(_, callContext) <-
|
||||
if (bankId.isDefined) { //if it is the bank level entity, we need to check the bankId
|
||||
NewStyle.function.getBank(bankId.map(BankId(_)).orNull, callContext)
|
||||
} else {
|
||||
Future.successful {
|
||||
("", callContext)
|
||||
}
|
||||
}
|
||||
_ <- NewStyle.function.hasEntitlement(bankId.getOrElse(""), u.userId, DynamicEntityInfo.canCreateRole(entityName, bankId), callContext)
|
||||
|
||||
// process after authentication interceptor, get intercept result
|
||||
jsonResponse: Box[ErrorMessage] = afterAuthenticateInterceptResult(callContext, operationId).collect({
|
||||
case JsonResponseExtractor(message, code) => ErrorMessage(code, message)
|
||||
})
|
||||
_ <- Helper.booleanToFuture(failMsg = jsonResponse.map(_.message).orNull, failCode = jsonResponse.map(_.code).openOr(400), cc = callContext) {
|
||||
jsonResponse.isEmpty
|
||||
}
|
||||
|
||||
(box, _) <- NewStyle.function.invokeDynamicConnector(operation, entityName, Some(json.asInstanceOf[JObject]), None, bankId, None, Some(cc))
|
||||
singleObject: JValue = unboxResult(box.asInstanceOf[Box[JValue]], entityName)
|
||||
} yield {
|
||||
val result: JObject = (singleName -> singleObject)
|
||||
val entity = if (bankId.isDefined) {
|
||||
val bankIdJobject: JObject = ("bank_id" -> bankId.getOrElse(""))
|
||||
bankIdJobject merge result
|
||||
} else {
|
||||
result
|
||||
}
|
||||
(entity, HttpCode.`201`(Some(cc)))
|
||||
}
|
||||
}
|
||||
case EntityName(bankId, entityName, id) JsonPut json -> _ => { cc =>
|
||||
val singleName = StringHelpers.snakify(entityName).replaceFirst("[-_]*$", "")
|
||||
val operation: DynamicEntityOperation = UPDATE
|
||||
val resourceDoc = DynamicEntityHelper.operationToResourceDoc.get(operation -> entityName)
|
||||
val operationId = resourceDoc.map(_.operationId).orNull
|
||||
val callContext = cc.copy(operationId = Some(operationId), resourceDocument = resourceDoc)
|
||||
|
||||
// process before authentication interceptor, get intercept result
|
||||
val beforeInterceptResult: Box[JsonResponse] = beforeAuthenticateInterceptResult(Option(callContext), operationId)
|
||||
if (beforeInterceptResult.isDefined) beforeInterceptResult
|
||||
else for {
|
||||
(Full(u), callContext) <- authenticatedAccess(callContext) // Inject operationId into Call Context. It's used by Rate Limiting.
|
||||
(_, callContext) <-
|
||||
if (bankId.isDefined) { //if it is the bank level entity, we need to check the bankId
|
||||
NewStyle.function.getBank(bankId.map(BankId(_)).orNull, callContext)
|
||||
} else {
|
||||
Future.successful {
|
||||
("", callContext)
|
||||
}
|
||||
}
|
||||
_ <- NewStyle.function.hasEntitlement(bankId.getOrElse(""), u.userId, DynamicEntityInfo.canUpdateRole(entityName, bankId), callContext)
|
||||
|
||||
// process after authentication interceptor, get intercept result
|
||||
jsonResponse: Box[ErrorMessage] = afterAuthenticateInterceptResult(callContext, operationId).collect({
|
||||
case JsonResponseExtractor(message, code) => ErrorMessage(code, message)
|
||||
})
|
||||
_ <- Helper.booleanToFuture(failMsg = jsonResponse.map(_.message).orNull, failCode = jsonResponse.map(_.code).openOr(400), cc = callContext) {
|
||||
jsonResponse.isEmpty
|
||||
}
|
||||
|
||||
(box, _) <- NewStyle.function.invokeDynamicConnector(GET_ONE, entityName, None, Some(id), bankId, None, Some(cc))
|
||||
_ <- Helper.booleanToFuture(EntityNotFoundByEntityId, 404, cc = callContext) {
|
||||
box.isDefined
|
||||
}
|
||||
(box: Box[JValue], _) <- NewStyle.function.invokeDynamicConnector(operation, entityName, Some(json.asInstanceOf[JObject]), Some(id), bankId, None, Some(cc))
|
||||
singleObject: JValue = unboxResult(box.asInstanceOf[Box[JValue]], entityName)
|
||||
} yield {
|
||||
val result: JObject = (singleName -> singleObject)
|
||||
val entity = if (bankId.isDefined) {
|
||||
val bankIdJobject: JObject = ("bank_id" -> bankId.getOrElse(""))
|
||||
bankIdJobject merge result
|
||||
} else {
|
||||
result
|
||||
}
|
||||
(entity, HttpCode.`200`(Some(cc)))
|
||||
}
|
||||
}
|
||||
case EntityName(bankId, entityName, id) JsonDelete _ => { cc =>
|
||||
val operation: DynamicEntityOperation = DELETE
|
||||
val resourceDoc = DynamicEntityHelper.operationToResourceDoc.get(operation -> entityName)
|
||||
val operationId = resourceDoc.map(_.operationId).orNull
|
||||
val callContext = cc.copy(operationId = Some(operationId), resourceDocument = resourceDoc)
|
||||
|
||||
// process before authentication interceptor, get intercept result
|
||||
val beforeInterceptResult: Box[JsonResponse] = beforeAuthenticateInterceptResult(Option(callContext), operationId)
|
||||
if (beforeInterceptResult.isDefined) beforeInterceptResult
|
||||
else for {
|
||||
(Full(u), callContext) <- authenticatedAccess(callContext) // Inject operationId into Call Context. It's used by Rate Limiting.
|
||||
(_, callContext) <-
|
||||
if (bankId.isDefined) { //if it is the bank level entity, we need to check the bankId
|
||||
NewStyle.function.getBank(bankId.map(BankId(_)).orNull, callContext)
|
||||
} else {
|
||||
Future.successful {
|
||||
("", callContext)
|
||||
}
|
||||
}
|
||||
_ <- NewStyle.function.hasEntitlement(bankId.getOrElse(""), u.userId, DynamicEntityInfo.canDeleteRole(entityName, bankId), callContext)
|
||||
|
||||
// process after authentication interceptor, get intercept result
|
||||
jsonResponse: Box[ErrorMessage] = afterAuthenticateInterceptResult(callContext, operationId).collect({
|
||||
case JsonResponseExtractor(message, code) => ErrorMessage(code, message)
|
||||
})
|
||||
_ <- Helper.booleanToFuture(failMsg = jsonResponse.map(_.message).orNull, failCode = jsonResponse.map(_.code).openOr(400), cc = callContext) {
|
||||
jsonResponse.isEmpty
|
||||
}
|
||||
|
||||
(box, _) <- NewStyle.function.invokeDynamicConnector(GET_ONE, entityName, None, Some(id), bankId, None, Some(cc))
|
||||
_ <- Helper.booleanToFuture(EntityNotFoundByEntityId, 404, cc = callContext) {
|
||||
box.isDefined
|
||||
}
|
||||
(box, _) <- NewStyle.function.invokeDynamicConnector(operation, entityName, None, Some(id), bankId, None, Some(cc))
|
||||
deleteResult: JBool = unboxResult(box.asInstanceOf[Box[JBool]], entityName)
|
||||
} yield {
|
||||
(deleteResult, HttpCode.`204`(Some(cc)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object APIMethodsDynamicEntity extends RestHelper with APIMethodsDynamicEntity {
|
||||
lazy val newStyleEndpoints: List[(String, String)] = ImplementationsDynamicEntity.resourceDocs.map {
|
||||
rd => (rd.partialFunctionName, rd.implementedInApiVersion.toString())
|
||||
}.toList
|
||||
}
|
||||
|
||||
@ -0,0 +1,75 @@
|
||||
/**
|
||||
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.api.dynamic.entity
|
||||
|
||||
import APIMethodsDynamicEntity.ImplementationsDynamicEntity
|
||||
import code.api.OBPRestHelper
|
||||
import code.api.dynamic.endpoint.helper.DynamicEndpoints
|
||||
import code.api.util.APIUtil.OBPEndpoint
|
||||
import code.api.util.{APIUtil, VersionedOBPApis}
|
||||
import code.api.v5_0_0.OBPAPI5_0_0.{allResourceDocs, apiPrefix, registerRoutes, routes}
|
||||
import code.util.Helper.MdcLoggable
|
||||
import com.openbankproject.commons.util.ApiVersion
|
||||
import net.liftweb.common.{Box, Full}
|
||||
import net.liftweb.http.{LiftResponse, PlainTextResponse}
|
||||
import org.apache.http.HttpStatus
|
||||
|
||||
/*
|
||||
This file defines which endpoints from all the versions are available in v4.0.0
|
||||
*/
|
||||
object OBPAPIDynamicEntity extends OBPRestHelper with MdcLoggable with VersionedOBPApis{
|
||||
|
||||
val version : ApiVersion = ApiVersion.`dynamic-entity`
|
||||
|
||||
val versionStatus = "BLEEDING-EDGE" // TODO this should be a property of ApiVersion.
|
||||
|
||||
// if old version ResourceDoc objects have the same name endpoint with new version, omit old version ResourceDoc.
|
||||
def allResourceDocs = collectResourceDocs(ImplementationsDynamicEntity.resourceDocs)
|
||||
|
||||
val routes : List[OBPEndpoint] = List(ImplementationsDynamicEntity.genericEndpoint)
|
||||
|
||||
routes.map(endpoint => oauthServe(apiPrefix{endpoint}, None))
|
||||
|
||||
logger.info(s"version $version has been run! There are ${routes.length} routes.")
|
||||
// specified response for OPTIONS request.
|
||||
private val corsResponse: Box[LiftResponse] = Full{
|
||||
val corsHeaders = List(
|
||||
"Access-Control-Allow-Origin" -> "*",
|
||||
"Access-Control-Allow-Methods" -> "GET, POST, OPTIONS, PUT, PATCH, DELETE",
|
||||
"Access-Control-Allow-Headers" -> "*",
|
||||
"Access-Control-Allow-Credentials" -> "true",
|
||||
"Access-Control-Max-Age" -> "1728000" //Tell client that this pre-flight info is valid for 20 days
|
||||
)
|
||||
PlainTextResponse("", corsHeaders, HttpStatus.SC_NO_CONTENT)
|
||||
}
|
||||
/*
|
||||
* process OPTIONS http request, just return no content and status is 204
|
||||
*/
|
||||
this.serve({
|
||||
case req if req.requestType.method == "OPTIONS" => corsResponse
|
||||
})
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package code.api.v4_0_0.dynamic
|
||||
package code.api.dynamic.entity.helper
|
||||
|
||||
import code.api.util.APIUtil.{EmptyBody, ResourceDoc, authenticationRequiredMessage, generateUUID}
|
||||
import code.api.util.ApiRole.getOrCreateDynamicApiRole
|
||||
@ -33,12 +33,13 @@ import java.nio.charset.Charset
|
||||
import java.text.{ParsePosition, SimpleDateFormat}
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.{Calendar, Date, UUID}
|
||||
|
||||
import code.UserRefreshes.UserRefreshes
|
||||
import code.accountholders.AccountHolders
|
||||
import code.api.Constant._
|
||||
import code.api.OAuthHandshake._
|
||||
import code.api.builder.OBP_APIBuilder
|
||||
import code.api.dynamic.endpoint.OBPAPIDynamicEndpoint
|
||||
import code.api.dynamic.endpoint.helper.{DynamicEndpointHelper, DynamicEndpoints, DynamicEntityHelper}
|
||||
import code.api.oauth1a.Arithmetics
|
||||
import code.api.oauth1a.OauthParams._
|
||||
import code.api.util.APIUtil.ResourceDoc.{findPathVariableNames, isPathVariable}
|
||||
@ -48,7 +49,8 @@ import code.api.util.Glossary.GlossaryItem
|
||||
import code.api.util.RateLimitingJson.CallLimit
|
||||
import code.api.v1_2.ErrorMessage
|
||||
import code.api.v2_0_0.CreateEntitlementJSON
|
||||
import code.api.v4_0_0.dynamic.{DynamicEndpointHelper, DynamicEndpoints, DynamicEntityHelper}
|
||||
import code.api.dynamic.endpoint.helper.DynamicEndpointHelper
|
||||
import code.api.dynamic.entity.OBPAPIDynamicEntity
|
||||
import code.api.{DirectLogin, _}
|
||||
import code.authtypevalidation.AuthenticationTypeValidationProvider
|
||||
import code.bankconnectors.Connector
|
||||
@ -103,8 +105,8 @@ import javassist.{ClassPool, LoaderClassPath}
|
||||
import javassist.expr.{ExprEditor, MethodCall}
|
||||
import org.apache.commons.io.IOUtils
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import java.security.AccessControlException
|
||||
|
||||
import java.security.AccessControlException
|
||||
import scala.collection.mutable
|
||||
import scala.collection.mutable.{ArrayBuffer, ListBuffer}
|
||||
import scala.concurrent.Future
|
||||
@ -542,7 +544,6 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
def getHeadersCommonPart() = headers ::: List((ResponseHeader.`Correlation-Id`, getCorrelationId()))
|
||||
|
||||
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(
|
||||
@ -2444,6 +2445,9 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
case ApiVersion.v3_0_0 => LiftRules.statelessDispatch.append(v3_0_0.OBPAPI3_0_0)
|
||||
case ApiVersion.v3_1_0 => LiftRules.statelessDispatch.append(v3_1_0.OBPAPI3_1_0)
|
||||
case ApiVersion.v4_0_0 => LiftRules.statelessDispatch.append(v4_0_0.OBPAPI4_0_0)
|
||||
case ApiVersion.v5_0_0 => LiftRules.statelessDispatch.append(v5_0_0.OBPAPI5_0_0)
|
||||
case ApiVersion.`dynamic-endpoint` => LiftRules.statelessDispatch.append(OBPAPIDynamicEndpoint)
|
||||
case ApiVersion.`dynamic-entity` => LiftRules.statelessDispatch.append(OBPAPIDynamicEntity)
|
||||
case ApiVersion.`b1` => LiftRules.statelessDispatch.append(OBP_APIBuilder)
|
||||
case version: ScannedApiVersion => LiftRules.statelessDispatch.append(ScannedApis.versionMapScannedApis(version))
|
||||
case _ => logger.info(s"There is no ${version.toString}")
|
||||
@ -3321,7 +3325,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
* to the account specified by parameter bankIdAccountId over the view specified by parameter viewId
|
||||
* Note: The public views means you can use anonymous access which implies that the user is an optional value
|
||||
*/
|
||||
final def checkViewAccessAndReturnView(viewId : ViewId, bankIdAccountId: BankIdAccountId, user: Option[User]): Box[View] = {
|
||||
final def checkViewAccessAndReturnView(viewId : ViewId, bankIdAccountId: BankIdAccountId, user: Option[User], consumerId: Option[String] = None): Box[View] = {
|
||||
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.
|
||||
@ -3329,7 +3333,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
// 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
|
||||
case Full(v) if(user.isDefined && user.get.hasAccountAccess(v, bankIdAccountId)) => customView
|
||||
case Full(v) if(user.isDefined && user.get.hasAccountAccess(v, bankIdAccountId, consumerId)) => customView
|
||||
// The user has NO account access via custom view
|
||||
case _ =>
|
||||
val systemView = Views.views.vend.systemView(viewId)
|
||||
@ -3339,7 +3343,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
// 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
|
||||
case Full(v) if (user.isDefined && user.get.hasAccountAccess(v, bankIdAccountId, consumerId)) => 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
|
||||
@ -4195,4 +4199,6 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
|* You can define a collection (also known as baskets or buckets) of products using Product Collections.
|
||||
|
|
||||
""".stripMargin
|
||||
|
||||
val transactionRequestChallengeTtl = APIUtil.getPropsAsLongValue("transaction_request_challenge_ttl", 600)
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ package code.api.util
|
||||
|
||||
import java.util.Date
|
||||
|
||||
import code.accountholders.AccountHolders
|
||||
import code.api.Constant
|
||||
import code.api.util.APIUtil.getPropsAsBoolValue
|
||||
import code.api.util.ApiRole.{CanCreateAccount, CanCreateHistoricalTransactionAtBank}
|
||||
@ -15,7 +16,7 @@ import code.ratelimiting.{RateLimiting, RateLimitingDI}
|
||||
import code.users.{UserInitActionProvider, Users}
|
||||
import code.util.Helper.MdcLoggable
|
||||
import code.views.Views
|
||||
import com.openbankproject.commons.model.{AccountId, Bank, BankAccount, User, ViewId}
|
||||
import com.openbankproject.commons.model.{AccountId, Bank, BankAccount, BankId, BankIdAccountId, User, ViewId}
|
||||
import net.liftweb.common.{Box, Empty, Failure, Full}
|
||||
import com.openbankproject.commons.ExecutionContext.Implicits.global
|
||||
import net.liftweb.mapper.By
|
||||
@ -178,12 +179,14 @@ object AfterApiAuth extends MdcLoggable{
|
||||
}.isDefined
|
||||
UserInitActionProvider.createOrUpdateInitAction(resourceUser.userId, "add-entitlement", CanCreateHistoricalTransactionAtBank.toString(), addCanCreateHistoricalTransactionAtBank)
|
||||
// Create Cash account
|
||||
val bankAccount = getOrCreateBankAccount(bank, "cash", "cash-flow").flatMap( account =>
|
||||
Views.views.vend.systemView(ViewId(Constant.SYSTEM_OWNER_VIEW_ID)).flatMap( view =>
|
||||
val bankAccount = getOrCreateBankAccount(bank, "cash", "cash-flow").flatMap { account =>
|
||||
Views.views.vend.systemView(ViewId(Constant.SYSTEM_OWNER_VIEW_ID)).flatMap(view =>
|
||||
// Grant account access
|
||||
Views.views.vend.grantAccessToSystemView(bank.bankId, account.accountId, view, resourceUser)
|
||||
)
|
||||
).isDefined
|
||||
// Create account holder
|
||||
AccountHolders.accountHolders.vend.getOrCreateAccountHolder(resourceUser, BankIdAccountId(bank.bankId, account.accountId))
|
||||
}.isDefined
|
||||
UserInitActionProvider.createOrUpdateInitAction(resourceUser.userId, "add-bank-account", "cache", bankAccount)
|
||||
addCanCreateAccount && addCanCreateHistoricalTransactionAtBank && bankAccount
|
||||
case _ =>
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
package code.api.util
|
||||
|
||||
import code.api.dynamic.endpoint.helper.{DynamicEndpointHelper, DynamicEntityHelper}
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import code.api.v4_0_0.dynamic.{DynamicEndpointHelper, DynamicEntityHelper}
|
||||
import code.api.dynamic.endpoint.helper.DynamicEndpointHelper
|
||||
import com.openbankproject.commons.util.{JsonAble, ReflectUtils}
|
||||
import net.liftweb.json.{Formats, JsonAST}
|
||||
import net.liftweb.json.JsonDSL._
|
||||
@ -116,6 +118,9 @@ object ApiRole {
|
||||
case class CanUpdateCustomerCreditRatingAndSource(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canUpdateCustomerCreditRatingAndSource = CanUpdateCustomerCreditRatingAndSource()
|
||||
|
||||
case class CanUpdateCustomerCreditRatingAndSourceAtAnyBank(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canUpdateCustomerCreditRatingAndSourceAtAnyBank = CanUpdateCustomerCreditRatingAndSourceAtAnyBank()
|
||||
|
||||
case class CanCreateCustomerAtAnyBank(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canCreateCustomerAtAnyBank = CanCreateCustomerAtAnyBank()
|
||||
|
||||
@ -265,9 +270,21 @@ object ApiRole {
|
||||
|
||||
case class CanCreateCounterpartyAtAnyBank(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canCreateCounterpartyAtAnyBank = CanCreateCounterpartyAtAnyBank()
|
||||
|
||||
case class CanDeleteCounterparty(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canDeleteCounterparty = CanDeleteCounterparty()
|
||||
|
||||
case class CanDeleteCounterpartyAtAnyBank(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canDeleteCounterpartyAtAnyBank = CanDeleteCounterpartyAtAnyBank()
|
||||
|
||||
case class CanGetCounterparty(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canGetCounterparty = CanGetCounterparty()
|
||||
lazy val canGetCounterparty = CanGetCounterparty()
|
||||
|
||||
case class CanGetCounterpartiesAtAnyBank(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canGetCounterpartiesAtAnyBank = CanGetCounterpartiesAtAnyBank()
|
||||
|
||||
case class CanGetCounterparties(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canGetCounterparties = CanGetCounterparties()
|
||||
|
||||
case class CanGetApiCollection(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canGetApiCollection = CanGetApiCollection()
|
||||
@ -386,6 +403,12 @@ object ApiRole {
|
||||
case class CanCreateWebhook(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canCreateWebhook = CanCreateWebhook()
|
||||
|
||||
case class CanCreateSystemAccountNotificationWebhook(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canCreateSystemAccountNotificationWebhook = CanCreateSystemAccountNotificationWebhook()
|
||||
|
||||
case class CanCreateAccountNotificationWebhookAtOneBank(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canCreateAccountNotificationWebhookAtOneBank = CanCreateAccountNotificationWebhookAtOneBank()
|
||||
|
||||
case class CanUpdateWebhook(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canUpdateWebhook = CanUpdateWebhook()
|
||||
|
||||
@ -591,18 +614,33 @@ object ApiRole {
|
||||
case class CanCreateCustomerAttributeAtOneBank(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canCreateCustomerAttributeAtOneBank = CanCreateCustomerAttributeAtOneBank()
|
||||
|
||||
case class CanCreateCustomerAttributeAtAnyBank(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canCreateCustomerAttributeAtAnyBank = CanCreateCustomerAttributeAtAnyBank()
|
||||
|
||||
case class CanUpdateCustomerAttributeAtOneBank(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canUpdateCustomerAttributeAtOneBank = CanUpdateCustomerAttributeAtOneBank()
|
||||
|
||||
case class CanUpdateCustomerAttributeAtAnyBank(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canUpdateCustomerAttributeAtAnyBank = CanUpdateCustomerAttributeAtAnyBank()
|
||||
|
||||
case class CanDeleteCustomerAttributeAtOneBank(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canDeleteCustomerAttributeAtOneBank = CanDeleteCustomerAttributeAtOneBank()
|
||||
|
||||
case class CanDeleteCustomerAttributeAtAnyBank(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canDeleteCustomerAttributeAtAnyBank = CanDeleteCustomerAttributeAtAnyBank()
|
||||
|
||||
case class CanGetCustomerAttributesAtOneBank(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canGetCustomerAttributesAtOneBank = CanGetCustomerAttributesAtOneBank()
|
||||
|
||||
case class CanGetCustomerAttributesAtAnyBank(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canGetCustomerAttributesAtAnyBank = CanGetCustomerAttributesAtAnyBank()
|
||||
|
||||
case class CanGetCustomerAttributeAtOneBank(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canGetCustomerAttributeAtOneBank = CanGetCustomerAttributeAtOneBank()
|
||||
|
||||
case class CanGetCustomerAttributeAtAnyBank(requiresBankId: Boolean = false) extends ApiRole
|
||||
lazy val canGetCustomerAttributeAtAnyBank = CanGetCustomerAttributeAtAnyBank()
|
||||
|
||||
case class CanCreateTransactionAttributeAtOneBank(requiresBankId: Boolean = true) extends ApiRole
|
||||
lazy val canCreateTransactionAttributeAtOneBank = CanCreateTransactionAttributeAtOneBank()
|
||||
|
||||
|
||||
@ -6,6 +6,9 @@ sealed trait ApiTrigger{
|
||||
|
||||
object ApiTrigger {
|
||||
|
||||
case class OnCreateTransaction() extends ApiTrigger
|
||||
lazy val onCreateTransaction = OnCreateTransaction()
|
||||
|
||||
case class OnBalanceChange() extends ApiTrigger
|
||||
lazy val onBalanceChange = OnBalanceChange()
|
||||
|
||||
|
||||
@ -16,6 +16,9 @@ object ApiVersionUtils {
|
||||
v3_0_0 ::
|
||||
v3_1_0 ::
|
||||
v4_0_0 ::
|
||||
v5_0_0 ::
|
||||
`dynamic-endpoint` ::
|
||||
`dynamic-entity` ::
|
||||
b1::
|
||||
scannedApis
|
||||
|
||||
@ -34,6 +37,9 @@ object ApiVersionUtils {
|
||||
case v3_0_0.fullyQualifiedVersion | v3_0_0.apiShortVersion => v3_0_0
|
||||
case v3_1_0.fullyQualifiedVersion | v3_1_0.apiShortVersion => v3_1_0
|
||||
case v4_0_0.fullyQualifiedVersion | v4_0_0.apiShortVersion => v4_0_0
|
||||
case v5_0_0.fullyQualifiedVersion | v5_0_0.apiShortVersion => v5_0_0
|
||||
case `dynamic-endpoint`.fullyQualifiedVersion | `dynamic-endpoint`.apiShortVersion => `dynamic-endpoint`
|
||||
case `dynamic-entity`.fullyQualifiedVersion | `dynamic-entity`.apiShortVersion => `dynamic-entity`
|
||||
case b1.fullyQualifiedVersion | b1.apiShortVersion => b1
|
||||
case version if(scannedApis.map(_.fullyQualifiedVersion).contains(version))
|
||||
=>scannedApis.filter(_.fullyQualifiedVersion==version).head
|
||||
|
||||
@ -1,26 +1,19 @@
|
||||
package code.api.util
|
||||
|
||||
import code.api.{APIFailureNewStyle, JsonResponseException}
|
||||
import code.api.util.APIUtil.ResourceDoc
|
||||
import code.api.util.ErrorMessages.DynamicResourceDocMethodDependency
|
||||
import code.api.util.NewStyle.HttpCode
|
||||
import code.api.v4_0_0.JSONFactory400
|
||||
import code.api.v4_0_0.dynamic.{CompiledObjects, DynamicCompileEndpoint}
|
||||
import code.api.v4_0_0.dynamic.practise.PractiseEndpoint
|
||||
import com.openbankproject.commons.ExecutionContext
|
||||
import com.openbankproject.commons.model.BankId
|
||||
import com.openbankproject.commons.util.Functions.Memo
|
||||
import com.openbankproject.commons.util.{JsonUtils, ReflectUtils}
|
||||
import javassist.{ClassPool, LoaderClassPath}
|
||||
import net.liftweb.common.{Box, Empty, Failure, Full, ParamFailure}
|
||||
import net.liftweb.http.JsonResponse
|
||||
|
||||
import net.liftweb.json.{Extraction, JValue, prettyRender}
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import org.graalvm.polyglot.{Context, Engine, HostAccess, PolyglotAccess}
|
||||
|
||||
import java.lang.reflect.ReflectPermission
|
||||
import java.net.NetPermission
|
||||
import java.security.{AccessControlContext, AccessController, CodeSource, Permission, PermissionCollection, Permissions, Policy, PrivilegedAction, ProtectionDomain}
|
||||
import java.util.{PropertyPermission, UUID}
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.function.Consumer
|
||||
import java.util.regex.Pattern
|
||||
@ -28,7 +21,6 @@ import javax.script.ScriptEngineManager
|
||||
import scala.collection.immutable.List
|
||||
import scala.collection.mutable.ListBuffer
|
||||
import scala.concurrent.{Future, Promise}
|
||||
import scala.reflect.api
|
||||
import scala.reflect.runtime.universe
|
||||
import scala.reflect.runtime.universe.runtimeMirror
|
||||
import scala.runtime.NonLocalReturnControl
|
||||
@ -246,7 +238,7 @@ object DynamicUtil {
|
||||
|import code.api.util.ErrorMessages._
|
||||
|import code.api.util.ExampleValue._
|
||||
|import code.api.util.{APIUtil, CallContext, OBPQueryParam}
|
||||
|import code.api.v4_0_0.dynamic.MockResponseHolder
|
||||
|import code.api.dynamic.endpoint.helper.MockResponseHolder
|
||||
|import code.bankconnectors._
|
||||
|import code.customer.internalMapping.MappedCustomerIdMappingProvider
|
||||
|import code.kafka.KafkaHelper
|
||||
@ -278,8 +270,8 @@ object DynamicUtil {
|
||||
|import code.api.util.NewStyle.HttpCode
|
||||
|import code.api.util._
|
||||
|import code.api.v4_0_0.JSONFactory400
|
||||
|import code.api.v4_0_0.dynamic.{CompiledObjects, DynamicCompileEndpoint}
|
||||
|import code.api.v4_0_0.dynamic.practise.PractiseEndpoint
|
||||
|import code.api.dynamic.endpoint.helper.{CompiledObjects, DynamicCompileEndpoint}
|
||||
|import code.api.dynamic.endpoint.helper.practise.PractiseEndpoint
|
||||
|import com.openbankproject.commons.ExecutionContext
|
||||
|import code.api.util.CustomJsonFormats
|
||||
|import com.openbankproject.commons.model.BankId
|
||||
|
||||
@ -93,6 +93,8 @@ object ErrorMessages {
|
||||
"Note: When you are making a POST or PUT request, the Content-Type header MUST be `application/json`. Note: OBP only supports JSON formatted bodies."
|
||||
val ResourceDoesNotExist = "OBP-10405: Resource does not exist."
|
||||
val InvalidJsonValue = "OBP-10035: Incorrect json value."
|
||||
val InvalidHttpMethod = "OBP-10037: Incorrect http_method."
|
||||
val InvalidHttpProtocol = "OBP-10038: Incorrect http_protocol."
|
||||
|
||||
// General Sort and Paging
|
||||
val FilterSortDirectionError = "OBP-10023: obp_sort_direction parameter can only take two values: DESC or ASC!" // was OBP-20023
|
||||
@ -213,6 +215,8 @@ object ErrorMessages {
|
||||
val Oauth2TokenHaveNoConsumer = "OBP-20209: The token have no linked consumer. "
|
||||
val Oauth2TokenMatchCertificateFail = "OBP-20210: The token linked with a different client certificate. "
|
||||
|
||||
val OneTimePasswordExpired = "OBP-20211: The One Time Password (OTP) has expired. "
|
||||
|
||||
// X.509
|
||||
val X509GeneralError = "OBP-20300: PEM Encoded Certificate issue."
|
||||
val X509ParsingFailed = "OBP-20301: Parsing failed for PEM Encoded Certificate."
|
||||
@ -419,6 +423,8 @@ object ErrorMessages {
|
||||
val EntitlementCannotBeDeleted = "OBP-30219: EntitlementId cannot be deleted."
|
||||
val EntitlementCannotBeGranted = "OBP-30220: Entitlement cannot be granted."
|
||||
val EntitlementCannotBeGrantedGrantorIssue = "OBP-30221: Entitlement cannot be granted due to the grantor's insufficient privileges."
|
||||
|
||||
val CounterpartyNotFoundByRoutings = "OBP-30222: Counterparty not found. Please specify valid value for Routings."
|
||||
|
||||
val CreateSystemViewError = "OBP-30250: Could not create the system view"
|
||||
val DeleteSystemViewError = "OBP-30251: Could not delete the system view"
|
||||
@ -439,6 +445,9 @@ object ErrorMessages {
|
||||
val UpdateAccountApplicationStatusError = "OBP-30315: AccountApplication Status can not be updated. "
|
||||
val CreateAccountApplicationError = "OBP-30316: AccountApplication Status can not be created. "
|
||||
|
||||
val DeleteCounterpartyError = "OBP-30317: Could not delete the Counterparty."
|
||||
val DeleteCounterpartyMetadataError = "OBP-30318: Could not delete CounterpartyMetadata"
|
||||
|
||||
// Branch related messages
|
||||
val BranchesNotFoundLicense = "OBP-32001: No branches available. License may not be set."
|
||||
val BranchesNotFound = "OBP-32002: No branches available."
|
||||
@ -495,7 +504,12 @@ object ErrorMessages {
|
||||
|
||||
// Transaction Request related messages (OBP-40XXX)
|
||||
val InvalidTransactionRequestType = "OBP-40001: Invalid value for TRANSACTION_REQUEST_TYPE"
|
||||
val InsufficientAuthorisationToCreateTransactionRequest = "OBP-40002: Insufficient authorisation to create TransactionRequest. The Transaction Request could not be created because you don't have access to the owner view of the from account or you don't have access to canCreateAnyTransactionRequest."
|
||||
val InsufficientAuthorisationToCreateTransactionRequest = "OBP-40002: Insufficient authorisation to create TransactionRequest. " +
|
||||
"The Transaction Request could not be created " +
|
||||
"because the login user doesn't have access to the view of the from account " +
|
||||
"or the view don't have the `canAddTransactionRequestToAnyAccount` permission " +
|
||||
"or your consumer doesn't not have the access to the view of the from account " +
|
||||
"or you don't have the role CanCreateAnyTransactionRequest."
|
||||
val InvalidTransactionRequestCurrency = "OBP-40003: Transaction Request Currency must be the same as From Account Currency."
|
||||
val InvalidTransactionRequestId = "OBP-40004: Transaction Request Id not found."
|
||||
val InsufficientAuthorisationToCreateTransactionType = "OBP-40005: Insufficient authorisation to Create Transaction Type offered by the bank. The Request could not be created because you don't have access to CanCreateTransactionType."
|
||||
@ -510,8 +524,10 @@ object ErrorMessages {
|
||||
val AllowedAttemptsUsedUp = "OBP-40014: Sorry, you've used up your allowed attempts. "
|
||||
val InvalidChallengeType = "OBP-40015: Invalid Challenge Type. Please specify a valid value for CHALLENGE_TYPE, when you create the transaction request."
|
||||
val InvalidChallengeAnswer = "OBP-40016: Invalid Challenge Answer. Please specify a valid value for answer in Json body. " +
|
||||
"If connector = mapped and transactionRequestType_OTP_INSTRUCTION_TRANSPORT = DUMMY and suggested_default_sca_method=DUMMY, the answer must be `123`. " +
|
||||
"If connector = others, the challenge answer can be got by phone message or other security ways."
|
||||
"The challenge answer may be expired." +
|
||||
"Or you've used up your allowed attempts (3 times)." +
|
||||
"Or if connector = mapped and transactionRequestType_OTP_INSTRUCTION_TRANSPORT = DUMMY and suggested_default_sca_method=DUMMY, the answer must be `123`. " +
|
||||
"Or if connector = others, the challenge answer can be got by phone message or other security ways."
|
||||
val InvalidPhoneNumber = "OBP-40017: Invalid Phone Number. Please specify a valid value for PHONE_NUMBER. Eg:+9722398746 "
|
||||
val TransactionRequestsNotEnabled = "OBP-40018: Sorry, Transaction Requests are not enabled in this API instance."
|
||||
val NextChallengePending = s"OBP-40019: Cannot create transaction due to transaction request is in status: ${NEXT_CHALLENGE_PENDING}."
|
||||
|
||||
@ -173,6 +173,9 @@ object ExampleValue {
|
||||
lazy val transactionIdExample = ConnectorField("2fg8a7e4-6d02-40e3-a129-0b2bf89de8ub", s"The Transaction ID used in URLs. Used to store Metadata for the Transaction.")
|
||||
glossaryItems += makeGlossaryItem("Transaction.transactionId", transactionIdExample)
|
||||
|
||||
lazy val chargePolicyExample = ConnectorField("SHARED", s"The transaction fee charge policy, can be shared, debit account or credit account.")
|
||||
glossaryItems += makeGlossaryItem("Transaction.charge_policy", chargePolicyExample)
|
||||
|
||||
lazy val transactionAttributeIdExample = ConnectorField("7uy8a7e4-6d02-40e3-a129-0b2bf89de8uh", s"Transaction attribute id")
|
||||
glossaryItems += makeGlossaryItem("Transaction.attributeId", transactionAttributeIdExample)
|
||||
|
||||
@ -197,7 +200,7 @@ object ExampleValue {
|
||||
lazy val transactionRequestAttributeValueExample = ConnectorField("123456789", s"Transaction Request attribute value.")
|
||||
glossaryItems += makeGlossaryItem("Transaction Requests.attributeValue", transactionRequestAttributeValueExample)
|
||||
|
||||
lazy val transactionDescriptionExample = ConnectorField("For the piano lesson in June 2018 - Invoice No: 68", s"A description or reference for the transaction")
|
||||
lazy val transactionDescriptionExample = ConnectorField("The piano lession-Invoice No:68", s"A description or reference for the transaction")
|
||||
glossaryItems += makeGlossaryItem("Transaction.transactionDescription", transactionDescriptionExample)
|
||||
|
||||
lazy val transactionTypeExample = ConnectorField("DEBIT", s"A code for the type of transaction")
|
||||
@ -1562,9 +1565,6 @@ object ExampleValue {
|
||||
lazy val currentStateExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
|
||||
glossaryItems += makeGlossaryItem("current_state", currentStateExample)
|
||||
|
||||
lazy val chargePolicyExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
|
||||
glossaryItems += makeGlossaryItem("charge_policy", chargePolicyExample)
|
||||
|
||||
lazy val customersExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
|
||||
glossaryItems += makeGlossaryItem("customers", customersExample)
|
||||
|
||||
@ -1679,7 +1679,7 @@ object ExampleValue {
|
||||
lazy val canCreateDirectDebitExample = ConnectorField(booleanTrue,NoDescriptionProvided)
|
||||
glossaryItems += makeGlossaryItem("can_create_direct_debit", canCreateDirectDebitExample)
|
||||
|
||||
lazy val futureDateExample = ConnectorField("2020-01-27",NoDescriptionProvided)
|
||||
lazy val futureDateExample = ConnectorField("20200127",NoDescriptionProvided)
|
||||
glossaryItems += makeGlossaryItem("future_date", futureDateExample)
|
||||
|
||||
lazy val toTransferToAccountExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
|
||||
|
||||
@ -708,8 +708,56 @@ object Glossary extends MdcLoggable {
|
||||
title = "Transaction",
|
||||
description =
|
||||
"""
|
||||
|Records of successful movements of money from / to an `Account`. OBP Transactions don't contain any "draft" or "pending" Transactions. (see Transaction Requests). Transactions contain infomration including type, description, from, to, currency, amount and new balance information.
|
||||
|Transactions are records of successful movements of value into or out of an `Account`.
|
||||
|
|
||||
|OBP Transactions don't contain any "draft" or "pending" Transactions; pending transactions see represented by Transaction Requests.
|
||||
|
|
||||
|OBP Transactions are modelled on a Bank statement where everything is based on the perspective of my account.
|
||||
|That is, if I look at "my account", I see credits (positive numbers) and debits (negative numbers)
|
||||
|
||||
|An OBP transaction stores information including the:
|
||||
|Bank ID
|
||||
|Account ID
|
||||
|Currency
|
||||
|Amount (positive for a credit, negative for a debit)
|
||||
|Date
|
||||
|Counterparty (information that describes the other party in the transaction)
|
||||
|- optionally description and new balance.
|
||||
|
|
||||
|Note, OBP operates a Double-Entry Bookkeeping system which means that every transfer of value within OBP is represented by *two* transactions.
|
||||
|
|
||||
|For instance, to represent 5 Euros going from Account A to Account B, we would have 2 transactions:
|
||||
|
|
||||
|Transaction 1.
|
||||
|
|
||||
|Account: A
|
||||
|Currency: EUR
|
||||
|Amount: -5
|
||||
|Counterparty: Account B
|
||||
|
|
||||
|Transaction 2.
|
||||
|
|
||||
|Account: B
|
||||
|Currency: EUR
|
||||
|Amount: +5
|
||||
|Counterparty: Account A
|
||||
|
|
||||
|The sum of the two transactions must be zero.
|
||||
|
|
||||
|What about representing value coming into or out of the system? Here we use "settlement accounts":
|
||||
|
|
||||
|OBP-INCOMING-SETTLEMENT-ACCOUNT is typically the ID for a default incoming settlement account
|
||||
|
|
||||
|OBP-OUTGOING-SETTLEMENT-ACCOUNT is typically the ID for a default outgoing settlement account
|
||||
|
|
||||
|See the following diagram:
|
||||
|
|
||||
|
|
||||
|
|
||||
|See the [Get Double Entry Transaction](/index?version=OBPv4.0.0&operation_id=OBPv4_0_0-getDoubleEntryTransaction¤tTag=Transaction#OBPv4_0_0-getDoubleEntryTransaction) endpoint
|
||||
|
|
||||
|
|
||||
|
|
||||
""")
|
||||
|
||||
glossaryItems += GlossaryItem(
|
||||
@ -1355,6 +1403,138 @@ object Glossary extends MdcLoggable {
|
||||
|
|
||||
|""")
|
||||
|
||||
glossaryItems += GlossaryItem(
|
||||
title = "Scenario 7: Onboarding a User with multiple User Auth Context records",
|
||||
description =
|
||||
s"""
|
||||
|### 1) Assuming a User is registered.
|
||||
|
|
||||
|The User can authenticate using OAuth, OIDC, Direct Login etc.
|
||||
|
|
||||
|### 2) Create a first User Auth Context record e.g. ACCOUNT_NUMBER
|
||||
|
|
||||
| The setting of the first User Auth Context record for a User, typically involves sending an SMS to the User.
|
||||
| The phone number used for the SMS is retrieved from the bank's Core Banking System via an Account Number to Phone Number lookup.
|
||||
| If this step succeeds we can be reasonably confident that the User who initiated it has access to a SIM card that can use the Phone Number linked to the Bank Account on the Core Banking System.
|
||||
|
|
||||
|Action: Create User Auth Context Update Request
|
||||
|
|
||||
| POST $getObpApiRoot/obp/v5.0.0/banks/BANK_ID/users/current/auth-context-updates/SMS
|
||||
|
|
||||
|Body:
|
||||
|
|
||||
| { "key":"ACCOUNT_NUMBER", "value":"78987432"}
|
||||
|
|
||||
|Headers:
|
||||
|
|
||||
| Content-Type: application/json
|
||||
|
|
||||
| DirectLogin: token="your-token-from-direct-login"
|
||||
|
|
||||
| When customer get the the challenge answer from SMS, then need to call `Answer Auth Context Update Challenge` to varify the challenge.
|
||||
| Then the customer create the 1st `User Auth Context` successfully.
|
||||
|
|
||||
|
|
||||
|Action: Answer Auth Context Update Challenge
|
||||
|
|
||||
| POST $getObpApiRoot/obp/v5.0.0/banks/BANK_ID/users/current/auth-context-updates/AUTH_CONTEXT_UPDATE_ID/challenge
|
||||
|
|
||||
|Body:
|
||||
|
|
||||
| { "answer": "12345678"}
|
||||
|
|
||||
|Headers:
|
||||
|
|
||||
| Content-Type: application/json
|
||||
|
|
||||
| DirectLogin: token="your-token-from-direct-login"
|
||||
|
|
||||
|### 3) Create a second User Auth Context record e.g. SMALL_PAYMENT_VERIFIED
|
||||
|
|
||||
| Once the first User Auth Context record is set, we can require the App to set a second record which builds on the information of the first.
|
||||
|
|
||||
|Action: Create User Auth Context Update Request
|
||||
|
|
||||
| POST $getObpApiRoot/obp/v5.0.0/banks/BANK_ID/users/current/auth-context-updates/SMS
|
||||
|
|
||||
|Body:
|
||||
|
|
||||
| { "key":"SMALL_PAYMENT_VERIFIED", "value":"78987432"}
|
||||
|
|
||||
|Headers:
|
||||
|
|
||||
| Content-Type: application/json
|
||||
|
|
||||
| DirectLogin: token="your-token-from-direct-login"
|
||||
|
|
||||
|
|
||||
|
|
||||
|Following `Create User Auth Context Update Request` request the API will send a small payment with a random code from the Users bank account specified in the SMALL_PAYMENT_VERIFIED key value.
|
||||
|
|
||||
|In order to answer the challenge, the User must have access to the online banking statement (or some other App that already can read transactions in realtime) so they can read the code in the description of the payment.
|
||||
|
|
||||
|
|
||||
|Then Action:Answer Auth Context Update Challenge
|
||||
|
|
||||
| POST $getObpApiRoot/obp/v5.0.0/banks/BANK_ID/users/current/auth-context-updates/AUTH_CONTEXT_UPDATE_ID/challenge
|
||||
|
|
||||
|Body:
|
||||
|
|
||||
| { "answer": "12345678"}
|
||||
|
|
||||
|Headers:
|
||||
|
|
||||
| Content-Type: application/json
|
||||
|
|
||||
| DirectLogin: token="your-token-from-direct-login"
|
||||
|
|
||||
| Note! The above logic must be encoded in a dynamic connector method for the OBP internal function validateUserAuthContextUpdateRequest which is used by the endpoint Create User Auth Context Update Request See the next step.
|
||||
|
|
||||
|### 4) Create or Update Connector Method for validateUserAuthContextUpdateRequest
|
||||
|
|
||||
| Using this endpoint you can modify the Scala logic
|
||||
|
|
||||
|Action:
|
||||
|
|
||||
| POST $getObpApiRoot/obp/v4.0.0/management/connector-methods
|
||||
|
|
||||
|Body:
|
||||
|
|
||||
| { "method_name":"validateUserAuthContextUpdateRequest", "method_body":"%20%20%20%20%20%20Future.successful%28%0A%20%20%20%20%20%20%20%20Full%28%28BankCommons%28%0A%20%20%20%20%20%20%20%20%20%20BankId%28%22Hello%20bank%20id%22%29%2C%0A%20%20%20%20%20%20%20%20%20%20%221%22%2C%0A%20%20%20%20%20%20%20%20%20%20%221%22%2C%0A%20%20%20%20%20%20%20%20%20%20%221%22%2C%0A%20%20%20%20%20%20%20%20%20%20%221%22%2C%0A%20%20%20%20%20%20%20%20%20%20%221%22%2C%0A%20%20%20%20%20%20%20%20%20%20%221%22%2C%0A%20%20%20%20%20%20%20%20%20%20%221%22%2C%0A%20%20%20%20%20%20%20%20%20%20%228%22%0A%20%20%20%20%20%20%20%20%29%2C%20None%29%29%0A%20%20%20%20%20%20%29"}
|
||||
|
|
||||
|Headers:
|
||||
|
|
||||
| Content-Type: application/json
|
||||
|
|
||||
| DirectLogin: token="your-token-from-direct-login"
|
||||
|
|
||||
|### 5) Allow automated access to the App with Create Consent (SMS)
|
||||
|
|
||||
|
|
||||
| Following the creation of User Auth Context records, OBP will create the relevant Account Access Views which allows the User to access their account(s).
|
||||
| The App can then request an OBP consent which can be used as a bearer token and have automated access to the accounts.
|
||||
| The Consent can be deleted at any time by the User.
|
||||
|
|
||||
| The Consent can have access to everything the User has access to, or a subset of this.
|
||||
|
|
||||
|Action:
|
||||
|
|
||||
| POST $getObpApiRoot/obp/v4.0.0/banks/BANK_ID/my/consents/SMS
|
||||
|
|
||||
|Body:
|
||||
|
|
||||
| { "everything":false, "views":[{ "bank_id":"gh.29.uk", "account_id":"8ca8a7e4-6d02-40e3-a129-0b2bf89de9f0", "view_id":"owner" }], "entitlements":[{ "bank_id":"gh.29.uk", "role_name":"CanGetCustomer" }], "consumer_id":"7uy8a7e4-6d02-40e3-a129-0b2bf89de8uh", "phone_number":"+44 07972 444 876", "valid_from":"2022-04-29T10:40:03Z", "time_to_live":3600}
|
||||
|
|
||||
|Headers:
|
||||
|
|
||||
| Content-Type: application/json
|
||||
|
|
||||
| DirectLogin: token="your-token-from-direct-login"
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
""")
|
||||
|
||||
|
||||
glossaryItems += GlossaryItem(
|
||||
@ -2728,11 +2908,11 @@ object Glossary extends MdcLoggable {
|
||||
title = "Connector Method",
|
||||
description =
|
||||
s"""
|
||||
| The developer can override all the existing Connector methods on their own.
|
||||
| Developers can override all the existing Connector methods.
|
||||
| This function needs to be used together with the Method Routing.
|
||||
| when set "connector = internal", then the developer can call their own method body at API level.
|
||||
| When we set "connector = internal", then the developer can call their own method body at API level.
|
||||
|
|
||||
|eg: Get Banks endpoint, it calls the connector "getBanks" method, then the developers can use these endpoints to modify the business logic in the getBanks method body.
|
||||
|For example, the GetBanks endpoint calls the connector "getBanks" method. Then, developers can use these endpoints to modify the business logic in the getBanks method body.
|
||||
|
|
||||
| The following videos are available:
|
||||
|* [Introduction for Connector Method] (https://vimeo.com/507795470)
|
||||
@ -2745,13 +2925,19 @@ object Glossary extends MdcLoggable {
|
||||
title = "Dynamic Resource Doc",
|
||||
description =
|
||||
s"""
|
||||
| The developers can create their own endpoints by this endpoint.
|
||||
| Need to prepare the obp resource doc format json.
|
||||
| And all the business logic code can be written in the *method_body* field, it is the encoded scala code.
|
||||
| In OBP we largely define our endpoints using an internal case class or model called ResourceDoc
|
||||
|
|
||||
| Using this endpoint, developers can create their own Resource Docs at run time thus creating fully featured
|
||||
| Open Bank Project style endpoints dynamically.
|
||||
|
|
||||
|
|
||||
| In order to do this you need to prepare your desired Resource Doc as JSON.
|
||||
| The business logic code can be written in the *method_body* field as encoded Scala code.
|
||||
|
|
||||
| It is still working in the processing ..
|
||||
| This feature is somewhat work in progress (WIP).
|
||||
|
|
||||
|The following videos are available:
|
||||
|* [Introduction for dConnector Method] (https://vimeo.com/623381607)
|
||||
|* [Introduction to Dynamic Resource Docs] (https://vimeo.com/623381607)
|
||||
|
|
||||
|""".stripMargin)
|
||||
|
||||
@ -2759,16 +2945,21 @@ object Glossary extends MdcLoggable {
|
||||
title = "Dynamic Message Doc",
|
||||
description =
|
||||
s"""
|
||||
| The developers can create their own scala methods in OBP code.
|
||||
| In OBP we represent messages sent by a Connector method / function as MessageDocs.
|
||||
| A MessageDoc defines the message the Connector sends to an Adapter and the response it expects from the Adapter.
|
||||
|
|
||||
| Using this endpoint, developers can create their own scala methods aka Connectors in OBP code.
|
||||
| These endpoints are designed for extending the current connector methods.
|
||||
| when you call the dynamic resource doc endpoints, sometimes you need to call internal scala methods,
|
||||
| which are not existing in OBP code, then you can use these endpoints to prepare them on your own.
|
||||
|
|
||||
| When you call the Dynamic Resource Doc endpoints, sometimes you need to call internal Scala methods which
|
||||
|don't yet exist in the OBP code. In this case you can use these endpoints to create your own internal Scala methods.
|
||||
|
|
||||
| And you can use these endpoints to design your own helper methods in OBP code.
|
||||
|You can also use these endpoints to create your own helper methods in OBP code.
|
||||
|
|
||||
| It is still working in the processing ..
|
||||
| This feature is somewhat work in progress (WIP).
|
||||
|
|
||||
|The following videos are available:
|
||||
|* [Introduction for Connector Method] (https://vimeo.com/623317747)
|
||||
|* [Introduction to Dynamic Message Doc] (https://vimeo.com/623317747)
|
||||
|
|
||||
|""".stripMargin)
|
||||
|
||||
|
||||
35
obp-api/src/main/scala/code/api/util/I18NUtil.scala
Normal file
35
obp-api/src/main/scala/code/api/util/I18NUtil.scala
Normal file
@ -0,0 +1,35 @@
|
||||
package code.api.util
|
||||
|
||||
import java.util.{Date, Locale}
|
||||
|
||||
import net.liftweb.http.S
|
||||
import net.liftweb.util.Props
|
||||
|
||||
object I18NUtil {
|
||||
// Copied from Sofit
|
||||
def getLocalDate(date: Date): String = {
|
||||
import java.text.DateFormat
|
||||
val df = DateFormat.getDateInstance(DateFormat.LONG, currentLocale())
|
||||
val formattedDate = df.format(date)
|
||||
formattedDate
|
||||
}
|
||||
|
||||
def getLocale(): Locale = Locale.getAvailableLocales().toList.filter { l =>
|
||||
l.toLanguageTag == Props.get("language_tag", "en-GB")
|
||||
}.headOption.getOrElse(Locale.ENGLISH)
|
||||
|
||||
def currentLocale() : Locale = {
|
||||
// Cookie name
|
||||
val localeCookieName = "SELECTED_LOCALE"
|
||||
S.findCookie(localeCookieName).flatMap {
|
||||
cookie => cookie.value.map(computeLocale)
|
||||
} openOr getLocale()
|
||||
}
|
||||
// Properly convert a language tag to a Locale
|
||||
def computeLocale(tag : String) = tag.split(Array('-', '_')) match {
|
||||
case Array(lang) => new Locale(lang)
|
||||
case Array(lang, country) => new Locale(lang, country)
|
||||
case Array(lang, country, variant) => new Locale(lang, country, variant)
|
||||
}
|
||||
|
||||
}
|
||||
@ -3,13 +3,12 @@ package code.api.util
|
||||
|
||||
import java.util.Date
|
||||
import java.util.UUID.randomUUID
|
||||
|
||||
import akka.http.scaladsl.model.HttpMethod
|
||||
import code.DynamicEndpoint.{DynamicEndpointProvider, DynamicEndpointT}
|
||||
import code.api.APIFailureNewStyle
|
||||
import code.api.Constant.SYSTEM_READ_ACCOUNTS_BERLIN_GROUP_VIEW_ID
|
||||
import code.api.cache.Caching
|
||||
import code.api.util.APIUtil.{EntitlementAndScopeStatus, OBPReturnType, afterAuthenticateInterceptResult, canGrantAccessToViewCommon, canRevokeAccessToViewCommon, connectorEmptyResponse, createHttpParamsByUrl, createHttpParamsByUrlFuture, createQueriesByHttpParamsFuture, fullBoxOrException, generateUUID, unboxFull, unboxFullOrFail}
|
||||
import code.api.util.APIUtil._
|
||||
import code.api.util.ApiRole.canCreateAnyTransactionRequest
|
||||
import code.api.util.ErrorMessages.{InsufficientAuthorisationToCreateTransactionRequest, _}
|
||||
import code.api.ResourceDocs1_4_0.ResourceDocs140.ImplementationsResourceDocs
|
||||
@ -54,8 +53,8 @@ import net.liftweb.json.JsonDSL._
|
||||
import net.liftweb.json.{JField, JInt, JNothing, JNull, JObject, JString, JValue, _}
|
||||
import net.liftweb.util.Helpers.tryo
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import java.security.AccessControlException
|
||||
|
||||
import java.security.AccessControlException
|
||||
import scala.collection.immutable.List
|
||||
import scala.concurrent.Future
|
||||
import scala.math.BigDecimal
|
||||
@ -64,8 +63,9 @@ import code.validation.{JsonSchemaValidationProvider, JsonValidation}
|
||||
import net.liftweb.http.JsonResponse
|
||||
import net.liftweb.util.Props
|
||||
import code.api.JsonResponseException
|
||||
import code.api.dynamic.endpoint.helper.{DynamicEndpointHelper, DynamicEntityHelper, DynamicEntityInfo}
|
||||
import code.api.v4_0_0.JSONFactory400
|
||||
import code.api.v4_0_0.dynamic.{DynamicEndpointHelper, DynamicEntityHelper, DynamicEntityInfo}
|
||||
import code.api.dynamic.endpoint.helper.DynamicEndpointHelper
|
||||
import code.bankattribute.BankAttribute
|
||||
import code.connectormethod.{ConnectorMethodProvider, JsonConnectorMethod}
|
||||
import code.dynamicMessageDoc.{DynamicMessageDocProvider, JsonDynamicMessageDoc}
|
||||
@ -73,6 +73,8 @@ import code.dynamicResourceDoc.{DynamicResourceDocProvider, JsonDynamicResourceD
|
||||
import code.endpointMapping.{EndpointMappingProvider, EndpointMappingT}
|
||||
import code.endpointTag.EndpointTagT
|
||||
import code.util.Helper.MdcLoggable
|
||||
import code.views.system.AccountAccess
|
||||
import net.liftweb.mapper.By
|
||||
|
||||
object NewStyle extends MdcLoggable{
|
||||
lazy val endpoints: List[(String, String)] = List(
|
||||
@ -591,15 +593,30 @@ object NewStyle extends MdcLoggable{
|
||||
|
||||
def checkAuthorisationToCreateTransactionRequest(viewId : ViewId, bankAccountId: BankIdAccountId, user: User, callContext: Option[CallContext]) : Future[Boolean] = {
|
||||
Future{
|
||||
APIUtil.hasEntitlement(bankAccountId.bankId.value, user.userId, canCreateAnyTransactionRequest) match {
|
||||
case true => Full(true)
|
||||
case false => user.hasOwnerViewAccess(BankIdAccountId(bankAccountId.bankId,bankAccountId.accountId)) match {
|
||||
case true => Full(true)
|
||||
case false => Empty
|
||||
}
|
||||
|
||||
lazy val hasCanCreateAnyTransactionRequestRole = APIUtil.hasEntitlement(bankAccountId.bankId.value, user.userId, canCreateAnyTransactionRequest)
|
||||
|
||||
lazy val consumerIdFromCallContext = callContext.map(_.consumer.map(_.consumerId.get).getOrElse(""))
|
||||
|
||||
lazy val view = APIUtil.checkViewAccessAndReturnView(viewId, bankAccountId, Some(user), consumerIdFromCallContext)
|
||||
|
||||
lazy val canAddTransactionRequestToAnyAccount = view.map(_.canAddTransactionRequestToAnyAccount).getOrElse(false)
|
||||
|
||||
//1st check the admin level role/entitlement `canCreateAnyTransactionRequest`
|
||||
if(hasCanCreateAnyTransactionRequestRole) {
|
||||
Full(true)
|
||||
//2rd: check if the user have the view access and the view has the `canAddTransactionRequestToAnyAccount` permission
|
||||
} else if (canAddTransactionRequestToAnyAccount) {
|
||||
Full(true)
|
||||
} else{
|
||||
Empty
|
||||
}
|
||||
} map {
|
||||
unboxFullOrFail(_, callContext, s"$InsufficientAuthorisationToCreateTransactionRequest")
|
||||
unboxFullOrFail(_, callContext, s"$InsufficientAuthorisationToCreateTransactionRequest " +
|
||||
s"Current ViewId(${viewId.value})," +
|
||||
s"current UserId(${user.userId})"+
|
||||
s"current ConsumerId(${callContext.map(_.consumer.map(_.consumerId.get).getOrElse("")).getOrElse("")})"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -884,17 +901,18 @@ object NewStyle extends MdcLoggable{
|
||||
|
||||
def getMetadata(bankId : BankId, accountId : AccountId, counterpartyId : String, callContext: Option[CallContext]): Future[CounterpartyMetadata] = {
|
||||
Future(Counterparties.counterparties.vend.getMetadata(bankId, accountId, counterpartyId)) map {
|
||||
x => fullBoxOrException(x ~> APIFailureNewStyle(CounterpartyMetadataNotFound, 400, callContext.map(_.toLight)))
|
||||
x => fullBoxOrException(x ~> APIFailureNewStyle(CounterpartyNotFoundByCounterpartyId, 400, callContext.map(_.toLight)))
|
||||
} map { unboxFull(_) }
|
||||
}
|
||||
|
||||
def getCounterpartyTrait(bankId : BankId, accountId : AccountId, counterpartyId : String, callContext: Option[CallContext]): OBPReturnType[CounterpartyTrait] = {
|
||||
Connector.connector.vend.getCounterpartyTrait(bankId, accountId, counterpartyId, callContext) map { i=>
|
||||
(connectorEmptyResponse(i._1, callContext), i._2)
|
||||
}
|
||||
def getCounterpartyTrait(bankId : BankId, accountId : AccountId, counterpartyId : String, callContext: Option[CallContext]): OBPReturnType[CounterpartyTrait] =
|
||||
{
|
||||
Connector.connector.vend.getCounterpartyTrait(bankId, accountId, counterpartyId, callContext) map { i =>
|
||||
(unboxFullOrFail(i._1, callContext, s"$CounterpartyNotFoundByCounterpartyId Current counterpartyId($counterpartyId) ", 400),
|
||||
i._2)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def isEnabledTransactionRequests(callContext: Option[CallContext]): Future[Box[Unit]] = Helper.booleanToFuture(failMsg = TransactionRequestsNotEnabled, cc=callContext)(APIUtil.getPropsAsBoolValue("transactionRequests_enabled", false))
|
||||
|
||||
/**
|
||||
@ -1032,18 +1050,14 @@ object NewStyle extends MdcLoggable{
|
||||
validateRequestPayload(callContext)(boxResult)
|
||||
}
|
||||
|
||||
def createUserAuthContext(userId: String, key: String, value: String, callContext: Option[CallContext]): OBPReturnType[UserAuthContext] = {
|
||||
Connector.connector.vend.createUserAuthContext(userId, key, value, callContext) map {
|
||||
def createUserAuthContext(user: User, key: String, value: String, callContext: Option[CallContext]): OBPReturnType[UserAuthContext] = {
|
||||
Connector.connector.vend.createUserAuthContext(user.userId, key, value, callContext) map {
|
||||
i => (connectorEmptyResponse(i._1, callContext), i._2)
|
||||
} map {
|
||||
result =>
|
||||
//We will call the `refreshUserAccountAccess` after we successfully create the UserAuthContext
|
||||
// because `createUserAuthContext` is a connector method, here is the entry point for OBP to refreshUserAccountAccess
|
||||
if(callContext.isDefined && callContext.get.user.isDefined) {
|
||||
AuthUser.refreshUser(callContext.get.user.head, callContext)
|
||||
} else {
|
||||
logger.info(s"AuthUser.refreshUserAccountAccess can not be run properly. The user is missing in the current callContext.")
|
||||
}
|
||||
// because `createUserAuthContext` is a connector method, here is the entry point for OBP to refreshUser
|
||||
AuthUser.refreshUser(user, callContext)
|
||||
result
|
||||
}
|
||||
}
|
||||
@ -1063,9 +1077,15 @@ object NewStyle extends MdcLoggable{
|
||||
}
|
||||
}
|
||||
|
||||
def deleteUserAuthContextById(userAuthContextId: String, callContext: Option[CallContext]): OBPReturnType[Boolean] = {
|
||||
def deleteUserAuthContextById(user: User, userAuthContextId: String, callContext: Option[CallContext]): OBPReturnType[Boolean] = {
|
||||
Connector.connector.vend.deleteUserAuthContextById(userAuthContextId, callContext) map {
|
||||
i => (connectorEmptyResponse(i._1, callContext), i._2)
|
||||
}map {
|
||||
result =>
|
||||
// We will call the `refreshUserAccountAccess` after we successfully delete the UserAuthContext
|
||||
// because `deleteUserAuthContextById` is a connector method, here is the entry point for OBP to refreshUser
|
||||
AuthUser.refreshUser(user, callContext)
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
@ -1172,6 +1192,14 @@ object NewStyle extends MdcLoggable{
|
||||
i._2)
|
||||
}
|
||||
}
|
||||
|
||||
def deleteCounterpartyByCounterpartyId(counterpartyId: CounterpartyId, callContext: Option[CallContext]): OBPReturnType[Boolean] =
|
||||
{
|
||||
Connector.connector.vend.deleteCounterpartyByCounterpartyId(counterpartyId: CounterpartyId, callContext: Option[CallContext]) map { i =>
|
||||
(unboxFullOrFail(i._1, callContext, s"$DeleteCounterpartyError Current counterpartyId($counterpartyId) ", 400),
|
||||
i._2)
|
||||
}
|
||||
}
|
||||
def getBankAccountFromCounterparty(counterparty: CounterpartyTrait, isOutgoingAccount: Boolean, callContext: Option[CallContext]) : Future[BankAccount] =
|
||||
{
|
||||
Future{BankAccountX.getBankAccountFromCounterparty(counterparty, isOutgoingAccount)} map {
|
||||
@ -1206,6 +1234,92 @@ object NewStyle extends MdcLoggable{
|
||||
i._2)
|
||||
}
|
||||
}
|
||||
|
||||
def getOrCreateCounterparty(
|
||||
name: String,
|
||||
description: String,
|
||||
currency: String,
|
||||
createdByUserId: String,
|
||||
thisBankId: String,
|
||||
thisAccountId: String,
|
||||
thisViewId: String,
|
||||
otherBankRoutingScheme: String,
|
||||
otherBankRoutingAddress: String,
|
||||
otherBranchRoutingScheme: String,
|
||||
otherBranchRoutingAddress: String,
|
||||
otherAccountRoutingScheme: String,
|
||||
otherAccountRoutingAddress: String,
|
||||
otherAccountSecondaryRoutingScheme: String,
|
||||
otherAccountSecondaryRoutingAddress: String,
|
||||
callContext: Option[CallContext]
|
||||
) : OBPReturnType[CounterpartyTrait] =
|
||||
{
|
||||
Connector.connector.vend.getOrCreateCounterparty(
|
||||
name: String,
|
||||
description: String,
|
||||
currency: String,
|
||||
createdByUserId: String,
|
||||
thisBankId: String,
|
||||
thisAccountId: String,
|
||||
thisViewId: String,
|
||||
otherBankRoutingScheme: String,
|
||||
otherBankRoutingAddress: String,
|
||||
otherBranchRoutingScheme: String,
|
||||
otherBranchRoutingAddress: String,
|
||||
otherAccountRoutingScheme: String,
|
||||
otherAccountRoutingAddress: String,
|
||||
otherAccountSecondaryRoutingScheme: String,
|
||||
otherAccountSecondaryRoutingAddress: String,
|
||||
callContext: Option[CallContext]
|
||||
) map { i =>
|
||||
(unboxFullOrFail(
|
||||
i._1,
|
||||
callContext,
|
||||
s"$CreateCounterpartyError.",
|
||||
404),
|
||||
i._2)
|
||||
}
|
||||
}
|
||||
|
||||
def getCounterpartyByRoutings(
|
||||
otherBankRoutingScheme: String,
|
||||
otherBankRoutingAddress: String,
|
||||
otherBranchRoutingScheme: String,
|
||||
otherBranchRoutingAddress: String,
|
||||
otherAccountRoutingScheme: String,
|
||||
otherAccountRoutingAddress: String,
|
||||
otherAccountSecondaryRoutingScheme: String,
|
||||
otherAccountSecondaryRoutingAddress: String,
|
||||
callContext: Option[CallContext]
|
||||
) : OBPReturnType[CounterpartyTrait] =
|
||||
{
|
||||
Connector.connector.vend.getCounterpartyByRoutings(
|
||||
otherBankRoutingScheme: String,
|
||||
otherBankRoutingAddress: String,
|
||||
otherBranchRoutingScheme: String,
|
||||
otherBranchRoutingAddress: String,
|
||||
otherAccountRoutingScheme: String,
|
||||
otherAccountRoutingAddress: String,
|
||||
otherAccountSecondaryRoutingScheme: String,
|
||||
otherAccountSecondaryRoutingAddress: String,
|
||||
callContext: Option[CallContext]
|
||||
) map { i =>
|
||||
(unboxFullOrFail(
|
||||
i._1,
|
||||
callContext,
|
||||
s"$CounterpartyNotFoundByRoutings. Current routings: " +
|
||||
s"otherBankRoutingScheme($otherBankRoutingScheme), " +
|
||||
s"otherBankRoutingAddress($otherBankRoutingAddress)"+
|
||||
s"otherBranchRoutingScheme($otherBranchRoutingScheme)"+
|
||||
s"otherBranchRoutingAddress($otherBranchRoutingAddress)"+
|
||||
s"otherAccountRoutingScheme($otherAccountRoutingScheme)"+
|
||||
s"otherAccountRoutingAddress($otherAccountRoutingAddress)"+
|
||||
s"otherAccountSecondaryRoutingScheme($otherAccountSecondaryRoutingScheme)"+
|
||||
s"otherAccountSecondaryRoutingAddress($otherAccountSecondaryRoutingAddress)"+
|
||||
404),
|
||||
i._2)
|
||||
}
|
||||
}
|
||||
|
||||
def getTransactionRequestImpl(transactionRequestId: TransactionRequestId, callContext: Option[CallContext]): OBPReturnType[TransactionRequest] =
|
||||
{
|
||||
@ -1384,6 +1498,10 @@ object NewStyle extends MdcLoggable{
|
||||
Connector.connector.vend.getDoubleEntryBookTransaction(bankId: BankId, accountId: AccountId, transactionId: TransactionId, callContext: Option[CallContext]) map { i =>
|
||||
(unboxFullOrFail(i._1, callContext, s"$DoubleEntryTransactionNotFound ", 404), i._2)
|
||||
}
|
||||
def getBalancingTransaction(transactionId: TransactionId, callContext: Option[CallContext]): OBPReturnType[DoubleEntryTransaction] =
|
||||
Connector.connector.vend.getBalancingTransaction(transactionId: TransactionId, callContext: Option[CallContext]) map { i =>
|
||||
(unboxFullOrFail(i._1, callContext, s"$DoubleEntryTransactionNotFound ", 404), i._2)
|
||||
}
|
||||
|
||||
def cancelPaymentV400(transactionId: TransactionId, callContext: Option[CallContext]): OBPReturnType[CancelPayment] = {
|
||||
Connector.connector.vend.cancelPaymentV400(transactionId: TransactionId, callContext) map { i =>
|
||||
@ -3216,6 +3334,18 @@ object NewStyle extends MdcLoggable{
|
||||
counterpartyName:String
|
||||
), callContext, CreateOrUpdateCounterpartyMetadataError), callContext)}
|
||||
}
|
||||
def deleteMetadata(
|
||||
bankId: BankId,
|
||||
accountId : AccountId,
|
||||
counterpartyId:String,
|
||||
callContext: Option[CallContext]
|
||||
) : OBPReturnType[Boolean]= {
|
||||
Future{(unboxFullOrFail(Counterparties.counterparties.vend.deleteMetadata(
|
||||
bankId: BankId,
|
||||
accountId : AccountId,
|
||||
counterpartyId:String
|
||||
), callContext, DeleteCounterpartyMetadataError), callContext)}
|
||||
}
|
||||
|
||||
def getPhysicalCardsForUser(user : User, callContext: Option[CallContext]) : OBPReturnType[List[PhysicalCard]] = {
|
||||
Connector.connector.vend.getPhysicalCardsForUser(user : User, callContext) map {
|
||||
|
||||
@ -61,6 +61,7 @@ object Migration extends MdcLoggable {
|
||||
|
||||
def executeScripts(startedBeforeSchemifier: Boolean): Boolean = executeScript {
|
||||
dummyScript()
|
||||
addAccountAccessConsumerId()
|
||||
populateTableViewDefinition()
|
||||
populateTableAccountAccess()
|
||||
generateAndPopulateMissingCustomerUUIDs()
|
||||
@ -90,6 +91,8 @@ object Migration extends MdcLoggable {
|
||||
alterUserAuthContextColumnKeyAndValueLength(startedBeforeSchemifier)
|
||||
dropIndexAtColumnUsernameAtTableAuthUser(startedBeforeSchemifier)
|
||||
dropIndexAtUserAuthContext()
|
||||
alterWebhookColumnUrlLength()
|
||||
dropConsentAuthContextDropIndex()
|
||||
}
|
||||
|
||||
private def dummyScript(): Boolean = {
|
||||
@ -354,6 +357,27 @@ object Migration extends MdcLoggable {
|
||||
}
|
||||
}
|
||||
|
||||
private def addAccountAccessConsumerId(): Boolean = {
|
||||
val name = nameOf(addAccountAccessConsumerId)
|
||||
runOnce(name) {
|
||||
MigrationOfAccountAccessAddedConsumerId.addAccountAccessConsumerId(name)
|
||||
}
|
||||
}
|
||||
|
||||
private def alterWebhookColumnUrlLength(): Boolean = {
|
||||
val name = nameOf(alterWebhookColumnUrlLength)
|
||||
runOnce(name) {
|
||||
MigrationOfWebhookUrlFieldLength.alterColumnUrlLength(name)
|
||||
}
|
||||
}
|
||||
|
||||
private def dropConsentAuthContextDropIndex(): Boolean = {
|
||||
val name = nameOf(dropConsentAuthContextDropIndex)
|
||||
runOnce(name) {
|
||||
MigrationOfConsentAuthContextDropIndex.dropUniqueIndex(name)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -0,0 +1,65 @@
|
||||
package code.api.util.migration
|
||||
|
||||
import code.api.Constant.ALL_CONSUMERS
|
||||
import code.api.util.APIUtil
|
||||
import code.api.util.migration.Migration.{DbFunction, saveLog}
|
||||
import code.views.system.AccountAccess
|
||||
import net.liftweb.common.Full
|
||||
import net.liftweb.mapper.{DB, Schemifier}
|
||||
import net.liftweb.util.DefaultConnectionIdentifier
|
||||
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.{ZoneId, ZonedDateTime}
|
||||
|
||||
object MigrationOfAccountAccessAddedConsumerId {
|
||||
|
||||
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 addAccountAccessConsumerId(name: String): Boolean = {
|
||||
DbFunction.tableExists(AccountAccess, (DB.use(DefaultConnectionIdentifier){ conn => conn})) match {
|
||||
case true =>
|
||||
val startDate = System.currentTimeMillis()
|
||||
val commitId: String = APIUtil.gitCommit
|
||||
var isSuccessful = false
|
||||
|
||||
val executedSql =
|
||||
DbFunction.maybeWrite(true, Schemifier.infoF _, DB.use(DefaultConnectionIdentifier){ conn => conn}) {
|
||||
APIUtil.getPropsValue("db.driver") match {
|
||||
case Full(value) if value.contains("com.microsoft.sqlserver.jdbc.SQLServerDriver") =>
|
||||
() =>
|
||||
s"""
|
||||
|ALTER TABLE accountaccess ADD COLUMN "consumer_id" character varying(255) DEFAULT '$ALL_CONSUMERS';
|
||||
|DROP INDEX IF EXISTS accountaccess_bank_id_account_id_view_fk_user_fk;
|
||||
|""".stripMargin
|
||||
case _ =>
|
||||
() =>
|
||||
s"""
|
||||
|ALTER TABLE accountaccess ADD COLUMN "consumer_id" character varying(255) DEFAULT '$ALL_CONSUMERS';
|
||||
|DROP INDEX IF EXISTS accountaccess_bank_id_account_id_view_fk_user_fk;
|
||||
|""".stripMargin
|
||||
}
|
||||
}
|
||||
|
||||
val endDate = System.currentTimeMillis()
|
||||
val comment: String =
|
||||
s"""Executed SQL:
|
||||
|$executedSql
|
||||
|""".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"""${AccountAccess._dbTableNameLC} table does not exist""".stripMargin
|
||||
saveLog(name, commitId, isSuccessful, startDate, endDate, comment)
|
||||
isSuccessful
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
package code.api.util.migration
|
||||
|
||||
import code.api.util.APIUtil
|
||||
import code.api.util.migration.Migration.{DbFunction, saveLog}
|
||||
import code.context.MappedConsentAuthContext
|
||||
import net.liftweb.common.Full
|
||||
import net.liftweb.mapper.{DB, Schemifier}
|
||||
import net.liftweb.util.DefaultConnectionIdentifier
|
||||
import scalikejdbc.DB.CPContext
|
||||
import scalikejdbc._
|
||||
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.{ZoneId, ZonedDateTime}
|
||||
|
||||
object MigrationOfConsentAuthContextDropIndex {
|
||||
|
||||
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'")
|
||||
|
||||
private lazy val getDbConnectionParameters: (String, String, String) = {
|
||||
val dbUrl = APIUtil.getPropsValue("db.url") openOr "jdbc:h2:mem:OBPTest;DB_CLOSE_DELAY=-1"
|
||||
val username = dbUrl.split(";").filter(_.contains("user")).toList.headOption.map(_.split("=")(1))
|
||||
val password = dbUrl.split(";").filter(_.contains("password")).toList.headOption.map(_.split("=")(1))
|
||||
val dbUser = APIUtil.getPropsValue("db.user").orElse(username)
|
||||
val dbPassword = APIUtil.getPropsValue("db.password").orElse(password)
|
||||
(dbUrl, dbUser.getOrElse(""), dbPassword.getOrElse(""))
|
||||
}
|
||||
|
||||
/**
|
||||
* this connection pool context corresponding db.url in default.props
|
||||
*/
|
||||
implicit lazy val context: CPContext = {
|
||||
val settings = ConnectionPoolSettings(
|
||||
initialSize = 5,
|
||||
maxSize = 20,
|
||||
connectionTimeoutMillis = 3000L,
|
||||
validationQuery = "select 1",
|
||||
connectionPoolFactoryName = "commons-dbcp2"
|
||||
)
|
||||
val (dbUrl, user, password) = getDbConnectionParameters
|
||||
val dbName = "DB_NAME" // corresponding props db.url DB
|
||||
ConnectionPool.add(dbName, dbUrl, user, password, settings)
|
||||
val connectionPool = ConnectionPool.get(dbName)
|
||||
MultipleConnectionPoolContext(ConnectionPool.DEFAULT_NAME -> connectionPool)
|
||||
}
|
||||
|
||||
def dropUniqueIndex(name: String): Boolean = {
|
||||
DbFunction.tableExists(MappedConsentAuthContext, (DB.use(DefaultConnectionIdentifier){ conn => conn})) match {
|
||||
case true =>
|
||||
val startDate = System.currentTimeMillis()
|
||||
val commitId: String = APIUtil.gitCommit
|
||||
var isSuccessful = false
|
||||
|
||||
val executedSql =
|
||||
DbFunction.maybeWrite(true, Schemifier.infoF _, DB.use(DefaultConnectionIdentifier){ conn => conn}) {
|
||||
APIUtil.getPropsValue("db.driver") match {
|
||||
case Full(value) if value.contains("com.microsoft.sqlserver.jdbc.SQLServerDriver") =>
|
||||
() => "DROP INDEX IF EXISTS consentauthcontext_consentid_key_c;"
|
||||
case _ =>
|
||||
() => "DROP INDEX IF EXISTS consentauthcontext_consentid_key_c;"
|
||||
}
|
||||
}
|
||||
|
||||
val endDate = System.currentTimeMillis()
|
||||
val comment: String =
|
||||
s"""Executed SQL:
|
||||
|$executedSql
|
||||
|""".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"""${MappedConsentAuthContext._dbTableNameLC} table does not exist""".stripMargin
|
||||
saveLog(name, commitId, isSuccessful, startDate, endDate, comment)
|
||||
isSuccessful
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,7 +3,7 @@ package code.api.util.migration
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.{ZoneId, ZonedDateTime}
|
||||
|
||||
import code.api.Constant.{INCOMING_ACCOUNT_ID, OUTGOING_ACCOUNT_ID}
|
||||
import code.api.Constant.{INCOMING_SETTLEMENT_ACCOUNT_ID, OUTGOING_SETTLEMENT_ACCOUNT_ID}
|
||||
import code.api.util.APIUtil
|
||||
import code.api.util.migration.Migration.saveLog
|
||||
import code.model.dataAccess.{MappedBank, MappedBankAccount}
|
||||
@ -30,17 +30,17 @@ object MigrationOfSettlementAccounts {
|
||||
|
||||
// Insert the default settlement accounts if they doesn't exist
|
||||
|
||||
val insertedIncomingSettlementAccount = MappedBankAccount.find(By(MappedBankAccount.bank, bank.bankId.value), By(MappedBankAccount.theAccountId, INCOMING_ACCOUNT_ID)) match {
|
||||
val insertedIncomingSettlementAccount = MappedBankAccount.find(By(MappedBankAccount.bank, bank.bankId.value), By(MappedBankAccount.theAccountId, INCOMING_SETTLEMENT_ACCOUNT_ID)) match {
|
||||
case Full(_) =>
|
||||
Try {
|
||||
Console.println(s"Settlement BankAccount(${bank.bankId.value}, $INCOMING_ACCOUNT_ID) found.")
|
||||
Console.println(s"Settlement BankAccount(${bank.bankId.value}, $INCOMING_SETTLEMENT_ACCOUNT_ID) found.")
|
||||
0
|
||||
}
|
||||
case _ =>
|
||||
Try {
|
||||
MappedBankAccount.create
|
||||
.bank(bank.bankId.value)
|
||||
.theAccountId(INCOMING_ACCOUNT_ID)
|
||||
.theAccountId(INCOMING_SETTLEMENT_ACCOUNT_ID)
|
||||
.accountCurrency("EUR")
|
||||
.accountBalance(0)
|
||||
.kind("SETTLEMENT")
|
||||
@ -48,22 +48,22 @@ object MigrationOfSettlementAccounts {
|
||||
.accountName("Default incoming settlement account")
|
||||
.accountLabel("Settlement account: Do not delete!")
|
||||
.saveMe()
|
||||
Console.println(s"Creating settlement BankAccount(${bank.bankId.value}, $INCOMING_ACCOUNT_ID).")
|
||||
Console.println(s"Creating settlement BankAccount(${bank.bankId.value}, $INCOMING_SETTLEMENT_ACCOUNT_ID).")
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
val insertedOutgoingSettlementAccount = MappedBankAccount.find(By(MappedBankAccount.bank, bank.bankId.value), By(MappedBankAccount.theAccountId, OUTGOING_ACCOUNT_ID)) match {
|
||||
val insertedOutgoingSettlementAccount = MappedBankAccount.find(By(MappedBankAccount.bank, bank.bankId.value), By(MappedBankAccount.theAccountId, OUTGOING_SETTLEMENT_ACCOUNT_ID)) match {
|
||||
case Full(_) =>
|
||||
Try {
|
||||
Console.println(s"Settlement BankAccount(${bank.bankId.value}, $OUTGOING_ACCOUNT_ID) found.")
|
||||
Console.println(s"Settlement BankAccount(${bank.bankId.value}, $OUTGOING_SETTLEMENT_ACCOUNT_ID) found.")
|
||||
0
|
||||
}
|
||||
case _ =>
|
||||
Try {
|
||||
MappedBankAccount.create
|
||||
.bank(bank.bankId.value)
|
||||
.theAccountId(OUTGOING_ACCOUNT_ID)
|
||||
.theAccountId(OUTGOING_SETTLEMENT_ACCOUNT_ID)
|
||||
.accountCurrency("EUR")
|
||||
.accountBalance(0)
|
||||
.kind("SETTLEMENT")
|
||||
@ -71,7 +71,7 @@ object MigrationOfSettlementAccounts {
|
||||
.accountName("Default outgoing settlement account")
|
||||
.accountLabel("Settlement account: Do not delete!")
|
||||
.saveMe()
|
||||
Console.println(s"Creating settlement BankAccount(${bank.bankId.value}, $OUTGOING_ACCOUNT_ID).")
|
||||
Console.println(s"Creating settlement BankAccount(${bank.bankId.value}, $OUTGOING_SETTLEMENT_ACCOUNT_ID).")
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,6 +91,8 @@ object TableViewDefinition {
|
||||
|
||||
viewDefinition
|
||||
.canAddCounterparty_(view.canAddCounterparty)
|
||||
.canGetCounterparty_(view.canGetCounterparty)
|
||||
.canDeleteCounterparty_(view.canDeleteCounterparty)
|
||||
.canDeleteCorporateLocation_(view.canDeleteCorporateLocation)
|
||||
.canDeletePhysicalLocation_(view.canDeletePhysicalLocation)
|
||||
.canEditOwnerComment_(view.canEditOwnerComment)
|
||||
|
||||
@ -0,0 +1,72 @@
|
||||
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.webhook.MappedAccountWebhook
|
||||
import code.webhook.BankAccountNotificationWebhook
|
||||
import code.webhook.SystemAccountNotificationWebhook
|
||||
import net.liftweb.common.Full
|
||||
import net.liftweb.mapper.{DB, Schemifier}
|
||||
import net.liftweb.util.DefaultConnectionIdentifier
|
||||
|
||||
object MigrationOfWebhookUrlFieldLength {
|
||||
|
||||
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 alterColumnUrlLength(name: String): Boolean = {
|
||||
DbFunction.tableExists(SystemAccountNotificationWebhook, (DB.use(DefaultConnectionIdentifier){ conn => conn})) &&
|
||||
DbFunction.tableExists(BankAccountNotificationWebhook, (DB.use(DefaultConnectionIdentifier){ conn => conn}))&&
|
||||
DbFunction.tableExists(MappedAccountWebhook, (DB.use(DefaultConnectionIdentifier){ conn => conn}))
|
||||
match {
|
||||
case true =>
|
||||
val startDate = System.currentTimeMillis()
|
||||
val commitId: String = APIUtil.gitCommit
|
||||
var isSuccessful = false
|
||||
|
||||
val executedSql =
|
||||
DbFunction.maybeWrite(true, Schemifier.infoF _, DB.use(DefaultConnectionIdentifier){ conn => conn}) {
|
||||
APIUtil.getPropsValue("db.driver") match {
|
||||
case Full(value) if value.contains("com.microsoft.sqlserver.jdbc.SQLServerDriver") =>
|
||||
() =>
|
||||
"""
|
||||
|ALTER TABLE SystemAccountNotificationWebhook ALTER COLUMN Url varchar(1024);
|
||||
|ALTER TABLE BankAccountNotificationWebhook ALTER COLUMN Url varchar(1024);
|
||||
|ALTER TABLE MappedAccountWebhook ALTER COLUMN mUrl varchar(1024);
|
||||
|""".stripMargin
|
||||
case _ =>
|
||||
() =>
|
||||
"""
|
||||
|ALTER TABLE SystemAccountNotificationWebhook ALTER COLUMN Url TYPE character varying(1024);
|
||||
|ALTER TABLE BankAccountNotificationWebhook ALTER COLUMN Url TYPE character varying(1024);
|
||||
|ALTER TABLE MappedAccountWebhook ALTER COLUMN mUrl TYPE character varying(1024);
|
||||
|""".stripMargin
|
||||
}
|
||||
}
|
||||
|
||||
val endDate = System.currentTimeMillis()
|
||||
val comment: String =
|
||||
s"""Executed SQL:
|
||||
|$executedSql
|
||||
|""".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"""${MappedAccountWebhook._dbTableNameLC} table does not exist or
|
||||
|${BankAccountNotificationWebhook._dbTableNameLC} table does not exist or
|
||||
|${SystemAccountNotificationWebhook._dbTableNameLC} table does not exist""".stripMargin
|
||||
saveLog(name, commitId, isSuccessful, startDate, endDate, comment)
|
||||
isSuccessful
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -637,11 +637,6 @@ trait APIMethods210 {
|
||||
existingTransactionRequest.challenge.id.equals(challengeAnswerJson.id)
|
||||
}
|
||||
|
||||
//Check the allowed attemps, Note: not support yet, the default value is 3
|
||||
_ <- Helper.booleanToFuture(s"${AllowedAttemptsUsedUp}", cc=callContext) {
|
||||
existingTransactionRequest.challenge.allowed_attempts > 0
|
||||
}
|
||||
|
||||
//Check the challenge type, Note: not support yet, the default value is SANDBOX_TAN
|
||||
_ <- Helper.booleanToFuture(s"${InvalidChallengeType} ", cc=callContext) {
|
||||
existingTransactionRequest.challenge.challenge_type == TransactionChallengeTypes.OTP_VIA_API.toString
|
||||
|
||||
@ -312,8 +312,8 @@ trait APIMethods220 {
|
||||
(Full(u), callContext) <- authenticatedAccess(cc)
|
||||
(account, callContext) <- NewStyle.function.checkBankAccountExists(bankId, accountId, callContext)
|
||||
view <- NewStyle.function.checkViewAccessAndReturnView(viewId, BankIdAccountId(account.bankId, account.accountId), Some(u), callContext)
|
||||
_ <- Helper.booleanToFuture(failMsg = s"${NoViewPermission}canAddCounterparty", cc=callContext) {
|
||||
view.canAddCounterparty == true
|
||||
_ <- Helper.booleanToFuture(failMsg = s"${NoViewPermission} can_get_counterparty", cc=callContext) {
|
||||
view.canGetCounterparty == true
|
||||
}
|
||||
(counterparties, callContext) <- NewStyle.function.getCounterparties(bankId,accountId,viewId, callContext)
|
||||
//Here we need create the metadata for all the explicit counterparties. maybe show them in json response.
|
||||
@ -362,8 +362,8 @@ trait APIMethods220 {
|
||||
(Full(u), callContext) <- authenticatedAccess(cc)
|
||||
(account, callContext) <- NewStyle.function.checkBankAccountExists(bankId, accountId, callContext)
|
||||
view <- NewStyle.function.checkViewAccessAndReturnView(viewId, BankIdAccountId(account.bankId, account.accountId), Some(u), callContext)
|
||||
_ <- Helper.booleanToFuture(failMsg = s"${NoViewPermission}canAddCounterparty", cc=callContext) {
|
||||
view.canAddCounterparty == true
|
||||
_ <- Helper.booleanToFuture(failMsg = s"${NoViewPermission}can_get_counterparty", cc=callContext) {
|
||||
view.canGetCounterparty == true
|
||||
}
|
||||
counterpartyMetadata <- NewStyle.function.getMetadata(bankId, accountId, counterpartyId.value, callContext)
|
||||
(counterparty, callContext) <- NewStyle.function.getCounterpartyTrait(bankId, accountId, counterpartyId.value, callContext)
|
||||
|
||||
@ -1341,10 +1341,7 @@ trait APIMethods300 {
|
||||
fullBoxOrException(Empty ?~! BranchesNotFound)
|
||||
case Full((List(), callContext)) =>
|
||||
Full(List())
|
||||
case Full((list, callContext)) =>
|
||||
val branchesWithLicense = for { branch <- list if branch.meta.license.name.size > 3 } yield branch
|
||||
if (branchesWithLicense.size == 0) fullBoxOrException(Empty ?~! BranchesNotFoundLicense)
|
||||
else Full(branchesWithLicense)
|
||||
case Full((list, callContext)) => Full(list)
|
||||
case Failure(msg, _, _) => fullBoxOrException(Empty ?~! msg)
|
||||
case ParamFailure(msg,_,_,_) => fullBoxOrException(Empty ?~! msg)
|
||||
} map { unboxFull(_) } map {
|
||||
@ -1465,10 +1462,7 @@ trait APIMethods300 {
|
||||
fullBoxOrException(Empty ?~! atmsNotFound)
|
||||
case Full((List(), callContext)) =>
|
||||
Full(List())
|
||||
case Full((list, _)) =>
|
||||
val branchesWithLicense = for { branch <- list if branch.meta.license.name.size > 3 } yield branch
|
||||
if (branchesWithLicense.size == 0) fullBoxOrException(Empty ?~! atmsNotFoundLicense)
|
||||
else Full(branchesWithLicense)
|
||||
case Full((list, _)) => Full(list)
|
||||
case Failure(msg, _, _) => fullBoxOrException(Empty ?~! msg)
|
||||
case ParamFailure(msg,_,_,_) => fullBoxOrException(Empty ?~! msg)
|
||||
} map { unboxFull(_) } map {
|
||||
@ -1812,7 +1806,7 @@ trait APIMethods300 {
|
||||
val msg = s"$InvalidJsonFormat The Json body should be the $CreateEntitlementRequestJSON "
|
||||
x => unboxFullOrFail(x, callContext, msg)
|
||||
}
|
||||
_ <- Future { if (postedData.bank_id == "") Full() else NewStyle.function.getBank(BankId(postedData.bank_id), callContext)}
|
||||
_ <- if (postedData.bank_id == "") Future.successful("") else NewStyle.function.getBank(BankId(postedData.bank_id), callContext)
|
||||
|
||||
_ <- Helper.booleanToFuture(failMsg = IncorrectRoleName + postedData.role_name + ". Possible roles are " + ApiRole.availableRoles.sorted.mkString(", "), cc=callContext) {
|
||||
availableRoles.exists(_ == postedData.role_name)
|
||||
|
||||
@ -1424,8 +1424,8 @@ trait APIMethods310 {
|
||||
postedData <- NewStyle.function.tryons(failMsg, 400, callContext) {
|
||||
json.extract[PostUserAuthContextJson]
|
||||
}
|
||||
(_, callContext) <- NewStyle.function.findByUserId(userId, callContext)
|
||||
(userAuthContext, callContext) <- NewStyle.function.createUserAuthContext(userId, postedData.key, postedData.value, callContext)
|
||||
(user, callContext) <- NewStyle.function.findByUserId(userId, callContext)
|
||||
(userAuthContext, callContext) <- NewStyle.function.createUserAuthContext(user, postedData.key, postedData.value, callContext)
|
||||
} yield {
|
||||
(JSONFactory310.createUserAuthContextJson(userAuthContext), HttpCode.`201`(callContext))
|
||||
}
|
||||
@ -1538,8 +1538,8 @@ trait APIMethods310 {
|
||||
for {
|
||||
(Full(u), callContext) <- authenticatedAccess(cc)
|
||||
_ <- NewStyle.function.hasEntitlement("", u.userId, canDeleteUserAuthContext, callContext)
|
||||
(_, callContext) <- NewStyle.function.findByUserId(userId, callContext)
|
||||
(userAuthContext, callContext) <- NewStyle.function.deleteUserAuthContextById(userAuthContextId, callContext)
|
||||
(user, callContext) <- NewStyle.function.findByUserId(userId, callContext)
|
||||
(userAuthContext, callContext) <- NewStyle.function.deleteUserAuthContextById(user, userAuthContextId, callContext)
|
||||
} yield {
|
||||
(Full(userAuthContext), HttpCode.`200`(callContext))
|
||||
}
|
||||
@ -3535,9 +3535,14 @@ trait APIMethods310 {
|
||||
postConsentEmailJson <- NewStyle.function.tryons(failMsg, 400, callContext) {
|
||||
json.extract[PostConsentEmailJsonV310]
|
||||
}
|
||||
params = PlainMailBodyType(challengeText) :: List(To(postConsentEmailJson.email))
|
||||
_ <- Future{Mailer.sendMail(From("challenge@tesobe.com"), Subject("OBP Consent Challenge"), params :_*)}
|
||||
} yield Future{true}
|
||||
(Full(status), callContext) <- Connector.connector.vend.sendCustomerNotification(
|
||||
StrongCustomerAuthentication.EMAIL,
|
||||
postConsentEmailJson.email,
|
||||
Some("OBP Consent Challenge"),
|
||||
challengeText,
|
||||
callContext
|
||||
)
|
||||
} yield Future{status}
|
||||
case v if v == StrongCustomerAuthentication.SMS.toString => // Not implemented
|
||||
for {
|
||||
failMsg <- Future {
|
||||
@ -3547,27 +3552,15 @@ trait APIMethods310 {
|
||||
json.extract[PostConsentPhoneJsonV310]
|
||||
}
|
||||
phoneNumber = postConsentPhoneJson.phone_number
|
||||
failMsg =s"$MissingPropsValueAtThisInstance sca_phone_api_key"
|
||||
smsProviderApiKey <- NewStyle.function.tryons(failMsg, 400, callContext) {
|
||||
APIUtil.getPropsValue("sca_phone_api_key").openOrThrowException(s"")
|
||||
}
|
||||
failMsg = s"$MissingPropsValueAtThisInstance sca_phone_api_secret"
|
||||
smsProviderApiSecret <- NewStyle.function.tryons(failMsg, 400, callContext) {
|
||||
APIUtil.getPropsValue("sca_phone_api_secret").openOrThrowException(s"")
|
||||
}
|
||||
client = new NexmoClient.Builder()
|
||||
.apiKey(smsProviderApiKey)
|
||||
.apiSecret(smsProviderApiSecret)
|
||||
.build();
|
||||
messageText = challengeText;
|
||||
message = new TextMessage("OBP-API", phoneNumber, messageText);
|
||||
response <- Future{client.getSmsClient().submitMessage(message)}
|
||||
failMsg = s"$SmsServerNotResponding: $phoneNumber. Or Please to use EMAIL first."
|
||||
_ <- Helper.booleanToFuture(failMsg, cc=callContext) {
|
||||
response.getMessages.get(0).getStatus == com.nexmo.client.sms.MessageStatus.OK
|
||||
}
|
||||
} yield Future{true}
|
||||
case _ =>Future{true}
|
||||
(Full(status), callContext) <- Connector.connector.vend.sendCustomerNotification(
|
||||
StrongCustomerAuthentication.SMS,
|
||||
phoneNumber,
|
||||
None,
|
||||
challengeText,
|
||||
callContext
|
||||
)
|
||||
} yield Future{status}
|
||||
case _ =>Future{"Success"}
|
||||
}
|
||||
} yield {
|
||||
(ConsentJsonV310(createdConsent.consentId, consentJWT, createdConsent.status), HttpCode.`201`(callContext))
|
||||
@ -3790,11 +3783,12 @@ trait APIMethods310 {
|
||||
json.extract[PostUserAuthContextUpdateJsonV310]
|
||||
}
|
||||
(userAuthContextUpdate, callContext) <- NewStyle.function.checkAnswer(authContextUpdateId, postUserAuthContextUpdateJson.answer, callContext)
|
||||
(user, callContext) <- NewStyle.function.getUserByUserId(userAuthContextUpdate.userId, callContext)
|
||||
(_, callContext) <-
|
||||
userAuthContextUpdate.status match {
|
||||
case status if status == UserAuthContextUpdateStatus.ACCEPTED.toString =>
|
||||
NewStyle.function.createUserAuthContext(
|
||||
userAuthContextUpdate.userId,
|
||||
user,
|
||||
userAuthContextUpdate.key,
|
||||
userAuthContextUpdate.value,
|
||||
callContext).map(x => (Some(x._1), x._2))
|
||||
@ -3807,7 +3801,7 @@ trait APIMethods310 {
|
||||
NewStyle.function.getOCreateUserCustomerLink(
|
||||
bankId,
|
||||
userAuthContextUpdate.value, // Customer number
|
||||
userAuthContextUpdate.userId,
|
||||
user.userId,
|
||||
callContext
|
||||
)
|
||||
case _ =>
|
||||
@ -4638,7 +4632,7 @@ trait APIMethods310 {
|
||||
UnknownError
|
||||
),
|
||||
List(apiTagCustomer, apiTagNewStyle),
|
||||
Some(canUpdateCustomerCreditRatingAndSource :: Nil)
|
||||
Some(canUpdateCustomerCreditRatingAndSource :: canUpdateCustomerCreditRatingAndSourceAtAnyBank :: Nil)
|
||||
)
|
||||
|
||||
lazy val updateCustomerCreditRatingAndSource : OBPEndpoint = {
|
||||
@ -4647,7 +4641,7 @@ trait APIMethods310 {
|
||||
for {
|
||||
(Full(u), callContext) <- authenticatedAccess(cc)
|
||||
(_, callContext) <- NewStyle.function.getBank(bankId, callContext)
|
||||
_ <- NewStyle.function.hasEntitlement(bankId.value, u.userId, canUpdateCustomerCreditRatingAndSource, callContext)
|
||||
_ <- NewStyle.function.hasAtLeastOneEntitlement(bankId.value, u.userId, List(canUpdateCustomerCreditRatingAndSource,canUpdateCustomerCreditRatingAndSourceAtAnyBank), callContext)
|
||||
failMsg = s"$InvalidJsonFormat The Json body should be the $PutUpdateCustomerCreditRatingAndSourceJsonV310 "
|
||||
putData <- NewStyle.function.tryons(failMsg, 400, callContext) {
|
||||
json.extract[PutUpdateCustomerCreditRatingAndSourceJsonV310]
|
||||
|
||||
@ -305,7 +305,7 @@ case class UserAuthContextJson(
|
||||
user_id: String,
|
||||
key: String,
|
||||
value: String,
|
||||
timeStamp: Date
|
||||
time_stamp: Date
|
||||
)
|
||||
case class UserAuthContextUpdateJson(
|
||||
user_auth_context_update_id: String,
|
||||
@ -994,7 +994,7 @@ object JSONFactory310{
|
||||
user_id = userAuthContext.userId,
|
||||
key = userAuthContext.key,
|
||||
value = userAuthContext.value,
|
||||
timeStamp = userAuthContext.timeStamp
|
||||
time_stamp = userAuthContext.timeStamp
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -37,7 +37,7 @@ import code.api.v1_2_1.{BankRoutingJsonV121, JSONFactory, UserJSONV121, ViewJSON
|
||||
import code.api.v1_4_0.JSONFactory1_4_0.{LocationJsonV140, MetaJsonV140, TransactionRequestAccountJsonV140, transformToLocationFromV140, transformToMetaFromV140}
|
||||
import code.api.v2_0_0.JSONFactory200.UserJsonV200
|
||||
import code.api.v2_0_0.{CreateEntitlementJSON, EntitlementJSONs, JSONFactory200, TransactionRequestChargeJsonV200}
|
||||
import code.api.v2_1_0.{IbanJson, JSONFactory210, PostCounterpartyBespokeJson, ResourceUserJSON}
|
||||
import code.api.v2_1_0.{IbanJson, JSONFactory210, PostCounterpartyBespokeJson, ResourceUserJSON, TransactionRequestBodyCounterpartyJSON}
|
||||
import code.api.v2_2_0.CounterpartyMetadataJson
|
||||
import code.api.v3_0_0.JSONFactory300._
|
||||
import code.api.v3_0_0._
|
||||
@ -58,6 +58,7 @@ import code.transactionrequests.TransactionRequests.TransactionChallengeTypes
|
||||
import code.userlocks.UserLocks
|
||||
import code.users.{UserAgreement, UserAttribute, UserInvitation}
|
||||
import code.views.system.AccountAccess
|
||||
import code.webhook.{AccountWebhook, BankAccountNotificationWebhookTrait, SystemAccountNotificationWebhookTrait}
|
||||
import com.openbankproject.commons.model.{DirectDebitTrait, ProductFeeTrait, _}
|
||||
import net.liftweb.common.{Box, Full}
|
||||
import net.liftweb.json.JValue
|
||||
@ -389,6 +390,26 @@ case class TransactionRequestBodySEPAJsonV400(
|
||||
future_date: Option[String] = None,
|
||||
reasons: Option[List[TransactionRequestReasonJsonV400]] = None
|
||||
) extends TransactionRequestCommonBodyJSON
|
||||
case class PostSimpleCounterpartyJson400(
|
||||
name: String,
|
||||
description: String,
|
||||
other_bank_routing_scheme: String,
|
||||
other_bank_routing_address: String,
|
||||
other_account_routing_scheme: String,
|
||||
other_account_routing_address: String,
|
||||
other_account_secondary_routing_scheme: String,
|
||||
other_account_secondary_routing_address: String,
|
||||
other_branch_routing_scheme: String,
|
||||
other_branch_routing_address: String
|
||||
)
|
||||
|
||||
case class TransactionRequestBodySimpleJsonV400(
|
||||
to: PostSimpleCounterpartyJson400,
|
||||
value: AmountOfMoneyJsonV121,
|
||||
description: String,
|
||||
charge_policy: String,
|
||||
future_date: Option[String] = None
|
||||
) extends TransactionRequestCommonBodyJSON
|
||||
|
||||
case class TransactionRequestReasonJsonV400(
|
||||
code: String,
|
||||
@ -559,7 +580,8 @@ case class DatabaseInfoJson(product_name: String, product_version: String)
|
||||
case class ChallengeJson(
|
||||
challenge_id: String,
|
||||
transaction_request_id: String,
|
||||
expected_user_id: String
|
||||
expected_user_id: String,
|
||||
allowed_attempts: Int
|
||||
)
|
||||
|
||||
case class SettlementAccountRequestJson(
|
||||
@ -1003,6 +1025,31 @@ case class CustomerMessagesJsonV400(
|
||||
messages: List[CustomerMessageJsonV400]
|
||||
)
|
||||
|
||||
case class AccountNotificationWebhookPostJson(
|
||||
url: String,
|
||||
http_method: String,
|
||||
http_protocol: String,
|
||||
)
|
||||
|
||||
case class SystemAccountNotificationWebhookJson(
|
||||
webhook_id: String,
|
||||
trigger_name: String,
|
||||
url: String,
|
||||
http_method: String,
|
||||
http_protocol: String,
|
||||
created_by_user_id: String
|
||||
)
|
||||
|
||||
case class BankAccountNotificationWebhookJson(
|
||||
webhook_id: String,
|
||||
bank_id: String,
|
||||
trigger_name: String,
|
||||
url: String,
|
||||
http_method: String,
|
||||
http_protocol: String,
|
||||
created_by_user_id: String,
|
||||
)
|
||||
|
||||
case class CustomerMessageJsonV400(
|
||||
id: String,
|
||||
date: Date,
|
||||
@ -1186,7 +1233,7 @@ object JSONFactory400 {
|
||||
case _ => ""
|
||||
}
|
||||
challenges.map(
|
||||
e => ChallengeJsonV400(id = stringOrNull(e.challenge_id), user_id = e.expected_user_id, allowed_attempts = tr.challenge.allowed_attempts, challenge_type = stringOrNull(tr.challenge.challenge_type), link = link)
|
||||
e => ChallengeJsonV400(id = stringOrNull(e.challenge_id), user_id = e.expected_user_id, allowed_attempts = e.allowed_attempts, challenge_type = stringOrNull(tr.challenge.challenge_type), link = link)
|
||||
)
|
||||
}
|
||||
// catch { case _ : Throwable => ChallengeJSON (id = "", allowed_attempts = 0, challenge_type = "")}
|
||||
@ -1911,7 +1958,29 @@ object JSONFactory400 {
|
||||
def createCustomersMinimalJson(customers : List[Customer]) : CustomersMinimalJsonV400 = {
|
||||
CustomersMinimalJsonV400(customers.map(i => CustomerMinimalJsonV400(i.bankId, i.customerId)))
|
||||
}
|
||||
|
||||
def createSystemLevelAccountWebhookJsonV400(wh: SystemAccountNotificationWebhookTrait) = {
|
||||
SystemAccountNotificationWebhookJson(
|
||||
webhook_id = wh.webhookId,
|
||||
trigger_name = wh.triggerName,
|
||||
url = wh.url,
|
||||
http_method = wh.httpMethod,
|
||||
http_protocol = wh.httpProtocol,
|
||||
created_by_user_id = wh.createdByUserId
|
||||
)
|
||||
}
|
||||
|
||||
def createBankLevelAccountWebhookJsonV400(wh: BankAccountNotificationWebhookTrait) = {
|
||||
BankAccountNotificationWebhookJson(
|
||||
webhook_id = wh.webhookId,
|
||||
bank_id = wh.bankId,
|
||||
trigger_name = wh.triggerName,
|
||||
url = wh.url,
|
||||
http_method = wh.httpMethod,
|
||||
http_protocol = wh.httpProtocol,
|
||||
created_by_user_id = wh.createdByUserId
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -36,9 +36,7 @@ import code.api.v2_1_0.APIMethods210
|
||||
import code.api.v2_2_0.APIMethods220
|
||||
import code.api.v3_0_0.APIMethods300
|
||||
import code.api.v3_0_0.custom.CustomAPIMethods300
|
||||
import code.api.v3_1_0.OBPAPI3_1_0.Implementations3_1_0
|
||||
import code.api.v3_1_0.{APIMethods310, OBPAPI3_1_0}
|
||||
import code.api.v4_0_0.dynamic.DynamicEndpoints
|
||||
import code.util.Helper.MdcLoggable
|
||||
import com.github.dwickern.macros.NameOf.nameOf
|
||||
import com.openbankproject.commons.util.ApiVersion
|
||||
@ -57,7 +55,7 @@ object OBPAPI4_0_0 extends OBPRestHelper with APIMethods130 with APIMethods140 w
|
||||
|
||||
// Possible Endpoints from 4.0.0, exclude one endpoint use - method,exclude multiple endpoints use -- method,
|
||||
// e.g getEndpoints(Implementations4_0_0) -- List(Implementations4_0_0.genericEndpoint, Implementations4_0_0.root)
|
||||
val endpointsOf4_0_0 = getEndpoints(Implementations4_0_0) - Implementations4_0_0.genericEndpoint - Implementations4_0_0.dynamicEndpoint
|
||||
val endpointsOf4_0_0 = getEndpoints(Implementations4_0_0)
|
||||
|
||||
lazy val excludeEndpoints =
|
||||
nameOf(Implementations1_2_1.addPermissionForUserForBankAccountForMultipleViews) ::
|
||||
@ -77,30 +75,12 @@ object OBPAPI4_0_0 extends OBPRestHelper with APIMethods130 with APIMethods140 w
|
||||
private val endpoints: List[OBPEndpoint] = OBPAPI3_1_0.routes ++ endpointsOf4_0_0
|
||||
|
||||
// Filter the possible endpoints by the disabled / enabled Props settings and add them together
|
||||
val routes : List[OBPEndpoint] =
|
||||
APIUtil.dynamicEndpointStub :: // corresponding all dynamic generated endpoints's OBPEndpoint
|
||||
Implementations4_0_0.root :: // For now we make this mandatory
|
||||
val routes : List[OBPEndpoint] = Implementations4_0_0.root :: // For now we make this mandatory
|
||||
getAllowedEndpoints(endpoints, allResourceDocs)
|
||||
|
||||
// register v4.0.0 apis first, Make them available for use!
|
||||
registerRoutes(routes, allResourceDocs, apiPrefix, true)
|
||||
|
||||
//This is the dynamic endpoints which are created by dynamic entities
|
||||
oauthServe(apiPrefix{Implementations4_0_0.genericEndpoint}, None)
|
||||
//This is for the dynamic endpoints which are created by dynamic swagger files
|
||||
oauthServe(apiPrefix{Implementations4_0_0.dynamicEndpoint}, None)
|
||||
/**
|
||||
* Here is the place where we register the dynamicEndpoint, all the dynamic resource docs endpoints are here.
|
||||
* Actually, we only register one endpoint for all the dynamic resource docs endpoints.
|
||||
* For Liftweb, it just need to handle one endpoint,
|
||||
* all the router functionalities are in OBP code.
|
||||
* details: please also check code/api/v4_0_0/dynamic/DynamicEndpoints.findEndpoint method
|
||||
* NOTE: this must be the last one endpoint to register into Liftweb
|
||||
* Because firstly, Liftweb should look for the static endpoints --> then the dynamic ones.
|
||||
* This is for the dynamic endpoints which are createdy by dynamic resourceDocs
|
||||
*/
|
||||
oauthServe(apiPrefix{DynamicEndpoints.dynamicEndpoint}, None)
|
||||
|
||||
logger.info(s"version $version has been run! There are ${routes.length} routes.")
|
||||
|
||||
// specified response for OPTIONS request.
|
||||
|
||||
237
obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala
Normal file
237
obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala
Normal file
@ -0,0 +1,237 @@
|
||||
package code.api.v5_0_0
|
||||
|
||||
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._
|
||||
import code.api.util.APIUtil._
|
||||
import code.api.util.ApiRole.{CanCreateUserAuthContextUpdate, canCreateUserAuthContext, canGetUserAuthContext}
|
||||
import code.api.util.ApiTag._
|
||||
import code.api.util.ErrorMessages._
|
||||
import code.api.util.{ApiRole, NewStyle}
|
||||
import code.api.util.NewStyle.HttpCode
|
||||
import code.api.v3_1_0.{PostUserAuthContextJson, PostUserAuthContextUpdateJsonV310}
|
||||
import code.transactionrequests.TransactionRequests.TransactionRequestTypes.{apply => _}
|
||||
import code.util.Helper
|
||||
import com.github.dwickern.macros.NameOf.nameOf
|
||||
import com.openbankproject.commons.ExecutionContext.Implicits.global
|
||||
import com.openbankproject.commons.model.{BankId, UserAuthContextUpdateStatus}
|
||||
import com.openbankproject.commons.model.enums.StrongCustomerAuthentication
|
||||
import com.openbankproject.commons.util.ApiVersion
|
||||
import net.liftweb.common.Full
|
||||
import net.liftweb.http.rest.RestHelper
|
||||
|
||||
import scala.collection.immutable.{List, Nil}
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.concurrent.Future
|
||||
|
||||
trait APIMethods500 {
|
||||
self: RestHelper =>
|
||||
|
||||
val Implementations5_0_0 = new Implementations500()
|
||||
|
||||
class Implementations500 {
|
||||
|
||||
val implementedInApiVersion = ApiVersion.v5_0_0
|
||||
|
||||
private val staticResourceDocs = ArrayBuffer[ResourceDoc]()
|
||||
def resourceDocs = staticResourceDocs
|
||||
|
||||
val apiRelations = ArrayBuffer[ApiRelation]()
|
||||
val codeContext = CodeContext(staticResourceDocs, apiRelations)
|
||||
|
||||
staticResourceDocs += ResourceDoc(
|
||||
createUserAuthContext,
|
||||
implementedInApiVersion,
|
||||
nameOf(createUserAuthContext),
|
||||
"POST",
|
||||
"/users/USER_ID/auth-context",
|
||||
"Create User Auth Context",
|
||||
s"""Create User Auth Context. These key value pairs will be propagated over connector to adapter. Normally used for mapping OBP user and
|
||||
| Bank User/Customer.
|
||||
|${authenticationRequiredMessage(true)}
|
||||
|""",
|
||||
postUserAuthContextJson,
|
||||
userAuthContextJsonV500,
|
||||
List(
|
||||
UserNotLoggedIn,
|
||||
InvalidJsonFormat,
|
||||
CreateUserAuthContextError,
|
||||
UnknownError
|
||||
),
|
||||
List(apiTagUser, apiTagNewStyle),
|
||||
Some(List(canCreateUserAuthContext)))
|
||||
lazy val createUserAuthContext : OBPEndpoint = {
|
||||
case "users" :: userId ::"auth-context" :: Nil JsonPost json -> _ => {
|
||||
cc =>
|
||||
for {
|
||||
(Full(u), callContext) <- authenticatedAccess(cc)
|
||||
_ <- NewStyle.function.hasEntitlement("", u.userId, canCreateUserAuthContext, callContext)
|
||||
failMsg = s"$InvalidJsonFormat The Json body should be the $PostUserAuthContextJson "
|
||||
postedData <- NewStyle.function.tryons(failMsg, 400, callContext) {
|
||||
json.extract[PostUserAuthContextJson]
|
||||
}
|
||||
(user, callContext) <- NewStyle.function.findByUserId(userId, callContext)
|
||||
(userAuthContext, callContext) <- NewStyle.function.createUserAuthContext(user, postedData.key, postedData.value, callContext)
|
||||
} yield {
|
||||
(JSONFactory500.createUserAuthContextJson(userAuthContext), HttpCode.`201`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
staticResourceDocs += ResourceDoc(
|
||||
getUserAuthContexts,
|
||||
implementedInApiVersion,
|
||||
nameOf(getUserAuthContexts),
|
||||
"GET",
|
||||
"/users/USER_ID/auth-context",
|
||||
"Get User Auth Contexts",
|
||||
s"""Get User Auth Contexts for a User.
|
||||
|
|
||||
|
|
||||
|${authenticationRequiredMessage(true)}
|
||||
|
|
||||
|""",
|
||||
emptyObjectJson,
|
||||
userAuthContextJsonV500,
|
||||
List(
|
||||
UserNotLoggedIn,
|
||||
UserHasMissingRoles,
|
||||
UnknownError
|
||||
),
|
||||
List(apiTagUser, apiTagNewStyle),
|
||||
Some(canGetUserAuthContext :: Nil)
|
||||
)
|
||||
lazy val getUserAuthContexts : OBPEndpoint = {
|
||||
case "users" :: userId :: "auth-context" :: Nil JsonGet _ => {
|
||||
cc =>
|
||||
for {
|
||||
(Full(u), callContext) <- authenticatedAccess(cc)
|
||||
_ <- NewStyle.function.hasEntitlement("", u.userId, canGetUserAuthContext, callContext)
|
||||
(_, callContext) <- NewStyle.function.findByUserId(userId, callContext)
|
||||
(userAuthContexts, callContext) <- NewStyle.function.getUserAuthContexts(userId, callContext)
|
||||
} yield {
|
||||
(JSONFactory500.createUserAuthContextsJson(userAuthContexts), HttpCode.`200`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
staticResourceDocs += ResourceDoc(
|
||||
createUserAuthContextUpdateRequest,
|
||||
implementedInApiVersion,
|
||||
nameOf(createUserAuthContextUpdateRequest),
|
||||
"POST",
|
||||
"/banks/BANK_ID/users/current/auth-context-updates/SCA_METHOD",
|
||||
"Create User Auth Context Update Request",
|
||||
s"""Create User Auth Context Update Request.
|
||||
|${authenticationRequiredMessage(true)}
|
||||
|
|
||||
|A One Time Password (OTP) (AKA security challenge) is sent Out of Band (OOB) to the User via the transport defined in SCA_METHOD
|
||||
|SCA_METHOD is typically "SMS" or "EMAIL". "EMAIL" is used for testing purposes.
|
||||
|
|
||||
|""",
|
||||
postUserAuthContextJson,
|
||||
userAuthContextUpdateJsonV500,
|
||||
List(
|
||||
UserNotLoggedIn,
|
||||
InvalidJsonFormat,
|
||||
CreateUserAuthContextError,
|
||||
UnknownError
|
||||
),
|
||||
List(apiTagUser, apiTagNewStyle),
|
||||
None
|
||||
)
|
||||
|
||||
lazy val createUserAuthContextUpdateRequest : OBPEndpoint = {
|
||||
case "banks" :: BankId(bankId) :: "users" :: "current" ::"auth-context-updates" :: scaMethod :: Nil JsonPost json -> _ => {
|
||||
cc =>
|
||||
for {
|
||||
(Full(user), callContext) <- authenticatedAccess(cc)
|
||||
_ <- Helper.booleanToFuture(failMsg = ConsumerHasMissingRoles + CanCreateUserAuthContextUpdate, cc=callContext) {
|
||||
checkScope(bankId.value, getConsumerPrimaryKey(callContext), ApiRole.canCreateUserAuthContextUpdate)
|
||||
}
|
||||
(_, callContext) <- NewStyle.function.getBank(bankId, callContext)
|
||||
_ <- Helper.booleanToFuture(ConsentAllowedScaMethods, cc=callContext){
|
||||
List(StrongCustomerAuthentication.SMS.toString(), StrongCustomerAuthentication.EMAIL.toString()).exists(_ == scaMethod)
|
||||
}
|
||||
failMsg = s"$InvalidJsonFormat The Json body should be the $PostUserAuthContextJson "
|
||||
postedData <- NewStyle.function.tryons(failMsg, 400, callContext) {
|
||||
json.extract[PostUserAuthContextJson]
|
||||
}
|
||||
(userAuthContextUpdate, callContext) <- NewStyle.function.validateUserAuthContextUpdateRequest(bankId.value, user.userId, postedData.key, postedData.value, scaMethod, callContext)
|
||||
} yield {
|
||||
|
||||
(JSONFactory500.createUserAuthContextUpdateJson(userAuthContextUpdate), HttpCode.`201`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
staticResourceDocs += ResourceDoc(
|
||||
answerUserAuthContextUpdateChallenge,
|
||||
implementedInApiVersion,
|
||||
nameOf(answerUserAuthContextUpdateChallenge),
|
||||
"POST",
|
||||
"/banks/BANK_ID/users/current/auth-context-updates/AUTH_CONTEXT_UPDATE_ID/challenge",
|
||||
"Answer Auth Context Update Challenge",
|
||||
s"""
|
||||
|Answer Auth Context Update Challenge.
|
||||
|""",
|
||||
postUserAuthContextUpdateJsonV310,
|
||||
userAuthContextUpdateJsonV500,
|
||||
List(
|
||||
UserNotLoggedIn,
|
||||
BankNotFound,
|
||||
InvalidJsonFormat,
|
||||
InvalidConnectorResponse,
|
||||
UnknownError
|
||||
),
|
||||
apiTagUser :: apiTagNewStyle :: Nil)
|
||||
|
||||
lazy val answerUserAuthContextUpdateChallenge : OBPEndpoint = {
|
||||
case "banks" :: BankId(bankId) :: "users" :: "current" ::"auth-context-updates" :: authContextUpdateId :: "challenge" :: Nil JsonPost json -> _ => {
|
||||
cc =>
|
||||
for {
|
||||
(_, callContext) <- authenticatedAccess(cc)
|
||||
failMsg = s"$InvalidJsonFormat The Json body should be the $PostUserAuthContextUpdateJsonV310 "
|
||||
postUserAuthContextUpdateJson <- NewStyle.function.tryons(failMsg, 400, callContext) {
|
||||
json.extract[PostUserAuthContextUpdateJsonV310]
|
||||
}
|
||||
(userAuthContextUpdate, callContext) <- NewStyle.function.checkAnswer(authContextUpdateId, postUserAuthContextUpdateJson.answer, callContext)
|
||||
(user, callContext) <- NewStyle.function.getUserByUserId(userAuthContextUpdate.userId, callContext)
|
||||
(_, callContext) <-
|
||||
userAuthContextUpdate.status match {
|
||||
case status if status == UserAuthContextUpdateStatus.ACCEPTED.toString =>
|
||||
NewStyle.function.createUserAuthContext(
|
||||
user,
|
||||
userAuthContextUpdate.key,
|
||||
userAuthContextUpdate.value,
|
||||
callContext).map(x => (Some(x._1), x._2))
|
||||
case _ =>
|
||||
Future((None, callContext))
|
||||
}
|
||||
(_, callContext) <-
|
||||
userAuthContextUpdate.key match {
|
||||
case "CUSTOMER_NUMBER" =>
|
||||
NewStyle.function.getOCreateUserCustomerLink(
|
||||
bankId,
|
||||
userAuthContextUpdate.value, // Customer number
|
||||
user.userId,
|
||||
callContext
|
||||
)
|
||||
case _ =>
|
||||
Future((None, callContext))
|
||||
}
|
||||
} yield {
|
||||
(JSONFactory500.createUserAuthContextUpdateJson(userAuthContextUpdate), HttpCode.`200`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
object APIMethods500 extends RestHelper with APIMethods500 {
|
||||
lazy val newStyleEndpoints: List[(String, String)] = Implementations5_0_0.resourceDocs.map {
|
||||
rd => (rd.partialFunctionName, rd.implementedInApiVersion.toString())
|
||||
}.toList
|
||||
}
|
||||
|
||||
@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Open Bank Project - API
|
||||
* Copyright (C) 2011-2019, TESOBE GmbH
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
* *
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* *
|
||||
* Email: contact@tesobe.com
|
||||
* TESOBE GmbH
|
||||
* Osloerstrasse 16/17
|
||||
* Berlin 13359, Germany
|
||||
* *
|
||||
* This product includes software developed at
|
||||
* TESOBE (http://www.tesobe.com/)
|
||||
*
|
||||
*/
|
||||
package code.api.v5_0_0
|
||||
|
||||
import com.openbankproject.commons.model.{UserAuthContext, UserAuthContextUpdate}
|
||||
|
||||
import java.util.Date
|
||||
|
||||
case class UserAuthContextJsonV500(
|
||||
user_auth_context_id: String,
|
||||
user_id: String,
|
||||
key: String,
|
||||
value: String,
|
||||
time_stamp: Date,
|
||||
consumer_id: String,
|
||||
)
|
||||
|
||||
case class UserAuthContextsJsonV500(
|
||||
user_auth_contexts: List[UserAuthContextJsonV500]
|
||||
)
|
||||
|
||||
case class UserAuthContextUpdateJsonV500(
|
||||
user_auth_context_update_id: String,
|
||||
user_id: String,
|
||||
key: String,
|
||||
value: String,
|
||||
status: String,
|
||||
consumer_id: String,
|
||||
)
|
||||
|
||||
object JSONFactory500 {
|
||||
|
||||
def createUserAuthContextJson(userAuthContext: UserAuthContext): UserAuthContextJsonV500 = {
|
||||
UserAuthContextJsonV500(
|
||||
user_auth_context_id= userAuthContext.userAuthContextId,
|
||||
user_id = userAuthContext.userId,
|
||||
key = userAuthContext.key,
|
||||
value = userAuthContext.value,
|
||||
time_stamp = userAuthContext.timeStamp,
|
||||
consumer_id = userAuthContext.consumerId,
|
||||
)
|
||||
}
|
||||
|
||||
def createUserAuthContextsJson(userAuthContext: List[UserAuthContext]): UserAuthContextsJsonV500 = {
|
||||
UserAuthContextsJsonV500(userAuthContext.map(createUserAuthContextJson))
|
||||
}
|
||||
|
||||
def createUserAuthContextUpdateJson(userAuthContextUpdate: UserAuthContextUpdate): UserAuthContextUpdateJsonV500 = {
|
||||
UserAuthContextUpdateJsonV500(
|
||||
user_auth_context_update_id= userAuthContextUpdate.userAuthContextUpdateId,
|
||||
user_id = userAuthContextUpdate.userId,
|
||||
key = userAuthContextUpdate.key,
|
||||
value = userAuthContextUpdate.value,
|
||||
status = userAuthContextUpdate.status,
|
||||
consumer_id = userAuthContextUpdate.consumerId
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
110
obp-api/src/main/scala/code/api/v5_0_0/OBPAPI5_0_0.scala
Normal file
110
obp-api/src/main/scala/code/api/v5_0_0/OBPAPI5_0_0.scala
Normal file
@ -0,0 +1,110 @@
|
||||
/**
|
||||
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.api.v5_0_0
|
||||
|
||||
import code.api.OBPRestHelper
|
||||
import code.api.util.APIUtil.{OBPEndpoint, getAllowedEndpoints}
|
||||
import code.api.util.{APIUtil, VersionedOBPApis}
|
||||
import code.api.v1_3_0.APIMethods130
|
||||
import code.api.v1_4_0.APIMethods140
|
||||
import code.api.v2_0_0.APIMethods200
|
||||
import code.api.v2_1_0.APIMethods210
|
||||
import code.api.v2_2_0.APIMethods220
|
||||
import code.api.v3_0_0.APIMethods300
|
||||
import code.api.v3_0_0.custom.CustomAPIMethods300
|
||||
import code.api.v3_1_0.{APIMethods310, OBPAPI3_1_0}
|
||||
import code.api.v4_0_0.{APIMethods400, OBPAPI4_0_0}
|
||||
import code.api.v4_0_0.OBPAPI4_0_0.{Implementations4_0_0, endpointsOf4_0_0}
|
||||
import code.util.Helper.MdcLoggable
|
||||
import com.github.dwickern.macros.NameOf.nameOf
|
||||
import com.openbankproject.commons.util.ApiVersion
|
||||
import net.liftweb.common.{Box, Full}
|
||||
import net.liftweb.http.{LiftResponse, PlainTextResponse}
|
||||
import org.apache.http.HttpStatus
|
||||
|
||||
/*
|
||||
This file defines which endpoints from all the versions are available in v5.0.0
|
||||
*/
|
||||
object OBPAPI5_0_0 extends OBPRestHelper
|
||||
with APIMethods130
|
||||
with APIMethods140
|
||||
with APIMethods200
|
||||
with APIMethods210
|
||||
with APIMethods220
|
||||
with APIMethods300
|
||||
with CustomAPIMethods300
|
||||
with APIMethods310
|
||||
with APIMethods400
|
||||
with APIMethods500
|
||||
with MdcLoggable
|
||||
with VersionedOBPApis{
|
||||
|
||||
val version : ApiVersion = ApiVersion.v5_0_0
|
||||
|
||||
val versionStatus = "BLEEDING-EDGE" // TODO this should be a property of ApiVersion.
|
||||
|
||||
// Possible Endpoints from 5.0.0, exclude one endpoint use - method,exclude multiple endpoints use -- method,
|
||||
// e.g getEndpoints(Implementations5_0_0) -- List(Implementations5_0_0.genericEndpoint, Implementations5_0_0.root)
|
||||
val endpointsOf5_0_0 = getEndpoints(Implementations5_0_0)
|
||||
|
||||
// if old version ResourceDoc objects have the same name endpoint with new version, omit old version ResourceDoc.
|
||||
def allResourceDocs = collectResourceDocs(
|
||||
OBPAPI4_0_0.allResourceDocs,
|
||||
Implementations5_0_0.resourceDocs
|
||||
)
|
||||
|
||||
// all endpoints
|
||||
private val endpoints: List[OBPEndpoint] = OBPAPI4_0_0.routes ++ endpointsOf5_0_0
|
||||
|
||||
// Filter the possible endpoints by the disabled / enabled Props settings and add them together
|
||||
val routes : List[OBPEndpoint] = Implementations4_0_0.root :: // For now we make this mandatory
|
||||
getAllowedEndpoints(endpoints, allResourceDocs)
|
||||
|
||||
// register v5.0.0 apis first, Make them available for use!
|
||||
registerRoutes(routes, allResourceDocs, apiPrefix, true)
|
||||
|
||||
|
||||
logger.info(s"version $version has been run! There are ${routes.length} routes.")
|
||||
|
||||
// specified response for OPTIONS request.
|
||||
private val corsResponse: Box[LiftResponse] = Full{
|
||||
val corsHeaders = List(
|
||||
"Access-Control-Allow-Origin" -> "*",
|
||||
"Access-Control-Allow-Methods" -> "GET, POST, OPTIONS, PUT, PATCH, DELETE",
|
||||
"Access-Control-Allow-Headers" -> "*",
|
||||
"Access-Control-Allow-Credentials" -> "true",
|
||||
"Access-Control-Max-Age" -> "1728000" //Tell client that this pre-flight info is valid for 20 days
|
||||
)
|
||||
PlainTextResponse("", corsHeaders, HttpStatus.SC_NO_CONTENT)
|
||||
}
|
||||
/*
|
||||
* process OPTIONS http request, just return no content and status is 204
|
||||
*/
|
||||
this.serve({
|
||||
case req if req.requestType.method == "OPTIONS" => corsResponse
|
||||
})
|
||||
}
|
||||
@ -89,23 +89,14 @@ trait AtmsProvider {
|
||||
*/
|
||||
final def getAtms(bankId : BankId, queryParams: List[OBPQueryParam]) : Option[List[AtmT]] = {
|
||||
// If we get atms filter them
|
||||
getAtmsFromProvider(bankId,queryParams) match {
|
||||
case Some(atms) => {
|
||||
val atmsWithLicense = for {
|
||||
branch <- atms if branch.meta.license.name.size > 3
|
||||
} yield branch
|
||||
Option(atmsWithLicense)
|
||||
}
|
||||
case None => None
|
||||
}
|
||||
getAtmsFromProvider(bankId,queryParams)
|
||||
}
|
||||
|
||||
/*
|
||||
Return one Atm
|
||||
*/
|
||||
final def getAtm(bankId: BankId, branchId : AtmId) : Option[AtmT] = {
|
||||
// Filter out if no license data
|
||||
getAtmFromProvider(bankId,branchId).filter(x => x.meta.license.id != "" && x.meta.license.name != "")
|
||||
getAtmFromProvider(bankId,branchId)
|
||||
}
|
||||
|
||||
protected def getAtmFromProvider(bankId: BankId, branchId : AtmId) : Option[AtmT]
|
||||
|
||||
@ -558,6 +558,8 @@ trait Connector extends MdcLoggable {
|
||||
def getCounterpartyByCounterpartyIdLegacy(counterpartyId: CounterpartyId, callContext: Option[CallContext]): Box[(CounterpartyTrait, Option[CallContext])]= Failure(setUnimplementedError)
|
||||
|
||||
def getCounterpartyByCounterpartyId(counterpartyId: CounterpartyId, callContext: Option[CallContext]): OBPReturnType[Box[CounterpartyTrait]] = Future{(Failure(setUnimplementedError), callContext)}
|
||||
|
||||
def deleteCounterpartyByCounterpartyId(counterpartyId: CounterpartyId, callContext: Option[CallContext]): OBPReturnType[Box[Boolean]] = Future{(Failure(setUnimplementedError), callContext)}
|
||||
|
||||
|
||||
/**
|
||||
@ -567,6 +569,37 @@ trait Connector extends MdcLoggable {
|
||||
def getCounterpartyByIban(iban: String, callContext: Option[CallContext]) : OBPReturnType[Box[CounterpartyTrait]] = Future {(Failure(setUnimplementedError), callContext)}
|
||||
|
||||
def getCounterpartyByIbanAndBankAccountId(iban: String, bankId: BankId, accountId: AccountId, callContext: Option[CallContext]) : OBPReturnType[Box[CounterpartyTrait]] = Future {(Failure(setUnimplementedError), callContext)}
|
||||
|
||||
def getOrCreateCounterparty(
|
||||
name: String,
|
||||
description: String,
|
||||
currency: String,
|
||||
createdByUserId: String,
|
||||
thisBankId: String,
|
||||
thisAccountId: String,
|
||||
thisViewId: String,
|
||||
other_bank_routing_scheme: String,
|
||||
other_bank_routing_address: String,
|
||||
other_branch_routing_scheme: String,
|
||||
other_branch_routing_address: String,
|
||||
other_account_routing_scheme: String,
|
||||
other_account_routing_address: String,
|
||||
other_account_secondary_routing_scheme: String,
|
||||
other_account_secondary_routing_address: String,
|
||||
callContext: Option[CallContext]
|
||||
): OBPReturnType[Box[CounterpartyTrait]] = Future {(Failure(setUnimplementedError), callContext)}
|
||||
|
||||
def getCounterpartyByRoutings(
|
||||
otherBankRoutingScheme: String,
|
||||
otherBankRoutingAddress: String,
|
||||
otherBranchRoutingScheme: String,
|
||||
otherBranchRoutingAddress: String,
|
||||
otherAccountRoutingScheme: String,
|
||||
otherAccountRoutingAddress: String,
|
||||
otherAccountSecondaryRoutingScheme: String,
|
||||
otherAccountSecondaryRoutingAddress: String,
|
||||
callContext: Option[CallContext]
|
||||
): OBPReturnType[Box[CounterpartyTrait]] = Future {(Failure(setUnimplementedError), callContext)}
|
||||
|
||||
def getCounterpartiesLegacy(thisBankId: BankId, thisAccountId: AccountId, viewId :ViewId, callContext: Option[CallContext] = None): Box[(List[CounterpartyTrait], Option[CallContext])]= Failure(setUnimplementedError)
|
||||
|
||||
@ -739,6 +772,8 @@ trait Connector extends MdcLoggable {
|
||||
|
||||
def getDoubleEntryBookTransaction(bankId: BankId, accountId: AccountId, transactionId: TransactionId,
|
||||
callContext: Option[CallContext]): OBPReturnType[Box[DoubleEntryTransaction]]= Future{(Failure(setUnimplementedError), callContext)}
|
||||
def getBalancingTransaction(transactionId: TransactionId,
|
||||
callContext: Option[CallContext]): OBPReturnType[Box[DoubleEntryTransaction]]= Future{(Failure(setUnimplementedError), callContext)}
|
||||
|
||||
protected def makePaymentImpl(fromAccount: BankAccount, toAccount: BankAccount, transactionRequestCommonBody: TransactionRequestCommonBodyJSON, amt: BigDecimal, description: String, transactionRequestType: TransactionRequestType, chargePolicy: String): Box[TransactionId]= Failure(setUnimplementedError)
|
||||
|
||||
@ -2514,4 +2549,12 @@ trait Connector extends MdcLoggable {
|
||||
|
||||
def checkAnswer(authContextUpdateId: String, challenge: String, callContext: Option[CallContext]): OBPReturnType[Box[UserAuthContextUpdate]] = Future{(Failure(setUnimplementedError), callContext)}
|
||||
|
||||
def sendCustomerNotification(
|
||||
scaMethod: StrongCustomerAuthentication,
|
||||
recipient: String,
|
||||
subject: Option[String], //Only for EMAIL, SMS do not need it, so here it is Option
|
||||
message: String,
|
||||
callContext: Option[CallContext]
|
||||
): OBPReturnType[Box[String]] = Future{(Failure(setUnimplementedError), callContext)}
|
||||
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@ package code.bankconnectors
|
||||
|
||||
import java.util.Date
|
||||
import java.util.UUID.randomUUID
|
||||
|
||||
import _root_.akka.http.scaladsl.model.HttpMethod
|
||||
import code.DynamicData.DynamicDataProvider
|
||||
import code.DynamicEndpoint.{DynamicEndpointProvider, DynamicEndpointT}
|
||||
@ -10,7 +9,7 @@ import code.accountapplication.AccountApplicationX
|
||||
import code.accountattribute.AccountAttributeX
|
||||
import code.accountholders.{AccountHolders, MapperAccountHolders}
|
||||
import code.api.BerlinGroup.{AuthenticationType, ScaStatus}
|
||||
import code.api.Constant.{INCOMING_ACCOUNT_ID, OUTGOING_ACCOUNT_ID}
|
||||
import code.api.Constant.{INCOMING_SETTLEMENT_ACCOUNT_ID, OUTGOING_SETTLEMENT_ACCOUNT_ID}
|
||||
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON
|
||||
import code.api.attributedefinition.{AttributeDefinition, AttributeDefinitionDI}
|
||||
import code.api.cache.Caching
|
||||
@ -20,6 +19,7 @@ import code.api.util.ErrorMessages.{attemptedToOpenAnEmptyBox, _}
|
||||
import code.api.util._
|
||||
import code.api.v1_4_0.JSONFactory1_4_0.TransactionRequestAccountJsonV140
|
||||
import code.api.v2_1_0._
|
||||
import code.api.v4_0_0.{PostSimpleCounterpartyJson400, TransactionRequestBodySimpleJsonV400}
|
||||
import code.atms.Atms.Atm
|
||||
import code.atms.MappedAtm
|
||||
import code.bankattribute.{BankAttribute, BankAttributeX}
|
||||
@ -978,6 +978,10 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
(Counterparties.counterparties.vend.getCounterparty(counterpartyId.value), callContext)
|
||||
}
|
||||
|
||||
override def deleteCounterpartyByCounterpartyId(counterpartyId: CounterpartyId, callContext: Option[CallContext]): OBPReturnType[Box[Boolean]] = Future {
|
||||
(Counterparties.counterparties.vend.deleteCounterparty(counterpartyId.value), callContext)
|
||||
}
|
||||
|
||||
override def getCounterpartyByIban(iban: String, callContext: Option[CallContext]): OBPReturnType[Box[CounterpartyTrait]] = {
|
||||
Future(Counterparties.counterparties.vend.getCounterpartyByIban(iban), callContext)
|
||||
}
|
||||
@ -986,6 +990,118 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
Future(Counterparties.counterparties.vend.getCounterpartyByIbanAndBankAccountId(iban, bankId, accountId), callContext)
|
||||
}
|
||||
|
||||
override def getCounterpartyByRoutings(
|
||||
otherBankRoutingScheme: String,
|
||||
otherBankRoutingAddress: String,
|
||||
otherBranchRoutingScheme: String,
|
||||
otherBranchRoutingAddress: String,
|
||||
otherAccountRoutingScheme: String,
|
||||
otherAccountRoutingAddress: String,
|
||||
otherAccountSecondaryRoutingScheme: String,
|
||||
otherAccountSecondaryRoutingAddress: String,
|
||||
callContext: Option[CallContext]
|
||||
): OBPReturnType[Box[CounterpartyTrait]] = Future {
|
||||
lazy val counterpartyFromRoutings= Counterparties.counterparties.vend.getCounterpartyByRoutings(
|
||||
otherBankRoutingScheme: String,
|
||||
otherBankRoutingAddress: String,
|
||||
otherBranchRoutingScheme: String,
|
||||
otherBranchRoutingAddress: String,
|
||||
otherAccountRoutingScheme: String,
|
||||
otherAccountRoutingAddress: String
|
||||
)
|
||||
|
||||
lazy val counterpartyFromSecondaryRouting = Counterparties.counterparties.vend.getCounterpartyBySecondaryRouting(
|
||||
otherAccountSecondaryRoutingScheme: String,
|
||||
otherAccountSecondaryRoutingAddress: String
|
||||
)
|
||||
|
||||
if(counterpartyFromRoutings.isDefined) {
|
||||
(counterpartyFromRoutings, callContext)
|
||||
} else if(counterpartyFromSecondaryRouting.isDefined) {
|
||||
(counterpartyFromSecondaryRouting, callContext)
|
||||
} else {
|
||||
(Failure(CounterpartyNotFoundByRoutings), callContext)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
override def getOrCreateCounterparty(
|
||||
name: String,
|
||||
description: String,
|
||||
currency: String,
|
||||
createdByUserId: String,
|
||||
thisBankId: String,
|
||||
thisAccountId: String,
|
||||
thisViewId: String,
|
||||
otherBankRoutingScheme: String,
|
||||
otherBankRoutingAddress: String,
|
||||
otherBranchRoutingScheme: String,
|
||||
otherBranchRoutingAddress: String,
|
||||
otherAccountRoutingScheme: String,
|
||||
otherAccountRoutingAddress: String,
|
||||
otherAccountSecondaryRoutingScheme: String,
|
||||
otherAccountSecondaryRoutingAddress: String,
|
||||
callContext: Option[CallContext]
|
||||
): OBPReturnType[Box[CounterpartyTrait]] = Future {
|
||||
|
||||
lazy val counterpartyFromRoutings= Counterparties.counterparties.vend.getCounterpartyByRoutings(
|
||||
otherBankRoutingScheme: String,
|
||||
otherBankRoutingAddress: String,
|
||||
otherBranchRoutingScheme: String,
|
||||
otherBranchRoutingAddress: String,
|
||||
otherAccountRoutingScheme: String,
|
||||
otherAccountRoutingAddress: String
|
||||
)
|
||||
|
||||
lazy val counterpartyFromSecondaryRouting = Counterparties.counterparties.vend.getCounterpartyBySecondaryRouting(
|
||||
otherAccountSecondaryRoutingScheme: String,
|
||||
otherAccountSecondaryRoutingAddress: String
|
||||
)
|
||||
|
||||
|
||||
if(counterpartyFromRoutings.isDefined) {
|
||||
(counterpartyFromRoutings, callContext)
|
||||
} else if(counterpartyFromSecondaryRouting.isDefined) {
|
||||
(counterpartyFromSecondaryRouting, callContext)
|
||||
} else{
|
||||
val newCounterparty = for{
|
||||
_ <- Helper.booleanToBox(
|
||||
Counterparties.counterparties.vend.checkCounterpartyExists(
|
||||
name: String,
|
||||
thisBankId: String,
|
||||
thisAccountId: String,
|
||||
thisViewId: String
|
||||
).isEmpty,
|
||||
CounterpartyAlreadyExists.replace("value for BANK_ID or ACCOUNT_ID or VIEW_ID or NAME.",
|
||||
s"COUNTERPARTY_NAME(${name}) for the BANK_ID(${thisBankId}) and ACCOUNT_ID(${thisAccountId}) and VIEW_ID($thisViewId)")
|
||||
)
|
||||
|
||||
counterparty <- Counterparties.counterparties.vend.createCounterparty(
|
||||
createdByUserId = createdByUserId,
|
||||
thisBankId = thisBankId,
|
||||
thisAccountId = thisAccountId,
|
||||
thisViewId = thisViewId,
|
||||
name = name,
|
||||
otherAccountRoutingScheme = otherAccountRoutingScheme,
|
||||
otherAccountRoutingAddress = otherAccountRoutingAddress,
|
||||
otherBankRoutingScheme = otherBankRoutingScheme,
|
||||
otherBankRoutingAddress = otherBankRoutingAddress,
|
||||
otherBranchRoutingScheme = otherBranchRoutingScheme,
|
||||
otherBranchRoutingAddress = otherBranchRoutingAddress,
|
||||
isBeneficiary = true,
|
||||
otherAccountSecondaryRoutingScheme = otherAccountSecondaryRoutingScheme,
|
||||
otherAccountSecondaryRoutingAddress = otherAccountSecondaryRoutingAddress,
|
||||
description = description,
|
||||
currency = currency,
|
||||
bespoke = Nil
|
||||
)
|
||||
} yield{
|
||||
counterparty
|
||||
}
|
||||
(newCounterparty, callContext)
|
||||
}
|
||||
}
|
||||
|
||||
override def getPhysicalCardsForUser(user: User, callContext: Option[CallContext]): OBPReturnType[Box[List[PhysicalCard]]] = Future {
|
||||
val list = code.cards.PhysicalCard.physicalCardProvider.vend.getPhysicalCards(user)
|
||||
@ -1409,6 +1525,16 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
))
|
||||
).map(doubleEntryTransaction => (doubleEntryTransaction, callContext))
|
||||
}
|
||||
override def getBalancingTransaction(transactionId: TransactionId,
|
||||
callContext: Option[CallContext]): OBPReturnType[Box[DoubleEntryTransaction]] = {
|
||||
Future(
|
||||
DoubleEntryBookTransaction.find(
|
||||
By(DoubleEntryBookTransaction.DebitTransactionId, transactionId.value)
|
||||
).or(DoubleEntryBookTransaction.find(
|
||||
By(DoubleEntryBookTransaction.CreditTransactionId, transactionId.value)
|
||||
))
|
||||
).map(doubleEntryTransaction => (doubleEntryTransaction, callContext))
|
||||
}
|
||||
|
||||
override def makePaymentV400(transactionRequest: TransactionRequest,
|
||||
reasons: Option[List[TransactionRequestReason]],
|
||||
@ -1486,12 +1612,12 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
// If it doesn't exist, we look for a default settlement account regarding the currency
|
||||
.or(BankAccountX(toAccount.bankId, AccountId("DEFAULT_SETTLEMENT_ACCOUNT_" + fromAccount.currency), callContext))
|
||||
// If no specific settlement account exist for this currency, we use the default incoming account (EUR)
|
||||
.or(BankAccountX(toAccount.bankId, AccountId(INCOMING_ACCOUNT_ID), callContext))
|
||||
.or(BankAccountX(toAccount.bankId, AccountId(INCOMING_SETTLEMENT_ACCOUNT_ID), callContext))
|
||||
}
|
||||
settlementAccount.flatMap(settlementAccount => {
|
||||
val fromTransAmtSettlementAccount: BigDecimal = {
|
||||
// In the case we selected the default settlement account INCOMING_ACCOUNT_ID account and that the counterparty currency is different from EUR, we need to calculate the amount in EUR
|
||||
if (settlementAccount._1.accountId.value == INCOMING_ACCOUNT_ID && settlementAccount._1.currency != fromAccount.currency) {
|
||||
if (settlementAccount._1.accountId.value == INCOMING_SETTLEMENT_ACCOUNT_ID && settlementAccount._1.currency != fromAccount.currency) {
|
||||
val rate = fx.exchangeRate(currency, settlementAccount._1.currency, Some(bankIdExchangeRate.bankId.value))
|
||||
Try(-fx.convert(amount, rate)).getOrElse(throw new Exception(s"$InvalidCurrency The requested currency conversion ($currency to ${settlementAccount._1.currency}) is not supported."))
|
||||
} else fromTransAmt
|
||||
@ -1512,11 +1638,11 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
// If it doesn't exist, we look for a default settlement account regarding the currency
|
||||
.or(BankAccountX(fromAccount.bankId, AccountId("DEFAULT_SETTLEMENT_ACCOUNT_" + toAccount.currency), callContext))
|
||||
// If no specific settlement account exist for this currency, we use the default outgoing account (EUR)
|
||||
.or(BankAccountX(fromAccount.bankId, AccountId(OUTGOING_ACCOUNT_ID), callContext))
|
||||
.or(BankAccountX(fromAccount.bankId, AccountId(OUTGOING_SETTLEMENT_ACCOUNT_ID), callContext))
|
||||
settlementAccount.flatMap(settlementAccount => {
|
||||
val toTransAmtSettlementAccount: BigDecimal = {
|
||||
// In the case we selected the default settlement account OUTGOING_ACCOUNT_ID account and that the counterparty currency is different from EUR, we need to calculate the amount in EUR
|
||||
if (settlementAccount._1.accountId.value == OUTGOING_ACCOUNT_ID && settlementAccount._1.currency != toAccount.currency) {
|
||||
if (settlementAccount._1.accountId.value == OUTGOING_SETTLEMENT_ACCOUNT_ID && settlementAccount._1.currency != toAccount.currency) {
|
||||
val rate = fx.exchangeRate(currency, settlementAccount._1.currency, Some(bankIdExchangeRate.bankId.value))
|
||||
Try(fx.convert(amount, rate)).getOrElse(throw new Exception(s"$InvalidCurrency The requested currency conversion ($currency to ${settlementAccount._1.currency}) is not supported."))
|
||||
} else toTransAmt
|
||||
@ -1527,8 +1653,8 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
}
|
||||
)
|
||||
|
||||
debitTransaction = debitTransactionBox.openOrThrowException(s"Error while opening debitTransaction. This error can happen when no settlement can be found, please check that $INCOMING_ACCOUNT_ID exists at bank ${toAccount.bankId.value}")
|
||||
creditTransaction = creditTransactionBox.openOrThrowException(s"Error while opening creditTransaction. This error can happen when no settlement can be found, please check that $OUTGOING_ACCOUNT_ID exists at bank ${fromAccount.bankId.value}")
|
||||
debitTransaction = debitTransactionBox.openOrThrowException(s"Error while opening debitTransaction. This error can happen when no settlement can be found, please check that $INCOMING_SETTLEMENT_ACCOUNT_ID exists at bank ${toAccount.bankId.value}")
|
||||
creditTransaction = creditTransactionBox.openOrThrowException(s"Error while opening creditTransaction. This error can happen when no settlement can be found, please check that $OUTGOING_SETTLEMENT_ACCOUNT_ID exists at bank ${fromAccount.bankId.value}")
|
||||
|
||||
_ <- NewStyle.function.saveDoubleEntryBookTransaction(
|
||||
DoubleEntryTransaction(
|
||||
@ -1643,11 +1769,11 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
// If it doesn't exist, we look for a default settlement account regarding the currency
|
||||
.or(BankAccountX(toAccount.bankId, AccountId("DEFAULT_SETTLEMENT_ACCOUNT_" + fromAccount.currency), callContext))
|
||||
// If no specific settlement account exist for this currency, we use the default incoming account (EUR)
|
||||
.or(BankAccountX(toAccount.bankId, AccountId(INCOMING_ACCOUNT_ID), callContext))
|
||||
.or(BankAccountX(toAccount.bankId, AccountId(INCOMING_SETTLEMENT_ACCOUNT_ID), callContext))
|
||||
settlementAccount.flatMap(settlementAccount => {
|
||||
val fromTransAmtSettlementAccount = {
|
||||
// In the case we selected the default settlement account INCOMING_ACCOUNT_ID account and that the counterparty currency is different from EUR, we need to calculate the amount in EUR
|
||||
if (settlementAccount._1.accountId.value == INCOMING_ACCOUNT_ID && settlementAccount._1.currency != fromAccount.currency) {
|
||||
if (settlementAccount._1.accountId.value == INCOMING_SETTLEMENT_ACCOUNT_ID && settlementAccount._1.currency != fromAccount.currency) {
|
||||
val rate = fx.exchangeRate(transactionCurrency, settlementAccount._1.currency, Some(bankIdExchangeRate.bankId.value))
|
||||
Try(-fx.convert(amount, rate)).getOrElse(throw new Exception(s"$InvalidCurrency The requested currency conversion ($transactionCurrency to ${settlementAccount._1.currency}) is not supported."))
|
||||
} else fromTransAmt
|
||||
@ -1668,11 +1794,11 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
// If it doesn't exist, we look for a default settlement account regarding the currency
|
||||
.or(BankAccountX(fromAccount.bankId, AccountId("DEFAULT_SETTLEMENT_ACCOUNT_" + toAccount.currency), callContext))
|
||||
// If no specific settlement account exist for this currency, we use the default outgoing account (EUR)
|
||||
.or(BankAccountX(fromAccount.bankId, AccountId(OUTGOING_ACCOUNT_ID), callContext))
|
||||
.or(BankAccountX(fromAccount.bankId, AccountId(OUTGOING_SETTLEMENT_ACCOUNT_ID), callContext))
|
||||
settlementAccount.flatMap(settlementAccount => {
|
||||
val toTransAmtSettlementAccount = {
|
||||
// In the case we selected the default settlement account OUTGOING_ACCOUNT_ID account and that the counterparty currency is different from EUR, we need to calculate the amount in EUR
|
||||
if (settlementAccount._1.accountId.value == OUTGOING_ACCOUNT_ID && settlementAccount._1.currency != toAccount.currency) {
|
||||
if (settlementAccount._1.accountId.value == OUTGOING_SETTLEMENT_ACCOUNT_ID && settlementAccount._1.currency != toAccount.currency) {
|
||||
val rate = fx.exchangeRate(transactionCurrency, settlementAccount._1.currency, Some(bankIdExchangeRate.bankId.value))
|
||||
Try(fx.convert(amount, rate)).getOrElse(throw new Exception(s"$InvalidCurrency The requested currency conversion ($transactionCurrency to ${settlementAccount._1.currency}) is not supported."))
|
||||
} else toTransAmt
|
||||
@ -1683,8 +1809,8 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
}
|
||||
}
|
||||
|
||||
debitTransaction = debitTransactionBox.openOrThrowException(s"Error while opening debitTransaction. This error can happen when no settlement can be found, please check that $INCOMING_ACCOUNT_ID exists at bank ${toAccount.bankId.value}")
|
||||
creditTransaction = creditTransactionBox.openOrThrowException(s"Error while opening creditTransaction. This error can happen when no settlement can be found, please check that $OUTGOING_ACCOUNT_ID exists at bank ${fromAccount.bankId.value}")
|
||||
debitTransaction = debitTransactionBox.openOrThrowException(s"Error while opening debitTransaction. This error can happen when no settlement can be found, please check that $INCOMING_SETTLEMENT_ACCOUNT_ID exists at bank ${toAccount.bankId.value}")
|
||||
creditTransaction = creditTransactionBox.openOrThrowException(s"Error while opening creditTransaction. This error can happen when no settlement can be found, please check that $OUTGOING_SETTLEMENT_ACCOUNT_ID exists at bank ${fromAccount.bankId.value}")
|
||||
|
||||
_ <- NewStyle.function.saveDoubleEntryBookTransaction(
|
||||
DoubleEntryTransaction(
|
||||
@ -3168,36 +3294,36 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
}
|
||||
|
||||
// Insert the default settlement accounts if they doesn't exist
|
||||
MappedBankAccount.find(By(MappedBankAccount.bank, bankId), By(MappedBankAccount.theAccountId, INCOMING_ACCOUNT_ID)) match {
|
||||
MappedBankAccount.find(By(MappedBankAccount.bank, bankId), By(MappedBankAccount.theAccountId, INCOMING_SETTLEMENT_ACCOUNT_ID)) match {
|
||||
case Full(_) =>
|
||||
logger.debug(s"BankAccount(${bankId}, $INCOMING_ACCOUNT_ID) is found.")
|
||||
logger.debug(s"BankAccount(${bankId}, $INCOMING_SETTLEMENT_ACCOUNT_ID) is found.")
|
||||
case _ =>
|
||||
MappedBankAccount.create
|
||||
.bank(bankId)
|
||||
.theAccountId(INCOMING_ACCOUNT_ID)
|
||||
.theAccountId(INCOMING_SETTLEMENT_ACCOUNT_ID)
|
||||
.accountCurrency("EUR")
|
||||
.kind("SETTLEMENT")
|
||||
.holder(fullBankName)
|
||||
.holder(fullBankName)// TODO Consider to use the table MapperAccountHolder
|
||||
.accountName("Default incoming settlement account")
|
||||
.accountLabel("Settlement account: Do not delete!")
|
||||
.saveMe()
|
||||
logger.debug(s"creating BankAccount(${bankId}, $INCOMING_ACCOUNT_ID).")
|
||||
logger.debug(s"creating BankAccount(${bankId}, $INCOMING_SETTLEMENT_ACCOUNT_ID).")
|
||||
}
|
||||
|
||||
MappedBankAccount.find(By(MappedBankAccount.bank, bankId), By(MappedBankAccount.theAccountId, OUTGOING_ACCOUNT_ID)) match {
|
||||
MappedBankAccount.find(By(MappedBankAccount.bank, bankId), By(MappedBankAccount.theAccountId, OUTGOING_SETTLEMENT_ACCOUNT_ID)) match {
|
||||
case Full(_) =>
|
||||
logger.debug(s"BankAccount(${bankId}, $OUTGOING_ACCOUNT_ID) is found.")
|
||||
logger.debug(s"BankAccount(${bankId}, $OUTGOING_SETTLEMENT_ACCOUNT_ID) is found.")
|
||||
case _ =>
|
||||
MappedBankAccount.create
|
||||
.bank(bankId)
|
||||
.theAccountId(OUTGOING_ACCOUNT_ID)
|
||||
.theAccountId(OUTGOING_SETTLEMENT_ACCOUNT_ID)
|
||||
.accountCurrency("EUR")
|
||||
.kind("SETTLEMENT")
|
||||
.holder(fullBankName)
|
||||
.accountName("Default outgoing settlement account")
|
||||
.accountLabel("Settlement account: Do not delete!")
|
||||
.saveMe()
|
||||
logger.debug(s"creating BankAccount(${bankId}, $OUTGOING_ACCOUNT_ID).")
|
||||
logger.debug(s"creating BankAccount(${bankId}, $OUTGOING_SETTLEMENT_ACCOUNT_ID).")
|
||||
}
|
||||
|
||||
bank
|
||||
@ -3514,18 +3640,22 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
override def createUserAuthContext(userId: String,
|
||||
key: String,
|
||||
value: String,
|
||||
callContext: Option[CallContext]): OBPReturnType[Box[UserAuthContext]] =
|
||||
UserAuthContextProvider.userAuthContextProvider.vend.createUserAuthContext(userId, key, value) map {
|
||||
callContext: Option[CallContext]): OBPReturnType[Box[UserAuthContext]] = {
|
||||
val consumerId = callContext.map(_.consumer.map(_.consumerId.get).getOrElse("")).getOrElse("")
|
||||
UserAuthContextProvider.userAuthContextProvider.vend.createUserAuthContext(userId, key, value, consumerId) map {
|
||||
(_, callContext)
|
||||
}
|
||||
}
|
||||
|
||||
override def createUserAuthContextUpdate(userId: String,
|
||||
key: String,
|
||||
value: String,
|
||||
callContext: Option[CallContext]): OBPReturnType[Box[UserAuthContextUpdate]] =
|
||||
UserAuthContextUpdateProvider.userAuthContextUpdateProvider.vend.createUserAuthContextUpdates(userId, key, value) map {
|
||||
callContext: Option[CallContext]): OBPReturnType[Box[UserAuthContextUpdate]] = {
|
||||
val consumerId = callContext.map(_.consumer.map(_.consumerId.get).getOrElse("")).getOrElse("")
|
||||
UserAuthContextUpdateProvider.userAuthContextUpdateProvider.vend.createUserAuthContextUpdates(userId,consumerId, key, value) map {
|
||||
(_, callContext)
|
||||
}
|
||||
}
|
||||
|
||||
override def getUserAuthContexts(userId: String,
|
||||
callContext: Option[CallContext]): OBPReturnType[Box[List[UserAuthContext]]] =
|
||||
@ -5017,6 +5147,55 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
} yield {
|
||||
(transactionId, callContext)
|
||||
}
|
||||
case SIMPLE =>
|
||||
for {
|
||||
bodyToSimple <- NewStyle.function.tryons(s"$TransactionRequestDetailsExtractException It can not extract to $TransactionRequestBodyCounterpartyJSON", 400, callContext) {
|
||||
body.to_simple.get
|
||||
}
|
||||
(toCounterparty, callContext) <- NewStyle.function.getCounterpartyByRoutings(
|
||||
bodyToSimple.otherBankRoutingScheme,
|
||||
bodyToSimple.otherBankRoutingAddress,
|
||||
bodyToSimple.otherBranchRoutingScheme,
|
||||
bodyToSimple.otherBranchRoutingAddress,
|
||||
bodyToSimple.otherAccountRoutingScheme,
|
||||
bodyToSimple.otherAccountRoutingAddress,
|
||||
bodyToSimple.otherAccountSecondaryRoutingScheme,
|
||||
bodyToSimple.otherAccountSecondaryRoutingAddress,
|
||||
callContext
|
||||
)
|
||||
toAccount <- NewStyle.function.getBankAccountFromCounterparty(toCounterparty, true, callContext)
|
||||
counterpartyBody = TransactionRequestBodySimpleJsonV400(
|
||||
to = PostSimpleCounterpartyJson400(
|
||||
name = toCounterparty.name,
|
||||
description = toCounterparty.description,
|
||||
other_bank_routing_scheme = toCounterparty.otherBankRoutingScheme,
|
||||
other_bank_routing_address = toCounterparty.otherBankRoutingAddress,
|
||||
other_account_routing_scheme = toCounterparty.otherAccountRoutingScheme,
|
||||
other_account_routing_address = toCounterparty.otherAccountRoutingAddress,
|
||||
other_account_secondary_routing_scheme = toCounterparty.otherAccountSecondaryRoutingScheme,
|
||||
other_account_secondary_routing_address = toCounterparty.otherAccountSecondaryRoutingAddress,
|
||||
other_branch_routing_scheme = toCounterparty.otherBranchRoutingScheme,
|
||||
other_branch_routing_address = toCounterparty.otherBranchRoutingAddress,
|
||||
),
|
||||
value = AmountOfMoneyJsonV121(body.value.currency, body.value.amount),
|
||||
description = body.description,
|
||||
charge_policy = transactionRequest.charge_policy,
|
||||
future_date = transactionRequest.future_date
|
||||
)
|
||||
(transactionId, callContext) <- NewStyle.function.makePaymentv210(
|
||||
fromAccount,
|
||||
toAccount,
|
||||
transactionRequest.id,
|
||||
transactionRequestCommonBody = counterpartyBody,
|
||||
BigDecimal(counterpartyBody.value.amount),
|
||||
counterpartyBody.description,
|
||||
TransactionRequestType(transactionRequestType),
|
||||
transactionRequest.charge_policy,
|
||||
callContext
|
||||
)
|
||||
} yield {
|
||||
(transactionId, callContext)
|
||||
}
|
||||
// In the case of a REFUND (currently working only implemented for SEPA refund request)
|
||||
case REFUND =>
|
||||
for {
|
||||
@ -5349,4 +5528,42 @@ object LocalMappedConnector extends Connector with MdcLoggable {
|
||||
|
||||
override def checkAnswer(authContextUpdateId: String, challenge: String, callContext: Option[CallContext]) =
|
||||
UserAuthContextUpdateProvider.userAuthContextUpdateProvider.vend.checkAnswer(authContextUpdateId, challenge) map { ( _, callContext) }
|
||||
|
||||
override def sendCustomerNotification(
|
||||
scaMethod: StrongCustomerAuthentication,
|
||||
recipient: String,
|
||||
subject: Option[String], //Only for EMAIL, SMS do not need it, so here it is Option
|
||||
message: String,
|
||||
callContext: Option[CallContext]
|
||||
): OBPReturnType[Box[String]] = {
|
||||
if (scaMethod == StrongCustomerAuthentication.EMAIL){ // Send the email
|
||||
val params = PlainMailBodyType(message) :: List(To(recipient))
|
||||
Mailer.sendMail(From("challenge@tesobe.com"), Subject("OBP Consent Challenge"), params :_*)
|
||||
Future{(Full("Success"), callContext)}
|
||||
} else if (scaMethod == StrongCustomerAuthentication.SMS){ // Send the SMS
|
||||
for {
|
||||
phoneNumber <- Future.successful(recipient)
|
||||
failMsg =s"$MissingPropsValueAtThisInstance sca_phone_api_key"
|
||||
smsProviderApiKey <- NewStyle.function.tryons(failMsg, 400, callContext) {
|
||||
APIUtil.getPropsValue("sca_phone_api_key").openOrThrowException(s"")
|
||||
}
|
||||
failMsg = s"$MissingPropsValueAtThisInstance sca_phone_api_secret"
|
||||
smsProviderApiSecret <- NewStyle.function.tryons(failMsg, 400, callContext) {
|
||||
APIUtil.getPropsValue("sca_phone_api_secret").openOrThrowException(s"")
|
||||
}
|
||||
client = new NexmoClient.Builder()
|
||||
.apiKey(smsProviderApiKey)
|
||||
.apiSecret(smsProviderApiSecret)
|
||||
.build();
|
||||
messageSent = new TextMessage("OBP-API", phoneNumber, message);
|
||||
response <- Future{client.getSmsClient().submitMessage(messageSent)}
|
||||
failMsg = s"$SmsServerNotResponding: $phoneNumber. Or Please to use EMAIL first."
|
||||
_ <- Helper.booleanToFuture(failMsg, cc=callContext) {
|
||||
response.getMessages.get(0).getStatus == com.nexmo.client.sms.MessageStatus.OK
|
||||
}
|
||||
}yield Future{(Full("Success"), callContext)}
|
||||
} else
|
||||
Future{(Full("Success"), callContext)}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -4480,7 +4480,9 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit {
|
||||
userId=userIdExample.value,
|
||||
key=keyExample.value,
|
||||
value=valueExample.value,
|
||||
timeStamp=toDate(timeStampExample)))
|
||||
timeStamp=toDate(timeStampExample),
|
||||
consumerId=consumerIdExample.value
|
||||
))
|
||||
),
|
||||
adapterImplementation = Some(AdapterImplementation("- Core", 1))
|
||||
)
|
||||
@ -4513,7 +4515,9 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit {
|
||||
key=keyExample.value,
|
||||
value=valueExample.value,
|
||||
challenge=challengeExample.value,
|
||||
status=statusExample.value))
|
||||
status=statusExample.value,
|
||||
consumerId=consumerIdExample.value
|
||||
))
|
||||
),
|
||||
adapterImplementation = Some(AdapterImplementation("- Core", 1))
|
||||
)
|
||||
@ -4595,7 +4599,8 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit {
|
||||
userId=userIdExample.value,
|
||||
key=keyExample.value,
|
||||
value=valueExample.value,
|
||||
timeStamp=toDate(timeStampExample))))
|
||||
timeStamp=toDate(timeStampExample),
|
||||
consumerId=consumerIdExample.value)))
|
||||
),
|
||||
adapterImplementation = Some(AdapterImplementation("- Core", 1))
|
||||
)
|
||||
|
||||
@ -26,7 +26,6 @@ Berlin 13359, Germany
|
||||
import java.net.{ConnectException, URLEncoder, UnknownHostException}
|
||||
import java.util.Date
|
||||
import java.util.UUID.randomUUID
|
||||
|
||||
import _root_.akka.stream.StreamTcpException
|
||||
import akka.http.scaladsl.model.headers.RawHeader
|
||||
import akka.http.scaladsl.model.{HttpProtocol, _}
|
||||
@ -34,12 +33,12 @@ import akka.util.ByteString
|
||||
import code.api.APIFailureNewStyle
|
||||
import code.api.ResourceDocs1_4_0.MessageDocsSwaggerDefinitions
|
||||
import code.api.cache.Caching
|
||||
import code.api.dynamic.endpoint.helper.MockResponseHolder
|
||||
import code.api.util.APIUtil.{AdapterImplementation, MessageDoc, OBPReturnType, saveConnectorMetric, _}
|
||||
import code.api.util.ErrorMessages._
|
||||
import code.api.util.ExampleValue._
|
||||
import code.api.util.RSAUtil.{computeXSign, getPrivateKeyFromString}
|
||||
import code.api.util.{APIUtil, CallContext, OBPQueryParam}
|
||||
import code.api.v4_0_0.dynamic.MockResponseHolder
|
||||
import code.bankconnectors._
|
||||
import code.context.UserAuthContextProvider
|
||||
import code.customer.internalMapping.MappedCustomerIdMappingProvider
|
||||
@ -4669,7 +4668,8 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable
|
||||
userId=userIdExample.value,
|
||||
key=keyExample.value,
|
||||
value=valueExample.value,
|
||||
timeStamp=toDate(timeStampExample)))
|
||||
timeStamp=toDate(timeStampExample),
|
||||
consumerId=consumerIdExample.value))
|
||||
),
|
||||
adapterImplementation = Some(AdapterImplementation("- Core", 1))
|
||||
)
|
||||
@ -4702,7 +4702,8 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable
|
||||
key=keyExample.value,
|
||||
value=valueExample.value,
|
||||
challenge=challengeExample.value,
|
||||
status=statusExample.value))
|
||||
status=statusExample.value,
|
||||
consumerId=consumerIdExample.value))
|
||||
),
|
||||
adapterImplementation = Some(AdapterImplementation("- Core", 1))
|
||||
)
|
||||
@ -4784,7 +4785,8 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable
|
||||
userId=userIdExample.value,
|
||||
key=keyExample.value,
|
||||
value=valueExample.value,
|
||||
timeStamp=toDate(timeStampExample))))
|
||||
timeStamp=toDate(timeStampExample),
|
||||
consumerId=consumerIdExample.value)))
|
||||
),
|
||||
adapterImplementation = Some(AdapterImplementation("- Core", 1))
|
||||
)
|
||||
|
||||
@ -4647,7 +4647,8 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable {
|
||||
userId=userIdExample.value,
|
||||
key=keyExample.value,
|
||||
value=valueExample.value,
|
||||
timeStamp=toDate(timeStampExample)))
|
||||
timeStamp=toDate(timeStampExample),
|
||||
consumerId=consumerIdExample.value))
|
||||
),
|
||||
adapterImplementation = Some(AdapterImplementation("- Core", 1))
|
||||
)
|
||||
@ -4680,8 +4681,8 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable {
|
||||
key=keyExample.value,
|
||||
value=valueExample.value,
|
||||
challenge=challengeExample.value,
|
||||
status=statusExample.value))
|
||||
),
|
||||
status=statusExample.value,
|
||||
consumerId=consumerIdExample.value))),
|
||||
adapterImplementation = Some(AdapterImplementation("- Core", 1))
|
||||
)
|
||||
|
||||
@ -4762,7 +4763,8 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable {
|
||||
userId=userIdExample.value,
|
||||
key=keyExample.value,
|
||||
value=valueExample.value,
|
||||
timeStamp=toDate(timeStampExample))))
|
||||
timeStamp=toDate(timeStampExample),
|
||||
consumerId=consumerIdExample.value)))
|
||||
),
|
||||
adapterImplementation = Some(AdapterImplementation("- Core", 1))
|
||||
)
|
||||
|
||||
@ -220,29 +220,14 @@ trait BranchesProvider {
|
||||
// If we get branches filter them
|
||||
val branches: Option[List[BranchT]] = getBranchesFromProvider(bankId : BankId ,queryParams)
|
||||
|
||||
branches match {
|
||||
case Some(branches) => {
|
||||
logger.debug(s"getBranches says there are ${branches.length} branches with or without license")
|
||||
|
||||
val branchesWithLicense = for {
|
||||
branch <- branches if branch.meta.license.name.size > 3
|
||||
} yield branch
|
||||
logger.debug(s"getBranches says there are ${branches.length} branchesWithLicense")
|
||||
Option(branchesWithLicense)
|
||||
}
|
||||
case None => {
|
||||
logger.debug(s"getBranches says there are None branches")
|
||||
None
|
||||
}
|
||||
}
|
||||
branches
|
||||
}
|
||||
|
||||
/*
|
||||
Return one Branch
|
||||
*/
|
||||
final def getBranch(bankId: BankId, branchId : BranchId) : Option[BranchT] = {
|
||||
// Filter out if no license data
|
||||
getBranchFromProvider(bankId,branchId).filter(x => x.meta.license.id != "" && x.meta.license.name != "")
|
||||
getBranchFromProvider(bankId,branchId)
|
||||
}
|
||||
|
||||
protected def getBranchFromProvider(bankId: BankId, branchId : BranchId) : Option[BranchT]
|
||||
|
||||
@ -3,6 +3,7 @@ package code.consumer
|
||||
import code.api.util.APIUtil
|
||||
import code.model.{AppType, Consumer, MappedConsumersProvider}
|
||||
import code.remotedata.RemotedataConsumers
|
||||
import com.openbankproject.commons.model.{BankIdAccountId, User, View}
|
||||
import net.liftweb.common.Box
|
||||
import net.liftweb.util.SimpleInjector
|
||||
|
||||
@ -50,6 +51,7 @@ trait ConsumersProvider {
|
||||
redirectURL: Option[String],
|
||||
createdByUserId: Option[String]): Box[Consumer]
|
||||
def populateMissingUUIDs(): Boolean
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -17,9 +17,10 @@ class MappedConsentAuthContext extends ConsentAuthContext with LongKeyedMapper[M
|
||||
override def key = Key.get
|
||||
override def value = Value.get
|
||||
override def consentAuthContextId = ConsentAuthContextId.get
|
||||
override def timeStamp = createdAt.get
|
||||
}
|
||||
|
||||
object MappedConsentAuthContext extends MappedConsentAuthContext with LongKeyedMetaMapper[MappedConsentAuthContext] {
|
||||
override def dbTableName = "ConsentAuthContext" // define a custom DB table name
|
||||
override def dbIndexes = UniqueIndex(ConsentId, Key) :: super.dbIndexes
|
||||
override def dbIndexes = UniqueIndex(ConsentId, Key, createdAt) :: super.dbIndexes
|
||||
}
|
||||
|
||||
@ -12,13 +12,15 @@ class MappedUserAuthContext extends UserAuthContext with LongKeyedMapper[MappedU
|
||||
object mUserId extends UUIDString(this)
|
||||
object mKey extends MappedString(this, 255)
|
||||
object mValue extends MappedString(this, 255)
|
||||
object mConsumerId extends MappedString(this, 255)
|
||||
|
||||
override def userId = mUserId.get
|
||||
override def key = mKey.get
|
||||
override def value = mValue.get
|
||||
override def userAuthContextId = mUserAuthContextId.get
|
||||
override def timeStamp = createdAt.get
|
||||
|
||||
override def consumerId = mConsumerId.get
|
||||
|
||||
}
|
||||
|
||||
object MappedUserAuthContext extends MappedUserAuthContext with LongKeyedMetaMapper[MappedUserAuthContext] {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package code.context
|
||||
|
||||
import code.api.util.ErrorMessages
|
||||
import code.api.util.ErrorMessages.CreateUserAuthContextError
|
||||
import code.util.Helper.MdcLoggable
|
||||
import net.liftweb.common.{Box, Empty, Full}
|
||||
import net.liftweb.mapper.By
|
||||
@ -13,13 +14,17 @@ import scala.concurrent.Future
|
||||
|
||||
object MappedUserAuthContextProvider extends UserAuthContextProvider with MdcLoggable {
|
||||
|
||||
override def createUserAuthContext(userId: String, key: String, value: String): Future[Box[MappedUserAuthContext]] =
|
||||
override def createUserAuthContext(userId: String, key: String, value: String, consumerId: String): Future[Box[MappedUserAuthContext]] =
|
||||
Future {
|
||||
createUserAuthContextAkka(userId, key, value)
|
||||
createUserAuthContextAkka(userId, key, value, consumerId)
|
||||
}
|
||||
def createUserAuthContextAkka(userId: String, key: String, value: String): Box[MappedUserAuthContext] =
|
||||
def createUserAuthContextAkka(userId: String, key: String, value: String, consumerId: String): Box[MappedUserAuthContext] =
|
||||
tryo {
|
||||
MappedUserAuthContext.create.mUserId(userId).mKey(key).mValue(value).saveMe()
|
||||
if(consumerId.isEmpty || consumerId == null){
|
||||
throw new RuntimeException(s"$CreateUserAuthContextError current consumerId is empty here.")
|
||||
}else{
|
||||
MappedUserAuthContext.create.mUserId(userId).mKey(key).mValue(value).mConsumerId(consumerId).saveMe()
|
||||
}
|
||||
}
|
||||
|
||||
override def getUserAuthContexts(userId: String): Future[Box[List[MappedUserAuthContext]]] = Future {
|
||||
|
||||
@ -13,6 +13,7 @@ class MappedUserAuthContextUpdate extends UserAuthContextUpdate with LongKeyedMa
|
||||
|
||||
object mUserAuthContextUpdateId extends MappedUUID(this)
|
||||
object mUserId extends UUIDString(this)
|
||||
object mConsumerId extends MappedString(this, 255)
|
||||
object mKey extends MappedString(this, 50)
|
||||
object mValue extends MappedString(this, 50)
|
||||
object mChallenge extends MappedString(this, 10) {
|
||||
@ -21,6 +22,7 @@ class MappedUserAuthContextUpdate extends UserAuthContextUpdate with LongKeyedMa
|
||||
object mStatus extends MappedString(this, 20)
|
||||
|
||||
override def userId = mUserId.get
|
||||
override def consumerId: String = mConsumerId.get
|
||||
override def key = mKey.get
|
||||
override def value = mValue.get
|
||||
override def userAuthContextUpdateId = mUserAuthContextUpdateId.get
|
||||
|
||||
@ -12,12 +12,13 @@ import scala.concurrent.Future
|
||||
|
||||
object MappedUserAuthContextUpdateProvider extends UserAuthContextUpdateProvider with MdcLoggable {
|
||||
|
||||
override def createUserAuthContextUpdates(userId: String, key: String, value: String): Future[Box[MappedUserAuthContextUpdate]] =
|
||||
override def createUserAuthContextUpdates(userId: String, consumerId:String, key: String, value: String): Future[Box[MappedUserAuthContextUpdate]] =
|
||||
Future {
|
||||
tryo {
|
||||
MappedUserAuthContextUpdate
|
||||
.create
|
||||
.mUserId(userId)
|
||||
.mConsumerId(consumerId)
|
||||
.mKey(key)
|
||||
.mValue(value)
|
||||
.mStatus(UserAuthContextUpdateStatus.INITIATED.toString)
|
||||
|
||||
@ -22,7 +22,7 @@ object UserAuthContextProvider extends SimpleInjector {
|
||||
}
|
||||
|
||||
trait UserAuthContextProvider {
|
||||
def createUserAuthContext(userId: String, key: String, value: String): Future[Box[UserAuthContext]]
|
||||
def createUserAuthContext(userId: String, key: String, value: String, consumerId: String): Future[Box[UserAuthContext]]
|
||||
def getUserAuthContexts(userId: String): Future[Box[List[UserAuthContext]]]
|
||||
def getUserAuthContextsBox(userId: String): Box[List[UserAuthContext]]
|
||||
def createOrUpdateUserAuthContexts(userId: String, userAuthContexts: List[BasicUserAuthContext]): Box[List[UserAuthContext]]
|
||||
@ -31,7 +31,7 @@ trait UserAuthContextProvider {
|
||||
}
|
||||
|
||||
class RemotedataUserAuthContextCaseClasses {
|
||||
case class createUserAuthContext(userId: String, key: String, value: String)
|
||||
case class createUserAuthContext(userId: String, key: String, value: String, consumerId: String)
|
||||
case class getUserAuthContexts(userId: String)
|
||||
case class getUserAuthContextsBox(userId: String)
|
||||
case class createOrUpdateUserAuthContexts(userId: String, userAuthContext: List[BasicUserAuthContext])
|
||||
|
||||
@ -21,7 +21,7 @@ object UserAuthContextUpdateProvider extends SimpleInjector {
|
||||
}
|
||||
|
||||
trait UserAuthContextUpdateProvider {
|
||||
def createUserAuthContextUpdates(userId: String, key: String, value: String): Future[Box[UserAuthContextUpdate]]
|
||||
def createUserAuthContextUpdates(userId: String, consumerId:String, key: String, value: String): Future[Box[UserAuthContextUpdate]]
|
||||
def getUserAuthContextUpdates(userId: String): Future[Box[List[UserAuthContextUpdate]]]
|
||||
def getUserAuthContextUpdatesBox(userId: String): Box[List[UserAuthContextUpdate]]
|
||||
def deleteUserAuthContextUpdates(userId: String): Future[Box[Boolean]]
|
||||
@ -30,7 +30,7 @@ trait UserAuthContextUpdateProvider {
|
||||
}
|
||||
|
||||
class RemotedataUserAuthContextUpdateCaseClasses {
|
||||
case class createUserAuthContextUpdate(userId: String, key: String, value: String)
|
||||
case class createUserAuthContextUpdate(userId: String, consumerId:String, key: String, value: String)
|
||||
case class getUserAuthContextUpdates(userId: String)
|
||||
case class getUserAuthContextUpdatesBox(userId: String)
|
||||
case class deleteUserAuthContextUpdates(userId: String)
|
||||
|
||||
@ -2,8 +2,8 @@ package code.DynamicEndpoint
|
||||
|
||||
import java.util.UUID.randomUUID
|
||||
import code.api.cache.Caching
|
||||
import code.api.dynamic.endpoint.helper.DynamicEndpointHelper
|
||||
import code.api.util.{APIUtil, CustomJsonFormats}
|
||||
import code.api.v4_0_0.dynamic.DynamicEndpointHelper
|
||||
import code.util.MappedUUID
|
||||
import com.tesobe.CacheKeyFromArguments
|
||||
import net.liftweb.common.Box
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
package code.entitlement
|
||||
|
||||
import code.api.dynamic.endpoint.helper.DynamicEntityInfo
|
||||
import code.api.util.ApiRole.{CanCreateEntitlementAtAnyBank, CanCreateEntitlementAtOneBank}
|
||||
import code.api.util.ErrorMessages
|
||||
import code.api.v4_0_0.dynamic.DynamicEntityInfo
|
||||
import code.util.{MappedUUID, UUIDString}
|
||||
import net.liftweb.common.{Box, Failure, Full}
|
||||
import net.liftweb.mapper._
|
||||
|
||||
@ -59,7 +59,7 @@ trait ThingProvider {
|
||||
case Some(things) => {
|
||||
|
||||
val certainThings = for {
|
||||
thing <- things // if thing.meta.license.name.size > 3
|
||||
thing <- things
|
||||
} yield thing
|
||||
Option(certainThings)
|
||||
}
|
||||
|
||||
@ -32,11 +32,29 @@ trait Counterparties {
|
||||
|
||||
def getMetadata(originalPartyBankId: BankId, originalPartyAccountId : AccountId, counterpartyMetadataId : String) : Box[CounterpartyMetadata]
|
||||
|
||||
def deleteMetadata(originalPartyBankId: BankId, originalPartyAccountId: AccountId, counterpartyMetadataId: String): Box[Boolean]
|
||||
|
||||
def getCounterparty(counterpartyId : String): Box[CounterpartyTrait]
|
||||
|
||||
def deleteCounterparty(counterpartyId : String): Box[Boolean]
|
||||
|
||||
def getCounterpartyByIban(iban : String): Box[CounterpartyTrait]
|
||||
|
||||
def getCounterpartyByIbanAndBankAccountId(iban: String, bankId: BankId, accountId: AccountId): Box[CounterpartyTrait]
|
||||
|
||||
def getCounterpartyByRoutings(
|
||||
otherBankRoutingScheme: String,
|
||||
otherBankRoutingAddress: String,
|
||||
otherBranchRoutingScheme: String,
|
||||
otherBranchRoutingAddress: String,
|
||||
otherAccountRoutingScheme: String,
|
||||
otherAccountRoutingAddress: String
|
||||
): Box[CounterpartyTrait]
|
||||
|
||||
def getCounterpartyBySecondaryRouting(
|
||||
otherAccountSecondaryRoutingScheme: String,
|
||||
otherAccountSecondaryRoutingAddress: String
|
||||
): Box[CounterpartyTrait]
|
||||
|
||||
def getCounterparties(thisBankId: BankId, thisAccountId: AccountId, viewId: ViewId): Box[List[CounterpartyTrait]]
|
||||
|
||||
@ -94,8 +112,12 @@ class RemotedataCounterpartiesCaseClasses {
|
||||
case class getMetadatas(originalPartyBankId: BankId, originalPartyAccountId: AccountId)
|
||||
|
||||
case class getMetadata(originalPartyBankId: BankId, originalPartyAccountId: AccountId, counterpartyMetadataId: String)
|
||||
|
||||
case class deleteMetadata(originalPartyBankId: BankId, originalPartyAccountId: AccountId, counterpartyMetadataId: String)
|
||||
|
||||
case class getCounterparty(counterpartyId: String)
|
||||
|
||||
case class deleteCounterparty(counterpartyId: String)
|
||||
|
||||
case class getCounterpartyByIban(iban: String)
|
||||
|
||||
@ -103,6 +125,20 @@ class RemotedataCounterpartiesCaseClasses {
|
||||
|
||||
case class getCounterparties(thisBankId: BankId, thisAccountId: AccountId, viewId: ViewId)
|
||||
|
||||
case class getCounterpartyByRoutings(
|
||||
otherBankRoutingScheme: String,
|
||||
otherBankRoutingAddress: String,
|
||||
otherBranchRoutingScheme: String,
|
||||
otherBranchRoutingAddress: String,
|
||||
otherAccountRoutingScheme: String,
|
||||
otherAccountRoutingAddress: String
|
||||
)
|
||||
|
||||
case class getCounterpartyBySecondaryRouting(
|
||||
otherAccountSecondaryRoutingScheme: String,
|
||||
otherAccountSecondaryRoutingAddress: String
|
||||
)
|
||||
|
||||
case class createCounterparty(
|
||||
createdByUserId: String,
|
||||
thisBankId: String,
|
||||
|
||||
@ -111,6 +111,14 @@ object MapperCounterparties extends Counterparties with MdcLoggable {
|
||||
)
|
||||
}
|
||||
|
||||
override def deleteMetadata(originalPartyBankId: BankId, originalPartyAccountId: AccountId, counterpartyMetadataId: String): Box[Boolean] = {
|
||||
MappedCounterpartyMetadata.find(
|
||||
By(MappedCounterpartyMetadata.thisBankId, originalPartyBankId.value),
|
||||
By(MappedCounterpartyMetadata.thisAccountId, originalPartyAccountId.value),
|
||||
By(MappedCounterpartyMetadata.counterpartyId, counterpartyMetadataId)
|
||||
).map(_.delete_!)
|
||||
}
|
||||
|
||||
def addMetadata(bankId: BankId, accountId : AccountId): Box[CounterpartyMetadata] = {
|
||||
Full(
|
||||
MappedCounterpartyMetadata.create
|
||||
@ -124,6 +132,10 @@ object MapperCounterparties extends Counterparties with MdcLoggable {
|
||||
override def getCounterparty(counterpartyId : String): Box[CounterpartyTrait] = {
|
||||
MappedCounterparty.find(By(MappedCounterparty.mCounterPartyId, counterpartyId))
|
||||
}
|
||||
|
||||
override def deleteCounterparty(counterpartyId : String): Box[Boolean] = {
|
||||
MappedCounterparty.find(By(MappedCounterparty.mCounterPartyId, counterpartyId)).map(_.delete_!)
|
||||
}
|
||||
|
||||
//TODO, here has a problem, MappedCounterparty has no unique constrain on IBan. But we get Counterparty By Iban. For now, we do not support update Counterpary endpoint. Here we only return the latest record.
|
||||
override def getCounterpartyByIban(iban : String)= {
|
||||
@ -141,6 +153,36 @@ object MapperCounterparties extends Counterparties with MdcLoggable {
|
||||
)
|
||||
}
|
||||
|
||||
override def getCounterpartyByRoutings(
|
||||
otherBankRoutingScheme: String,
|
||||
otherBankRoutingAddress: String,
|
||||
otherBranchRoutingScheme: String,
|
||||
otherBranchRoutingAddress: String,
|
||||
otherAccountRoutingScheme: String,
|
||||
otherAccountRoutingAddress: String
|
||||
): Box[CounterpartyTrait] = {
|
||||
MappedCounterparty.find(
|
||||
By(MappedCounterparty.mOtherBankRoutingScheme,otherBankRoutingScheme),
|
||||
By(MappedCounterparty.mOtherBankRoutingAddress,otherBankRoutingAddress),
|
||||
By(MappedCounterparty.mOtherBranchRoutingScheme,otherBranchRoutingScheme),
|
||||
By(MappedCounterparty.mOtherBranchRoutingAddress,otherBranchRoutingAddress),
|
||||
By(MappedCounterparty.mOtherAccountRoutingScheme,otherAccountRoutingScheme),
|
||||
By(MappedCounterparty.mOtherAccountRoutingAddress,otherAccountRoutingAddress),
|
||||
)
|
||||
}
|
||||
|
||||
override def getCounterpartyBySecondaryRouting(
|
||||
otherAccountSecondaryRoutingScheme: String,
|
||||
otherAccountSecondaryRoutingAddress: String
|
||||
): Box[CounterpartyTrait] ={
|
||||
MappedCounterparty.find(
|
||||
By(MappedCounterparty.mOtherAccountSecondaryRoutingScheme, otherAccountSecondaryRoutingScheme),
|
||||
By(MappedCounterparty.mOtherAccountSecondaryRoutingAddress, otherAccountSecondaryRoutingAddress),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
override def getCounterparties(thisBankId: BankId, thisAccountId: AccountId, viewId: ViewId): Box[List[CounterpartyTrait]] = {
|
||||
Full(MappedCounterparty.findAll(By(MappedCounterparty.mThisAccountId, thisAccountId.value),
|
||||
By(MappedCounterparty.mThisBankId, thisBankId.value),
|
||||
@ -439,17 +481,21 @@ class MappedCounterparty extends CounterpartyTrait with LongKeyedMapper[MappedCo
|
||||
object mThisAccountId extends AccountIdString(this)
|
||||
object mThisViewId extends MappedString(this, 36)
|
||||
object mCounterPartyId extends UUIDString(this)
|
||||
object mOtherAccountRoutingScheme extends MappedString(this, 255)
|
||||
object mOtherAccountRoutingAddress extends MappedString(this, 255)
|
||||
|
||||
object mOtherBankRoutingScheme extends MappedString(this, 255)
|
||||
object mOtherBankRoutingAddress extends MappedString(this, 255)
|
||||
object mOtherBranchRoutingScheme extends MappedString(this, 255)
|
||||
object mOtherBranchRoutingAddress extends MappedString(this, 255)
|
||||
object mOtherAccountRoutingScheme extends MappedString(this, 255)
|
||||
object mOtherAccountRoutingAddress extends MappedString(this, 255)
|
||||
|
||||
object mOtherAccountSecondaryRoutingScheme extends MappedString(this, 255)
|
||||
object mOtherAccountSecondaryRoutingAddress extends MappedString(this, 255)
|
||||
|
||||
object mIsBeneficiary extends MappedBoolean(this)
|
||||
object mDescription extends MappedString(this, 36)
|
||||
object mCurrency extends MappedString(this, 255)
|
||||
object mOtherAccountSecondaryRoutingScheme extends MappedString(this, 255)
|
||||
object mOtherAccountSecondaryRoutingAddress extends MappedString(this, 255)
|
||||
|
||||
object mBespoke extends MappedOneToMany(MappedCounterpartyBespoke, MappedCounterpartyBespoke.mCounterparty, OrderBy(MappedCounterpartyBespoke.id, Ascending))
|
||||
|
||||
override def createdByUserId = mCreatedByUserId.get
|
||||
@ -458,17 +504,20 @@ class MappedCounterparty extends CounterpartyTrait with LongKeyedMapper[MappedCo
|
||||
override def thisAccountId = mThisAccountId.get
|
||||
override def thisViewId = mThisViewId.get
|
||||
override def counterpartyId = mCounterPartyId.get
|
||||
override def otherAccountRoutingScheme = mOtherAccountRoutingScheme.get
|
||||
override def otherAccountRoutingAddress: String = mOtherAccountRoutingAddress.get
|
||||
|
||||
override def otherBankRoutingScheme: String = mOtherBankRoutingScheme.get
|
||||
override def otherBankRoutingAddress: String = mOtherBankRoutingAddress.get
|
||||
override def otherBranchRoutingScheme: String = mOtherBranchRoutingScheme.get
|
||||
override def otherBranchRoutingAddress: String = mOtherBranchRoutingAddress.get
|
||||
override def otherBankRoutingAddress: String = mOtherBankRoutingAddress.get
|
||||
override def otherAccountRoutingScheme = mOtherAccountRoutingScheme.get
|
||||
override def otherAccountRoutingAddress: String = mOtherAccountRoutingAddress.get
|
||||
|
||||
override def otherAccountSecondaryRoutingScheme: String = mOtherAccountSecondaryRoutingScheme.get
|
||||
override def otherAccountSecondaryRoutingAddress: String = mOtherAccountSecondaryRoutingAddress.get
|
||||
|
||||
override def isBeneficiary: Boolean = mIsBeneficiary.get
|
||||
override def description: String = mDescription.get
|
||||
override def currency: String = mCurrency.toString
|
||||
override def otherAccountSecondaryRoutingScheme: String = mOtherAccountSecondaryRoutingScheme.get
|
||||
override def otherAccountSecondaryRoutingAddress: String = mOtherAccountSecondaryRoutingAddress.get
|
||||
override def bespoke: List[CounterpartyBespoke] =
|
||||
CounterpartyBespokes.counterpartyBespokers.vend
|
||||
.getCounterpartyBespokesByCounterpartyId(this.id.get)
|
||||
@ -478,5 +527,10 @@ class MappedCounterparty extends CounterpartyTrait with LongKeyedMapper[MappedCo
|
||||
}
|
||||
|
||||
object MappedCounterparty extends MappedCounterparty with LongKeyedMetaMapper[MappedCounterparty] {
|
||||
override def dbIndexes = UniqueIndex(mCounterPartyId) :: UniqueIndex(mName, mThisBankId, mThisAccountId, mThisViewId) :: super.dbIndexes
|
||||
override def dbIndexes =
|
||||
UniqueIndex(mCounterPartyId) ::
|
||||
UniqueIndex(mName, mThisBankId, mThisAccountId, mThisViewId) ::
|
||||
// UniqueIndex(mOtherBankRoutingScheme,mOtherBankRoutingAddress,mOtherBranchRoutingScheme,mOtherBranchRoutingAddress,mOtherAccountRoutingScheme,mOtherAccountRoutingAddress) ::
|
||||
// UniqueIndex(mOtherAccountSecondaryRoutingScheme, mOtherAccountSecondaryRoutingAddress) ::
|
||||
super.dbIndexes
|
||||
}
|
||||
@ -31,6 +31,8 @@ object MongoCounterparties extends Counterparties with MdcLoggable {
|
||||
} yield m
|
||||
}
|
||||
|
||||
def deleteMetadata(originalPartyBankId: BankId, originalPartyAccountId: AccountId, counterpartyMetadataId: String): Box[Boolean] = Empty
|
||||
|
||||
def getOrCreateMetadata(bankId: BankId, accountId : AccountId, counterpartyId:String, counterpartyName:String) : Box[CounterpartyMetadata] = {
|
||||
|
||||
/**
|
||||
@ -108,11 +110,27 @@ object MongoCounterparties extends Counterparties with MdcLoggable {
|
||||
}
|
||||
|
||||
override def getCounterparty(counterpartyId : String): Box[CounterpartyTrait] = Empty
|
||||
|
||||
override def deleteCounterparty(counterpartyId : String): Box[Boolean] = Empty
|
||||
|
||||
override def getCounterpartyByIban(iban : String): Box[CounterpartyTrait] = Empty
|
||||
|
||||
override def getCounterpartyByIbanAndBankAccountId(iban : String, bankId: BankId, accountId: AccountId): Box[CounterpartyTrait] = Empty
|
||||
|
||||
override def getCounterpartyByRoutings(
|
||||
otherBankRoutingScheme: String,
|
||||
otherBankRoutingAddress: String,
|
||||
otherBranchRoutingScheme: String,
|
||||
otherBranchRoutingAddress: String,
|
||||
otherAccountRoutingScheme: String,
|
||||
otherAccountRoutingAddress: String
|
||||
): Box[CounterpartyTrait] = Empty
|
||||
|
||||
override def getCounterpartyBySecondaryRouting(
|
||||
otherAccountSecondaryRoutingScheme: String,
|
||||
otherAccountSecondaryRoutingAddress: String
|
||||
): Box[CounterpartyTrait] = Empty
|
||||
|
||||
override def createCounterparty(
|
||||
createdByUserId: String,
|
||||
thisBankId: String,
|
||||
|
||||
@ -39,8 +39,10 @@ import code.users.Users
|
||||
import code.util.Helper.MdcLoggable
|
||||
import code.util.HydraUtil
|
||||
import code.util.HydraUtil._
|
||||
import code.views.system.{AccountAccess, ViewDefinition}
|
||||
import com.github.dwickern.macros.NameOf
|
||||
import com.openbankproject.commons.ExecutionContext.Implicits.global
|
||||
import com.openbankproject.commons.model.{BankIdAccountId, User, View}
|
||||
import net.liftweb.common._
|
||||
import net.liftweb.http.S
|
||||
import net.liftweb.mapper.{LongKeyedMetaMapper, _}
|
||||
|
||||
@ -57,16 +57,39 @@ case class UserExtended(val user: User) extends MdcLoggable {
|
||||
* But it need the view object in the parameters.
|
||||
* @param view the view object, need check the existence before calling the method
|
||||
* @param bankIdAccountId for the system view there is not ids in the view, so we need get it from parameters.
|
||||
* @param consumerId the consumerId, we will check if any accountAccess contains this consumerId or not.
|
||||
* @return if has the input view access, return true, otherwise false.
|
||||
*/
|
||||
final def hasAccountAccess(view: View, bankIdAccountId: BankIdAccountId): Boolean ={
|
||||
*/
|
||||
final def hasAccountAccess(view: View, bankIdAccountId: BankIdAccountId, consumerId:Option[String] = None): Boolean ={
|
||||
val viewDefinition = view.asInstanceOf[ViewDefinition]
|
||||
!(AccountAccess.count(
|
||||
By(AccountAccess.user_fk, this.userPrimaryKey.value),
|
||||
By(AccountAccess.view_fk, viewDefinition.id),
|
||||
By(AccountAccess.bank_id, bankIdAccountId.bankId.value),
|
||||
By(AccountAccess.account_id, bankIdAccountId.accountId.value),
|
||||
) == 0)
|
||||
|
||||
val consumerAccountAccess = {
|
||||
//If we find the AccountAccess by consumerId, this mean the accountAccess already assigned to some consumers
|
||||
val explictConsumerHasAccountAccess = if(consumerId.isDefined){
|
||||
AccountAccess.find(
|
||||
By(AccountAccess.bank_id, bankIdAccountId.bankId.value),
|
||||
By(AccountAccess.account_id, bankIdAccountId.accountId.value),
|
||||
By(AccountAccess.view_fk, viewDefinition.id),
|
||||
By(AccountAccess.user_fk, this.userPrimaryKey.value),
|
||||
By(AccountAccess.consumer_id, consumerId.get)).isDefined
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
if(explictConsumerHasAccountAccess) {
|
||||
true
|
||||
}else{
|
||||
//If we can not find accountAccess by consumerId, then we will find AccountAccess by default "ALL_CONSUMERS" , this mean the accountAccess can be used for all consumers
|
||||
AccountAccess.find(
|
||||
By(AccountAccess.bank_id, bankIdAccountId.bankId.value),
|
||||
By(AccountAccess.account_id, bankIdAccountId.accountId.value),
|
||||
By(AccountAccess.view_fk, viewDefinition.id),
|
||||
By(AccountAccess.user_fk, this.userPrimaryKey.value),
|
||||
By(AccountAccess.consumer_id, ALL_CONSUMERS)
|
||||
).isDefined
|
||||
}
|
||||
}
|
||||
consumerAccountAccess
|
||||
}
|
||||
|
||||
final def checkOwnerViewAccessAndReturnOwnerView(bankIdAccountId: BankIdAccountId) = {
|
||||
|
||||
@ -29,10 +29,10 @@ package code.model.dataAccess
|
||||
import code.api.util.CommonFunctions.validUri
|
||||
import code.UserRefreshes.UserRefreshes
|
||||
import code.accountholders.AccountHolders
|
||||
import code.api.dynamic.endpoint.helper.DynamicEndpointHelper
|
||||
import code.api.util.APIUtil.{hasAnOAuthHeader, logger, validatePasswordOnCreation, _}
|
||||
import code.api.util.ErrorMessages._
|
||||
import code.api.util._
|
||||
import code.api.v4_0_0.dynamic.DynamicEndpointHelper
|
||||
import code.api.{APIFailure, Constant, DirectLogin, GatewayLogin, OAuthHandshake}
|
||||
import code.bankconnectors.Connector
|
||||
import code.context.UserAuthContextProvider
|
||||
@ -615,7 +615,14 @@ import net.liftweb.util.Helpers._
|
||||
grantDefaultEntitlementsToAuthUser(user)
|
||||
logUserIn(user, () => {
|
||||
S.notice(S.?("account.validated"))
|
||||
S.redirectTo(homePage)
|
||||
APIUtil.getPropsValue("user_account_validated_redirect_url") match {
|
||||
case Full(redirectUrl) =>
|
||||
logger.debug(s"user_account_validated_redirect_url = $redirectUrl")
|
||||
S.redirectTo(redirectUrl)
|
||||
case _ =>
|
||||
logger.debug(s"user_account_validated_redirect_url is NOT defined")
|
||||
S.redirectTo(homePage)
|
||||
}
|
||||
})
|
||||
|
||||
case _ => S.error(S.?("invalid.validation.link")); S.redirectTo(homePage)
|
||||
@ -1263,7 +1270,7 @@ def restoreSomeSessions(): Unit = {
|
||||
(accountsHeld, _) <- Connector.connector.vend.getBankAccountsForUser(user.name,callContext) map {
|
||||
connectorEmptyResponse(_, callContext)
|
||||
}
|
||||
_ = logger.debug(s"-->AuthUser.refreshUserAccountAccess.accounts : ${accountsHeld}")
|
||||
_ = logger.debug(s"--> for user($user): AuthUser.refreshUserAccountAccess.accounts : ${accountsHeld}")
|
||||
}yield {
|
||||
refreshViewsAccountAccessAndHolders(user, accountsHeld)
|
||||
}
|
||||
|
||||
@ -156,6 +156,8 @@ class ViewImpl extends View with LongKeyedMapper[ViewImpl] with ManyToMany with
|
||||
canAddPublicAlias_(actions.exists(_ == "can_add_public_alias"))
|
||||
canAddPrivateAlias_(actions.exists(_ == "can_add_private_alias"))
|
||||
canAddCounterparty_(actions.exists(_ == "can_add_counterparty"))
|
||||
canGetCounterparty_(actions.exists(_ == "can_get_counterparty"))
|
||||
canDeleteCounterparty_(actions.exists(_ == "can_delete_counterparty"))
|
||||
canDeleteCorporateLocation_(actions.exists(_ == "can_delete_corporate_location"))
|
||||
canDeletePhysicalLocation_(actions.exists(_ == "can_delete_physical_location"))
|
||||
canEditOwnerComment_(actions.exists(_ == "can_edit_narrative"))
|
||||
@ -370,7 +372,13 @@ class ViewImpl extends View with LongKeyedMapper[ViewImpl] with ManyToMany with
|
||||
override def defaultValue = false
|
||||
}
|
||||
object canAddCounterparty_ extends MappedBoolean(this){
|
||||
override def defaultValue = true
|
||||
override def defaultValue = false
|
||||
}
|
||||
object canGetCounterparty_ extends MappedBoolean(this){
|
||||
override def defaultValue = false
|
||||
}
|
||||
object canDeleteCounterparty_ extends MappedBoolean(this){
|
||||
override def defaultValue = false
|
||||
}
|
||||
object canDeleteCorporateLocation_ extends MappedBoolean(this){
|
||||
override def defaultValue = false
|
||||
@ -505,6 +513,8 @@ class ViewImpl extends View with LongKeyedMapper[ViewImpl] with ManyToMany with
|
||||
def canAddPublicAlias : Boolean = canAddPublicAlias_.get
|
||||
def canAddPrivateAlias : Boolean = canAddPrivateAlias_.get
|
||||
def canAddCounterparty : Boolean = canAddCounterparty_.get
|
||||
def canGetCounterparty : Boolean = canGetCounterparty_.get
|
||||
def canDeleteCounterparty : Boolean = canDeleteCounterparty_.get
|
||||
def canDeleteCorporateLocation : Boolean = canDeleteCorporateLocation_.get
|
||||
def canDeletePhysicalLocation : Boolean = canDeletePhysicalLocation_.get
|
||||
|
||||
|
||||
@ -38,25 +38,14 @@ trait ProductsProvider {
|
||||
*/
|
||||
final def getProducts(bankId : BankId, adminView: Boolean = false) : Option[List[Product]] = {
|
||||
logger.info(s"Hello from getProducts bankId is: $bankId")
|
||||
|
||||
getProductsFromProvider(bankId) match {
|
||||
case Some(products) => {
|
||||
val productsWithLicense = for {
|
||||
// Only return products that have a license set unless its for an admin view
|
||||
product <- products if (adminView || (product.meta.license.name.size > 3))
|
||||
} yield product
|
||||
Option(productsWithLicense)
|
||||
}
|
||||
case None => None
|
||||
}
|
||||
getProductsFromProvider(bankId)
|
||||
}
|
||||
|
||||
/*
|
||||
Return one Product at a bank
|
||||
*/
|
||||
final def getProduct(bankId : BankId, productCode : ProductCode, adminView: Boolean = false) : Option[Product] = {
|
||||
// Filter out if no license data
|
||||
getProductFromProvider(bankId, productCode).filter(x => (adminView || (x.meta.license.id != "" && x.meta.license.name != "")))
|
||||
getProductFromProvider(bankId, productCode)
|
||||
}
|
||||
|
||||
protected def getProductFromProvider(bankId : BankId, productCode : ProductCode) : Option[Product]
|
||||
|
||||
@ -28,10 +28,18 @@ object RemotedataCounterparties extends ObpActorInit with Counterparties {
|
||||
(actor ? cc.getMetadata(originalPartyBankId: BankId, originalPartyAccountId: AccountId, counterpartyMetadataId: String)).mapTo[Box[CounterpartyMetadata]]
|
||||
)
|
||||
|
||||
override def deleteMetadata(originalPartyBankId: BankId, originalPartyAccountId: AccountId, counterpartyMetadataId: String): Box[Boolean] = getValueFromFuture(
|
||||
(actor ? cc.deleteMetadata(originalPartyBankId: BankId, originalPartyAccountId: AccountId, counterpartyMetadataId: String)).mapTo[Box[Boolean]]
|
||||
)
|
||||
|
||||
override def getCounterparty(counterpartyId: String): Box[CounterpartyTrait] = getValueFromFuture(
|
||||
(actor ? cc.getCounterparty(counterpartyId: String)).mapTo[Box[CounterpartyTrait]]
|
||||
)
|
||||
|
||||
override def deleteCounterparty(counterpartyId : String): Box[Boolean]= getValueFromFuture(
|
||||
(actor ? cc.deleteCounterparty(counterpartyId: String)).mapTo[Box[Boolean]]
|
||||
)
|
||||
|
||||
override def getCounterpartyByIban(iban: String): Box[CounterpartyTrait] = getValueFromFuture(
|
||||
(actor ? cc.getCounterpartyByIban(iban: String)).mapTo[Box[CounterpartyTrait]]
|
||||
)
|
||||
@ -43,6 +51,34 @@ object RemotedataCounterparties extends ObpActorInit with Counterparties {
|
||||
override def getCounterparties(thisBankId: BankId, thisAccountId: AccountId, viewId: ViewId): Box[List[CounterpartyTrait]] = getValueFromFuture(
|
||||
(actor ? cc.getCounterparties(thisBankId, thisAccountId, viewId)).mapTo[Box[List[CounterpartyTrait]]]
|
||||
)
|
||||
|
||||
override def getCounterpartyByRoutings(
|
||||
otherBankRoutingScheme: String,
|
||||
otherBankRoutingAddress: String,
|
||||
otherBranchRoutingScheme: String,
|
||||
otherBranchRoutingAddress: String,
|
||||
otherAccountRoutingScheme: String,
|
||||
otherAccountRoutingAddress: String
|
||||
): Box[CounterpartyTrait] = getValueFromFuture(
|
||||
(actor ? cc.getCounterpartyByRoutings(
|
||||
otherBankRoutingScheme: String,
|
||||
otherBankRoutingAddress: String,
|
||||
otherBranchRoutingScheme: String,
|
||||
otherBranchRoutingAddress: String,
|
||||
otherAccountRoutingScheme: String,
|
||||
otherAccountRoutingAddress: String
|
||||
)).mapTo[Box[CounterpartyTrait]]
|
||||
)
|
||||
|
||||
override def getCounterpartyBySecondaryRouting(
|
||||
otherAccountSecondaryRoutingScheme: String,
|
||||
otherAccountSecondaryRoutingAddress: String
|
||||
): Box[CounterpartyTrait] = getValueFromFuture(
|
||||
(actor ? cc.getCounterpartyBySecondaryRouting(
|
||||
otherAccountSecondaryRoutingScheme: String,
|
||||
otherAccountSecondaryRoutingAddress: String
|
||||
)).mapTo[Box[CounterpartyTrait]]
|
||||
)
|
||||
|
||||
override def createCounterparty(
|
||||
createdByUserId: String,
|
||||
|
||||
@ -77,9 +77,17 @@ class RemotedataCounterpartiesActor extends Actor with ObpActorHelper with MdcLo
|
||||
logger.debug(s"getMetadata($originalPartyBankId, $originalPartyAccountId)")
|
||||
sender ! (mapper.getMetadata(originalPartyBankId: BankId, originalPartyAccountId: AccountId, counterpartyMetadataId: String))
|
||||
|
||||
case cc.deleteMetadata(originalPartyBankId: BankId, originalPartyAccountId: AccountId, counterpartyMetadataId: String) =>
|
||||
logger.debug(s"deleteMetadata($originalPartyBankId, $originalPartyAccountId, $counterpartyMetadataId)")
|
||||
sender ! (mapper.deleteMetadata(originalPartyBankId: BankId, originalPartyAccountId: AccountId, counterpartyMetadataId: String))
|
||||
|
||||
case cc.getCounterparty(counterpartyId: String) =>
|
||||
logger.debug(s"getCounterparty($counterpartyId)")
|
||||
sender ! (mapper.getCounterparty(counterpartyId: String))
|
||||
|
||||
case cc.deleteCounterparty(counterpartyId: String) =>
|
||||
logger.debug(s"deleteCounterparty($counterpartyId)")
|
||||
sender ! (mapper.deleteCounterparty(counterpartyId: String))
|
||||
|
||||
case cc.getCounterparties(thisBankId: BankId, thisAccountId: AccountId, viewId: ViewId) =>
|
||||
logger.debug(s"getCounterparties($thisBankId)")
|
||||
|
||||
@ -24,8 +24,8 @@ object RemotedataUserAuthContext extends ObpActorInit with UserAuthContextProvid
|
||||
(actor ? cc.createOrUpdateUserAuthContexts(userId, userAuthContexts)).mapTo[Box[List[UserAuthContext]]]
|
||||
)
|
||||
|
||||
def createUserAuthContext(userId: String, key: String, value: String): Future[Box[UserAuthContext]] =
|
||||
(actor ? cc.createUserAuthContext(userId, key, value)).mapTo[Box[UserAuthContext]]
|
||||
def createUserAuthContext(userId: String, key: String, value: String, consumerId: String): Future[Box[UserAuthContext]] =
|
||||
(actor ? cc.createUserAuthContext(userId, key, value, consumerId)).mapTo[Box[UserAuthContext]]
|
||||
|
||||
override def deleteUserAuthContexts(userId: String): Future[Box[Boolean]] =
|
||||
(actor ? cc.deleteUserAuthContexts(userId)).mapTo[Box[Boolean]]
|
||||
|
||||
@ -21,8 +21,8 @@ object RemotedataUserAuthContextUpdate extends ObpActorInit with UserAuthContext
|
||||
(actor ? cc.getUserAuthContextUpdatesBox(userId)).mapTo[Box[List[UserAuthContextUpdate]]]
|
||||
)
|
||||
|
||||
def createUserAuthContextUpdates(userId: String, key: String, value: String): Future[Box[UserAuthContextUpdate]] =
|
||||
(actor ? cc.createUserAuthContextUpdate(userId, key, value)).mapTo[Box[UserAuthContextUpdate]]
|
||||
def createUserAuthContextUpdates(userId: String, consumerId:String, key: String, value: String): Future[Box[UserAuthContextUpdate]] =
|
||||
(actor ? cc.createUserAuthContextUpdate(userId: String, consumerId:String, key, value)).mapTo[Box[UserAuthContextUpdate]]
|
||||
|
||||
override def deleteUserAuthContextUpdates(userId: String): Future[Box[Boolean]] =
|
||||
(actor ? cc.deleteUserAuthContextUpdates(userId)).mapTo[Box[Boolean]]
|
||||
|
||||
@ -15,9 +15,9 @@ class RemotedataUserAuthContextActor extends Actor with ObpActorHelper with MdcL
|
||||
|
||||
def receive = {
|
||||
|
||||
case cc.createUserAuthContext(userId: String, key: String, value: String) =>
|
||||
logger.debug(s"createUserAuthContext($userId, $key, $value)")
|
||||
sender ! (mapper.createUserAuthContextAkka(userId, key, value))
|
||||
case cc.createUserAuthContext(userId: String, key: String, value: String, consumerId: String) =>
|
||||
logger.debug(s"createUserAuthContext($userId, $key, $value, $consumerId)")
|
||||
sender ! (mapper.createUserAuthContextAkka(userId, key, value, consumerId))
|
||||
|
||||
case cc.getUserAuthContexts(userId: String) =>
|
||||
logger.debug(s"getUserAuthContexts($userId)")
|
||||
|
||||
@ -15,9 +15,9 @@ class RemotedataUserAuthContextUpdateActor extends Actor with ObpActorHelper wit
|
||||
|
||||
def receive = {
|
||||
|
||||
case cc.createUserAuthContextUpdate(userId: String, key: String, value: String) =>
|
||||
case cc.createUserAuthContextUpdate(userId: String, consumerId:String, key: String, value: String) =>
|
||||
logger.debug("createUserAuthContext(" + userId + ", " + key + ", " + value + ")")
|
||||
mapper.createUserAuthContextUpdates(userId, key, value) pipeTo sender
|
||||
mapper.createUserAuthContextUpdates(userId,consumerId, key, value) pipeTo sender
|
||||
|
||||
case cc.getUserAuthContextUpdates(userId: String) =>
|
||||
logger.debug("getUserAuthContexts(" + userId + ")")
|
||||
|
||||
@ -174,4 +174,12 @@ object RemotedataViews extends ObpActorInit with Views {
|
||||
)
|
||||
|
||||
|
||||
def revokeAccessToSystemViewForConsumer(bankId: BankId, accountId: AccountId, view : View, consumerId : String) : Box[Boolean] = getValueFromFuture(
|
||||
(actor ? cc.revokeAccessToSystemViewForConsumer(bankId: BankId, accountId: AccountId, view : View, consumerId : String)).mapTo[Box[Boolean]]
|
||||
)
|
||||
|
||||
def revokeAccessToCustomViewForConsumer(view : View, consumerId : String) : Box[Boolean] = getValueFromFuture(
|
||||
(actor ? cc.revokeAccessToCustomViewForConsumer(view : View, consumerId : String)).mapTo[Box[Boolean]]
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
@ -249,7 +249,7 @@ trait OBPDataImport extends MdcLoggable {
|
||||
// Check the data products is OK before calling calling createSaveableProducts
|
||||
|
||||
logger.debug("Get existing products that match the bank id and product code")
|
||||
val existing = data.products.flatMap(p => Products.productsProvider.vend.getProduct(BankId(p.bank_id), ProductCode(p.code), true))
|
||||
val existing = data.products.flatMap(p => Products.productsProvider.vend.getProduct(BankId(p.bank_id), ProductCode(p.code)))
|
||||
|
||||
val allNewCodes = data.products.map(_.code)
|
||||
val emptyCodes = allNewCodes.filter(_.isEmpty)
|
||||
|
||||
@ -132,6 +132,9 @@ class UserInvitation extends MdcLoggable {
|
||||
// Set the status of the user invitation to "FINISHED"
|
||||
UserInvitationProvider.userInvitationProvider.vend.updateStatusOfUserInvitation(userInvitation.map(_.userInvitationId).getOrElse(""), "FINISHED")
|
||||
// Set a new password
|
||||
// Please note that the query parameter is used to alter the message at password reset page i.e. at next code:
|
||||
// <h1>{if(S.queryString.isDefined) Helper.i18n("set.your.password") else S.?("reset.your.password")}</h1>
|
||||
// placed into function AuthZUser.passwordResetXhtml
|
||||
val resetLink = AuthUser.passwordResetUrl(u.idGivenByProvider, u.emailAddress, u.userId) + "?action=set"
|
||||
S.redirectTo(resetLink)
|
||||
}
|
||||
|
||||
@ -1,21 +1,23 @@
|
||||
package code.transaction
|
||||
|
||||
|
||||
import code.accountholders.AccountHolders
|
||||
import code.actorsystem.ObpLookupSystem
|
||||
import code.api.util.{APIUtil, ApiTrigger}
|
||||
import code.bankconnectors.{Connector, LocalMappedConnector}
|
||||
import code.bankconnectors.LocalMappedConnector.getBankAccountCommon
|
||||
import code.model._
|
||||
import code.usercustomerlinks.UserCustomerLink
|
||||
import code.util.Helper.MdcLoggable
|
||||
import code.util._
|
||||
import code.webhook.WebhookActor
|
||||
import code.webhook.WebhookActor.RelatedEntity
|
||||
import com.openbankproject.commons.model._
|
||||
import net.liftweb.common._
|
||||
import net.liftweb.common.Box.tryo
|
||||
import net.liftweb.mapper._
|
||||
|
||||
class MappedTransaction extends LongKeyedMapper[MappedTransaction] with IdPK with CreatedUpdated with TransactionUUID {
|
||||
|
||||
private val logger = Logger(classOf[MappedTransaction])
|
||||
class MappedTransaction extends LongKeyedMapper[MappedTransaction] with IdPK with CreatedUpdated with TransactionUUID with MdcLoggable {
|
||||
|
||||
def getSingleton = MappedTransaction
|
||||
|
||||
@ -58,7 +60,7 @@ class MappedTransaction extends LongKeyedMapper[MappedTransaction] with IdPK wit
|
||||
|
||||
//The following are the fields from CounterpartyTrait, previous just save BankAccount to simulate the counterparty.
|
||||
//Now we save the real Counterparty data
|
||||
//CP--> CounterParty
|
||||
//CP means CounterParty
|
||||
object CPCounterPartyId extends UUIDString(this)
|
||||
object CPOtherAccountProvider extends MappedString(this, 36)
|
||||
object CPOtherAccountRoutingScheme extends MappedString(this, 255)
|
||||
@ -242,23 +244,49 @@ object MappedTransaction extends MappedTransaction with LongKeyedMetaMapper[Mapp
|
||||
Helper.smallestCurrencyUnitToBigDecimal(value, t.currency.get).toString() + " " + t.currency.get
|
||||
}
|
||||
def sendMessage(apiTrigger: ApiTrigger): Unit = {
|
||||
actor ! WebhookActor.WebhookRequest(
|
||||
apiTrigger,
|
||||
APIUtil.generateUUID(),
|
||||
t.theBankId.value,
|
||||
t.theAccountId.value,
|
||||
getAmount(t.amount.get),
|
||||
getAmount(t.newAccountBalance.get)
|
||||
)
|
||||
if(apiTrigger.equals(ApiTrigger.onCreateTransaction)){
|
||||
|
||||
val userIdCustomerIdPairs: List[(String, String)] = for{
|
||||
holder <- AccountHolders.accountHolders.vend.getAccountHolders(t.theBankId, t.theAccountId).toList
|
||||
userCustomerLink <- UserCustomerLink.userCustomerLink.vend.getUserCustomerLinksByUserId(holder.userId)
|
||||
} yield{
|
||||
(holder.userId, userCustomerLink.customerId)
|
||||
}
|
||||
|
||||
val userIdCustomerIdsPairs: Map[String, List[String]] = userIdCustomerIdPairs.groupBy(_._1).map( a => (a._1,a._2.map(_._2)))
|
||||
val eventId = APIUtil.generateUUID()
|
||||
logger.debug("Before firing WebhookActor.AccountNotificationWebhookRequest.eventId: " + eventId)
|
||||
actor ! WebhookActor.AccountNotificationWebhookRequest(
|
||||
apiTrigger,
|
||||
eventId,
|
||||
t.theBankId.value,
|
||||
t.theAccountId.value,
|
||||
t.theTransactionId.value,
|
||||
userIdCustomerIdsPairs.map(pair => RelatedEntity(pair._1, pair._2)).toList
|
||||
)
|
||||
} else{
|
||||
val eventId = APIUtil.generateUUID()
|
||||
logger.debug("Before firing WebhookActor.WebhookRequest.eventId: " + eventId)
|
||||
actor ! WebhookActor.WebhookRequest(
|
||||
apiTrigger,
|
||||
eventId,
|
||||
t.theBankId.value,
|
||||
t.theAccountId.value,
|
||||
getAmount(t.amount.get),
|
||||
getAmount(t.newAccountBalance.get)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
t.amount.get match {
|
||||
case amount if amount > 0 =>
|
||||
sendMessage(ApiTrigger.onBalanceChange)
|
||||
sendMessage(ApiTrigger.onCreditTransaction)
|
||||
sendMessage(ApiTrigger.onCreateTransaction)
|
||||
case amount if amount < 0 =>
|
||||
sendMessage(ApiTrigger.onBalanceChange)
|
||||
sendMessage(ApiTrigger.onDebitTransaction)
|
||||
sendMessage(ApiTrigger.onCreateTransaction)
|
||||
case _ =>
|
||||
// Do not send anything
|
||||
}
|
||||
|
||||
@ -1,15 +1,19 @@
|
||||
package code.transactionChallenge
|
||||
|
||||
import code.api.util.ErrorMessages
|
||||
import code.api.util.APIUtil.transactionRequestChallengeTtl
|
||||
import code.api.util.{APIUtil, ErrorMessages}
|
||||
import com.openbankproject.commons.model.{ChallengeTrait, ErrorMessage}
|
||||
import com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SCA
|
||||
import com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus
|
||||
import com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.SCAStatus
|
||||
import net.liftweb.common.{Box, Failure, Full}
|
||||
import net.liftweb.mapper.By
|
||||
import net.liftweb.util.Helpers
|
||||
import org.mindrot.jbcrypt.BCrypt
|
||||
import net.liftweb.util.Helpers.tryo
|
||||
|
||||
import scala.compat.Platform
|
||||
|
||||
object MappedChallengeProvider extends ChallengeProvider {
|
||||
|
||||
override def saveChallenge(
|
||||
@ -54,23 +58,41 @@ object MappedChallengeProvider extends ChallengeProvider {
|
||||
): Box[ChallengeTrait] = {
|
||||
|
||||
val challenge = getChallenge(challengeId).openOrThrowException(s"${ErrorMessages.InvalidChallengeAnswer}")
|
||||
val currentAttemptCounterValue = challenge.attemptCounter
|
||||
//We update the counter anyway.
|
||||
challenge.mAttemptCounter(currentAttemptCounterValue+1).saveMe()
|
||||
|
||||
val currentHashedAnswer = BCrypt.hashpw(challengeAnswer, challenge.salt).substring(0, 44)
|
||||
val expectedHashedAnswer = challenge.expectedAnswer
|
||||
|
||||
userId match {
|
||||
case None =>
|
||||
if(currentHashedAnswer==expectedHashedAnswer) {
|
||||
tryo{challenge.mSuccessful(true).mScaStatus(StrongCustomerAuthenticationStatus.finalised.toString).saveMe()}
|
||||
} else {
|
||||
Failure(s"${ErrorMessages.InvalidChallengeAnswer}")
|
||||
}
|
||||
case Some(id) =>
|
||||
if(currentHashedAnswer==expectedHashedAnswer && id==challenge.expectedUserId) {
|
||||
tryo{challenge.mSuccessful(true).mScaStatus(StrongCustomerAuthenticationStatus.finalised.toString).saveMe()}
|
||||
} else {
|
||||
Failure(s"${ErrorMessages.InvalidChallengeAnswer}")
|
||||
val createDateTime = challenge.createdAt.get
|
||||
val challengeTTL : Long = Helpers.seconds(APIUtil.transactionRequestChallengeTtl)
|
||||
|
||||
val expiredDateTime: Long = createDateTime.getTime+challengeTTL
|
||||
val currentTime: Long = Platform.currentTime
|
||||
|
||||
//TODO, add column maxAttemptsAllowed (Int) to `mappedexpectedchallengeanswer` table instead of this hardcode number 3
|
||||
if(currentAttemptCounterValue <3){
|
||||
if(expiredDateTime > currentTime) {
|
||||
val currentHashedAnswer = BCrypt.hashpw(challengeAnswer, challenge.salt).substring(0, 44)
|
||||
val expectedHashedAnswer = challenge.expectedAnswer
|
||||
|
||||
userId match {
|
||||
case None =>
|
||||
if(currentHashedAnswer==expectedHashedAnswer) {
|
||||
tryo{challenge.mSuccessful(true).mScaStatus(StrongCustomerAuthenticationStatus.finalised.toString).saveMe()}
|
||||
} else {
|
||||
Failure(s"${ErrorMessages.InvalidChallengeAnswer}")
|
||||
}
|
||||
case Some(id) =>
|
||||
if(currentHashedAnswer==expectedHashedAnswer && id==challenge.expectedUserId) {
|
||||
tryo{challenge.mSuccessful(true).mScaStatus(StrongCustomerAuthenticationStatus.finalised.toString).saveMe()}
|
||||
} else {
|
||||
Failure(s"${ErrorMessages.InvalidChallengeAnswer}")
|
||||
}
|
||||
}
|
||||
}else{
|
||||
Failure(s"${ErrorMessages.OneTimePasswordExpired} Current expiration time is $transactionRequestChallengeTtl seconds")
|
||||
}
|
||||
}else{
|
||||
Failure(s"${ErrorMessages.AllowedAttemptsUsedUp}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -24,6 +24,9 @@ class MappedExpectedChallengeAnswer extends ChallengeTrait with LongKeyedMapper[
|
||||
object mScaStatus extends MappedString(this,100)
|
||||
object mConsentId extends MappedString(this,100)
|
||||
object mAuthenticationMethodId extends MappedString(this,100)
|
||||
object mAttemptCounter extends MappedInt(this){
|
||||
override def defaultValue = 0
|
||||
}
|
||||
|
||||
override def challengeId: String = mChallengeId.get
|
||||
override def challengeType: String = mChallengeType.get
|
||||
@ -36,6 +39,7 @@ class MappedExpectedChallengeAnswer extends ChallengeTrait with LongKeyedMapper[
|
||||
override def scaMethod: Option[SCA] = Option(StrongCustomerAuthentication.withName(mScaMethod.get))
|
||||
override def scaStatus: Option[SCAStatus] = Option(StrongCustomerAuthenticationStatus.withName(mScaStatus.get))
|
||||
override def authenticationMethodId: Option[String] = Option(mAuthenticationMethodId.get)
|
||||
override def attemptCounter: Int = mAttemptCounter.get
|
||||
}
|
||||
|
||||
object MappedExpectedChallengeAnswer extends MappedExpectedChallengeAnswer with LongKeyedMetaMapper[MappedExpectedChallengeAnswer] {
|
||||
|
||||
@ -285,7 +285,37 @@ class MappedTransactionRequest extends LongKeyedMapper[MappedTransactionRequest]
|
||||
else
|
||||
None
|
||||
|
||||
val t_to_transfer_to_phone = if (TransactionRequestTypes.withName(transactionType) == TransactionRequestTypes.TRANSFER_TO_PHONE && details.nonEmpty)
|
||||
val t_to_simple = if (TransactionRequestTypes.withName(transactionType) == TransactionRequestTypes.SIMPLE && details.nonEmpty){
|
||||
val transactionRequestSimples = for {
|
||||
JObject(child) <- parsedDetails
|
||||
JField("other_bank_routing_scheme", JString(otherBankRoutingScheme)) <- child
|
||||
JField("other_bank_routing_address", JString(otherBankRoutingAddress)) <- child
|
||||
JField("other_branch_routing_scheme", JString(otherBranchRoutingScheme)) <- child
|
||||
JField("other_branch_routing_address", JString(otherBranchRoutingAddress)) <- child
|
||||
JField("other_account_routing_scheme", JString(otherAccountRoutingScheme)) <- child
|
||||
JField("other_account_routing_address", JString(otherAccountRoutingAddress)) <- child
|
||||
JField("other_account_secondary_routing_scheme", JString(otherAccountSecondaryRoutingScheme)) <- child
|
||||
JField("other_account_secondary_routing_address", JString(otherAccountSecondaryRoutingAddress)) <- child
|
||||
} yield
|
||||
TransactionRequestSimple (
|
||||
otherBankRoutingScheme,
|
||||
otherBankRoutingAddress,
|
||||
otherBranchRoutingScheme,
|
||||
otherBranchRoutingAddress,
|
||||
otherAccountRoutingScheme,
|
||||
otherAccountRoutingAddress,
|
||||
otherAccountSecondaryRoutingScheme,
|
||||
otherAccountSecondaryRoutingAddress
|
||||
)
|
||||
if(transactionRequestSimples.isEmpty)
|
||||
Some(TransactionRequestSimple("","","","","","","",""))
|
||||
else
|
||||
Some(transactionRequestSimples.head)
|
||||
}
|
||||
else
|
||||
None
|
||||
|
||||
val t_to_transfer_to_phone = if (TransactionRequestTypes.withName(transactionType) == TransactionRequestTypes.TRANSFER_TO_PHONE && details.nonEmpty)
|
||||
Some(parsedDetails.extract[TransactionRequestTransferToPhone])
|
||||
else
|
||||
None
|
||||
@ -309,6 +339,7 @@ class MappedTransactionRequest extends LongKeyedMapper[MappedTransactionRequest]
|
||||
to_sandbox_tan = t_to_sandbox_tan,
|
||||
to_sepa = t_to_sepa,
|
||||
to_counterparty = t_to_counterparty,
|
||||
to_simple = t_to_simple,
|
||||
to_transfer_to_phone = t_to_transfer_to_phone,
|
||||
to_transfer_to_atm = t_to_transfer_to_atm,
|
||||
to_transfer_to_account = t_to_transfer_to_account,
|
||||
|
||||
@ -22,7 +22,8 @@ object TransactionRequests extends SimpleInjector {
|
||||
|
||||
object TransactionRequestTypes extends Enumeration {
|
||||
type TransactionRequestTypes = Value
|
||||
val SANDBOX_TAN, ACCOUNT, ACCOUNT_OTP, COUNTERPARTY, SEPA, FREE_FORM, TRANSFER_TO_PHONE, TRANSFER_TO_ATM, TRANSFER_TO_ACCOUNT, TRANSFER_TO_REFERENCE_ACCOUNT,
|
||||
val SANDBOX_TAN, ACCOUNT, ACCOUNT_OTP, COUNTERPARTY, SEPA, FREE_FORM, SIMPLE,
|
||||
TRANSFER_TO_PHONE, TRANSFER_TO_ATM, TRANSFER_TO_ACCOUNT, TRANSFER_TO_REFERENCE_ACCOUNT,
|
||||
//The following are BerlinGroup Standard
|
||||
SEPA_CREDIT_TRANSFERS, INSTANT_SEPA_CREDIT_TRANSFERS, TARGET_2_PAYMENTS, CROSS_BORDER_CREDIT_TRANSFERS, REFUND = Value
|
||||
}
|
||||
|
||||
@ -230,6 +230,35 @@ object MapperViews extends Views with MdcLoggable {
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
//Custom View will have bankId and accountId inside the `View`, so no need both in the parameters
|
||||
def revokeAccessToCustomViewForConsumer(view : View, consumerId : String) : Box[Boolean] = {
|
||||
for {
|
||||
viewDefinition <- ViewDefinition.findCustomView(view.bankId.value, view.accountId.value, view.viewId.value)
|
||||
accountAccess <- AccountAccess.find(
|
||||
By(AccountAccess.consumer_id, consumerId),
|
||||
By(AccountAccess.view_fk, viewDefinition.id),
|
||||
By(AccountAccess.bank_id, view.bankId.value),
|
||||
By(AccountAccess.account_id, view.accountId.value)
|
||||
) ?~! CannotFindAccountAccess
|
||||
} yield {
|
||||
accountAccess.delete_!
|
||||
}
|
||||
}
|
||||
|
||||
//System View only have the viewId in inside the `View`, both bankId and accountId are empty in the `View`. So we need both in the parameters
|
||||
def revokeAccessToSystemViewForConsumer(bankId: BankId, accountId: AccountId, view : View, consumerId : String) : Box[Boolean] = {
|
||||
for {
|
||||
viewDefinition <- ViewDefinition.find(By(ViewDefinition.id_, view.id))
|
||||
accountAccess <- AccountAccess.find(
|
||||
By(AccountAccess.consumer_id, consumerId),
|
||||
By(AccountAccess.bank_id, bankId.value),
|
||||
By(AccountAccess.account_id, accountId.value),
|
||||
By(AccountAccess.view_fk, viewDefinition.id)) ?~! CannotFindAccountAccess
|
||||
} yield {
|
||||
accountAccess.delete_!
|
||||
}
|
||||
}
|
||||
|
||||
//returns Full if deletable, Failure if not
|
||||
def canRevokeAccessAsBox(viewImpl : ViewDefinition, user : User) : Box[Unit] = {
|
||||
@ -536,6 +565,7 @@ object MapperViews extends Views with MdcLoggable {
|
||||
val publicView = CUSTOM_PUBLIC_VIEW_ID.equals(viewId.toLowerCase)
|
||||
val accountantsView = SYSTEM_ACCOUNTANT_VIEW_ID.equals(viewId.toLowerCase)
|
||||
val auditorsView = SYSTEM_AUDITOR_VIEW_ID.equals(viewId.toLowerCase)
|
||||
val smallPaymentVerifiedView = SYSTEM_SMALL_PAYMENT_VERIFIED_VIEW_ID.equals(viewId.toLowerCase)
|
||||
|
||||
val theView =
|
||||
if (ownerView)
|
||||
@ -546,6 +576,8 @@ object MapperViews extends Views with MdcLoggable {
|
||||
getOrCreateSystemView(SYSTEM_ACCOUNTANT_VIEW_ID)
|
||||
else if (auditorsView)
|
||||
getOrCreateSystemView(SYSTEM_AUDITOR_VIEW_ID)
|
||||
else if (smallPaymentVerifiedView)
|
||||
getOrCreateSystemView(SYSTEM_SMALL_PAYMENT_VERIFIED_VIEW_ID)
|
||||
else {
|
||||
logger.error(ViewIdNotSupported+ s"Your input viewId is :$viewId")
|
||||
Failure(ViewIdNotSupported+ s"Your input viewId is :$viewId")
|
||||
@ -838,6 +870,8 @@ object MapperViews extends Views with MdcLoggable {
|
||||
.canAddPublicAlias_(true)
|
||||
.canAddPrivateAlias_(true)
|
||||
.canAddCounterparty_(true)
|
||||
.canGetCounterparty_(true)
|
||||
.canDeleteCounterparty_(true)
|
||||
.canDeleteCorporateLocation_(true)
|
||||
.canDeletePhysicalLocation_(true)
|
||||
.canEditOwnerComment_(true)
|
||||
@ -925,6 +959,8 @@ object MapperViews extends Views with MdcLoggable {
|
||||
.canAddPublicAlias_(true)
|
||||
.canAddPrivateAlias_(true)
|
||||
.canAddCounterparty_(true)
|
||||
.canGetCounterparty_(true)
|
||||
.canDeleteCounterparty_(true)
|
||||
.canDeleteCorporateLocation_(true)
|
||||
.canDeletePhysicalLocation_(true)
|
||||
.canEditOwnerComment_(true)
|
||||
@ -1013,6 +1049,8 @@ object MapperViews extends Views with MdcLoggable {
|
||||
.canAddPublicAlias_(true)
|
||||
.canAddPrivateAlias_(true)
|
||||
.canAddCounterparty_(true)
|
||||
.canGetCounterparty_(true)
|
||||
.canDeleteCounterparty_(true)
|
||||
.canDeleteCorporateLocation_(true)
|
||||
.canDeletePhysicalLocation_(true)
|
||||
.canEditOwnerComment_(true)
|
||||
@ -1112,6 +1150,8 @@ object MapperViews extends Views with MdcLoggable {
|
||||
canAddPublicAlias_(true).
|
||||
canAddPrivateAlias_(true).
|
||||
canAddCounterparty_(true).
|
||||
canGetCounterparty_(true).
|
||||
canDeleteCounterparty_(true).
|
||||
canDeleteCorporateLocation_(true).
|
||||
canDeletePhysicalLocation_(true).
|
||||
canEditOwnerComment_(true).
|
||||
@ -1208,6 +1248,8 @@ object MapperViews extends Views with MdcLoggable {
|
||||
canAddPublicAlias_(true).
|
||||
canAddPrivateAlias_(true).
|
||||
canAddCounterparty_(true).
|
||||
canGetCounterparty_(true).
|
||||
canDeleteCounterparty_(true).
|
||||
canDeleteCorporateLocation_(true).
|
||||
canDeletePhysicalLocation_(true).
|
||||
canEditOwnerComment_(true).
|
||||
@ -1303,6 +1345,8 @@ Auditors
|
||||
canAddPublicAlias_(true).
|
||||
canAddPrivateAlias_(true).
|
||||
canAddCounterparty_(true).
|
||||
canGetCounterparty_(true).
|
||||
canDeleteCounterparty_(true).
|
||||
canDeleteCorporateLocation_(true).
|
||||
canDeletePhysicalLocation_(true).
|
||||
canEditOwnerComment_(true).
|
||||
|
||||
@ -48,6 +48,9 @@ trait Views {
|
||||
def revokeAllAccountAccess(bankId : BankId, accountId : AccountId, user : User) : Box[Boolean]
|
||||
def revokeAccountAccessByUser(bankId : BankId, accountId : AccountId, user : User) : Box[Boolean]
|
||||
|
||||
def revokeAccessToSystemViewForConsumer(bankId: BankId, accountId: AccountId, view : View, consumerId : String) : Box[Boolean]
|
||||
def revokeAccessToCustomViewForConsumer(view : View, consumerId : String) : Box[Boolean]
|
||||
|
||||
def customView(viewId : ViewId, bankAccountId: BankIdAccountId) : Box[View]
|
||||
def systemView(viewId : ViewId) : Box[View]
|
||||
def customViewFuture(viewId : ViewId, bankAccountId: BankIdAccountId) : Future[Box[View]]
|
||||
@ -168,6 +171,10 @@ class RemotedataViewsCaseClasses {
|
||||
case class removeAllViews(bankId: BankId, accountId: AccountId)
|
||||
|
||||
case class bulkDeleteAllPermissionsAndViews()
|
||||
|
||||
case class revokeAccessToSystemViewForConsumer(bankId: BankId, accountId: AccountId, view : View, consumerId : String)
|
||||
case class revokeAccessToCustomViewForConsumer(view : View, consumerId : String)
|
||||
|
||||
}
|
||||
|
||||
object RemotedataViewsCaseClasses extends RemotedataViewsCaseClasses
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package code.views.system
|
||||
|
||||
import code.api.Constant.ALL_CONSUMERS
|
||||
import code.model.dataAccess.ResourceUser
|
||||
import code.util.UUIDString
|
||||
import net.liftweb.mapper._
|
||||
@ -11,10 +12,16 @@ class AccountAccess extends LongKeyedMapper[AccountAccess] with IdPK with Create
|
||||
def getSingleton = AccountAccess
|
||||
object user_fk extends MappedLongForeignKey(this, ResourceUser)
|
||||
object bank_id extends MappedString(this, 255)
|
||||
|
||||
//If consumer_id is `ALL-CONSUMERS`, any consumers can use this record
|
||||
//If consumer_id is consumerId (obp UUID), only same consumer can use this record
|
||||
object consumer_id extends MappedString(this, 255){
|
||||
override def defaultValue = ALL_CONSUMERS
|
||||
}
|
||||
object account_id extends MappedString(this, 255)
|
||||
object view_id extends UUIDString(this)
|
||||
object view_fk extends MappedLongForeignKey(this, ViewDefinition)
|
||||
}
|
||||
object AccountAccess extends AccountAccess with LongKeyedMetaMapper[AccountAccess] {
|
||||
override def dbIndexes: List[BaseIndex[AccountAccess]] = UniqueIndex(bank_id, account_id, view_fk, user_fk) :: super.dbIndexes
|
||||
override def dbIndexes: List[BaseIndex[AccountAccess]] = UniqueIndex(bank_id, account_id, view_fk, user_fk, consumer_id) :: super.dbIndexes
|
||||
}
|
||||
|
||||
@ -221,7 +221,13 @@ class ViewDefinition extends View with LongKeyedMapper[ViewDefinition] with Many
|
||||
override def defaultValue = false
|
||||
}
|
||||
object canAddCounterparty_ extends MappedBoolean(this){
|
||||
override def defaultValue = true
|
||||
override def defaultValue = false
|
||||
}
|
||||
object canGetCounterparty_ extends MappedBoolean(this){
|
||||
override def defaultValue = false
|
||||
}
|
||||
object canDeleteCounterparty_ extends MappedBoolean(this){
|
||||
override def defaultValue = false
|
||||
}
|
||||
object canDeleteCorporateLocation_ extends MappedBoolean(this){
|
||||
override def defaultValue = false
|
||||
@ -259,9 +265,13 @@ class ViewDefinition extends View with LongKeyedMapper[ViewDefinition] with Many
|
||||
object canDeleteWhereTag_ extends MappedBoolean(this){
|
||||
override def defaultValue = false
|
||||
}
|
||||
|
||||
//internal transfer between my own accounts
|
||||
object canAddTransactionRequestToOwnAccount_ extends MappedBoolean(this){
|
||||
override def defaultValue = false
|
||||
}
|
||||
|
||||
// transfer to any account
|
||||
object canAddTransactionRequestToAnyAccount_ extends MappedBoolean(this){
|
||||
override def defaultValue = false
|
||||
}
|
||||
@ -354,6 +364,8 @@ class ViewDefinition extends View with LongKeyedMapper[ViewDefinition] with Many
|
||||
canAddPublicAlias_(actions.exists(_ == "can_add_public_alias"))
|
||||
canAddPrivateAlias_(actions.exists(_ == "can_add_private_alias"))
|
||||
canAddCounterparty_(actions.exists(_ == "can_add_counterparty"))
|
||||
canDeleteCounterparty_(actions.exists(_ == "can_delete_counterparty"))
|
||||
canGetCounterparty_(actions.exists(_ == "can_get_counterparty"))
|
||||
canDeleteCorporateLocation_(actions.exists(_ == "can_delete_corporate_location"))
|
||||
canDeletePhysicalLocation_(actions.exists(_ == "can_delete_physical_location"))
|
||||
canEditOwnerComment_(actions.exists(_ == "can_edit_narrative"))
|
||||
@ -465,6 +477,8 @@ class ViewDefinition extends View with LongKeyedMapper[ViewDefinition] with Many
|
||||
def canAddPublicAlias : Boolean = canAddPublicAlias_.get
|
||||
def canAddPrivateAlias : Boolean = canAddPrivateAlias_.get
|
||||
def canAddCounterparty : Boolean = canAddCounterparty_.get
|
||||
def canGetCounterparty : Boolean = canGetCounterparty_.get
|
||||
def canDeleteCounterparty : Boolean = canDeleteCounterparty_.get
|
||||
def canDeleteCorporateLocation : Boolean = canDeleteCorporateLocation_.get
|
||||
def canDeletePhysicalLocation : Boolean = canDeletePhysicalLocation_.get
|
||||
|
||||
|
||||
@ -0,0 +1,39 @@
|
||||
package code.webhook
|
||||
|
||||
import code.api.util.OBPQueryParam
|
||||
import net.liftweb.common.Box
|
||||
import net.liftweb.util.SimpleInjector
|
||||
|
||||
import scala.collection.immutable.List
|
||||
import scala.concurrent.Future
|
||||
|
||||
object BankAccountNotificationWebhookTrait extends SimpleInjector {
|
||||
val bankAccountNotificationWebhook = new Inject(buildOne _) {}
|
||||
|
||||
def buildOne: BankAccountNotificationWebhookProvider = MappedBankAccountNotificationWebhookProvider
|
||||
}
|
||||
|
||||
|
||||
trait BankAccountNotificationWebhookProvider {
|
||||
def getBankAccountNotificationWebhookByIdFuture(webhookId: String): Future[Box[BankAccountNotificationWebhookTrait]]
|
||||
|
||||
def getBankAccountNotificationWebhooksByUserIdFuture(userId: String): Future[Box[List[BankAccountNotificationWebhookTrait]]]
|
||||
|
||||
def getBankAccountNotificationWebhooksFuture(queryParams: List[OBPQueryParam]): Future[Box[List[BankAccountNotificationWebhookTrait]]]
|
||||
|
||||
def createBankAccountNotificationWebhookFuture(
|
||||
bankId: String,
|
||||
userId: String,
|
||||
triggerName: String,
|
||||
url: String,
|
||||
httpMethod: String,
|
||||
httpProtocol: String
|
||||
): Future[Box[BankAccountNotificationWebhookTrait]]
|
||||
|
||||
def deleteBankAccountNotificationWebhookFuture(webhookId: String): Future[Box[Boolean]]
|
||||
|
||||
}
|
||||
|
||||
trait BankAccountNotificationWebhookTrait extends SystemAccountNotificationWebhookTrait{
|
||||
def bankId: String
|
||||
}
|
||||
@ -0,0 +1,93 @@
|
||||
package code.webhook
|
||||
|
||||
import code.api.util._
|
||||
import code.util.{AccountIdString, MappedUUID, UUIDString}
|
||||
import net.liftweb.common.{Box, Full}
|
||||
import net.liftweb.mapper._
|
||||
import net.liftweb.util.Helpers.tryo
|
||||
|
||||
import scala.collection.immutable.List
|
||||
import com.openbankproject.commons.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.Future
|
||||
|
||||
object MappedBankAccountNotificationWebhookProvider extends BankAccountNotificationWebhookProvider {
|
||||
|
||||
override def getBankAccountNotificationWebhookByIdFuture(webhookId: String): Future[Box[BankAccountNotificationWebhookTrait]] = {
|
||||
Future(
|
||||
BankAccountNotificationWebhook.find(
|
||||
By(BankAccountNotificationWebhook.WebhookId, webhookId)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override def getBankAccountNotificationWebhooksByUserIdFuture(userId: String): Future[Box[List[BankAccountNotificationWebhookTrait]]] = {
|
||||
Future(
|
||||
Full(
|
||||
BankAccountNotificationWebhook.findAll(
|
||||
By(BankAccountNotificationWebhook.CreatedByUserId, userId),
|
||||
OrderBy(BankAccountNotificationWebhook.updatedAt, Descending)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override def getBankAccountNotificationWebhooksFuture(queryParams: List[OBPQueryParam]): Future[Box[List[BankAccountNotificationWebhookTrait]]] = {
|
||||
val limit = queryParams.collectFirst { case OBPLimit(value) => MaxRows[BankAccountNotificationWebhook](value) }
|
||||
val offset = queryParams.collectFirst { case OBPOffset(value) => StartAt[BankAccountNotificationWebhook](value) }
|
||||
val userId = queryParams.collectFirst { case OBPUserId(value) => By(BankAccountNotificationWebhook.CreatedByUserId, value) }
|
||||
val optionalParams: Seq[QueryParam[BankAccountNotificationWebhook]] = Seq(limit.toSeq, offset.toSeq, userId.toSeq).flatten
|
||||
Future(
|
||||
Full(
|
||||
BankAccountNotificationWebhook.findAll(optionalParams: _*)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override def createBankAccountNotificationWebhookFuture(
|
||||
bankId: String,
|
||||
userId: String,
|
||||
triggerName: String,
|
||||
url: String,
|
||||
httpMethod: String,
|
||||
httpProtocol: String,
|
||||
): Future[Box[BankAccountNotificationWebhookTrait]] = {
|
||||
val createBankAccountNotificationWebhook = BankAccountNotificationWebhook.create
|
||||
.BankId(bankId)
|
||||
.CreatedByUserId(userId)
|
||||
.TriggerName(triggerName)
|
||||
.Url(url)
|
||||
.HttpMethod(httpMethod)
|
||||
.HttpProtocol(httpProtocol)
|
||||
.saveMe()
|
||||
Future(Full(createBankAccountNotificationWebhook))
|
||||
}
|
||||
|
||||
override def deleteBankAccountNotificationWebhookFuture(webhookId: String): Future[Box[Boolean]] = {
|
||||
Future{BankAccountNotificationWebhook.find(By(BankAccountNotificationWebhook.WebhookId, webhookId)).map(_.delete_!)}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class BankAccountNotificationWebhook extends BankAccountNotificationWebhookTrait with LongKeyedMapper[BankAccountNotificationWebhook] with IdPK with CreatedUpdated {
|
||||
def getSingleton = BankAccountNotificationWebhook
|
||||
|
||||
object WebhookId extends MappedUUID(this)
|
||||
object BankId extends UUIDString(this)
|
||||
object TriggerName extends MappedString(this, 64)
|
||||
object Url extends MappedString(this, 1024)
|
||||
object HttpMethod extends MappedString(this, 64)
|
||||
object HttpProtocol extends MappedString(this, 64)
|
||||
object CreatedByUserId extends UUIDString(this)
|
||||
|
||||
def webhookId: String = WebhookId.get
|
||||
def bankId: String = BankId.get
|
||||
def triggerName: String = TriggerName.get
|
||||
def url: String = Url.get
|
||||
def httpMethod: String = HttpMethod.get
|
||||
def httpProtocol: String = HttpProtocol.get
|
||||
def createdByUserId: String = CreatedByUserId.get
|
||||
}
|
||||
|
||||
object BankAccountNotificationWebhook extends BankAccountNotificationWebhook with LongKeyedMetaMapper[BankAccountNotificationWebhook] {
|
||||
override def dbIndexes = UniqueIndex(WebhookId) :: super.dbIndexes
|
||||
}
|
||||
@ -90,7 +90,7 @@ class MappedAccountWebhook extends AccountWebhook with LongKeyedMapper[MappedAcc
|
||||
object mBankId extends UUIDString(this)
|
||||
object mAccountId extends AccountIdString(this)
|
||||
object mTriggerName extends MappedString(this, 64)
|
||||
object mUrl extends MappedString(this, 64)
|
||||
object mUrl extends MappedString(this, 1024)
|
||||
object mHttpMethod extends MappedString(this, 64)
|
||||
object mHttpProtocol extends MappedString(this, 64)
|
||||
object mCreatedByUserId extends UUIDString(this)
|
||||
|
||||
@ -0,0 +1,50 @@
|
||||
package code.webhook
|
||||
|
||||
import code.api.util.OBPQueryParam
|
||||
import net.liftweb.common.Box
|
||||
import net.liftweb.util.SimpleInjector
|
||||
|
||||
import scala.collection.immutable.List
|
||||
import scala.concurrent.Future
|
||||
|
||||
object SystemAccountNotificationWebhookTrait extends SimpleInjector {
|
||||
val systemAccountNotificationWebhook = new Inject(buildOne _) {}
|
||||
|
||||
def buildOne: SystemAccountNotificationWebhookProvider = MappedSystemAccountNotificationWebhookProvider
|
||||
}
|
||||
|
||||
|
||||
trait SystemAccountNotificationWebhookProvider {
|
||||
def getSystemAccountNotificationWebhookByIdFuture(webhookId: String): Future[Box[SystemAccountNotificationWebhookTrait]]
|
||||
|
||||
def getSystemAccountNotificationWebhooksByUserIdFuture(userId: String): Future[Box[List[SystemAccountNotificationWebhookTrait]]]
|
||||
|
||||
def getSystemAccountNotificationWebhooksFuture(queryParams: List[OBPQueryParam]): Future[Box[List[SystemAccountNotificationWebhookTrait]]]
|
||||
|
||||
def createSystemAccountNotificationWebhookFuture(
|
||||
userId: String,
|
||||
triggerName: String,
|
||||
url: String,
|
||||
httpMethod: String,
|
||||
httpProtocol: String
|
||||
): Future[Box[SystemAccountNotificationWebhookTrait]]
|
||||
|
||||
def deleteSystemAccountNotificationWebhookFuture(webhookId: String): Future[Box[Boolean]]
|
||||
|
||||
}
|
||||
|
||||
trait SystemAccountNotificationWebhookTrait {
|
||||
|
||||
def webhookId: String
|
||||
|
||||
def triggerName: String
|
||||
|
||||
def url: String
|
||||
|
||||
def httpMethod: String
|
||||
|
||||
def httpProtocol: String
|
||||
|
||||
def createdByUserId: String
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user