diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..59d6e0955 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,37 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false + +[*.java] +indent_style = space +indent_size = 4 +continuation_indent_size = 4 + +[*.py] +indent_style = space +indent_size = 4 + +[*.{js,css,html}] +indent_style = space +indent_size = 4 +insert_final_newline = false + +[*.yml] +indent_style = space +indent_size = 2 + +[*.json] +indent_style = space +indent_size = 2 + +[*.php] +insert_final_newline = false diff --git a/.gitignore b/.gitignore index 8d7c76c19..1cbde2d09 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ multibanking-app/.history pom.xml.versionsBackup multibanking-app/.sourcemaps multibanking-app/platforms +multibanking-persistence-jpa/de.adorsys.multibanking.jpa.entity.BankJpaEntity diff --git a/multibanking-server/Dockerfile b/multibanking-server/Dockerfile new file mode 100644 index 000000000..ea44db97a --- /dev/null +++ b/multibanking-server/Dockerfile @@ -0,0 +1,7 @@ +FROM adorsys/java:8 + +MAINTAINER https://git.adorsys.de/adorsys/multibanking-service + +ENV JAVA_OPTS -Xmx1024m + +COPY ./target/multibanking-service.jar . diff --git a/multibanking-server/docker-compose.sh b/multibanking-server/docker-compose.sh new file mode 100755 index 000000000..3e6202309 --- /dev/null +++ b/multibanking-server/docker-compose.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +rm .env + +hostIp=$(ifconfig | grep '\' | cut -d ' ' -f2 | grep -v '127.0.0.1' | head -1) + +echo "HOST_IP=$hostIp" >> .env + +if [ $# -eq 1 ]; then + if [ $1 = "debug" ]; then + echo "KEYCLOAK_PARAMS=-b 0.0.0.0 --debug" >> .env + fi +fi + +cat .env + +docker-compose up + +rm .env diff --git a/multibanking-server/docker-compose.yml b/multibanking-server/docker-compose.yml new file mode 100644 index 000000000..497c469fb --- /dev/null +++ b/multibanking-server/docker-compose.yml @@ -0,0 +1,58 @@ +version: '3.1' +services: + postgres_keycloak: + image: postgres:9.6 + environment: + POSTGRES_USER: keycloak + POSTGRES_PASSWORD: keycloak + postgres_mbs: + image: postgres:9.6 + environment: + POSTGRES_USER: mbs + POSTGRES_PASSWORD: mbs + ports: + - 5432:5432 + keycloak: + image: openshift-registry.adorsys.de/adorsys-multibanking-dev/keycloak:latest + environment: + KEYCLOAK_USER: admin + KEYCLOAK_PASSWORD: admin123 + DB_VENDOR: postgres + DB_ADDR: postgres_keycloak + DB_USER: keycloak + DB_PASSWORD: keycloak + USER_SECRET_ENCRYPTION_PASSWORD: 123456789 + STS_RESOURCE_SERVER_LIST: MULTIBANKING + STS_RESOURCE_SERVERS_MULTIBANKING_JWKS_URL: http://host.docker.internal:8081/pop + STS_RESOURCE_SERVERS_MULTIBANKING_AUDIENCE: Multibanking + STS_RESOURCE_SERVERS_MULTIBANKING_USER_SECRET_CLAIM_NAME: Multibanking + STS_DEFAULT_AUDIENCE: Multibanking + ports: + - "8080:8080" + - "8787:8787" + depends_on: + - postgres_keycloak + command: + "${KEYCLOAK_PARAMS}" + mongodb: + image: mongo:3 + ports: + - "27017:27017" + multibanking: + image: adorsys/openjdk-jre-base:8-minideb + working_dir: /app + volumes: + - ./target:/app + environment: + spring.profiles.active: dev-mongo + server.port: 8081 + mongo.server: mongodb + mongo.databaseName: multibanking + db_secret: 1234567890123456 + mockConnectionUrl: http://localhost:8083 + SMARTANALYTICS_URL: http://localhost:8082 + SPRING_APPLICATION_JSON: '{"sts":{"authservers":[{"name":"keycloak","iss-url":"http://keycloak:8080/auth/realms/multibanking","jwks-url":"http://keycloak:8080/auth/realms/multibanking/protocol/openid-connect/certs"}]}}' + command: java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8787 -jar /app/multibanking-server.jar + ports: + - "8088:8081" + - "8788:8787" diff --git a/multibanking-server/pom.xml b/multibanking-server/pom.xml new file mode 100644 index 000000000..3473d3fcc --- /dev/null +++ b/multibanking-server/pom.xml @@ -0,0 +1,311 @@ + + + 4.0.0 + + de.adorsys.multibanking + multibanking + 4.6.2-SNAPSHOT + + multibanking-server + jar + + + 2.3.5 + 0.26.0 + 2.9.2 + 0.12 + 1.9.8 + 1.5.22 + + + + + org.springframework.boot + spring-boot-starter-web + + + + + de.adorsys.sts + sts-spring + ${sts.version} + + + de.adorsys.sts + sts-persistence-mongo + ${sts.version} + + + + + de.adorsys.multibanking + onlinebanking-figo + 4.6.2-SNAPSHOT + + + de.adorsys.multibanking + onlinebanking-finapi + 4.6.2-SNAPSHOT + + + de.adorsys.multibanking + onlinebanking-hbci4java + 4.6.2-SNAPSHOT + + + de.adorsys.multibanking + onlinebanking-mock + 4.6.2-SNAPSHOT + + + de.adorsys.multibanking + onlinebanking-bankinggateway + 4.6.2-SNAPSHOT + + + io.swagger.core.v3 + swagger-annotations + + + + + + + de.adorsys.multibanking + multibanking-persistence-mongodb + 4.6.2-SNAPSHOT + + + de.adorsys.multibanking + multibanking-persistence-jpa + 4.6.2-SNAPSHOT + + + com.github.mongobee + mongobee + ${mongobee.version} + + + + + de.adorsys.smartanalytics + smartanalytics-core + ${smartanalytics.version} + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + ch.qos.logback + logback-classic + + + + + org.springframework.boot + spring-boot-starter-hateoas + + + org.apache.httpcomponents + httpclient + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + + org.springframework.boot + spring-boot-starter-security + + + + + org.projectlombok + lombok + provided + + + org.apache.commons + commons-lang3 + + + org.mapstruct + mapstruct-jdk8 + ${org.mapstruct.version} + + + + + io.springfox + springfox-swagger2 + ${swagger.version} + + + io.springfox + springfox-swagger-ui + ${swagger.version} + + + io.swagger + swagger-annotations + ${swagger-annotations.version} + + + + + org.springframework.boot + spring-boot-starter-test + test + + + de.bwaldvogel + mongo-java-server + ${mongo-java-server.version} + test + + + + + ${project.artifactId} + + + + src/main/resources + false + + + src/main/resources + true + + **/application.yml + **/application.properties + + + + + + + + maven-resources-plugin + 3.0.1 + + + # + + false + + + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 2.7 + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + + maven-javadoc-plugin + 3.0.0 + + ${project.build.sourceEncoding} + ${project.build.sourceEncoding} + ${project.build.sourceEncoding} + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.0 + + true + ${java.version} + ${java.version} + + + org.mapstruct + mapstruct-processor + ${org.mapstruct.version} + + + org.projectlombok + lombok + ${lombok.version} + + + + + + + maven-jar-plugin + 3.0.2 + + + + true + true + + + ${project.version}_${maven.build.timestamp} + ${project.artifactId} + ${project.version} + ${maven.build.timestamp} + ${buildNumber} + ${scmBranch} + + + + + + + org.codehaus.mojo + buildnumber-maven-plugin + 1.3 + + + validate + + create + + + + + + 7 + + + + + + diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/Application.java b/multibanking-server/src/main/java/de/adorsys/multibanking/Application.java new file mode 100755 index 000000000..866943635 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/Application.java @@ -0,0 +1,58 @@ +package de.adorsys.multibanking; + +import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.core.env.Environment; +import org.springframework.scheduling.annotation.EnableAsync; + +import java.lang.reflect.Field; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.security.Security; + +@Slf4j +@SpringBootApplication +@EnableAsync +public class Application { + + public static void main(String... args) throws UnknownHostException { + // turnOffEncPolicy(); + Security.addProvider(new BouncyCastleProvider()); + + SpringApplication app = new SpringApplication(Application.class); + Environment env = app.run(args).getEnvironment(); + String protocol = "http"; + if (env.getProperty("server.ssl.key-store") != null) { + protocol = "https"; + } + log.info("\n----------------------------------------------------------\n\t" + + "Application '{}' is running! Access URLs:\n\t" + + "Local: \t\t{}://localhost:{}\n\t" + + "External: \t{}://{}:{}\n\t" + + "Profile(s): \t{}\n----------------------------------------------------------", + env.getProperty("spring.application.name"), + protocol, + env.getProperty("server.port"), + protocol, + InetAddress.getLocalHost().getHostAddress(), + env.getProperty("server.port"), + env.getActiveProfiles()); + } + + public static void turnOffEncPolicy() { + // Warning: do not do this for productive code. Download and install the + // jce unlimited strength policy file + // see + // http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html + try { + Field field = Class.forName("javax.crypto.JceSecurity").getDeclaredField("isRestricted"); + field.setAccessible(true); + field.set(null, java.lang.Boolean.FALSE); + } catch (ClassNotFoundException | NoSuchFieldException | SecurityException | IllegalArgumentException + | IllegalAccessException ex) { + ex.printStackTrace(System.err); + } + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/config/AsyncConfig.java b/multibanking-server/src/main/java/de/adorsys/multibanking/config/AsyncConfig.java new file mode 100644 index 000000000..008d741f8 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/config/AsyncConfig.java @@ -0,0 +1,125 @@ +package de.adorsys.multibanking.config; + +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; +import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.task.AsyncTaskExecutor; +import org.springframework.scheduling.annotation.AsyncConfigurer; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; +import java.util.concurrent.Future; + +@Slf4j +@Configuration +@EnableAsync +@EnableScheduling +public class AsyncConfig implements AsyncConfigurer { + + @Value("${core.pool.size:2}") + private int corePoolSize; + @Value("${max.pool.size:50}") + private int maxPoolSize; + @Value("${queue.pool.size:10000}") + private int queueCapacity; + + @Override + @Bean(name = "taskExecutor") + public Executor getAsyncExecutor() { + log.debug("Creating Async Task Executor"); + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(corePoolSize); + executor.setMaxPoolSize(maxPoolSize); + executor.setQueueCapacity(queueCapacity); + executor.setThreadNamePrefix("multibanking-Executor-"); + return new ExceptionHandlingAsyncTaskExecutor(executor); + } + + @Override + public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { + return new SimpleAsyncUncaughtExceptionHandler(); + } + + public class ExceptionHandlingAsyncTaskExecutor implements AsyncTaskExecutor, + InitializingBean, DisposableBean { + + private final Logger log = LoggerFactory.getLogger(ExceptionHandlingAsyncTaskExecutor.class); + + private final AsyncTaskExecutor executor; + + public ExceptionHandlingAsyncTaskExecutor(AsyncTaskExecutor executor) { + this.executor = executor; + } + + @Override + public void execute(Runnable task) { + executor.execute(createWrappedRunnable(task)); + } + + @Override + public void execute(Runnable task, long startTimeout) { + executor.execute(createWrappedRunnable(task), startTimeout); + } + + private Callable createCallable(final Callable task) { + return () -> { + try { + return task.call(); + } catch (Exception e) { + handle(e); + throw e; + } + }; + } + + private Runnable createWrappedRunnable(final Runnable task) { + return () -> { + try { + task.run(); + } catch (Exception e) { + handle(e); + } + }; + } + + protected void handle(Exception e) { + log.error("Caught async exception", e); + } + + @Override + public Future submit(Runnable task) { + return executor.submit(createWrappedRunnable(task)); + } + + @Override + public Future submit(Callable task) { + return executor.submit(createCallable(task)); + } + + @Override + public void destroy() throws Exception { + if (executor instanceof DisposableBean) { + DisposableBean bean = (DisposableBean) executor; + bean.destroy(); + } + } + + @Override + public void afterPropertiesSet() throws Exception { + if (executor instanceof InitializingBean) { + InitializingBean bean = (InitializingBean) executor; + bean.afterPropertiesSet(); + } + } + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/config/AuthorizationClientRequestFactory.java b/multibanking-server/src/main/java/de/adorsys/multibanking/config/AuthorizationClientRequestFactory.java new file mode 100644 index 000000000..4eb013c0f --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/config/AuthorizationClientRequestFactory.java @@ -0,0 +1,30 @@ +package de.adorsys.multibanking.config; + +import de.adorsys.sts.tokenauth.BearerToken; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.impl.client.HttpClients; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.stereotype.Component; + +@Component +public class AuthorizationClientRequestFactory extends HttpComponentsClientHttpRequestFactory implements ClientHttpRequestFactory { + + private static final String AUTHORIZATION_HEADER = "Authorization"; + + @Autowired + private BearerToken bearerToken; + + public AuthorizationClientRequestFactory() { + super(HttpClients.custom() + .disableCookieManagement() + .build() + ); + } + + @Override + protected void postProcessHttpRequest(HttpUriRequest request) { + request.setHeader(AUTHORIZATION_HEADER, bearerToken.getToken()); + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/config/BankImportConfig.java b/multibanking-server/src/main/java/de/adorsys/multibanking/config/BankImportConfig.java new file mode 100755 index 000000000..b8a4e9677 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/config/BankImportConfig.java @@ -0,0 +1,38 @@ +package de.adorsys.multibanking.config; + +import de.adorsys.multibanking.service.BankService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.StringUtils; + +import javax.annotation.PostConstruct; +import java.io.File; + +@RequiredArgsConstructor +@Slf4j +@Configuration +public class BankImportConfig { + + private final BankService bankService; + @Value("${bank.import.file:}") + private String bankImportFile; + + @PostConstruct + public void init() { + if (StringUtils.isEmpty(bankImportFile)) { + return; + } + + File importFile = new File(bankImportFile); + + if (!importFile.exists()) { + log.error("File for bank import does not exist: {}", importFile.getAbsolutePath()); + return; + } + + bankService.importBanks(importFile); + } + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/config/JpaConfig.java b/multibanking-server/src/main/java/de/adorsys/multibanking/config/JpaConfig.java new file mode 100644 index 000000000..f56e8e0ca --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/config/JpaConfig.java @@ -0,0 +1,10 @@ +package de.adorsys.multibanking.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +@Profile({"jpa"}) +@Configuration +//@EnableJpaRepositories(basePackages = "de.adorsys.multibanking.jpa.repository") +public class JpaConfig { +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/config/Logging.java b/multibanking-server/src/main/java/de/adorsys/multibanking/config/Logging.java new file mode 100755 index 000000000..1c0feed47 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/config/Logging.java @@ -0,0 +1,69 @@ +package de.adorsys.multibanking.config; + +import org.springframework.http.MediaType; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.charset.UnsupportedCharsetException; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Logging { + + private static final int MAX_ENTITY_SIZE = 1024; + + private static final String PATTERN_TEMPLATE = "((\"%s\"):(?\".[^\"]+\"))"; + private static final String[] JSON_LOG_EXCLUDES = { + "pin", + "pin2", + "accountNumber", + "iban" + }; + private static final List SUPRESS_VALUES = new ArrayList<>(); + + static { + for (String logExclude : JSON_LOG_EXCLUDES) { + SUPRESS_VALUES.add(Pattern.compile(String.format(PATTERN_TEMPLATE, logExclude))); + } + } + + static String cleanAndReduce(byte[] entity, Charset charset) { + StringBuilder sb = new StringBuilder(); + if (entity.length < MAX_ENTITY_SIZE) { + sb.append(new String(entity, 0, entity.length, charset)); + } else { + sb.append(new String(entity, 0, MAX_ENTITY_SIZE, charset)).append("..."); + } + return clean(sb); + } + + static Charset determineCharset(MediaType contentType) { + if (contentType != null) { + try { + Charset charSet = contentType.getCharset(); + if (charSet != null) { + return charSet; + } + } catch (UnsupportedCharsetException e) { + // ignore + } + } + return StandardCharsets.UTF_8; + } + + private static String clean(StringBuilder sb) { + SUPRESS_VALUES.forEach(pattern -> { + Matcher matcher = pattern.matcher(sb.toString()); + while (matcher.find()) { + int start = matcher.start("value"); + int end = matcher.end("value"); + for (int i=start +1 ; i< end -1; i++) { + sb.setCharAt(i, 'x'); + } + } + }); + return sb.toString(); + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/config/LoggingHandlerInterceptor.java b/multibanking-server/src/main/java/de/adorsys/multibanking/config/LoggingHandlerInterceptor.java new file mode 100755 index 000000000..6cd768a06 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/config/LoggingHandlerInterceptor.java @@ -0,0 +1,86 @@ +package de.adorsys.multibanking.config; + +import ch.qos.logback.classic.ClassicConstants; +import org.slf4j.MDC; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; +import org.springframework.web.util.UriTemplate; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Map; +import java.util.UUID; + +@Component +public class LoggingHandlerInterceptor extends HandlerInterceptorAdapter { + + public static final String BANK_ACCESS_ID = "accessId"; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + insertIntoMDC(request, handler); + + return super.preHandle(request, response, handler); + } + + public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { + super.postHandle(request, response, handler, modelAndView); + clearMDC(); + } + + void insertIntoMDC(HttpServletRequest httpServletRequest, Object handler) { + if (handler instanceof HandlerMethod) { + HandlerMethod handlerMethod = (HandlerMethod) handler; + + RequestMapping mapping = handlerMethod.getBean().getClass().getAnnotation(RequestMapping.class); + + if (mapping.path().length > 0) { + UriTemplate uriTemplate = new UriTemplate(mapping.path()[0]); + + Map map = uriTemplate.match(httpServletRequest.getRequestURI()); + + MDC.put(BANK_ACCESS_ID, map.get(BANK_ACCESS_ID)); + } + + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String userId; + if (authentication == null || authentication.getName().equalsIgnoreCase("anonymousUser")) { + userId = "ANON-" + UUID.randomUUID(); + } else { + userId = authentication.getName(); + } + + MDC.put(ClassicConstants.USER_MDC_KEY, userId); + MDC.put(ClassicConstants.REQUEST_REMOTE_HOST_MDC_KEY, httpServletRequest.getRemoteHost()); + MDC.put(ClassicConstants.REQUEST_REQUEST_URI, httpServletRequest.getRequestURI()); + MDC.put(ClassicConstants.REQUEST_METHOD, httpServletRequest.getMethod()); + MDC.put(ClassicConstants.REQUEST_QUERY_STRING, httpServletRequest.getQueryString()); + MDC.put(ClassicConstants.REQUEST_USER_AGENT_MDC_KEY, httpServletRequest.getHeader("User-Agent")); + MDC.put(ClassicConstants.REQUEST_X_FORWARDED_FOR, httpServletRequest.getHeader("X-Forwarded-For")); + + StringBuffer requestURL = httpServletRequest.getRequestURL(); + if (requestURL != null) { + MDC.put(ClassicConstants.REQUEST_REQUEST_URL, requestURL.toString()); + } + } + } + + void clearMDC() { + MDC.remove(ClassicConstants.USER_MDC_KEY); + MDC.remove(ClassicConstants.REQUEST_REMOTE_HOST_MDC_KEY); + MDC.remove(ClassicConstants.REQUEST_REQUEST_URI); + MDC.remove(ClassicConstants.REQUEST_QUERY_STRING); + // removing possibly inexistent item is OK + MDC.remove(ClassicConstants.REQUEST_REQUEST_URL); + MDC.remove(ClassicConstants.REQUEST_METHOD); + MDC.remove(ClassicConstants.REQUEST_USER_AGENT_MDC_KEY); + MDC.remove(ClassicConstants.REQUEST_X_FORWARDED_FOR); + } + + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/config/LoggingInterceptorConfig.java b/multibanking-server/src/main/java/de/adorsys/multibanking/config/LoggingInterceptorConfig.java new file mode 100755 index 000000000..27c1a5367 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/config/LoggingInterceptorConfig.java @@ -0,0 +1,18 @@ +package de.adorsys.multibanking.config; + +import lombok.AllArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@AllArgsConstructor +@Configuration +public class LoggingInterceptorConfig implements WebMvcConfigurer { + + private final LoggingHandlerInterceptor loggingHandlerInterceptor; + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(loggingHandlerInterceptor); + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/config/MigrationConfig.java b/multibanking-server/src/main/java/de/adorsys/multibanking/config/MigrationConfig.java new file mode 100644 index 000000000..e6f2b6be0 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/config/MigrationConfig.java @@ -0,0 +1,26 @@ +package de.adorsys.multibanking.config; + +import com.github.mongobee.Mongobee; +import com.mongodb.MongoClient; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.core.env.Environment; +import org.springframework.data.mongodb.core.MongoTemplate; + +@Configuration +@Profile({"mongo", "fongo"}) +public class MigrationConfig { + + @Bean + public Mongobee mongobee(MongoClient mongoClient, MongoTemplate mongoTemplate, Environment env) { + Mongobee mongobee = new Mongobee(mongoClient); + mongobee.setDbName(env.getProperty("mongo.databaseName")); + mongobee.setMongoTemplate(mongoTemplate); + // package to scan for migrations + mongobee.setChangeLogsScanPackage("de.adorsys.multibanking.dbmigrations"); + mongobee.setEnabled(true); + return mongobee; + } + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/config/MongoConfig.java b/multibanking-server/src/main/java/de/adorsys/multibanking/config/MongoConfig.java new file mode 100644 index 000000000..f599eeb9f --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/config/MongoConfig.java @@ -0,0 +1,92 @@ +package de.adorsys.multibanking.config; + +import com.mongodb.*; +import de.adorsys.smartanalytics.config.EnableSmartanalyticsMongoPersistence; +import lombok.AllArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.Environment; +import org.springframework.data.mongodb.MongoDbFactory; +import org.springframework.data.mongodb.config.AbstractMongoConfiguration; +import org.springframework.data.mongodb.core.SimpleMongoDbFactory; +import org.springframework.data.mongodb.gridfs.GridFsTemplate; +import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; + +import java.util.Optional; + +@AllArgsConstructor +@Profile({"mongo"}) +@Configuration +@EnableMongoRepositories(basePackages = "de.adorsys.multibanking.mongo.repository") +@EnableSmartanalyticsMongoPersistence +@PropertySource(value = "${mongo.properties.url}", ignoreResourceNotFound = true) +public class MongoConfig extends AbstractMongoConfiguration { + + private final Environment env; + + @Override + protected String getDatabaseName() { + return Optional.ofNullable(env.getProperty("mongo.databaseName")) + .orElseThrow(() -> new IllegalStateException("missing env property mongo.databaseName")); + } + + @Bean + public GridFsTemplate gridFsTemplate() throws Exception { + return new GridFsTemplate(mongoDbFactory(), mappingMongoConverter()); + } + + @Bean + @Override + public MongoClient mongoClient() { + MongoClientOptions mongoClientOptions = new MongoClientOptions.Builder() + .connectionsPerHost(50) + .writeConcern(WriteConcern.JOURNALED) + .readPreference(ReadPreference.secondaryPreferred()) + .build(); + + ServerAddress serverAddress = getServerAddress(); + + if (StringUtils.isEmpty(env.getProperty("mongo.userName"))) { + return new MongoClient(serverAddress, mongoClientOptions); + } else { + MongoCredential mongoCredential = createMongoCredential(); + + return new MongoClient(serverAddress, mongoCredential, mongoClientOptions); + } + } + + private MongoCredential createMongoCredential() { + String userName = Optional.ofNullable(env.getProperty("mongo.userName")) + .orElseThrow(() -> new IllegalStateException("missing env property mongo.userName")); + + String databaseName = Optional.ofNullable(env.getProperty("mongo.databaseName")) + .orElseThrow(() -> new IllegalStateException("missing env property mongo.databaseName")); + + String password = Optional.ofNullable(env.getProperty("mongo.password")) + .orElseThrow(() -> new IllegalStateException("missing env property mongo.password")); + + return MongoCredential.createCredential(userName, databaseName, + password.toCharArray()); + } + + private ServerAddress getServerAddress() { + return Optional.ofNullable(env.getProperty("mongo.server")) + .map(server -> server.replace("mongodb://", "").split(":")) + .map(serverParts -> new ServerAddress(serverParts[0], + 1 < serverParts.length ? Integer.valueOf(serverParts[1]) : ServerAddress.defaultPort())) + .orElseThrow(() -> new IllegalStateException("missing env property mongo.server")); + } + + @Override + public MongoDbFactory mongoDbFactory() { + return Optional.ofNullable(env.getProperty("mongo.databaseName")) + .map(databaseName -> new SimpleMongoDbFactory(mongoClient(), databaseName)) + .orElseThrow(() -> new IllegalStateException("missing env property mongo.databaseName")); + + } +} + + diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/config/ProxyStreamHandlerFactory.java b/multibanking-server/src/main/java/de/adorsys/multibanking/config/ProxyStreamHandlerFactory.java new file mode 100644 index 000000000..6af5efc8f --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/config/ProxyStreamHandlerFactory.java @@ -0,0 +1,58 @@ +package de.adorsys.multibanking.config; + +import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory; +import org.springframework.context.annotation.Configuration; + +import java.io.IOException; +import java.net.*; +import java.net.Proxy.Type; +import java.util.HashMap; +import java.util.Map; + +/** + * ProxyStreamHandlerFactory + * Erzeugt eine URL-Connection mit Proxy + * + * Format: proxy://proxy_host:proxy_port/http:... + */ +@Configuration +public class ProxyStreamHandlerFactory implements URLStreamHandlerFactory { + + private Map proxyCache; + + public ProxyStreamHandlerFactory() { + proxyCache = new HashMap<>(); + + TomcatURLStreamHandlerFactory.getInstance().addUserFactory(this); + } + + private Proxy createProxy(String host, int port) { + return proxyCache.computeIfAbsent(String.format("%s:%d", host, port), + proxy -> new Proxy(Type.HTTP, new InetSocketAddress(host, port))); + } + + @Override + public URLStreamHandler createURLStreamHandler(String protocol) { + if (!"proxy".equals(protocol)) { + return null; + } + return new URLStreamHandler() { + @Override + protected URLConnection openConnection(URL url) throws IOException { + String proxyHost = url.getHost(); + int proxyPort = url.getPort(); + String destination = clean(url.getFile()); + return new URL(destination).openConnection(createProxy(proxyHost, proxyPort)); + + } + }; + } + + private String clean(String path) { + if (path != null && path.startsWith("/")) { + return clean(path.substring(1)); + } + return path; + } + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/config/STSConfiguration.java b/multibanking-server/src/main/java/de/adorsys/multibanking/config/STSConfiguration.java new file mode 100644 index 000000000..d3aba92fc --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/config/STSConfiguration.java @@ -0,0 +1,19 @@ +package de.adorsys.multibanking.config; + +import de.adorsys.sts.decryption.EnableDecryption; +import de.adorsys.sts.keyrotation.EnableKeyRotation; +import de.adorsys.sts.persistence.mongo.config.EnableMongoPersistence; +import de.adorsys.sts.pop.EnablePOP; +import de.adorsys.sts.token.authentication.EnableTokenAuthentication; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +@Configuration +@EnableTokenAuthentication +@EnablePOP +@EnableDecryption +@EnableKeyRotation +@EnableMongoPersistence +@Profile({"sts-enable"}) +public class STSConfiguration { +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/config/SmartanalyticsEmbeddedConfig.java b/multibanking-server/src/main/java/de/adorsys/multibanking/config/SmartanalyticsEmbeddedConfig.java new file mode 100644 index 000000000..912b69274 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/config/SmartanalyticsEmbeddedConfig.java @@ -0,0 +1,13 @@ +package de.adorsys.multibanking.config; + +import de.adorsys.smartanalytics.config.EnableSmartanalytics; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +@Slf4j +@EnableSmartanalytics +@Configuration +@Profile("smartanalytics-embedded") +public class SmartanalyticsEmbeddedConfig { +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/config/SmartanalyticsRemoteConfig.java b/multibanking-server/src/main/java/de/adorsys/multibanking/config/SmartanalyticsRemoteConfig.java new file mode 100644 index 000000000..49e7f5cb6 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/config/SmartanalyticsRemoteConfig.java @@ -0,0 +1,133 @@ +package de.adorsys.multibanking.config; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.google.common.io.ByteStreams; +import de.adorsys.multibanking.exception.SmartanalyticsException; +import de.adorsys.multibanking.exception.domain.Message; +import de.adorsys.multibanking.exception.domain.Messages; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.hateoas.hal.Jackson2HalModule; +import org.springframework.http.HttpRequest; +import org.springframework.http.MediaType; +import org.springframework.http.client.BufferingClientHttpRequestFactory; +import org.springframework.http.client.ClientHttpRequestExecution; +import org.springframework.http.client.ClientHttpRequestInterceptor; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.lang.NonNull; +import org.springframework.web.client.DefaultResponseErrorHandler; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.DefaultUriBuilderFactory; + +import java.io.IOException; +import java.net.URI; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Collections; + +@Slf4j +@Configuration +@Profile("smartanalytics-remote") +public class SmartanalyticsRemoteConfig { + + @Value("${SMARTANALYTICS_URL:http://localhost:8082}") + private String smartanalyticsUrl; + + @Bean + @Qualifier("smartanalytics") + public RestTemplate restTemplate(AuthorizationClientRequestFactory authorizationClientRequestFactory) { + ObjectMapper mapper = new ObjectMapper(); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + mapper.registerModule(new Jackson2HalModule()); + mapper.registerModule(new JavaTimeModule()); + + MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); + converter.setSupportedMediaTypes(MediaType.parseMediaTypes("application/hal+json, application/json")); + converter.setObjectMapper(mapper); + + final RestTemplate restTemplate = + new RestTemplate(new BufferingClientHttpRequestFactory(authorizationClientRequestFactory)); + restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory(smartanalyticsUrl)); + restTemplate.setErrorHandler(new ErrorHandler()); + restTemplate.setMessageConverters(Collections.singletonList(converter)); + restTemplate.getInterceptors().add(new LoggingInterceptor("SMARTANALYTICS")); + return restTemplate; + } + + private class ErrorHandler extends DefaultResponseErrorHandler { + + @Override + public void handleError(ClientHttpResponse response) throws IOException { + String responseBody = new String(getResponseBody(response), getCharsetOrDefault(response)); + Message errorMessage = getErrorMessages(responseBody); + if (errorMessage != null) { + throw new SmartanalyticsException(response.getStatusCode(), errorMessage); + } + + super.handleError(response); + } + + private Charset getCharsetOrDefault(ClientHttpResponse response) { + Charset charset = getCharset(response); + return charset != null ? charset : StandardCharsets.UTF_8; + } + + private Message getErrorMessages(String responseBody) { + try { + Collection messages = new ObjectMapper().readValue(responseBody, Messages.class).getMessages(); + if (!messages.isEmpty()) { + return messages.iterator().next(); + } + } catch (IOException e) { + //ignore + } + return null; + } + } + + private class LoggingInterceptor implements ClientHttpRequestInterceptor { + + private String backend; + + LoggingInterceptor(String backend) { + this.backend = backend; + } + + @Override + public @NonNull + ClientHttpResponse intercept(HttpRequest request, @NonNull byte[] body, + @NonNull ClientHttpRequestExecution execution) throws IOException { + + URI uri = request.getURI(); + + String query = ""; + + if (uri.getQuery() != null) { + query = "?" + uri.getQuery() + " "; + } + + Charset charset = Logging.determineCharset(request.getHeaders().getContentType()); + String requestString = Logging.cleanAndReduce(body, charset); + + log.trace("{} > {} {}{} {}", backend, request.getMethod(), request.getURI().getPath(), query, + requestString); + + ClientHttpResponse response = execution.execute(request, body); + + String responseString = Logging.cleanAndReduce(ByteStreams.toByteArray(response.getBody()), charset); + + log.trace("{} < {} {}", backend, response.getStatusCode(), responseString); + + return response; + } + + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/config/SwaggerConfig.java b/multibanking-server/src/main/java/de/adorsys/multibanking/config/SwaggerConfig.java new file mode 100644 index 000000000..b2cd9d19d --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/config/SwaggerConfig.java @@ -0,0 +1,111 @@ +package de.adorsys.multibanking.config; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import de.adorsys.multibanking.web.UserResource; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.core.env.Environment; +import org.springframework.core.env.Profiles; +import org.springframework.http.ResponseEntity; +import springfox.documentation.RequestHandler; +import springfox.documentation.builders.*; +import springfox.documentation.service.*; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spi.service.contexts.SecurityContext; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger.web.SecurityConfiguration; +import springfox.documentation.swagger.web.SecurityConfigurationBuilder; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +import java.util.Arrays; +import java.util.Collections; + +@RequiredArgsConstructor +@Configuration +@EnableSwagger2 +@Profile("swagger") +public class SwaggerConfig { + + private final Environment environment; + + @Value("${idp.baseUrl}") + private String loginUrl; + @Value("${info.project.version}") + private String version; + @Value("${swagger.client.id:multibanking-client}") + private String swaggerClientId; + @Value("${idp.realm:multibanking}") + private String realm; + + @Bean + public Docket api() { + return new Docket(DocumentationType.SWAGGER_2) + .apiInfo(apiInfo()) + .select() + .apis(apis()) + .paths(PathSelectors.any()) + .build() + .useDefaultResponseMessages(false) + .directModelSubstitute(ResponseEntity.class, java.lang.Void.class) + .securitySchemes(Collections.singletonList(securityScheme())) + .securityContexts(Collections.singletonList(securityContext())); + } + + private ApiInfo apiInfo() { + return new ApiInfoBuilder() + .title("Multibanking REST Api") + .description("Use a bank code (blz) ending with X00 000 00 like 300 000 00 to" + + " run aggainst the mock backend. Find the mock backend at ${hostname}:10010") + .contact(new Contact("Alexander Geist adorsys GmbH & Co. KG", null, "age@adorsys.de")) + .version(version) + .build(); + } + + private Predicate apis() { + Predicate mbResourses = RequestHandlerSelectors.withClassAnnotation(UserResource.class); + + if (environment.acceptsProfiles(Profiles.of("smartanalytics-embedded"))) { + return Predicates.or(mbResourses, RequestHandlerSelectors.basePackage("de.adorsys.smartanalytics.web")); + } else { + return mbResourses; + } + } + + private SecurityContext securityContext() { + return SecurityContext.builder() + .securityReferences(Collections.singletonList(new SecurityReference("multibanking_auth", scopes()))) + .forPaths(PathSelectors.any()) + .build(); + } + + @Bean + public SecurityConfiguration security() { + return SecurityConfigurationBuilder.builder() + .clientId(swaggerClientId) + .build(); + } + + private SecurityScheme securityScheme() { + String tokenEndpoint = String.format("%s/auth/realms/%s/protocol/openid-connect/token", loginUrl, realm); + String tokenRequestEndpoint = String.format("%s/auth/realms/%s/protocol/openid-connect/auth", loginUrl, realm); + + GrantType grantType = new AuthorizationCodeGrantBuilder() + .tokenEndpoint(new TokenEndpoint(tokenEndpoint, "token")) + .tokenRequestEndpoint(new TokenRequestEndpoint(tokenRequestEndpoint, swaggerClientId, null)) + .build(); + + return new OAuthBuilder() + .name("multibanking_auth") + .grantTypes(Collections.singletonList(grantType)) + .scopes(Arrays.asList(scopes())) + .build(); + } + + private AuthorizationScope[] scopes() { + return new AuthorizationScope[]{new AuthorizationScope("openid", "openid connect")}; + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/config/SwaggerOkStatusCodeFilteringPlugin.java b/multibanking-server/src/main/java/de/adorsys/multibanking/config/SwaggerOkStatusCodeFilteringPlugin.java new file mode 100644 index 000000000..740a72589 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/config/SwaggerOkStatusCodeFilteringPlugin.java @@ -0,0 +1,28 @@ +package de.adorsys.multibanking.config; + +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spi.service.OperationBuilderPlugin; +import springfox.documentation.spi.service.contexts.OperationContext; + +@Component +public class SwaggerOkStatusCodeFilteringPlugin implements OperationBuilderPlugin { + + @Override + public void apply(OperationContext operationContext) { + if (!operationContext.httpMethod().equals(HttpMethod.GET) && !operationContext.httpMethod().equals(HttpMethod.PUT)) { + operationContext + .operationBuilder() + .build() + .getResponseMessages() + .removeIf(responseMessage -> responseMessage.getCode() == HttpStatus.OK.value()); + } + } + + @Override + public boolean supports(DocumentationType documentationType) { + return true; + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/config/WebMvcConfig.java b/multibanking-server/src/main/java/de/adorsys/multibanking/config/WebMvcConfig.java new file mode 100644 index 000000000..4c22a2dd1 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/config/WebMvcConfig.java @@ -0,0 +1,15 @@ +package de.adorsys.multibanking.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebMvcConfig implements WebMvcConfigurer { + + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry.addRedirectViewController("", "/swagger-ui.html"); + } + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/config/WebSecurityConfig.java b/multibanking-server/src/main/java/de/adorsys/multibanking/config/WebSecurityConfig.java new file mode 100644 index 000000000..fa95b029c --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/config/WebSecurityConfig.java @@ -0,0 +1,124 @@ +package de.adorsys.multibanking.config; + +import de.adorsys.multibanking.domain.UserSecret; +import de.adorsys.multibanking.service.SecretClaimDecryptionService; +import de.adorsys.sts.filter.JWTAuthenticationFilter; +import de.adorsys.sts.token.authentication.TokenAuthenticationService; +import de.adorsys.sts.tokenauth.BearerToken; +import de.adorsys.sts.tokenauth.BearerTokenValidator; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.*; +import org.springframework.core.env.Environment; +import org.springframework.core.env.Profiles; +import org.springframework.http.HttpMethod; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import javax.servlet.http.HttpServletRequest; +import java.security.Principal; +import java.util.Arrays; +import java.util.Collections; + +@RequiredArgsConstructor +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + + private static final String RULES_ADMIN_ROLE = "rules_admin"; + + private final Environment environment; + private final SecretClaimDecryptionService secretClaimDecryptionService; + + @Autowired(required = false) + private TokenAuthenticationService tokenAuthenticationService; + + @Autowired(required = false) + private BearerTokenValidator bearerTokenValidator; + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .csrf().disable() + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .and() + .authorizeRequests() + .antMatchers("/management/health").permitAll() + .antMatchers("/management/info").permitAll() + .antMatchers("/management/**").hasAuthority("admin") + .antMatchers("/").permitAll() + .antMatchers("/pop").permitAll() + .antMatchers("/swagger-ui.html").permitAll() + .antMatchers("/webjars/**").permitAll() + .antMatchers("/swagger-resources/**").permitAll() + .antMatchers("/v2/api-docs/**").permitAll() + .antMatchers("/api/v1/direct/**").permitAll() + .antMatchers("/status").permitAll() + .antMatchers(HttpMethod.GET, "/api/v1/bank/**").permitAll() + .antMatchers(HttpMethod.GET, "/api/v1/banks/**").permitAll() + .antMatchers(HttpMethod.POST, "/api/v1/bank/**").hasAuthority(RULES_ADMIN_ROLE) + .antMatchers(HttpMethod.POST, "/api/v1/banks/**").hasAuthority(RULES_ADMIN_ROLE) + .antMatchers(HttpMethod.GET, "/api/v1/images/**").permitAll() + .antMatchers(HttpMethod.POST, "/api/v1/images/**").hasAuthority(RULES_ADMIN_ROLE) + .antMatchers(HttpMethod.GET, "/api/v1/config/booking-categories").authenticated() + .antMatchers("/api/v1/config/**").hasAuthority(RULES_ADMIN_ROLE) + .antMatchers("/api/v1/**").authenticated() + .anyRequest().denyAll() + .and().cors(); + + if (environment.acceptsProfiles(Profiles.of("sts-enable"))) { + http.addFilterBefore(new JWTAuthenticationFilter(tokenAuthenticationService), + BasicAuthenticationFilter.class); + } + } + + @Bean + @Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS) + public BearerToken getBearerToken(HttpServletRequest request, + @Autowired(required = false) BearerTokenValidator bearerTokenValidator) { + String token = request.getHeader(BearerTokenValidator.HEADER_KEY); + return bearerTokenValidator.extract(token); + } + + @Bean + @Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS) + public Principal getPrincipal() { + return SecurityContextHolder.getContext().getAuthentication(); + } + + @Bean + @Primary + @Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS) + public UserSecret getRequestScopeUserSecret() { + return new UserSecret(secretClaimDecryptionService.decryptSecretClaim()); + } + + @Bean + public CorsConfigurationSource corsConfigurationSource() { + final CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowedOrigins(Collections.singletonList("*")); + configuration.setAllowedMethods(Arrays.asList("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH")); + // setAllowCredentials(true) is important, otherwise: + // The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when + // the request's credentials mode is 'include'. + configuration.setAllowCredentials(true); + // setAllowedHeaders is important! Without it, OPTIONS preflight request + // will fail with 403 Invalid CORS request + configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type")); + configuration.setExposedHeaders(Collections.singletonList("Location")); + final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + return source; + } + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/dbmigrations/InitialSetup.java b/multibanking-server/src/main/java/de/adorsys/multibanking/dbmigrations/InitialSetup.java new file mode 100644 index 000000000..fcc0f78ba --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/dbmigrations/InitialSetup.java @@ -0,0 +1,23 @@ +package de.adorsys.multibanking.dbmigrations; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.github.mongobee.changeset.ChangeLog; +import com.github.mongobee.changeset.ChangeSet; +import de.adorsys.multibanking.domain.BankEntity; +import de.adorsys.multibanking.domain.RuleEntity; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Query; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +/** + * Creates the initial database setup + */ +@ChangeLog(order = "001") +public class InitialSetup { + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/exception/BankAccessAlreadyExistException.java b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/BankAccessAlreadyExistException.java new file mode 100644 index 000000000..3df1ffe30 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/BankAccessAlreadyExistException.java @@ -0,0 +1,17 @@ +package de.adorsys.multibanking.exception; + + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus( + value = HttpStatus.BAD_REQUEST, + reason = "BANK_ACCESS_ALREADY_EXIST" +) +public class BankAccessAlreadyExistException extends ParametrizedMessageException { + + public BankAccessAlreadyExistException() { + super("Bank access already exist"); + } + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/exception/ExceptionHandlerAdvice.java b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/ExceptionHandlerAdvice.java new file mode 100755 index 000000000..6cb6eb57c --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/ExceptionHandlerAdvice.java @@ -0,0 +1,289 @@ +package de.adorsys.multibanking.exception; + +import de.adorsys.multibanking.domain.exception.MultibankingError; +import de.adorsys.multibanking.domain.exception.MultibankingException; +import de.adorsys.multibanking.exception.domain.Message; +import de.adorsys.multibanking.exception.domain.Messages; +import lombok.extern.slf4j.Slf4j; +import org.kapott.hbci.exceptions.HBCI_Exception; +import org.springframework.core.NestedExceptionUtils; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.validation.BindException; +import org.springframework.validation.ObjectError; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.client.HttpStatusCodeException; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; + +import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServletRequest; +import javax.validation.ConstraintViolationException; +import java.util.*; +import java.util.concurrent.CompletionException; +import java.util.stream.Collectors; + +import static de.adorsys.multibanking.exception.domain.Message.Severity.ERROR; +import static java.util.stream.Collectors.toList; +import static org.springframework.util.StringUtils.hasText; + +@Slf4j +@ControllerAdvice +public class ExceptionHandlerAdvice { + + private static final String VALIDATION_ERROR = "VALIDATION_ERROR"; + private static final String INVALD_FORMAT = "INVALID_FORMAT"; + + @ExceptionHandler + @ResponseBody + public ResponseEntity handleException(Exception e) { + return handleInternal(e); + } + + @ExceptionHandler + @ResponseBody + public ResponseEntity handleException(HBCI_Exception e) { + Throwable e2 = e; + List messages = new ArrayList<>(); + + while (e2 != null) { + if (e2.getMessage() != null) { + messages.add(Message.builder() + .key("HBCI_ERROR") + .severity(ERROR) + .renderedMessage(e2.getMessage()) + .build()); + } + e2 = e2.getCause(); + } + + return handleInternal(e, Messages.builder().messages(messages).build(), HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler + @ResponseBody + public ResponseEntity handleException(CompletionException e) { + Throwable cause = e.getCause(); + + if (HttpStatusCodeException.class.isAssignableFrom(cause.getClass())) { + return handleHttpStatusCodeException((HttpStatusCodeException) cause); + } else if (ParametrizedMessageException.class.isAssignableFrom(cause.getClass())) { + return handleException((ParametrizedMessageException) cause); + } + + return handleInternal(e); + } + + @ExceptionHandler + @ResponseBody + public ResponseEntity handleException(MultibankingException e) { + HttpStatus httpStatus = Optional.ofNullable(e.getMultibankingError()) + .map(this::toHttpStatus) + .orElse(HttpStatus.BAD_REQUEST); + + Messages messages = Messages.builder() + .message(Message.builder() + .key(e.getMultibankingError().toString()) + .renderedMessage(e.getMessage()) + .build()) + .build(); + + return handleInternal(e, messages, httpStatus); + } + + private HttpStatus toHttpStatus(MultibankingError multibankingError) { + switch (multibankingError) { + case HBCI_ERROR: + case INVALID_PAYMENT: + case INVALID_SCA_METHOD: + case INVALID_CONSENT: + case INVALID_PIN: + case INVALID_TAN: + default: + return HttpStatus.BAD_REQUEST; + } + } + + @ExceptionHandler + @ResponseBody + public ResponseEntity handleAccessDeniedException(ServletRequest request, AccessDeniedException e) { + try { + HttpServletRequest httpServletRequest = (HttpServletRequest) request; + log.info("User [{}] access denied to [{}] [{}]", httpServletRequest.getRemoteUser(), + httpServletRequest.getMethod(), httpServletRequest.getRequestURI()); + } catch (Exception ex) { + log.info("Can't LOG: {}", ex.getMessage()); + } + + return new ResponseEntity<>(HttpStatus.FORBIDDEN); + } + + @ExceptionHandler + @ResponseBody + public ResponseEntity handleException(ParametrizedMessageException e) { + + ResponseStatus responseStatus = AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class); + if (responseStatus != null) { + Messages messages = Messages.createError(responseStatus.reason(), e.getLocalizedMessage(), + e.getParamsMap()); + return handleInternal(e, messages, responseStatus.code()); + } else { + return handleException(e); + } + } + + @ExceptionHandler + @ResponseBody + public ResponseEntity handleHttpStatusCodeException(HttpStatusCodeException e) { + return handleInternal(e, null, e.getStatusCode()); + } + + @ExceptionHandler + @ResponseBody + public ResponseEntity handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) { + Message message = Message.builder() + .key(INVALD_FORMAT) + .severity(Message.Severity.ERROR) + .field(e.getName()) + .renderedMessage(e.getMessage()).build(); + + return handleInternal(e, Messages.builder().messages(Collections.singletonList(message)).build(), + HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler + @ResponseBody + public ResponseEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) { + Collection messages = ex.getBindingResult().getFieldErrors().stream() + .map(fieldError -> Message.builder() + .key(VALIDATION_ERROR) + .severity(Message.Severity.ERROR) + .field(fieldError.getField()) + .renderedMessage(fieldError.getDefaultMessage()) + .build()).collect(Collectors.toList()); + + if (ex.getBindingResult().hasGlobalErrors()) { + ObjectError objectError = ex.getBindingResult() + .getGlobalErrors().iterator().next(); + + Message message = Message.builder() + .key(VALIDATION_ERROR) + .severity(Message.Severity.ERROR) + .renderedMessage(objectError.getDefaultMessage()) + .build(); + + messages.add(message); + } + + return handleInternal( + ex, + Messages.builder().messages(messages).build(), + HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler + @ResponseBody + public ResponseEntity handleMethodBindException(BindException ex) { + Collection messages = ex.getBindingResult().getFieldErrors().stream() + .map(fieldError -> Message.builder() + .key(VALIDATION_ERROR) + .severity(Message.Severity.ERROR) + .field(fieldError.getField()) + .renderedMessage(fieldError.getDefaultMessage()) + .build()) + .collect(Collectors.toList()); + + if (ex.getBindingResult().hasGlobalErrors()) { + ObjectError objectError = ex.getBindingResult() + .getGlobalErrors().iterator().next(); + + messages.add(Message.builder() + .key(VALIDATION_ERROR) + .severity(Message.Severity.ERROR) + .renderedMessage(objectError.getDefaultMessage()) + .build() + ); + } + + return handleInternal( + ex, + Messages.builder().messages(messages).build(), + HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler + @ResponseBody + public ResponseEntity handleConstraintViolationException(ConstraintViolationException e) { + Collection messages = e.getConstraintViolations().stream() + .map(cv -> Message.builder() + .key(VALIDATION_ERROR) + .severity(Message.Severity.ERROR) + .field(cv.getPropertyPath().toString()) + .renderedMessage(cv.getMessage()) + .build()) + .collect(toList()); + + return handleInternal(e, Messages.builder().messages(messages).build(), HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler + @ResponseBody + public ResponseEntity handleUnsatisfiedServletRequestParameterException(ServletRequestBindingException ex) { + return handleInternal(ex, null, HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler + @ResponseBody + public ResponseEntity handleHttpMessageNotReadableException(HttpMessageNotReadableException ex) { + return handleInternal(ex, null, HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler + @ResponseBody + public ResponseEntity handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException ex) { + return handleInternal(ex, null, HttpStatus.METHOD_NOT_ALLOWED); + } + + private ResponseEntity handleInternal(Throwable throwable) { + ResponseStatus responseStatus = AnnotationUtils.findAnnotation(throwable.getClass(), ResponseStatus.class); + + Messages messages; + if (responseStatus != null && hasText(responseStatus.reason())) { + messages = Messages.createError(responseStatus.reason(), throwable.getMessage()); + } else { + messages = Messages.createError(throwable.getClass().toString(), throwable.getMessage()); + } + + HttpStatus statusCode = Optional.ofNullable(responseStatus) + .map(ResponseStatus::code) + .orElse(HttpStatus.INTERNAL_SERVER_ERROR); + + return handleInternal(throwable, messages, statusCode); + } + + private ResponseEntity handleInternal(Throwable throwable, Messages messages, HttpStatus httpStatus) { + if (httpStatus == HttpStatus.NOT_FOUND) { + log.info("Exception {} from Controller: {}", throwable.getClass(), throwable.getMessage()); + } else if (httpStatus.is4xxClientError()) { + log.warn("Exception {} from Controller: {}", throwable.getClass(), + NestedExceptionUtils.buildMessage(throwable.getMessage(), throwable.getCause())); + } else { + log.error("Exception {} from Controller: {}", throwable); + } + + if (messages == null) { + return new ResponseEntity<>(httpStatus); + } else { + return new ResponseEntity<>(messages, httpStatus); + } + } + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/exception/ExternalAuthorisationRequiredException.java b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/ExternalAuthorisationRequiredException.java new file mode 100644 index 000000000..36a4dcdaf --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/ExternalAuthorisationRequiredException.java @@ -0,0 +1,26 @@ +package de.adorsys.multibanking.exception; + +import de.adorsys.multibanking.domain.Consent; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus( + value = HttpStatus.BAD_REQUEST, + code = HttpStatus.BAD_REQUEST, + reason = "AUTHORISE_CONSENT" +) +@Data +@EqualsAndHashCode(callSuper = false) +public class ExternalAuthorisationRequiredException extends ParametrizedMessageException { + + private final Consent consent; + + public ExternalAuthorisationRequiredException(Consent consent) { + super("Consent authorisation required"); + this.addParam("authUrl", consent.getAuthUrl()); + this.consent = consent; + } + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/exception/InvalidBankAccessException.java b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/InvalidBankAccessException.java new file mode 100644 index 000000000..b5f2dc33a --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/InvalidBankAccessException.java @@ -0,0 +1,21 @@ +package de.adorsys.multibanking.exception; + + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +import java.text.MessageFormat; + +@ResponseStatus( + value = HttpStatus.BAD_REQUEST, + code = HttpStatus.BAD_REQUEST, + reason = "INVALID_BANK_ACCESS" +) +public class InvalidBankAccessException extends ParametrizedMessageException { + + public InvalidBankAccessException(String bankCode) { + super(MessageFormat.format("Unsupported bank access code [{0}]", new Object[]{bankCode})); + this.addParam("bankCode", bankCode); + } + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/exception/InvalidPinException.java b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/InvalidPinException.java new file mode 100644 index 000000000..6852cc120 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/InvalidPinException.java @@ -0,0 +1,20 @@ +package de.adorsys.multibanking.exception; + + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +import java.text.MessageFormat; + +@ResponseStatus( + value = HttpStatus.BAD_REQUEST, + reason = "INVALID_PIN" +) +public class InvalidPinException extends ParametrizedMessageException { + + public InvalidPinException(String accessId) { + super(MessageFormat.format("invalid pin for bank access [{0}]", new Object[]{accessId})); + this.addParam("account", accessId); + } + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/exception/InvalidRulesException.java b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/InvalidRulesException.java new file mode 100644 index 000000000..a967b007c --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/InvalidRulesException.java @@ -0,0 +1,18 @@ +package de.adorsys.multibanking.exception; + + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus( + value = HttpStatus.BAD_REQUEST, + reason = "INVALID_RULES" +) +public class InvalidRulesException extends ParametrizedMessageException { + + public InvalidRulesException(String message) { + super("unable import rule(s)"); + this.addParam("message", message); + } + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/exception/ParametrizedMessageException.java b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/ParametrizedMessageException.java new file mode 100644 index 000000000..67ef37b87 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/ParametrizedMessageException.java @@ -0,0 +1,25 @@ +package de.adorsys.multibanking.exception; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.HashMap; +import java.util.Map; + +@Data +@EqualsAndHashCode(callSuper = false) +public class ParametrizedMessageException extends RuntimeException { + private final Map paramsMap = new HashMap<>(); + + public ParametrizedMessageException() { + } + + public ParametrizedMessageException(String message) { + super(message); + } + + protected void addParam(String key, String value) { + this.paramsMap.put(key, value); + } + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/exception/PaymentException.java b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/PaymentException.java new file mode 100644 index 000000000..920c73d15 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/PaymentException.java @@ -0,0 +1,18 @@ +package de.adorsys.multibanking.exception; + + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus( + value = HttpStatus.BAD_REQUEST, + reason = "ERROR_PAYMENT" +) +public class PaymentException extends ParametrizedMessageException { + + public PaymentException(String msg) { + super(); + this.addParam("msg", msg); + } + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/exception/ResourceNotFoundException.java b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/ResourceNotFoundException.java new file mode 100644 index 000000000..867d2287c --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/ResourceNotFoundException.java @@ -0,0 +1,19 @@ +package de.adorsys.multibanking.exception; + + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +import java.text.MessageFormat; + +@ResponseStatus( + value = HttpStatus.NOT_FOUND, + reason = "RESOURCE_NOT_FOUND" +) +public class ResourceNotFoundException extends ParametrizedMessageException { + public ResourceNotFoundException(Class resourceClazz, String businessKey) { + super(MessageFormat.format("Resource [{0}] mit Key [{1}] nicht gefunden.", new Object[]{resourceClazz.getSimpleName(), businessKey})); + this.addParam("resource", resourceClazz.getSimpleName()); + this.addParam("businessKey", businessKey); + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/exception/SmartanalyticsException.java b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/SmartanalyticsException.java new file mode 100644 index 000000000..d905d4586 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/SmartanalyticsException.java @@ -0,0 +1,20 @@ +package de.adorsys.multibanking.exception; + +import de.adorsys.multibanking.exception.domain.Message; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.springframework.http.HttpStatus; + +@Data +@EqualsAndHashCode(callSuper = false) +public class SmartanalyticsException extends RuntimeException { + + private HttpStatus status; + private Message errorMessage; + + public SmartanalyticsException(HttpStatus status, Message errorMessage) { + this.status = status; + this.errorMessage = errorMessage; + } + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/exception/SyncInProgressException.java b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/SyncInProgressException.java new file mode 100644 index 000000000..1a1868386 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/SyncInProgressException.java @@ -0,0 +1,21 @@ +package de.adorsys.multibanking.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +import java.text.MessageFormat; + +/** + * Created by alexg on 19.05.17. + */ +@ResponseStatus( + value = HttpStatus.BAD_REQUEST, + reason = "SYNC_IN_PROGRESS" +) +public class SyncInProgressException extends ParametrizedMessageException { + + public SyncInProgressException(String account) { + super(MessageFormat.format("Account [{0}] sync in progress.", new Object[]{account})); + this.addParam("account", account); + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/exception/domain/Message.java b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/domain/Message.java new file mode 100755 index 000000000..01f5d6e88 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/domain/Message.java @@ -0,0 +1,34 @@ +package de.adorsys.multibanking.exception.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.Map; + +@NoArgsConstructor +@Data +@AllArgsConstructor +@Builder +public class Message implements Serializable { + + private static final long serialVersionUID = -1L; + + private String key; + + private Severity severity; + + private String field; + + private String renderedMessage; + + private Map paramsMap; + + public enum Severity { + ERROR, + WARNING, + INFO + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/exception/domain/Messages.java b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/domain/Messages.java new file mode 100755 index 000000000..49ebc6676 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/domain/Messages.java @@ -0,0 +1,53 @@ +package de.adorsys.multibanking.exception.domain; + +import lombok.*; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Map; + +import static de.adorsys.multibanking.exception.domain.Message.Severity.ERROR; + +@NoArgsConstructor +@Data +@AllArgsConstructor +@Builder +public class Messages implements Serializable { + + private static final long serialVersionUID = -1L; + + private String uuid; + + @Singular + private Collection messages; + + public static Messages createError(String key) { + return builder() + .message(Message.builder() + .key(key) + .severity(ERROR) + .build()) + .build(); + } + + public static Messages createError(String key, String renderedMessage) { + return builder() + .message(Message.builder() + .key(key) + .severity(ERROR) + .renderedMessage(renderedMessage) + .build()) + .build(); + } + + public static Messages createError(String key, String renderedMessage, Map params) { + return builder() + .message(Message.builder() + .key(key) + .severity(ERROR) + .renderedMessage(renderedMessage) + .paramsMap(params) + .build()) + .build(); + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/exception/domain/MissingPinException.java b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/domain/MissingPinException.java new file mode 100644 index 000000000..bd4b02df6 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/exception/domain/MissingPinException.java @@ -0,0 +1,19 @@ +package de.adorsys.multibanking.exception.domain; + + +import de.adorsys.multibanking.exception.ParametrizedMessageException; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus( + value = HttpStatus.BAD_REQUEST, + code = HttpStatus.BAD_REQUEST, + reason = "MISSING_PIN" +) +public class MissingPinException extends ParametrizedMessageException { + + public MissingPinException() { + super("Missing PIN for payment"); + } + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/service/AnonymizationService.java b/multibanking-server/src/main/java/de/adorsys/multibanking/service/AnonymizationService.java new file mode 100644 index 000000000..8568ccbf0 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/service/AnonymizationService.java @@ -0,0 +1,50 @@ +package de.adorsys.multibanking.service; + +import de.adorsys.multibanking.domain.AnonymizedBookingEntity; +import de.adorsys.multibanking.domain.BookingEntity; +import de.adorsys.multibanking.pers.spi.repository.AnonymizedBookingRepositoryIf; +import lombok.AllArgsConstructor; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.List; +import java.util.stream.Collectors; + +@AllArgsConstructor +@Service +public class AnonymizationService { + + private final AnonymizedBookingRepositoryIf anonymizedBookingRepository; + + @Async + void anonymizeAndStoreBookingsAsync(List bookingEntities) { + List uncategorizedBookings = bookingEntities.stream() + .filter(bookingEntity -> bookingEntity.getBookingCategory() == null && bookingEntity.getCreditorId() != null) + .collect(Collectors.toList()); + + List anonymizedBookings = uncategorizedBookings.stream() + .map(this::anonymizeBooking) + .collect(Collectors.toList()); + + try { + anonymizedBookingRepository.save(anonymizedBookings); + } catch (DuplicateKeyException e) { + //ignore it + } + } + + private AnonymizedBookingEntity anonymizeBooking(BookingEntity bookingEntity) { + AnonymizedBookingEntity anonymizedBookingEntity = new AnonymizedBookingEntity(); + if (bookingEntity.getAmount().compareTo(BigDecimal.ZERO) > 0) { + anonymizedBookingEntity.setAmount(new BigDecimal(1)); + } else { + anonymizedBookingEntity.setAmount(new BigDecimal(-1)); + } + anonymizedBookingEntity.setCreditorId(bookingEntity.getCreditorId()); + anonymizedBookingEntity.setPurpose(bookingEntity.getUsage()); + + return anonymizedBookingEntity; + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/service/BankAccessService.java b/multibanking-server/src/main/java/de/adorsys/multibanking/service/BankAccessService.java new file mode 100644 index 000000000..4bbc569fc --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/service/BankAccessService.java @@ -0,0 +1,138 @@ +package de.adorsys.multibanking.service; + +import de.adorsys.multibanking.domain.BankAccessEntity; +import de.adorsys.multibanking.domain.BankAccountEntity; +import de.adorsys.multibanking.domain.BankApiUser; +import de.adorsys.multibanking.domain.UserEntity; +import de.adorsys.multibanking.domain.spi.OnlineBankingService; +import de.adorsys.multibanking.exception.ExternalAuthorisationRequiredException; +import de.adorsys.multibanking.exception.InvalidBankAccessException; +import de.adorsys.multibanking.exception.ResourceNotFoundException; +import de.adorsys.multibanking.pers.spi.repository.*; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.iban4j.Iban; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.util.List; + +@Slf4j +@Service +@AllArgsConstructor +public class BankAccessService { + + private final AnalyticsRepositoryIf analyticsRepository; + private final ContractRepositoryIf contractRepository; + private final StandingOrderRepositoryIf standingOrderRepository; + private final UserRepositoryIf userRepository; + private final UserService userService; + private final BankAccountRepositoryIf bankAccountRepository; + private final BankAccessRepositoryIf bankAccessRepository; + private final BookingRepositoryIf bookingRepository; + private final BankService bankService; + private final BankAccountService bankAccountService; + private final OnlineBankingServiceProducer bankingServiceProducer; + + public BankAccessEntity createBankAccess(String userId, BankAccessEntity bankAccess) { + userService.checkUserExists(userId); + + bankAccess.setUserId(userId); + if (StringUtils.isNoneBlank(bankAccess.getIban())) { + bankAccess.setBankCode(Iban.valueOf(bankAccess.getIban()).getBankCode()); + } + + List bankAccounts; + try { + bankAccounts = bankAccountService.loadBankAccountsOnline(bankAccess, null); + } catch (ExternalAuthorisationRequiredException e) { + bankAccess.setAllAcountsConsent(e.getConsent()); + saveBankAccess(bankAccess); + return bankAccess; + } + + if (bankAccounts.isEmpty()) { + throw new InvalidBankAccessException(bankAccess.getBankCode()); + } + + saveBankAccess(bankAccess); + + bankAccounts.forEach(account -> account.setBankAccessId(bankAccess.getId())); + bankAccountRepository.save(bankAccounts); + + log.info("[{}] accounts for connection [{}] created.", bankAccounts.size(), bankAccess.getId()); + return bankAccess; + } + + private void saveBankAccess(BankAccessEntity bankAccess) { + if (!bankAccess.isStorePin()) { + bankAccess.setPin(null); + } + bankAccessRepository.save(bankAccess); + + log.info("Bank connection [{}] created.", bankAccess.getId()); + } + + public void updateBankAccess(String accessId, BankAccessEntity bankAccessEntity) { + BankAccessEntity bankAccessEntityDb = bankAccessRepository.findByUserIdAndId(bankAccessEntity.getUserId(), + accessId).orElseThrow(() -> new ResourceNotFoundException(BankAccessEntity.class, accessId)); + + bankAccessEntityDb.setStorePin(bankAccessEntity.isStorePin()); + bankAccessEntityDb.setStoreBookings(bankAccessEntity.isStoreBookings()); + bankAccessEntityDb.setCategorizeBookings(bankAccessEntity.isCategorizeBookings()); + bankAccessEntityDb.setStoreAnalytics(bankAccessEntity.isStoreAnalytics()); + bankAccessEntityDb.setStoreAnonymizedBookings(bankAccessEntity.isStoreAnonymizedBookings()); + if (!bankAccessEntityDb.isStorePin()) { + bankAccessEntityDb.setPin(null); + } else { + if (bankAccessEntity.getPin() == null) { + bankAccessEntityDb.setStorePin(false); + } else { + bankAccessEntityDb.setPin(bankAccessEntity.getPin()); + } + } + if (!bankAccessEntityDb.isStoreBookings() || !bankAccessEntityDb.isStoreAnalytics()) { + bankAccountRepository.findByUserIdAndBankAccessId(bankAccessEntityDb.getUserId(), + bankAccessEntityDb.getId()).forEach(bankAccountEntity -> { + if (!bankAccessEntityDb.isStoreBookings()) { + bookingRepository.deleteByAccountId(bankAccountEntity.getId()); + } + if (!bankAccessEntityDb.isStoreAnalytics()) { + analyticsRepository.deleteByAccountId(bankAccountEntity.getId()); + } + }); + } + bankAccessRepository.save(bankAccessEntityDb); + } + + @Transactional + public boolean deleteBankAccess(String userId, String accessId) { + UserEntity userEntity = + userRepository.findById(userId).orElseThrow(() -> new ResourceNotFoundException(UserEntity.class, userId)); + + return bankAccessRepository.findByUserIdAndId(userId, accessId).map(bankAccessEntity -> { + bankAccessRepository.deleteByUserIdAndBankAccessId(userId, accessId); + + List bankAccounts = bankAccountRepository.deleteByBankAccess(accessId); + bankAccounts.forEach(bankAccountEntity -> { + bookingRepository.deleteByAccountId(bankAccountEntity.getId()); + analyticsRepository.deleteByAccountId(bankAccountEntity.getId()); + contractRepository.deleteByAccountId(bankAccountEntity.getId()); + standingOrderRepository.deleteByAccountId(bankAccountEntity.getId()); + bankAccountEntity.getExternalIdMap().keySet().forEach(bankApi -> { + OnlineBankingService bankingService = bankingServiceProducer.getBankingService(bankApi); + //remove remote bank api user + if (bankingService.userRegistrationRequired()) { + BankApiUser bankApiUser = + userEntity.getApiUser().stream().filter(apiUser -> apiUser.getBankApi() == bankApi).findFirst().orElseThrow(() -> new ResourceNotFoundException(BankApiUser.class, bankApi.toString())); + bankingService.removeBankAccount(bankService.findBankingUrl(bankAccessEntity.getBankCode()), + bankAccountEntity, bankApiUser); + } + }); + }); + return true; + }).orElse(false); + } + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/service/BankAccountService.java b/multibanking-server/src/main/java/de/adorsys/multibanking/service/BankAccountService.java new file mode 100644 index 000000000..0c5b43a8f --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/service/BankAccountService.java @@ -0,0 +1,242 @@ +package de.adorsys.multibanking.service; + +import de.adorsys.multibanking.domain.*; +import de.adorsys.multibanking.domain.exception.MultibankingException; +import de.adorsys.multibanking.domain.request.CreateConsentRequest; +import de.adorsys.multibanking.domain.request.LoadAccountInformationRequest; +import de.adorsys.multibanking.domain.response.CreateConsentResponse; +import de.adorsys.multibanking.domain.spi.OnlineBankingService; +import de.adorsys.multibanking.exception.*; +import de.adorsys.multibanking.pers.spi.repository.BankAccessRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.BankAccountRepositoryIf; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; + +import java.security.Principal; +import java.time.LocalDate; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static de.adorsys.multibanking.domain.exception.MultibankingError.INVALID_CONSENT; +import static de.adorsys.multibanking.domain.exception.MultibankingError.INVALID_PIN; + +@Slf4j +@Service +@AllArgsConstructor +public class BankAccountService { + + private final BankAccessRepositoryIf bankAccessRepository; + private final BankAccountRepositoryIf bankAccountRepository; + private final OnlineBankingServiceProducer bankingServiceProducer; + private final UserService userService; + private final BankService bankService; + private final Principal principal; + + public List getBankAccounts(String userId, String accessId) { + BankAccessEntity bankAccessEntity = bankAccessRepository.findByUserIdAndId(userId, accessId) + .orElseThrow(() -> new ResourceNotFoundException(BankAccessEntity.class, accessId)); + + List bankAccounts = bankAccountRepository.findByUserIdAndBankAccessId(userId, accessId); + + if (bankAccounts.isEmpty()) { + //check for finalised sca + ScaStatus scaStatusStarted = Optional.ofNullable(bankAccessEntity.getAllAcountsConsent()) + .map(Consent::getScaStatus) + .filter(status -> status == ScaStatus.STARTED) + .orElse(null); + + if (scaStatusStarted != null) { + try { + bankAccounts = loadBankAccountsOnline(bankAccessEntity, null); + bankAccounts.forEach(account -> account.setBankAccessId(bankAccessEntity.getId())); + + bankAccountRepository.save(bankAccounts); + log.info("[{}] accounts for connection [{}] created.", bankAccounts.size(), + bankAccessEntity.getId()); + + bankAccessEntity.getAllAcountsConsent().setScaStatus(ScaStatus.FINALISED); + bankAccessRepository.save(bankAccessEntity); + } catch (ExternalAuthorisationRequiredException e) { + bankAccessEntity.setAllAcountsConsent(e.getConsent()); + bankAccessRepository.save(bankAccessEntity); + throw e; + } + } + } + return bankAccounts; + } + + private void checkAvailableAccountsConsent(BankAccessEntity bankAccess, OnlineBankingService onlineBankingService + , BankApiUser bankApiUser, BankEntity bankEntity) { + if (onlineBankingService.accountInformationConsentRequired() && bankAccess.getAllAcountsConsent() == null) { + throw new ExternalAuthorisationRequiredException(createAvailableAccountsConsent(onlineBankingService, + bankApiUser, bankAccess, bankEntity)); + } + } + + public List loadBankAccountsOnline(BankAccessEntity bankAccess, BankApi bankApi) { + OnlineBankingService onlineBankingService = bankApi != null ? + bankingServiceProducer.getBankingService(bankApi) : + bankingServiceProducer.getBankingService(bankAccess.getBankCode()); + + checkBankSupported(bankAccess, onlineBankingService); + + BankApiUser bankApiUser = userService.checkApiRegistration(bankAccess, bankApi); + BankEntity bankEntity = bankService.findBank(bankAccess.getBankCode()); + + checkAvailableAccountsConsent(bankAccess, onlineBankingService, bankApiUser, bankEntity); + + List bankAccounts = loadBankAccountsOnline(bankAccess, onlineBankingService, bankApiUser, + bankEntity); + + if (onlineBankingService.bankApi() == BankApi.FIGO) { + filterAccounts(bankAccess, onlineBankingService, bankAccounts); + } + return Optional.ofNullable(bankAccounts) + .map(Collection::stream) + .orElseGet(Stream::empty) + .map(source -> { + BankAccountEntity target = new BankAccountEntity(); + BeanUtils.copyProperties(source, target); + target.setUserId(bankAccess.getUserId()); + return target; + }) + .collect(Collectors.toList()); + } + + private Consent createAvailableAccountsConsent(OnlineBankingService onlineBankingService, + BankApiUser bankApiUser, + BankAccessEntity bankAccessEntity, + BankEntity bankEntity) { + //we assume the user belongs to same identity provider + bankApiUser.setApiUserId(principal.getName()); + + CreateConsentRequest createConsentRequest = CreateConsentRequest.builder() + .bankAccess(bankAccessEntity) + .bankApiUser(bankApiUser) + .availableAccountsConsent(true) + .frequencyPerDay(1) + .validUntil(LocalDate.now().plusDays(1)) + .recurringIndicator(false) + .build(); + + CreateConsentResponse accountInformationConsent = + onlineBankingService.createAccountInformationConsent(bankEntity.getBankingUrl(), + createConsentRequest); + + return toConsent(accountInformationConsent); + } + + void checkDedicatedConsent(BankAccessEntity bankAccess, BankAccountEntity bankAccount, + BankApiUser bankApiUser, OnlineBankingService onlineBankingService, + BankEntity bankEntity) { + if (onlineBankingService.accountInformationConsentRequired() && bankAccount.getDedicatedConsent() == null) { + throw new ExternalAuthorisationRequiredException(createDedicatedConsent(onlineBankingService, + bankApiUser, bankAccess, bankAccount, bankEntity)); + } + } + + Consent createDedicatedConsent(OnlineBankingService onlineBankingService, + BankApiUser bankApiUser, + BankAccessEntity bankAccessEntity, + BankAccountEntity bankAccountEntity, + BankEntity bankEntity) { + //we assume the user belongs to same identity provider + bankApiUser.setApiUserId(principal.getName()); + + CreateConsentRequest createConsentRequest = CreateConsentRequest.builder() + .bankAccess(bankAccessEntity) + .accounts(Collections.singletonList(bankAccountEntity)) + .balances(Collections.singletonList(bankAccountEntity)) + .transactions(Collections.singletonList(bankAccountEntity)) + .bankApiUser(bankApiUser) + .frequencyPerDay(5) + .validUntil(LocalDate.now().plusYears(1)) + .recurringIndicator(true) + .build(); + + CreateConsentResponse accountInformationConsent = + onlineBankingService.createAccountInformationConsent(bankEntity.getBankingUrl(), + createConsentRequest); + + return toConsent(accountInformationConsent); + } + + private List loadBankAccountsOnline(BankAccessEntity bankAccess, + OnlineBankingService onlineBankingService, + BankApiUser bankApiUser, BankEntity bankEntity) { + try { + return onlineBankingService.loadBankAccounts(bankEntity.getBankingUrl(), + LoadAccountInformationRequest.builder() + .consentId(bankAccess.getAllAcountsConsent() != null ? + bankAccess.getAllAcountsConsent().getConsentId() : null) + .bankApiUser(bankApiUser) + .bankAccess(bankAccess) + .bankCode(bankEntity.getBlzHbci()) + .pin(bankAccess.getPin()) + .storePin(bankAccess.isStorePin()) + .updateTanTransportTypes(true) + .build()) + .getBankAccounts(); + } catch (MultibankingException e) { + return handleMultibankingException(bankAccess, bankApiUser, onlineBankingService, bankEntity, e); + } + } + + private List handleMultibankingException(BankAccessEntity bankAccess, BankApiUser bankApiUser, + OnlineBankingService onlineBankingService, + BankEntity bankEntity, MultibankingException e) { + if (e.getMultibankingError() == INVALID_PIN) { + bankAccess.setPin(null); + bankAccessRepository.save(bankAccess); + throw new InvalidPinException(bankAccess.getId()); + } else if (e.getMultibankingError() == INVALID_CONSENT) { + throw new ExternalAuthorisationRequiredException(createAvailableAccountsConsent(onlineBankingService, + bankApiUser, bankAccess, bankEntity)); + } + throw e; + } + + private void checkBankSupported(BankAccessEntity bankAccess, OnlineBankingService onlineBankingService) { + if (!onlineBankingService.bankSupported(bankAccess.getBankCode())) { + bankAccess.setStorePin(false); + bankAccess.setPin(null); + throw new InvalidBankAccessException(bankAccess.getBankCode()); + } + } + + private void filterAccounts(BankAccessEntity bankAccess, OnlineBankingService onlineBankingService, + List bankAccounts) { + List userBankAccounts = bankAccountRepository.findByUserId(bankAccess.getUserId()); + //filter out previous created accounts + Iterator accountIterator = bankAccounts.iterator(); + while (accountIterator.hasNext()) { + BankAccount newAccount = accountIterator.next(); + userBankAccounts.stream() + .filter(bankAccountEntity -> { + String newAccountExternalID = newAccount.getExternalIdMap().get(onlineBankingService.bankApi()); + String existingAccountExternalID = + bankAccountEntity.getExternalIdMap().get(onlineBankingService.bankApi()); + return newAccountExternalID.equals(existingAccountExternalID); + }) + .findFirst() + .ifPresent(bankAccountEntity -> accountIterator.remove()); + } + //all accounts created in the past + if (bankAccounts.isEmpty()) { + throw new BankAccessAlreadyExistException(); + } + bankAccess.setBankName(bankAccounts.get(0).getBankName()); + } + + private Consent toConsent(CreateConsentResponse createConsentResponse) { + Consent consent = new Consent(); + consent.setConsentId(createConsentResponse.getConsentId()); + consent.setAuthUrl(createConsentResponse.getAuthorisationUrl()); + consent.setScaStatus(ScaStatus.STARTED); + return consent; + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/service/BankService.java b/multibanking-server/src/main/java/de/adorsys/multibanking/service/BankService.java new file mode 100644 index 000000000..ba94caa83 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/service/BankService.java @@ -0,0 +1,73 @@ +package de.adorsys.multibanking.service; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import de.adorsys.multibanking.domain.BankEntity; +import de.adorsys.multibanking.exception.ResourceNotFoundException; +import de.adorsys.multibanking.pers.spi.repository.BankRepositoryIf; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +@AllArgsConstructor +@Slf4j +@Service +public class BankService { + + private final BankRepositoryIf bankRepository; + + public void importBanks(MultipartFile file) { + try { + importBanks(file.getInputStream()); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + public void importBanks(File file) { + try { + importBanks(new FileInputStream(file)); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + private void importBanks(InputStream inputStream) throws IOException { + log.info("start import banks file"); + + final YAMLFactory ymlFactory = new YAMLFactory(); + ObjectMapper objectMapper = new ObjectMapper(ymlFactory) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + List banks = objectMapper.readValue(inputStream, new TypeReference>() { + }); + + bankRepository.deleteAll(); + bankRepository.save(banks); + + log.info("successfully imported [{}]", banks.size()); + } + + public BankEntity findBank(String bankCode) { + return bankRepository.findByBankCode(bankCode) + .orElseThrow(() -> new ResourceNotFoundException(BankEntity.class, bankCode)); + } + + public List search(String terms) { + return bankRepository.search(terms); + } + + String findBankingUrl(String bankCode) { + return bankRepository.findBankingUrl(bankCode) + .orElse(null); + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/service/BookingService.java b/multibanking-server/src/main/java/de/adorsys/multibanking/service/BookingService.java new file mode 100644 index 000000000..ebd29224f --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/service/BookingService.java @@ -0,0 +1,405 @@ +package de.adorsys.multibanking.service; + +import de.adorsys.multibanking.domain.*; +import de.adorsys.multibanking.domain.exception.MultibankingException; +import de.adorsys.multibanking.domain.request.LoadAccountInformationRequest; +import de.adorsys.multibanking.domain.request.LoadBookingsRequest; +import de.adorsys.multibanking.domain.response.LoadBookingsResponse; +import de.adorsys.multibanking.domain.spi.OnlineBankingService; +import de.adorsys.multibanking.domain.utils.Utils; +import de.adorsys.multibanking.exception.ExternalAuthorisationRequiredException; +import de.adorsys.multibanking.exception.InvalidPinException; +import de.adorsys.multibanking.pers.spi.repository.*; +import de.adorsys.multibanking.service.analytics.AnalyticsService; +import de.adorsys.multibanking.service.analytics.SmartAnalyticsIf; +import de.adorsys.multibanking.service.analytics.SmartAnalyticsMapper; +import de.adorsys.smartanalytics.api.AnalyticsResult; +import de.adorsys.smartanalytics.api.config.ConfigStatus; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeanUtils; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static de.adorsys.multibanking.domain.exception.MultibankingError.INVALID_CONSENT; +import static de.adorsys.multibanking.domain.exception.MultibankingError.INVALID_PIN; + +@Slf4j +@AllArgsConstructor +@Service +public class BookingService { + + private final BankAccessRepositoryIf bankAccessRepository; + private final BankAccountRepositoryIf bankAccountRepository; + private final BookingRepositoryIf bookingRepository; + private final BookingsIndexRepositoryIf bookingsIndexRepository; + private final StandingOrderRepositoryIf standingOrderRepository; + private final AnalyticsRepositoryIf analyticsRepository; + private final SmartAnalyticsIf smartAnalyticsService; + private final AnalyticsService analyticsService; + private final AnonymizationService anonymizationService; + private final BankAccountService bankAccountService; + private final BankService bankService; + private final UserService userService; + private final OnlineBankingServiceProducer bankingServiceProducer; + private final SmartAnalyticsMapper smartAnalyticsMapper; + + public String getBookingsCsv(String userId, String accessId, String accountId) { + List bookings = getBookings(userId, accessId, accountId); + StringBuilder builder = new StringBuilder(); + + bookings.forEach(bookingEntity -> { + builder.append(bookingEntity.getBookingDate() != null ? bookingEntity.getBookingDate().toString() : ""); + builder.append(";"); + builder.append(bookingEntity.getOtherAccount() != null ? bookingEntity.getOtherAccount().getOwner() : ""); + builder.append(";"); + builder.append(bookingEntity.getBookingCategory() != null ? + bookingEntity.getBookingCategory().getMainCategory() : ""); + builder.append(";"); + builder.append(bookingEntity.getBookingCategory() != null ? + bookingEntity.getBookingCategory().getSubCategory() : ""); + builder.append(";"); + builder.append(bookingEntity.getBookingCategory() != null ? + bookingEntity.getBookingCategory().getSpecification() : ""); + builder.append(";"); + builder.append(bookingEntity.getAmount() != null ? bookingEntity.getAmount().toString() : ""); + builder.append(";"); + builder.append(bookingEntity.getCreditorId() != null ? bookingEntity.getCreditorId() : ""); + builder.append(";"); + builder.append(bookingEntity.getUsage() != null ? bookingEntity.getUsage() : ""); + builder.append("\n"); + }); + return builder.toString(); + } + + public Page getBookingsPageable(Pageable pageable, String userId, String accessId, String accountId, + BankApi bankApi) { + if (bankApi == null) { + String bankCode = bankAccessRepository.getBankCode(accessId); + bankApi = bankingServiceProducer.getBankingService(bankCode).bankApi(); + } + + return bookingRepository.findPageableByUserIdAndAccountIdAndBankApi(pageable, userId, accountId, bankApi); + } + + public Iterable getBookingsById(String name, List ids) { + return bookingRepository.findByUserIdAndIds(name, ids); + } + + public Optional getSearchIndex(String userId, String accountId) { + return bookingsIndexRepository.findByUserIdAndAccountId(userId, accountId); + } + + private List getBookings(String userId, String accessId, String accountId) { + String bankCode = bankAccessRepository.getBankCode(accessId); + BankApi bankApi = bankingServiceProducer.getBankingService(bankCode).bankApi(); + + return bookingRepository.findByUserIdAndAccountIdAndBankApi(userId, accountId, bankApi); + } + + @Transactional + public List syncBookings(BankAccessEntity bankAccess, + BankAccountEntity bankAccount, @Nullable BankApi bankApi, String pin) throws ExternalAuthorisationRequiredException { + bankAccountRepository.updateSyncStatus(bankAccount.getId(), BankAccount.SyncStatus.SYNC); + + OnlineBankingService onlineBankingService = bankApi != null ? + bankingServiceProducer.getBankingService(bankApi) : + bankingServiceProducer.getBankingService(bankAccess.getBankCode()); + + try { + LoadBookingsResponse response = loadBookingsOnline(onlineBankingService.bankApi(), bankAccess, + bankAccount, pin); + + if (!bankAccess.isTemporary()) { + //update bankaccess, passportstate changed + bankAccessRepository.save(bankAccess); + } + + List result = processBookings(onlineBankingService, bankAccess, bankAccount, response); + + Optional.ofNullable(response.getBankAccountBalance()) + .ifPresent(bankAccount::setBalances); + + bankAccount.setSyncStatus(BankAccount.SyncStatus.READY); + bankAccount.setLastSync(LocalDateTime.now()); + bankAccountRepository.save(bankAccount); + + return result; + } catch (ExternalAuthorisationRequiredException e) { + bankAccount.setDedicatedConsent(e.getConsent()); + bankAccountRepository.save(bankAccount); + throw e; + } catch (Exception e) { + LoggerFactory.getLogger(getClass()).error("sync bookings failed", e); + throw e; + } finally { + bankAccountRepository.updateSyncStatus(bankAccount.getId(), BankAccount.SyncStatus.PENDING); + } + } + + private List processBookings(OnlineBankingService onlineBankingService, BankAccessEntity bankAccess, + BankAccountEntity bankAccount, LoadBookingsResponse response) { + List newBookings = mapBookings(bankAccount, response.getBookings()); + mapStandingOrders(response, newBookings); + + List existingBookings = bookingRepository.findByUserIdAndAccountIdAndBankApi( + bankAccess.getUserId(), bankAccount.getId(), onlineBankingService.bankApi()); + + List mergedBookings = mergeBookings(existingBookings, newBookings); + + if (mergedBookings.size() == existingBookings.size() && !rulesVersionChanged(bankAccess.getUserId(), + bankAccount.getId())) { + log.info("no bookings or rules changes, skip analytics"); + return existingBookings; + } + + AnalyticsResult analyticsResult = null; + if (bankAccess.isCategorizeBookings() || bankAccess.isStoreAnalytics()) { + analyticsResult = analyticsService.analyzeBookings(bankAccess.getUserId(), mergedBookings); + if (!onlineBankingService.bookingsCategorized()) { + smartAnalyticsMapper.applyCategories(mergedBookings, analyticsResult); + } + } + + if (bankAccess.isStoreBookings()) { + bookingRepository.save(mergedBookings); + saveStandingOrders(bankAccount, response.getStandingOrders()); + updateBookingsIndex(bankAccount, mergedBookings); + } + + if (bankAccess.isStoreAnonymizedBookings()) { + anonymizationService.anonymizeAndStoreBookingsAsync(mergedBookings); + } + + saveAnalytics(analyticsResult, bankAccess, bankAccount, mergedBookings); + + mergedBookings.sort((o1, o2) -> o2.getBookingDate().compareTo(o1.getBookingDate())); + + return mergedBookings; + } + + private boolean rulesVersionChanged(String userId, String accountId) { + ConfigStatus analyticsConfigStatus = smartAnalyticsService.getAnalyticsConfigStatus(); + + return analyticsRepository.findLastAnalyticsDateByUserIdAndAccountId(userId, accountId) + .map(lastAnalyticsDate -> { + if (lastAnalyticsDate.isBefore(analyticsConfigStatus.getLastChangeDate())) { + return true; + } + return userService.getRulesLastChangeDate(userId) + .map(lastAnalyticsDate::isBefore) + .orElse(false); + }) + .orElse(true); + + } + + private void saveAnalytics(AnalyticsResult analyticsResult, BankAccessEntity bankAccess, + BankAccountEntity bankAccount, List bookingEntities) { + if (analyticsResult == null) { + return; + } + + //period included booking should be mapped with entity db id + analyticsResult.getBookingGroups() + .stream() + .filter(bookingGroup -> bookingGroup.getBookingPeriods() != null) + .forEach(bookingGroup -> + bookingGroup.getBookingPeriods().forEach(period -> + period.getBookings().forEach(executedBooking -> bookingEntities.stream() + .filter(bookingEntity -> bookingEntity.getExternalId().equals(executedBooking.getBookingId())) + .findFirst() + .ifPresent(bookingEntity -> executedBooking.setBookingId(bookingEntity.getId()))))); + + if (bankAccess.isStoreAnalytics()) { + analyticsService.saveAccountAnalytics(bankAccount, analyticsResult.getBookingGroups()); + analyticsService.identifyAndStoreContracts(bankAccount.getUserId(), bankAccount.getId(), + analyticsResult.getBookingGroups()); + } + } + + private void mapStandingOrders(LoadBookingsResponse response, List bookingEntities) { + if (response.getStandingOrders() == null) { + return; + } + + bookingEntities.forEach(booking -> + response.getStandingOrders() + .stream() + .filter(so -> so.getAmount().negate().compareTo(booking.getAmount()) == 0 && + Utils.inCycle(booking.getValutaDate(), so.getExecutionDay()) && + Utils.usageContains(booking.getUsage(), so.getUsage()) + ) + .findFirst() + .ifPresent(standingOrder -> { + booking.setOtherAccount(standingOrder.getOtherAccount()); + booking.setStandingOrder(true); + })); + } + + private void saveStandingOrders(BankAccountEntity bankAccount, List standingOrders) { + Optional.ofNullable(standingOrders) + .ifPresent(sto -> { + List standingOrderEntities = sto.stream() + .map(booking -> { + StandingOrderEntity target = new StandingOrderEntity(); + BeanUtils.copyProperties(booking, target); + target.setAccountId(bankAccount.getId()); + target.setUserId(bankAccount.getUserId()); + return target; + }) + .collect(Collectors.toList()); + standingOrderRepository.deleteByAccountId(bankAccount.getId()); + standingOrderRepository.save(standingOrderEntities); + }); + + } + + private void updateBookingsIndex(BankAccountEntity bankAccount, List bookings) { + BookingsIndexEntity bookingsIndexEntity = + bookingsIndexRepository.findByUserIdAndAccountId(bankAccount.getUserId(), bankAccount.getId()) + .orElseGet(() -> { + BookingsIndexEntity newIndexEntity = new BookingsIndexEntity(); + newIndexEntity.setAccountId(bankAccount.getId()); + newIndexEntity.setUserId(bankAccount.getUserId()); + return newIndexEntity; + }); + + bookingsIndexEntity.updateSearchIndex(bookings); + + bookingsIndexRepository.save(bookingsIndexEntity); + } + + private LoadBookingsResponse loadBookingsOnline(@Nullable BankApi bankApi, BankAccessEntity bankAccess, + BankAccountEntity bankAccount, String pin) throws ExternalAuthorisationRequiredException { + + BankApiUser bankApiUser = userService.checkApiRegistration(bankAccess, bankApi); + OnlineBankingService onlineBankingService = checkAndGetOnlineBankingService(bankAccess, bankAccount, pin, + bankApi, bankApiUser); + BankEntity bankEntity = bankService.findBank(bankAccess.getBankCode()); + + bankAccountService.checkDedicatedConsent(bankAccess, bankAccount, bankApiUser, onlineBankingService, + bankEntity); + + try { + LoadBookingsRequest loadBookingsRequest = LoadBookingsRequest.builder() + .consentId(bankAccount.getDedicatedConsent() != null ? + bankAccount.getDedicatedConsent().getConsentId() : null) + .bankApiUser(bankApiUser) + .bankAccess(bankAccess) + .bankCode(bankEntity.getBlzHbci()) + .bankAccount(bankAccount) + .pin(pin) + .dateFrom(bankAccount.getLastSync() != null ? bankAccount.getLastSync().toLocalDate() : null) + .withTanTransportTypes(true) + .withBalance(true) + .withStandingOrders(true) + .build(); + + return onlineBankingService.loadBookings(bankEntity.getBankingUrl(), loadBookingsRequest); + } catch (MultibankingException e) { + return handleMultibankingException(bankAccess, bankAccount, bankApiUser, onlineBankingService, bankEntity + , e); + } + } + + private LoadBookingsResponse handleMultibankingException(BankAccessEntity bankAccess, + BankAccountEntity bankAccount, BankApiUser bankApiUser, + OnlineBankingService onlineBankingService, + BankEntity bankEntity, MultibankingException e) throws ExternalAuthorisationRequiredException { + if (e.getMultibankingError() == INVALID_PIN) { + bankAccess.setPin(null); + bankAccessRepository.save(bankAccess); + throw new InvalidPinException(bankAccess.getId()); + } else if (e.getMultibankingError() == INVALID_CONSENT) { + throw new ExternalAuthorisationRequiredException(bankAccountService.createDedicatedConsent(onlineBankingService, bankApiUser, bankAccess, bankAccount, + bankEntity)); + } + throw e; + } + + private List mapBookings(BankAccountEntity bankAccount, List bookings) { + return bookings.stream() + .map(booking -> { + BookingEntity target = new BookingEntity(); + BeanUtils.copyProperties(booking, target); + target.setAccountId(bankAccount.getId()); + target.setUserId(bankAccount.getUserId()); + return target; + }) + .collect(Collectors.toList()); + } + + private List mergeBookings(List dbBookings, List newBookings) { + return Stream.of(dbBookings, newBookings) + .flatMap(Collection::stream) + .collect(Collectors.collectingAndThen(Collectors.toCollection(() -> + new TreeSet<>(Comparator.comparing(Booking::getExternalId))), ArrayList::new)); + } + + private OnlineBankingService checkAndGetOnlineBankingService(BankAccessEntity bankAccess, + BankAccountEntity bankAccount, String pin, + BankApi bankApi, + BankApiUser bankApiUser) { + OnlineBankingService onlineBankingService = bankApi != null ? + bankingServiceProducer.getBankingService(bankApi) : + bankingServiceProducer.getBankingService(bankAccess.getBankCode()); + + //external (figo, finapi) account must exist, otherwise loading bookings will not work + if (onlineBankingService.externalBankAccountRequired()) { + checkExternalBankAccountExists(bankAccess, bankAccount, pin, bankApiUser, + onlineBankingService); + } + + return onlineBankingService; + } + + //only for figo + private void checkExternalBankAccountExists(BankAccessEntity bankAccess, + BankAccountEntity bankAccount, String pin, BankApiUser bankApiUser, + OnlineBankingService onlineBankingService) { + String externalAccountId = bankAccount.getExternalIdMap().get(onlineBankingService.bankApi()); + //account not created by given bank-api, account must be created, otherwise loading bookings will not work + if (externalAccountId == null) { + BankEntity bankEntity = bankService.findBank(bankAccess.getBankCode()); + + List apiBankAccounts = onlineBankingService + .loadBankAccounts( + bankEntity.getBankingUrl(), + LoadAccountInformationRequest.builder() + .bankApiUser(bankApiUser) + .bankAccess(bankAccess) + .bankCode(bankEntity.getBlzHbci()) + .updateTanTransportTypes(true) + .pin(pin) + .storePin(bankAccess.isStorePin()) + .build()) + .getBankAccounts(); + + List dbBankAccounts = bankAccountRepository + .findByUserIdAndBankAccessId(bankAccess.getUserId(), bankAccess.getId()); + + apiBankAccounts.forEach(apiBankAccount -> dbBankAccounts.forEach(dbBankAccount -> { + if (apiBankAccount.getAccountNumber().equals(dbBankAccount.getAccountNumber())) { + dbBankAccount.externalId(onlineBankingService.bankApi(), + apiBankAccount.getExternalIdMap().get(onlineBankingService.bankApi())); + bankAccountRepository.save(dbBankAccount); + if (bankAccess.getId().equals(dbBankAccount.getId())) { + bankAccess.externalId(onlineBankingService.bankApi(), + apiBankAccount.getExternalIdMap().get(onlineBankingService.bankApi())); + } + } + })); + } + } + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/service/DeleteExpiredUsersScheduled.java b/multibanking-server/src/main/java/de/adorsys/multibanking/service/DeleteExpiredUsersScheduled.java new file mode 100644 index 000000000..72542a5a7 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/service/DeleteExpiredUsersScheduled.java @@ -0,0 +1,46 @@ +package de.adorsys.multibanking.service; + +import de.adorsys.multibanking.pers.spi.repository.BankAccessRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.UserRepositoryIf; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; + +import java.util.concurrent.atomic.AtomicInteger; + +@AllArgsConstructor +@Slf4j +@Configuration +@EnableScheduling +public class DeleteExpiredUsersScheduled { + + private final UserRepositoryIf userRepository; + private final BankAccessRepositoryIf bankAccessRepository; + private final BankAccessService bankAccessService; + + @Scheduled(fixedDelay = 2 * 60 * 1000) + void deleteJob() { + AtomicInteger count = new AtomicInteger(0); + + userRepository.findExpiredUser().forEach(userId -> { + bankAccessRepository.findByUserId(userId).forEach(bankAccessEntity -> bankAccessService.deleteBankAccess(userId, bankAccessEntity.getId())); + userRepository.delete(userId); + count.incrementAndGet(); + }); + + log.info("delete job done, [{}] users deleted", count); + } + + @Bean + public TaskScheduler poolScheduler() { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.setThreadNamePrefix("poolScheduler"); + return scheduler; + } + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/service/OnlineBankingServiceProducer.java b/multibanking-server/src/main/java/de/adorsys/multibanking/service/OnlineBankingServiceProducer.java new file mode 100755 index 000000000..39f2e7f81 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/service/OnlineBankingServiceProducer.java @@ -0,0 +1,67 @@ +package de.adorsys.multibanking.service; + +import de.adorsys.multibanking.bg.BankingGatewayAdapter; +import de.adorsys.multibanking.domain.BankApi; +import de.adorsys.multibanking.domain.BankEntity; +import de.adorsys.multibanking.domain.spi.OnlineBankingService; +import de.adorsys.multibanking.figo.FigoBanking; +import de.adorsys.multibanking.finapi.FinapiBanking; +import de.adorsys.multibanking.hbci.Hbci4JavaBanking; +import de.adorsys.multibanking.mock.MockBanking; +import de.adorsys.multibanking.pers.spi.repository.BankRepositoryIf; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@RequiredArgsConstructor +@Service +public class OnlineBankingServiceProducer { + + private final BankRepositoryIf bankRepository; + + @Value("${defaultBankApi:HBCI}") + private String defaultBankApi; + + private Hbci4JavaBanking hbci4JavaBanking = new Hbci4JavaBanking(true); + private FigoBanking figoBanking = new FigoBanking(BankApi.FIGO); + private FigoBanking figoBankingAlternative = new FigoBanking(BankApi.FIGO_ALTERNATIVE); + private FinapiBanking finapiBanking = new FinapiBanking(); + private MockBanking mockBanking = new MockBanking(); + private BankingGatewayAdapter xs2ABanking = new BankingGatewayAdapter(); + + private BankApi getBankApiForBlz(String blz) { + BankEntity bankInfoEntity = bankRepository.findByBankCode(blz).orElse(null); + + if (bankInfoEntity != null && bankInfoEntity.getBankApi() != null) { + return bankInfoEntity.getBankApi(); + } + return BankApi.valueOf(defaultBankApi); + } + + public OnlineBankingService getBankingService(String bankCode) { + BankApi bankApi = getBankApiForBlz(bankCode); + + return getBankingService(bankApi); + } + + public OnlineBankingService getBankingService(BankApi bankApi) { + switch (bankApi) { + case HBCI: + return hbci4JavaBanking; + case FIGO: + return figoBanking; + case FIGO_ALTERNATIVE: + return figoBankingAlternative; + case FINAPI: + return finapiBanking; + case XS2A: + case BANKING_GATEWAY: + return xs2ABanking; + case MOCK: + return mockBanking; + case SCREEN_SCRAPPING: + break; + } + throw new IllegalStateException("unsupported bank api"); + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/service/PaymentService.java b/multibanking-server/src/main/java/de/adorsys/multibanking/service/PaymentService.java new file mode 100644 index 000000000..214ea6c2b --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/service/PaymentService.java @@ -0,0 +1,210 @@ +package de.adorsys.multibanking.service; + +import de.adorsys.multibanking.domain.*; +import de.adorsys.multibanking.domain.exception.MultibankingException; +import de.adorsys.multibanking.domain.request.SubmitAuthorizationCodeRequest; +import de.adorsys.multibanking.domain.request.TransactionRequest; +import de.adorsys.multibanking.domain.spi.OnlineBankingService; +import de.adorsys.multibanking.exception.domain.MissingPinException; +import de.adorsys.multibanking.pers.spi.repository.BulkPaymentRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.RawSepaTransactionRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.SinglePaymentRepositoryIf; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; + +import java.util.Date; + +@RequiredArgsConstructor +@Service +public class PaymentService { + + private final OnlineBankingServiceProducer bankingServiceProducer; + private final UserService userService; + private final BankService bankService; + private final RawSepaTransactionRepositoryIf rawSepaTransactionRepository; + private final SinglePaymentRepositoryIf singlePaymentRepository; + private final BulkPaymentRepositoryIf bulkPaymentRepository; + + RawSepaTransactionEntity createSepaRawPayment(BankAccessEntity bankAccess, + TanTransportType tanTransportType, + String pin, RawSepaPayment payment) { + OnlineBankingService bankingService = bankingServiceProducer.getBankingService(bankAccess.getBankCode()); + + BankApiUser bankApiUser = userService.checkApiRegistration(bankAccess, bankingService.bankApi()); + + pin = pin == null ? bankAccess.getPin() : pin; + if (pin == null) { + throw new MissingPinException(); + } + + BankEntity bankEntity = bankService.findBank(bankAccess.getBankCode()); + + try { + Object tanSubmit = bankingService.requestAuthorizationCode(null, + TransactionRequest.builder() + .bankApiUser(bankApiUser) + .tanTransportType(tanTransportType) + .transaction(payment) + .bankAccess(bankAccess) + .pin(pin) + .bankCode(bankEntity.getBlzHbci()) + .build()); + + RawSepaTransactionEntity target = new RawSepaTransactionEntity(); + BeanUtils.copyProperties(payment, target); + target.setUserId(bankAccess.getUserId()); + target.setCreatedDateTime(new Date()); + target.setTanSubmitExternal(tanSubmit); + + rawSepaTransactionRepository.save(target); + return target; + } catch (MultibankingException e) { + throw new de.adorsys.multibanking.exception.PaymentException(e.getMessage()); + } + } + + public SinglePaymentEntity createSinglePayment(BankAccessEntity bankAccess, TanTransportType tanTransportType, + String pin, SinglePayment payment) { + OnlineBankingService bankingService = bankingServiceProducer.getBankingService(bankAccess.getBankCode()); + + BankApiUser bankApiUser = userService.checkApiRegistration(bankAccess, bankingService.bankApi()); + + pin = pin == null ? bankAccess.getPin() : pin; + if (pin == null) { + throw new MissingPinException(); + } + + BankEntity bankEntity = bankService.findBank(bankAccess.getBankCode()); + + try { + Object tanSubmit = + bankingService.requestAuthorizationCode(null, + TransactionRequest.builder() + .bankApiUser(bankApiUser) + .tanTransportType(tanTransportType) + .transaction(payment) + .bankAccess(bankAccess) + .pin(pin) + .bankCode(bankEntity.getBlzHbci()) + .build()); + + SinglePaymentEntity target = new SinglePaymentEntity(); + BeanUtils.copyProperties(payment, target); + target.setUserId(bankAccess.getUserId()); + target.setCreatedDateTime(new Date()); + target.setTanSubmitExternal(tanSubmit); + + singlePaymentRepository.save(target); + return target; + } catch (MultibankingException e) { + throw new de.adorsys.multibanking.exception.PaymentException(e.getMessage()); + } + } + + BulkPaymentEntity createBulkPayment(BankAccessEntity bankAccess, TanTransportType tanTransportType, + String pin, BulkPayment payment) { + OnlineBankingService bankingService = bankingServiceProducer.getBankingService(bankAccess.getBankCode()); + + BankApiUser bankApiUser = userService.checkApiRegistration(bankAccess, bankingService.bankApi()); + + pin = pin == null ? bankAccess.getPin() : pin; + if (pin == null) { + throw new MissingPinException(); + } + + BankEntity bankEntity = bankService.findBank(bankAccess.getBankCode()); + + try { + Object tanSubmit = + bankingService.requestAuthorizationCode(null, + TransactionRequest.builder() + .bankApiUser(bankApiUser) + .transaction(payment) + .tanTransportType(tanTransportType) + .bankAccess(bankAccess) + .pin(pin) + .bankCode(bankEntity.getBlzHbci()) + .build()); + + BulkPaymentEntity target = new BulkPaymentEntity(); + BeanUtils.copyProperties(payment, target); + target.setUserId(bankAccess.getUserId()); + target.setCreatedDateTime(new Date()); + target.setTanSubmitExternal(tanSubmit); + + bulkPaymentRepository.save(target); + return target; + } catch (MultibankingException e) { + throw new de.adorsys.multibanking.exception.PaymentException(e.getMessage()); + } + } + + void submitRawSepaTransaction(RawSepaTransactionEntity transactionEntity, BankAccessEntity bankAccess, + String pin, String tan) { + OnlineBankingService bankingService = bankingServiceProducer.getBankingService(bankAccess.getBankCode()); + + pin = pin == null ? bankAccess.getPin() : pin; + if (pin == null) { + throw new MissingPinException(); + } + + try { + bankingService.submitAuthorizationCode(SubmitAuthorizationCodeRequest.builder() + .sepaTransaction(transactionEntity) + .tanSubmit(transactionEntity.getTanSubmitExternal()) + .pin(pin) + .tan(tan) + .build()); + } catch (MultibankingException e) { + throw new de.adorsys.multibanking.exception.PaymentException(e.getMessage()); + } + + rawSepaTransactionRepository.delete(transactionEntity.getId()); + } + + public void submitSinglePayment(SinglePaymentEntity paymentEntity, BankAccessEntity bankAccess, String pin, String tan) { + OnlineBankingService bankingService = bankingServiceProducer.getBankingService(bankAccess.getBankCode()); + + pin = pin == null ? bankAccess.getPin() : pin; + if (pin == null) { + throw new MissingPinException(); + } + + try { + bankingService.submitAuthorizationCode(SubmitAuthorizationCodeRequest.builder() + .sepaTransaction(paymentEntity) + .tanSubmit(paymentEntity.getTanSubmitExternal()) + .pin(pin) + .tan(tan) + .build()); + } catch (MultibankingException e) { + throw new de.adorsys.multibanking.exception.PaymentException(e.getMessage()); + } + + singlePaymentRepository.delete(paymentEntity.getId()); + } + + void submitBulkPayment(BulkPaymentEntity paymentEntity, BankAccessEntity bankAccess, String pin, + String tan) { + OnlineBankingService bankingService = bankingServiceProducer.getBankingService(bankAccess.getBankCode()); + + pin = pin == null ? bankAccess.getPin() : pin; + if (pin == null) { + throw new MissingPinException(); + } + + try { + bankingService.submitAuthorizationCode(SubmitAuthorizationCodeRequest.builder() + .sepaTransaction(paymentEntity) + .tanSubmit(paymentEntity.getTanSubmitExternal()) + .pin(pin) + .tan(tan) + .build()); + } catch (MultibankingException e) { + throw new de.adorsys.multibanking.exception.PaymentException(e.getMessage()); + } + + bulkPaymentRepository.delete(paymentEntity.getId()); + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/service/SecretClaimDecryptionService.java b/multibanking-server/src/main/java/de/adorsys/multibanking/service/SecretClaimDecryptionService.java new file mode 100644 index 000000000..b6b12ba6c --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/service/SecretClaimDecryptionService.java @@ -0,0 +1,46 @@ +package de.adorsys.multibanking.service; + +import com.nimbusds.jwt.JWTClaimsSet; +import de.adorsys.sts.keymanagement.service.DecryptionService; +import lombok.extern.slf4j.Slf4j; +import net.minidev.json.JSONObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +public class SecretClaimDecryptionService { + + @Value("${sts.audience_name:}") + private String audience; + @Value("${sts.secret_claim_property_key:}") + private String secretClaimPropertyKey; + @Autowired(required = false) + private DecryptionService decryptionService; + + public String decryptSecretClaim() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null) { + log.warn("not authenticated"); + return null; + } + + if (authentication.getCredentials() instanceof JWTClaimsSet) { + JWTClaimsSet credentials = (JWTClaimsSet) authentication.getCredentials(); + JSONObject encryptedSecretClaims = (JSONObject) credentials.getClaim(secretClaimPropertyKey); + String encryptedSecretClaim = encryptedSecretClaims.getAsString(audience); + + if (encryptedSecretClaim == null) { + log.warn("missing secret claim"); + return null; + } + + return decryptionService.decrypt(encryptedSecretClaim); + } + + return null; + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/service/StandingOrderService.java b/multibanking-server/src/main/java/de/adorsys/multibanking/service/StandingOrderService.java new file mode 100644 index 000000000..24fefba76 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/service/StandingOrderService.java @@ -0,0 +1,82 @@ +package de.adorsys.multibanking.service; + +import de.adorsys.multibanking.domain.*; +import de.adorsys.multibanking.domain.exception.MultibankingException; +import de.adorsys.multibanking.domain.request.SubmitAuthorizationCodeRequest; +import de.adorsys.multibanking.domain.request.TransactionRequest; +import de.adorsys.multibanking.domain.spi.OnlineBankingService; +import de.adorsys.multibanking.exception.domain.MissingPinException; +import de.adorsys.multibanking.pers.spi.repository.StandingOrderRepositoryIf; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; + +import java.util.Date; + +@RequiredArgsConstructor +@Slf4j +@Service +public class StandingOrderService { + + private final BankService bankService; + private final UserService userService; + private final StandingOrderRepositoryIf standingOrderRepository; + private final OnlineBankingServiceProducer bankingServiceProducer; + + Object createStandingOrder(BankAccessEntity bankAccess, String pin, StandingOrder standingOrder) { + OnlineBankingService bankingService = bankingServiceProducer.getBankingService(bankAccess.getBankCode()); + + BankApiUser bankApiUser = userService.checkApiRegistration(bankAccess, bankingService.bankApi()); + + pin = pin == null ? bankAccess.getPin() : pin; + if (pin == null) { + throw new MissingPinException(); + } + + BankEntity bankEntity = bankService.findBank(bankAccess.getBankCode()); + + try { + Object tanSubmit = bankingService.requestAuthorizationCode(bankEntity.getBankingUrl(), + TransactionRequest.builder() + .bankApiUser(bankApiUser) + .transaction(standingOrder) + .bankAccess(bankAccess) + .pin(pin) + .bankCode(bankEntity.getBlzHbci()) + .build()); + + StandingOrderEntity target = new StandingOrderEntity(); + BeanUtils.copyProperties(standingOrder, target); + target.setCreatedDateTime(new Date()); + target.setUserId(bankAccess.getUserId()); + target.setTanSubmitExternal(tanSubmit); + + standingOrderRepository.save(target); + return tanSubmit; + } catch (MultibankingException e) { + throw new de.adorsys.multibanking.exception.PaymentException(e.getMessage()); + } + } + + void submitStandingOrder(StandingOrder standingOrder, Object tanSubmit, BankAccessEntity bankAccess, + String pin, String tan) { + OnlineBankingService bankingService = bankingServiceProducer.getBankingService(bankAccess.getBankCode()); + + pin = pin == null ? bankAccess.getPin() : pin; + if (pin == null) { + throw new MissingPinException(); + } + + try { + bankingService.submitAuthorizationCode(SubmitAuthorizationCodeRequest.builder() + .sepaTransaction(standingOrder) + .tanSubmit(tanSubmit) + .pin(pin) + .tan(tan) + .build()); + } catch (MultibankingException e) { + throw new de.adorsys.multibanking.exception.PaymentException(e.getMessage()); + } + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/service/UserService.java b/multibanking-server/src/main/java/de/adorsys/multibanking/service/UserService.java new file mode 100644 index 000000000..ddead619f --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/service/UserService.java @@ -0,0 +1,84 @@ +package de.adorsys.multibanking.service; + +import de.adorsys.multibanking.domain.BankAccessEntity; +import de.adorsys.multibanking.domain.BankApi; +import de.adorsys.multibanking.domain.BankApiUser; +import de.adorsys.multibanking.domain.UserEntity; +import de.adorsys.multibanking.domain.spi.OnlineBankingService; +import de.adorsys.multibanking.exception.ResourceNotFoundException; +import de.adorsys.multibanking.pers.spi.repository.UserRepositoryIf; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * Created by alexg on 05.09.17. + */ +@Service +@AllArgsConstructor +public class UserService { + + private final UserRepositoryIf userRepository; + private final BankService bankService; + private final OnlineBankingServiceProducer bankingServiceProducer; + + BankApiUser checkApiRegistration(BankAccessEntity bankAccess, BankApi bankApi) { + OnlineBankingService onlineBankingService = bankApi != null + ? bankingServiceProducer.getBankingService(bankApi) + : bankingServiceProducer.getBankingService(bankAccess.getBankCode()); + + if (onlineBankingService.userRegistrationRequired()) { + UserEntity userEntity = userRepository.findById(bankAccess.getUserId()) + .orElseThrow(() -> new ResourceNotFoundException(UserEntity.class, bankAccess.getUserId())); + + return userEntity.getApiUser() + .stream() + .filter(bankApiUser -> bankApiUser.getBankApi() == onlineBankingService.bankApi()) + .findFirst() + .orElseGet(() -> registerUser(bankAccess, onlineBankingService, userEntity)); + } else { + BankApiUser bankApiUser = new BankApiUser(); + bankApiUser.setBankApi(onlineBankingService.bankApi()); + return bankApiUser; + } + } + + private BankApiUser registerUser(BankAccessEntity bankAccess, OnlineBankingService onlineBankingService, UserEntity userEntity) { + BankApiUser bankApiUser = onlineBankingService.registerUser( + bankService.findBankingUrl(bankAccess.getBankCode()), bankAccess, bankAccess.getPin()); + userEntity.getApiUser().add(bankApiUser); + userRepository.save(userEntity); + return bankApiUser; + } + + void checkUserExists(String userId) { + if (!userRepository.exists(userId)) { + UserEntity userEntity = new UserEntity(); + userEntity.setApiUser(new ArrayList<>()); + userEntity.setId(userId); + userRepository.save(userEntity); + } + } + + void updataeBankApiUser(String userId, BankApiUser bankApiUser) { + UserEntity userEntity = userRepository.findById(userId) + .orElseThrow(() -> new ResourceNotFoundException(UserEntity.class, userId)); + + userEntity.setApiUser( + userEntity.getApiUser().stream() + .filter(bau -> bau.getBankApi() != bankApiUser.getBankApi()) + .collect(Collectors.toList()) + ); + + userEntity.getApiUser().add(bankApiUser); + userRepository.save(userEntity); + } + + Optional getRulesLastChangeDate(String userId) { + return userRepository.getRulesLastChangeDate(userId); + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/service/analytics/AnalyticsService.java b/multibanking-server/src/main/java/de/adorsys/multibanking/service/analytics/AnalyticsService.java new file mode 100644 index 000000000..50dd578c8 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/service/analytics/AnalyticsService.java @@ -0,0 +1,120 @@ +package de.adorsys.multibanking.service.analytics; + +import de.adorsys.multibanking.domain.*; +import de.adorsys.multibanking.pers.spi.repository.AnalyticsRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.BookingRuleRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.ContractRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.UserRepositoryIf; +import de.adorsys.smartanalytics.api.AnalyticsResult; +import de.adorsys.smartanalytics.api.BookingGroup; +import de.adorsys.smartanalytics.api.Rule; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.security.Principal; +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +import static de.adorsys.multibanking.domain.Rule.SIMILARITY_MATCH_TYPE.PURPOSE; +import static de.adorsys.multibanking.domain.Rule.SIMILARITY_MATCH_TYPE.REFERENCE_NAME; + +@RequiredArgsConstructor +@Service +public class AnalyticsService { + + private final UserRepositoryIf userRepository; + private final BookingRuleRepositoryIf rulesRepository; + private final AnalyticsRepositoryIf analyticsRepository; + private final ContractRepositoryIf contractRepository; + private final SmartAnalyticsIf smartAnalyticsService; + private final SmartAnalyticsMapper smartAnalyticsMapper; + private final Principal principal; + + private static String normalize(String s) { + if (s == null) { + return null; + } + return s.toLowerCase() + .replace("ü", "ue") + .replace("ö", "oe") + .replace("ä", "ae") + .replace("ß", "ss") + .replaceAll("[^a-z ]+", " "); + } + + @Transactional + public void createCustomRule(RuleEntity ruleEntity) { + ruleEntity.setUserId(principal.getName()); + ruleEntity.setRuleType("STOP"); + ruleEntity.setRuleId("custom-" + UUID.randomUUID().toString()); + ruleEntity.setId(ruleEntity.getRuleId()); + if (ruleEntity.getSimilarityMatchType() == PURPOSE) { + ruleEntity.setExpression(normalize(ruleEntity.getExpression())); + } else if (ruleEntity.getSimilarityMatchType() == REFERENCE_NAME) { + ruleEntity.setExpression(ruleEntity.getExpression().toLowerCase()); + } + rulesRepository.createOrUpdateRule(ruleEntity); + userRepository.setRulesLastChangeDate(principal.getName(), LocalDateTime.now()); + } + + @Transactional + public void updateCustomRule(RuleEntity ruleEntity) { + ruleEntity.setUserId(principal.getName()); + rulesRepository.createOrUpdateRule(ruleEntity); + userRepository.setRulesLastChangeDate(principal.getName(), LocalDateTime.now()); + } + + private List loadUserRules(String userId) { + return rulesRepository.findByUserId(userId).stream() + .map(customRuleEntity -> { + Rule smartanalyticsRule = new Rule(); + BeanUtils.copyProperties(customRuleEntity, smartanalyticsRule); + if (customRuleEntity.getSimilarityMatchType() != null) { + smartanalyticsRule.setSimilarityMatchType(Rule.SIMILARITY_MATCH_TYPE.valueOf(customRuleEntity.getSimilarityMatchType().toString())); + } + smartanalyticsRule.setStop(customRuleEntity.getRuleType() == null || + customRuleEntity.getRuleType().equalsIgnoreCase("stop")); + return smartanalyticsRule; + }) + .collect(Collectors.toList()); + } + + @Transactional + public void deleteRule(String ruleId) { + rulesRepository.deleteRule(ruleId); + userRepository.setRulesLastChangeDate(principal.getName(), LocalDateTime.now()); + } + + public void saveAccountAnalytics(BankAccountEntity bankAccountEntity, List bookingGroups) { + AccountAnalyticsEntity accountAnalyticsEntity = new AccountAnalyticsEntity(); + accountAnalyticsEntity.setUserId(bankAccountEntity.getUserId()); + accountAnalyticsEntity.setAccountId(bankAccountEntity.getId()); + accountAnalyticsEntity.setAnalyticsDate(LocalDateTime.now()); + accountAnalyticsEntity.setBookingGroups(smartAnalyticsMapper.mapBookingGroups(bookingGroups)); + + analyticsRepository.deleteByAccountId(bankAccountEntity.getId()); + analyticsRepository.save(accountAnalyticsEntity); + } + + public void identifyAndStoreContracts(String userId, String accountId, List bookingGroups) { + List contractEntities = bookingGroups + .stream() + .filter(BookingGroup::isContract) + .map(category -> smartAnalyticsMapper.toContractEntity(userId, accountId, category)) + .collect(Collectors.toList()); + + contractRepository.deleteByAccountId(accountId); + contractRepository.save(contractEntities); + } + + public AnalyticsResult analyzeBookings(String userId, List bookingEntities) { + bookingEntities.forEach(bookingEntity -> bookingEntity.setBookingCategory(null)); + return smartAnalyticsService.analyzeBookings(loadUserRules(userId), + smartAnalyticsMapper.toSmartAnalyticsBookings(bookingEntities)); + } + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/service/analytics/SmartAnalyticsEmbeddedService.java b/multibanking-server/src/main/java/de/adorsys/multibanking/service/analytics/SmartAnalyticsEmbeddedService.java new file mode 100644 index 000000000..f6db95387 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/service/analytics/SmartAnalyticsEmbeddedService.java @@ -0,0 +1,40 @@ +package de.adorsys.multibanking.service.analytics; + +import de.adorsys.smartanalytics.api.AnalyticsRequest; +import de.adorsys.smartanalytics.api.AnalyticsResult; +import de.adorsys.smartanalytics.api.Booking; +import de.adorsys.smartanalytics.api.Rule; +import de.adorsys.smartanalytics.api.config.ConfigStatus; +import de.adorsys.smartanalytics.core.AnalyticsService; +import de.adorsys.smartanalytics.core.StatusService; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; + +import java.util.List; + +@RequiredArgsConstructor +@Service +@Profile("smartanalytics-embedded") +public class SmartAnalyticsEmbeddedService implements SmartAnalyticsIf { + + private final StatusService statusService; + private final AnalyticsService smartanalytics; + + public AnalyticsResult analyzeBookings(List userRules, List bookings) { + AnalyticsRequest analyticsRequest = createAnalyticsRequest(bookings, userRules); + return smartanalytics.analytics(analyticsRequest); + } + + public ConfigStatus getAnalyticsConfigStatus() { + return statusService.getStatus(); + } + + private AnalyticsRequest createAnalyticsRequest(List bookings, List customRules) { + return AnalyticsRequest.builder() + .bookings(bookings) + .customRules(customRules) + .build(); + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/service/analytics/SmartAnalyticsIf.java b/multibanking-server/src/main/java/de/adorsys/multibanking/service/analytics/SmartAnalyticsIf.java new file mode 100644 index 000000000..74768200a --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/service/analytics/SmartAnalyticsIf.java @@ -0,0 +1,15 @@ +package de.adorsys.multibanking.service.analytics; + +import de.adorsys.smartanalytics.api.AnalyticsResult; +import de.adorsys.smartanalytics.api.Booking; +import de.adorsys.smartanalytics.api.Rule; +import de.adorsys.smartanalytics.api.config.ConfigStatus; + +import java.util.List; + +public interface SmartAnalyticsIf { + + AnalyticsResult analyzeBookings(List userRules, List bookings); + + ConfigStatus getAnalyticsConfigStatus(); +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/service/analytics/SmartAnalyticsMapper.java b/multibanking-server/src/main/java/de/adorsys/multibanking/service/analytics/SmartAnalyticsMapper.java new file mode 100644 index 000000000..322de06ac --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/service/analytics/SmartAnalyticsMapper.java @@ -0,0 +1,92 @@ +package de.adorsys.multibanking.service.analytics; + +import de.adorsys.multibanking.domain.BookingCategory; +import de.adorsys.multibanking.domain.BookingEntity; +import de.adorsys.multibanking.domain.Contract; +import de.adorsys.multibanking.domain.ContractEntity; +import de.adorsys.smartanalytics.api.AnalyticsResult; +import de.adorsys.smartanalytics.api.Booking; +import de.adorsys.smartanalytics.api.BookingGroup; +import de.adorsys.smartanalytics.api.WrappedBooking; +import de.adorsys.smartanalytics.api.config.Group; +import org.mapstruct.AfterMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; + +import java.util.Collection; +import java.util.List; + +@Mapper(componentModel = "spring") +public abstract class SmartAnalyticsMapper { + + public void applyCategories(List bookingEntities, AnalyticsResult result) { + result.getBookings().forEach(categorizedBooking -> + bookingEntities.stream() + .filter(bookingEntity -> categorizedBooking.getBooking().getBookingId().equals(bookingEntity.getExternalId())) + .findFirst() + .ifPresent(bookingEntity -> { + if (categorizedBooking.getMainCategory() != null) { + bookingEntity.setBookingCategory(toBookingcategory(categorizedBooking)); + } + })); + } + + @Mapping(source = "cycle", target = "interval") + @Mapping(source = "otherAccount", target = "receiver") + @Mapping(source = "ruleIds", target = "rules") + @Mapping(target = "cancelled", ignore = true) + @Mapping(target = "provider", ignore = true) + abstract BookingCategory toBookingcategory(WrappedBooking wrappedBooking); + + abstract List mapBookingGroups(List bookingGroups); + + @Mapping(target = "otherAccount", ignore = true) + @Mapping(source = "groupType", target = "type") + @Mapping(source = ".", target = "contract") + abstract de.adorsys.multibanking.domain.BookingGroup mapBookingGroup(BookingGroup bookingGroup); + + @AfterMapping + public void mapBookingGroupAfterMapping(BookingGroup bookingsGroup, + @MappingTarget de.adorsys.multibanking.domain.BookingGroup.BookingGroupBuilder bookingGroup) { + if (bookingsGroup.getGroupType() == Group.Type.CUSTOM || bookingsGroup.getGroupType() == Group.Type.OTHER_INCOME + || bookingsGroup.getGroupType() == Group.Type.OTHER_EXPENSES) { + bookingGroup.otherAccount(""); + } else { + bookingGroup.otherAccount(bookingsGroup.getOtherAccount()); + } + } + + @Mapping(source = "mandatreference", target = "mandateReference") + @Mapping(source = "cycle", target = "interval") + @Mapping(target = "provider", ignore = true) + abstract Contract toContract(BookingGroup bookingsGroup); + + @Mapping(source = "bookingGroup.mandatreference", target = "mandateReference") + @Mapping(source = "bookingGroup.cycle", target = "interval") + @Mapping(source = "bookingGroup.otherAccount", target = "provider") + @Mapping(target = "id", ignore = true) + abstract ContractEntity toContractEntity(String userId, String accountId, BookingGroup bookingGroup); + + abstract List toSmartAnalyticsBookings(Collection bookings); + + @Mapping(target = "referenceName", ignore = true) + @Mapping(source = "externalId", target = "bookingId") + @Mapping(source = "usage", target = "purpose") + @Mapping(source = "valutaDate", target = "executionDate") + @Mapping(source = "otherAccount.iban", target = "iban") + @Mapping(source = "otherAccount.accountNumber", target = "accountNumber") + @Mapping(source = "otherAccount.blz", target = "bankCode") + abstract Booking toSmartAnalyticsBooking(BookingEntity booking); + + @AfterMapping + void toSmartAnalyticsBookingAfterMapping(BookingEntity bookingEntity, @MappingTarget Booking booking) { + if (bookingEntity.getOtherAccount() != null) { + if (bookingEntity.getOtherAccount().getOwner() != null) { + booking.setReferenceName(bookingEntity.getOtherAccount().getOwner()); + } else { + booking.setReferenceName(bookingEntity.getOtherAccount().getName()); + } + } + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/service/analytics/SmartAnalyticsRemoteService.java b/multibanking-server/src/main/java/de/adorsys/multibanking/service/analytics/SmartAnalyticsRemoteService.java new file mode 100644 index 000000000..332c6c150 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/service/analytics/SmartAnalyticsRemoteService.java @@ -0,0 +1,68 @@ +package de.adorsys.multibanking.service.analytics; + +import de.adorsys.multibanking.exception.SmartanalyticsException; +import de.adorsys.smartanalytics.api.AnalyticsRequest; +import de.adorsys.smartanalytics.api.AnalyticsResult; +import de.adorsys.smartanalytics.api.Booking; +import de.adorsys.smartanalytics.api.Rule; +import de.adorsys.smartanalytics.api.config.ConfigStatus; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Profile; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.hateoas.Resource; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import java.util.Collections; +import java.util.List; + +@Service +@Profile("smartanalytics-remote") +public class SmartAnalyticsRemoteService implements SmartAnalyticsIf { + + @Autowired + @Qualifier("smartanalytics") + private RestTemplate smartanalyticsRestTemplate; + + public AnalyticsResult analyzeBookings(List userRules, List bookings) { + AnalyticsRequest analyticsRequest = createAnalyticsRequest(bookings, userRules); + return analyzeBookingsRemote(analyticsRequest); + } + + public ConfigStatus getAnalyticsConfigStatus() { + ResponseEntity responseEntity = smartanalyticsRestTemplate + .getForEntity("/status", ConfigStatus.class); + + if (responseEntity.getStatusCode() == HttpStatus.OK) { + return responseEntity.getBody(); + } + + throw new SmartanalyticsException(responseEntity.getStatusCode(), null); + } + + private AnalyticsResult analyzeBookingsRemote(AnalyticsRequest analyticsRequest) { + ResponseEntity> responseEntity = smartanalyticsRestTemplate + .exchange( + "/api/v1/analytics", HttpMethod.PUT, new HttpEntity(analyticsRequest), + new ParameterizedTypeReference>() { + }, Collections.emptyMap()); + + if (responseEntity.getStatusCode() == HttpStatus.OK) { + return responseEntity.getBody().getContent(); + } + + throw new SmartanalyticsException(responseEntity.getStatusCode(), null); + } + + private AnalyticsRequest createAnalyticsRequest(List bookings, List customRules) { + return AnalyticsRequest.builder() + .bookings(bookings) + .customRules(customRules) + .build(); + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/BankAccessController.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/BankAccessController.java new file mode 100644 index 000000000..436c3d2ec --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/BankAccessController.java @@ -0,0 +1,140 @@ +package de.adorsys.multibanking.web; + +import de.adorsys.multibanking.domain.BankAccessEntity; +import de.adorsys.multibanking.domain.Consent; +import de.adorsys.multibanking.exception.ResourceNotFoundException; +import de.adorsys.multibanking.pers.spi.repository.BankAccessRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.UserRepositoryIf; +import de.adorsys.multibanking.service.BankAccessService; +import de.adorsys.multibanking.web.mapper.BankAccessMapper; +import de.adorsys.multibanking.web.model.BankAccessTO; +import io.swagger.annotations.*; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.hateoas.Resource; +import org.springframework.hateoas.Resources; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.security.Principal; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static java.util.stream.Collectors.toList; +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; + +@RequiredArgsConstructor +@Slf4j +@UserResource +@RestController +@RequestMapping(path = "api/v1/bankaccesses") +public class BankAccessController { + + private final BankAccessMapper bankAccessMapper; + private final BankAccessRepositoryIf bankAccessRepository; + private final UserRepositoryIf userRepository; + private final BankAccessService bankAccessService; + private final Principal principal; + + @ApiOperation( + value = "Read bank accesses", + authorizations = { + @Authorization(value = "multibanking_auth", scopes = { + @AuthorizationScope(scope = "openid", description = "") + })}) + @GetMapping + public Resources> getBankAccesses() { + if (!userRepository.exists(principal.getName())) { + return new Resources<>(Collections.emptyList()); + } + + List accessEntities = bankAccessRepository.findByUserId(principal.getName()); + return new Resources<>(mapToResources(accessEntities)); + } + + @ApiOperation( + value = "Create new bank accesses", + authorizations = { + @Authorization(value = "multibanking_auth", scopes = { + @AuthorizationScope(scope = "openid", description = "") + })}) + @ApiResponses({ + @ApiResponse(code = 201, message = "Created", response = void.class), + @ApiResponse(code = 202, message = "Consent authorisation required", response = Consent.class)}) + @PostMapping + public ResponseEntity createBankAccess(@RequestBody BankAccessTO bankAccess) { + BankAccessEntity persistedBankAccess = bankAccessService.createBankAccess(principal.getName(), + bankAccessMapper.toBankAccessEntity(bankAccess)); + + HttpHeaders headers = new HttpHeaders(); + headers.setLocation(linkTo(methodOn(BankAccessController.class).getBankAccess(persistedBankAccess.getId())).toUri()); + + return Optional.ofNullable(persistedBankAccess.getAllAcountsConsent()) + .map(consent -> new ResponseEntity<>(consent, headers, HttpStatus.ACCEPTED)) + .orElseGet(() -> new ResponseEntity<>(new Consent(), headers, HttpStatus.CREATED)); + } + + @ApiOperation( + value = "Read bank access", + authorizations = { + @Authorization(value = "multibanking_auth", scopes = { + @AuthorizationScope(scope = "openid", description = "") + })}) + @GetMapping("/{accessId}") + public Resource getBankAccess(@PathVariable String accessId) { + BankAccessEntity bankAccessEntity = bankAccessRepository.findByUserIdAndId(principal.getName(), accessId) + .orElseThrow(() -> new ResourceNotFoundException(BankAccessEntity.class, accessId)); + + return mapToResource(bankAccessEntity); + } + + @ApiOperation( + value = "Update bank access", + authorizations = { + @Authorization(value = "multibanking_auth", scopes = { + @AuthorizationScope(scope = "openid", description = "") + })}) + @PutMapping("/{accessId}") + public HttpEntity updateBankAccess(@PathVariable String accessId, + @RequestBody BankAccessTO bankAccess) { + bankAccessService.updateBankAccess(accessId, bankAccessMapper.toBankAccessEntity(bankAccess)); + log.info("Bank access [{}] updated.", accessId); + + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @ApiOperation( + value = "Delete bank accesses", + authorizations = { + @Authorization(value = "multibanking_auth", scopes = { + @AuthorizationScope(scope = "openid", description = "") + })}) + @DeleteMapping("/{accessId}") + public HttpEntity deleteBankAccess(@PathVariable String accessId) { + if (bankAccessService.deleteBankAccess(principal.getName(), accessId)) { + log.info("Bank Access [{}] deleted.", accessId); + } else { + throw new ResourceNotFoundException(BankAccessEntity.class, accessId); + } + + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + private List> mapToResources(List accessEntities) { + return accessEntities.stream() + .map(this::mapToResource) + .collect(toList()); + } + + private Resource mapToResource(BankAccessEntity accessEntity) { + return new Resource<>(bankAccessMapper.toBankAccessTO(accessEntity), + linkTo(methodOn(BankAccessController.class).getBankAccess(accessEntity.getId())).withSelfRel(), + linkTo(methodOn(BankAccountController.class).getBankAccounts(accessEntity.getId())).withRel("accounts")); + } + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/BankAccountAnalyticsController.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/BankAccountAnalyticsController.java new file mode 100644 index 000000000..9f336ed08 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/BankAccountAnalyticsController.java @@ -0,0 +1,66 @@ +package de.adorsys.multibanking.web; + +import de.adorsys.multibanking.domain.AccountAnalyticsEntity; +import de.adorsys.multibanking.domain.BankAccessEntity; +import de.adorsys.multibanking.domain.BankAccount; +import de.adorsys.multibanking.domain.BankAccountEntity; +import de.adorsys.multibanking.exception.ResourceNotFoundException; +import de.adorsys.multibanking.exception.SyncInProgressException; +import de.adorsys.multibanking.pers.spi.repository.AnalyticsRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.BankAccessRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.BankAccountRepositoryIf; +import de.adorsys.multibanking.web.mapper.AnalyticsMapper; +import de.adorsys.multibanking.web.model.AnalyticsTO; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.Authorization; +import io.swagger.annotations.AuthorizationScope; +import lombok.RequiredArgsConstructor; +import org.springframework.hateoas.Resource; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.security.Principal; + +@RequiredArgsConstructor +@UserResource +@RestController +@RequestMapping(path = "api/v1/bankaccesses/{accessId}/accounts/{accountId}/analytics") +public class BankAccountAnalyticsController { + + private final AnalyticsMapper analyticsMapper; + private final AnalyticsRepositoryIf analyticsRepository; + private final BankAccessRepositoryIf bankAccessRepository; + private final BankAccountRepositoryIf bankAccountRepository; + private final Principal principal; + + @ApiOperation( + value = "Read account analytics", + authorizations = { + @Authorization(value = "multibanking_auth", scopes = { + @AuthorizationScope(scope = "openid", description = "") + })}) + @GetMapping + public Resource getAccountAnalytics(@PathVariable String accessId, + @PathVariable String accountId) { + if (!bankAccessRepository.exists(accessId)) { + throw new ResourceNotFoundException(BankAccessEntity.class, accessId); + } + if (!bankAccountRepository.exists(accountId)) { + throw new ResourceNotFoundException(BankAccountEntity.class, accountId); + } + if (bankAccountRepository.getSyncStatus(accountId) == BankAccount.SyncStatus.SYNC) { + throw new SyncInProgressException(accountId); + } + + AccountAnalyticsEntity accountAnalyticsEntity = analyticsRepository + .findLastByUserIdAndAccountId(principal.getName(), accountId) + .orElseThrow(() -> new ResourceNotFoundException( + AccountAnalyticsEntity.class, + "user-id: " + principal.getName() + )); + + return new Resource<>(analyticsMapper.toAnalyticsTO(accountAnalyticsEntity)); + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/BankAccountController.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/BankAccountController.java new file mode 100644 index 000000000..7d5cc58bd --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/BankAccountController.java @@ -0,0 +1,132 @@ +package de.adorsys.multibanking.web; + +import de.adorsys.multibanking.domain.BankAccessEntity; +import de.adorsys.multibanking.domain.BankAccount; +import de.adorsys.multibanking.domain.BankAccountEntity; +import de.adorsys.multibanking.domain.Consent; +import de.adorsys.multibanking.exception.ExternalAuthorisationRequiredException; +import de.adorsys.multibanking.exception.ResourceNotFoundException; +import de.adorsys.multibanking.exception.SyncInProgressException; +import de.adorsys.multibanking.exception.domain.Messages; +import de.adorsys.multibanking.pers.spi.repository.BankAccessRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.BankAccountRepositoryIf; +import de.adorsys.multibanking.service.BankAccountService; +import de.adorsys.multibanking.service.BookingService; +import de.adorsys.multibanking.web.mapper.BankAccountMapper; +import de.adorsys.multibanking.web.model.BankAccountTO; +import io.swagger.annotations.*; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.hateoas.Resource; +import org.springframework.hateoas.Resources; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.security.Principal; +import java.util.List; + +import static java.util.stream.Collectors.toList; +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; + +/** + * Created by alexg on 07.02.17. + */ +@Slf4j +@UserResource +@RestController +@AllArgsConstructor +@RequestMapping(path = "api/v1/bankaccesses/{accessId}/accounts") +public class BankAccountController { + + private final BankAccountMapper bankAccountMapper; + private final BankAccountService bankAccountService; + private final BookingService bookingService; + private final BankAccountRepositoryIf bankAccountRepository; + private final BankAccessRepositoryIf bankAccessRepository; + private final Principal principal; + + @ApiOperation( + value = "Read bank accounts", + authorizations = { + @Authorization(value = "multibanking_auth", scopes = { + @AuthorizationScope(scope = "openid", description = "") + })}) + @ApiResponses({ + @ApiResponse(code = 400, message = "Consent authorisation required", response = Messages.class)}) + @GetMapping + public Resources> getBankAccounts(@PathVariable String accessId) { + List bankAccounts = bankAccountService.getBankAccounts(principal.getName(), accessId); + return new Resources<>(mapToResources(bankAccounts, accessId)); + } + + @ApiOperation( + value = "Read bank account", + authorizations = { + @Authorization(value = "multibanking_auth", scopes = { + @AuthorizationScope(scope = "openid", description = "") + })}) + @GetMapping("/{accountId}") + public Resource getBankAccount(@PathVariable String accessId, + @PathVariable("accountId") String accountId) { + if (!bankAccessRepository.exists(accessId)) { + throw new ResourceNotFoundException(BankAccessEntity.class, accessId); + } + + BankAccountEntity bankAccountEntity = bankAccountRepository.findByUserIdAndId(principal.getName(), accountId) + .orElseThrow(() -> new ResourceNotFoundException(BankAccountEntity.class, accountId)); + + return mapToResource(bankAccountEntity, accessId); + } + + @ApiOperation( + value = "Trigger account sync", + authorizations = { + @Authorization(value = "multibanking_auth", scopes = { + @AuthorizationScope(scope = "openid", description = "") + })}) + @ApiResponses({ + @ApiResponse(code = 202, message = "Consent authorisation required", response = Consent.class), + @ApiResponse(code = 204, message = "Sync started", response = void.class)}) + @PutMapping("/{accountId}/sync") + public ResponseEntity syncBookings( + @PathVariable String accessId, + @PathVariable String accountId, + @RequestBody(required = false) String pin) { + + BankAccessEntity bankAccess = bankAccessRepository.findByUserIdAndId(principal.getName(), accessId) + .orElseThrow(() -> new ResourceNotFoundException(BankAccessEntity.class, accessId)); + + BankAccountEntity bankAccount = bankAccountRepository.findByUserIdAndId(principal.getName(), accountId) + .orElseThrow(() -> new ResourceNotFoundException(BankAccountEntity.class, accountId)); + + if (bankAccount.getSyncStatus() == BankAccount.SyncStatus.SYNC) { + throw new SyncInProgressException(bankAccount.getId()); + } + try { + bookingService.syncBookings(bankAccess, bankAccount, null, pin != null ? pin : bankAccess.getPin()); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } catch (ExternalAuthorisationRequiredException e) { + return new ResponseEntity<>(e.getConsent(), HttpStatus.ACCEPTED); + } + } + + private List> mapToResources(List accountEntities, String accessId) { + return accountEntities.stream() + .map(accountEntity -> mapToResource(accountEntity, accessId)) + .collect(toList()); + } + + private Resource mapToResource(BankAccountEntity accountEntity, String accessId) { + return new Resource<>(bankAccountMapper.toBankAccountTO(accountEntity), + linkTo(methodOn(BankAccountController.class).getBankAccount(accessId, accountEntity.getId())).withSelfRel(), + linkTo(methodOn(BankAccessController.class).getBankAccess(accessId)).withRel("bankAccess"), + linkTo(methodOn(BankAccountAnalyticsController.class).getAccountAnalytics(accessId, + accountEntity.getId())).withRel("analytics"), + linkTo(methodOn(BankAccountController.class).syncBookings(accessId, accountEntity.getId(), null)).withRel("sync"), + linkTo(methodOn(BookingController.class).getBookings(accessId, accountEntity.getId(), null, null, null, + null)).withRel("bookings")); + } + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/BankController.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/BankController.java new file mode 100644 index 000000000..b35bc3c00 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/BankController.java @@ -0,0 +1,72 @@ +package de.adorsys.multibanking.web; + +import de.adorsys.multibanking.domain.BankEntity; +import de.adorsys.multibanking.service.BankService; +import de.adorsys.multibanking.web.mapper.BankMapper; +import de.adorsys.multibanking.web.model.BankTO; +import de.adorsys.smartanalytics.exception.FileUploadException; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.Authorization; +import io.swagger.annotations.AuthorizationScope; +import lombok.RequiredArgsConstructor; +import org.springframework.hateoas.Resource; +import org.springframework.hateoas.Resources; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +import static java.util.stream.Collectors.toList; +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; + +@RequiredArgsConstructor +@UserResource +@RestController +@RequestMapping(path = {"api/v1/bank", "api/v1/banks"}) +public class BankController { + + private final BankMapper bankMapper; + private final BankService bankService; + + @GetMapping(value = "/{bankCode}") + public Resource getBank(@PathVariable String bankCode) { + return mapToResource(bankService.findBank(bankCode)); + } + + @GetMapping + public Resources> searchBank(@RequestParam String query) { + return new Resources<>(mapToResources(bankService.search(query))); + } + + @ApiOperation( + value = "Upload banks configuration file", + authorizations = { + @Authorization(value = "multibanking_auth", scopes = { + @AuthorizationScope(scope = "openid", description = "") + })}) + @PostMapping("/upload") + public HttpEntity uploadBanks(@RequestParam MultipartFile banksFile) { + if (!banksFile.isEmpty()) { + bankService.importBanks(banksFile); + return new ResponseEntity<>(HttpStatus.CREATED); + } else { + throw new FileUploadException("File is empty"); + } + } + + private List> mapToResources(List entities) { + return entities.stream() + .map(this::mapToResource) + .collect(toList()); + } + + private Resource mapToResource(BankEntity entity) { + return new Resource<>(bankMapper.toBankTO(entity), + linkTo(methodOn(BankController.class).getBank(entity.getBankCode())).withSelfRel()); + } + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/BookingController.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/BookingController.java new file mode 100644 index 000000000..00be0ac01 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/BookingController.java @@ -0,0 +1,152 @@ +package de.adorsys.multibanking.web; + +import de.adorsys.multibanking.domain.*; +import de.adorsys.multibanking.exception.ResourceNotFoundException; +import de.adorsys.multibanking.exception.SyncInProgressException; +import de.adorsys.multibanking.pers.spi.repository.BankAccessRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.BankAccountRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.BookingRepositoryIf; +import de.adorsys.multibanking.service.BookingService; +import de.adorsys.multibanking.web.mapper.BankApiMapper; +import de.adorsys.multibanking.web.mapper.BookingMapper; +import de.adorsys.multibanking.web.model.BankApiTO; +import de.adorsys.multibanking.web.model.BookingTO; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.Authorization; +import io.swagger.annotations.AuthorizationScope; +import lombok.AllArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; +import org.springframework.data.web.PagedResourcesAssembler; +import org.springframework.hateoas.Resource; +import org.springframework.hateoas.Resources; +import org.springframework.http.HttpEntity; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.security.Principal; +import java.util.List; +import java.util.Optional; + +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; + +@AllArgsConstructor +@UserResource +@RestController +@RequestMapping(path = "api/v1/bankaccesses/{accessId}/accounts/{accountId}/bookings") +public class BookingController { + + private final BookingMapper bookingMapper; + private final BankApiMapper bankApiMapper; + private final BookingService bookingService; + private final BookingRepositoryIf bookingRepository; + private final BankAccessRepositoryIf bankAccessRepository; + private final BankAccountRepositoryIf bankAccountRepository; + private final Principal principal; + + @SuppressWarnings("unchecked") + @ApiOperation( + value = "Read account bookings", + authorizations = { + @Authorization(value = "multibanking_auth", scopes = { + @AuthorizationScope(scope = "openid", description = "") + })}) + @GetMapping + public Resources getBookings(@PathVariable String accessId, + @PathVariable String accountId, + @RequestParam(required = false) BankApiTO bankApi, + @RequestParam(required = false) List ids, + @PageableDefault(size = 20, sort = "valutaDate", direction = + Sort.Direction.DESC) Pageable pageable, + PagedResourcesAssembler assembler) { + chackBankAccountExists(accessId, accountId); + + if (bankAccountRepository.getSyncStatus(accountId) == BankAccount.SyncStatus.SYNC) { + throw new SyncInProgressException(accountId); + } + + return Optional.ofNullable(ids) + .map(strings -> { + Iterable bookingEntities = bookingService.getBookingsById(principal.getName(), ids); + return new Resources<>(bookingMapper.toBookingTOs(bookingEntities)); + }) + .orElseGet(() -> { + Page bookingEntities = bookingService.getBookingsPageable(pageable, + principal.getName(), accessId, accountId, bankApiMapper.toBankApi(bankApi)); + return assembler.toResource(bookingEntities.map(bookingMapper::toBookingTO)); + }); + } + + @ApiOperation( + value = "Read account bookings search index", + authorizations = { + @Authorization(value = "multibanking_auth", scopes = { + @AuthorizationScope(scope = "openid", description = "") + })}) + @GetMapping("/index") + public Resource getBookingsIndex(@PathVariable String accessId, + @PathVariable String accountId) { + chackBankAccountExists(accessId, accountId); + + if (bankAccountRepository.getSyncStatus(accountId) == BankAccount.SyncStatus.SYNC) { + throw new SyncInProgressException(accountId); + } + + BookingsIndexEntity bookingsIndexEntity = bookingService.getSearchIndex(principal.getName(), accountId) + .orElseThrow(() -> new ResourceNotFoundException(BookingsIndexEntity.class, accountId)); + + return new Resource<>(bookingsIndexEntity); + } + + @ApiOperation( + value = "Download bookings", + authorizations = { + @Authorization(value = "multibanking_auth", scopes = { + @AuthorizationScope(scope = "openid", description = "") + })}) + @GetMapping(path = "/download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) + public HttpEntity downloadBookings(@PathVariable String accessId, @PathVariable String accountId) { + chackBankAccountExists(accessId, accountId); + + String bookingsAsCSV = bookingService.getBookingsCsv(principal.getName(), accessId, accountId); + + return ResponseEntity.ok().body(bookingsAsCSV); + } + + @ApiOperation( + value = "Read booking", + authorizations = { + @Authorization(value = "multibanking_auth", scopes = { + @AuthorizationScope(scope = "openid", description = "") + })}) + @GetMapping("/{bookingId}") + public Resource getBooking(@PathVariable String accessId, @PathVariable String accountId, + @PathVariable String bookingId) { + chackBankAccountExists(accessId, accountId); + + BookingEntity bookingEntity = bookingRepository.findByUserIdAndId(principal.getName(), bookingId) + .orElseThrow(() -> new ResourceNotFoundException(BookingEntity.class, bookingId)); + + return mapToResource(bookingEntity, accessId, accountId); + } + + private void chackBankAccountExists(String accessId, String accountId) { + if (!bankAccessRepository.exists(accessId)) { + throw new ResourceNotFoundException(BankAccessEntity.class, accessId); + } + if (!bankAccountRepository.exists(accountId)) { + throw new ResourceNotFoundException(BankAccountEntity.class, accountId); + } + } + + private Resource mapToResource(BookingEntity entity, String accessId, String accountId) { + return new Resource<>(bookingMapper.toBookingTO(entity), + linkTo(methodOn(BookingController.class).getBooking(accessId, accountId, entity.getId())) + .withSelfRel()); + } + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/ContractController.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/ContractController.java new file mode 100644 index 000000000..fbcc66ccd --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/ContractController.java @@ -0,0 +1,68 @@ +package de.adorsys.multibanking.web; + +import de.adorsys.multibanking.domain.BankAccessEntity; +import de.adorsys.multibanking.domain.BankAccount; +import de.adorsys.multibanking.domain.BankAccountEntity; +import de.adorsys.multibanking.domain.ContractEntity; +import de.adorsys.multibanking.exception.ResourceNotFoundException; +import de.adorsys.multibanking.exception.SyncInProgressException; +import de.adorsys.multibanking.pers.spi.repository.BankAccessRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.BankAccountRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.ContractRepositoryIf; +import de.adorsys.multibanking.web.mapper.ContractMapper; +import de.adorsys.multibanking.web.model.ContractTO; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.Authorization; +import io.swagger.annotations.AuthorizationScope; +import lombok.RequiredArgsConstructor; +import org.springframework.hateoas.Resources; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.security.Principal; +import java.util.List; + +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; + +@RequiredArgsConstructor +@UserResource +@RestController +@RequestMapping(path = "api/v1/bankaccesses/{accessId}/accounts/{accountId}/contracts") +public class ContractController { + + private final ContractMapper contractMapper; + private final ContractRepositoryIf contractRepository; + private final BankAccessRepositoryIf bankAccessRepository; + private final BankAccountRepositoryIf bankAccountRepository; + private final Principal principal; + + @ApiOperation( + value = "Read account contracts", + authorizations = { + @Authorization(value = "multibanking_auth", scopes = { + @AuthorizationScope(scope = "openid", description = "") + })}) + @GetMapping + public Resources getContracts(@PathVariable String accessId, @PathVariable String accountId) { + if (!bankAccessRepository.exists(accessId)) { + throw new ResourceNotFoundException(BankAccessEntity.class, accessId); + } + if (!bankAccountRepository.exists(accountId)) { + throw new ResourceNotFoundException(BankAccountEntity.class, accountId); + } + if (bankAccountRepository.getSyncStatus(accountId) == BankAccount.SyncStatus.SYNC) { + throw new SyncInProgressException(accountId); + } + + List contractEntities = contractRepository.findByUserIdAndAccountId(principal.getName(), + accountId); + + return new Resources<>( + contractMapper.toContractTOs(contractEntities), + linkTo(methodOn(ContractController.class).getContracts(accessId, accountId)).withSelfRel() + ); + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/CustomRulesController.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/CustomRulesController.java new file mode 100644 index 000000000..d3a3c73ce --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/CustomRulesController.java @@ -0,0 +1,152 @@ +package de.adorsys.multibanking.web; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import de.adorsys.multibanking.domain.RuleEntity; +import de.adorsys.multibanking.exception.ResourceNotFoundException; +import de.adorsys.multibanking.pers.spi.repository.BookingRuleRepositoryIf; +import de.adorsys.multibanking.service.analytics.AnalyticsService; +import de.adorsys.multibanking.web.mapper.RuleMapper; +import de.adorsys.multibanking.web.model.RuleTO; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.Authorization; +import io.swagger.annotations.AuthorizationScope; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.io.InputStreamResource; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; +import org.springframework.data.web.PagedResourcesAssembler; +import org.springframework.hateoas.Resource; +import org.springframework.hateoas.Resources; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.io.ByteArrayInputStream; +import java.util.List; + +import static java.util.stream.Collectors.toList; +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; + +@RequiredArgsConstructor +@Slf4j +@UserResource +@RestController +@RequestMapping(path = "api/v1/analytics/rules") +public class CustomRulesController { + + private final RuleMapper ruleMapper; + private final AnalyticsService analyticsService; + private final BookingRuleRepositoryIf rulesRepository; + + @ApiOperation( + value = "Create user custom rule", + authorizations = { + @Authorization(value = "multibanking_auth", scopes = { + @AuthorizationScope(scope = "openid", description = "") + })}) + @PostMapping + public HttpEntity createRule(@RequestBody RuleTO rule) { + analyticsService.createCustomRule(ruleMapper.toRuleEntity(rule)); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @ApiOperation( + value = "Read user custom rule", + authorizations = { + @Authorization(value = "multibanking_auth", scopes = { + @AuthorizationScope(scope = "openid", description = "") + })}) + @GetMapping("{ruleId}") + public Resource getRule(@PathVariable String ruleId) { + RuleEntity ruleEntity = rulesRepository.findByRuleId(ruleId) + .orElseThrow(() -> new ResourceNotFoundException(RuleEntity.class, ruleId)); + + return mapToResource(ruleEntity); + } + + @ApiOperation( + value = "Update user custom rule", + authorizations = { + @Authorization(value = "multibanking_auth", scopes = { + @AuthorizationScope(scope = "openid", description = "") + })}) + @PutMapping("{ruleId}") + public HttpEntity updateRule(@PathVariable String ruleId, @RequestBody RuleTO rule) { + analyticsService.updateCustomRule(ruleMapper.toRuleEntity(rule)); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @ApiOperation( + value = "Delete user custom rule", + authorizations = { + @Authorization(value = "multibanking_auth", scopes = { + @AuthorizationScope(scope = "openid", description = "") + })}) + @DeleteMapping("{ruleId}") + public HttpEntity deleteRule(@PathVariable String ruleId) { + analyticsService.deleteRule(ruleId); + log.info("Rule [{}] deleted.", ruleId); + + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @ApiOperation( + value = "Read user custom rules", + authorizations = { + @Authorization(value = "multibanking_auth", scopes = { + @AuthorizationScope(scope = "openid", description = "") + })}) + @GetMapping + public Resources> getRules(@PageableDefault(size = 20) Pageable pageable, + PagedResourcesAssembler assembler) { + Page pageableResult = rulesRepository.findAllPageable(pageable); + return assembler.toResource(pageableResult.map(ruleMapper::toRuleTO)); + } + + @ApiOperation( + value = "Search user custom rules", + authorizations = { + @Authorization(value = "multibanking_auth", scopes = { + @AuthorizationScope(scope = "openid", description = "") + })}) + @GetMapping("/search") + public Resources> searchRules(@RequestParam String query) { + return new Resources<>(mapToResources(rulesRepository.search(query))); + } + + @ApiOperation( + value = "Download user custom rules", + authorizations = { + @Authorization(value = "multibanking_auth", scopes = { + @AuthorizationScope(scope = "openid", description = "") + })}) + @GetMapping(path = "/download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) + public HttpEntity downloadRules() throws JsonProcessingException { + List rules = ruleMapper.toRuleTOs(rulesRepository.findAll()); + + final YAMLFactory ymlFactory = new YAMLFactory(); + ObjectMapper objectMapper = new ObjectMapper(ymlFactory); + + return ResponseEntity.ok() + .body(new InputStreamResource(new ByteArrayInputStream(objectMapper.writeValueAsBytes(rules)))); + } + + private List> mapToResources(List entities) { + return entities.stream() + .map(this::mapToResource) + .collect(toList()); + } + + private Resource mapToResource(RuleEntity entity) { + return new Resource<>(ruleMapper.toRuleTO(entity), + linkTo(methodOn(CustomRulesController.class).getRule(entity.getId())).withSelfRel()); + } + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/DirectAccessController.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/DirectAccessController.java new file mode 100644 index 000000000..4d8875f4d --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/DirectAccessController.java @@ -0,0 +1,145 @@ +package de.adorsys.multibanking.web; + +import de.adorsys.multibanking.domain.*; +import de.adorsys.multibanking.exception.ExternalAuthorisationRequiredException; +import de.adorsys.multibanking.exception.ResourceNotFoundException; +import de.adorsys.multibanking.pers.spi.repository.BankAccessRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.BankAccountRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.UserRepositoryIf; +import de.adorsys.multibanking.service.BankAccountService; +import de.adorsys.multibanking.service.BookingService; +import de.adorsys.multibanking.web.mapper.BankAccessMapper; +import de.adorsys.multibanking.web.mapper.BankAccountMapper; +import de.adorsys.multibanking.web.mapper.BankApiMapper; +import de.adorsys.multibanking.web.mapper.BookingMapper; +import de.adorsys.multibanking.web.model.BankAccessTO; +import de.adorsys.multibanking.web.model.BankAccountTO; +import de.adorsys.multibanking.web.model.BankApiTO; +import de.adorsys.multibanking.web.model.BookingTO; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; + +@UserResource +@RestController +@RequiredArgsConstructor +@RequestMapping(path = "api/v1/direct") +public class DirectAccessController { + + private final BookingMapper bookingMapper; + private final BankApiMapper bankApiMapper; + private final BankAccessMapper bankAccessMapper; + private final BankAccountMapper bankAccountMapper; + private final BankAccountService bankAccountService; + private final BookingService bookingService; + private final UserRepositoryIf userRepository; + private final BankAccountRepositoryIf bankAccountRepository; + private final BankAccessRepositoryIf bankAccessRepository; + + @Value("${threshold_temporaryData:15}") + private Integer thresholdTemporaryData; + + @ApiOperation(value = "Read bank accounts") + @ApiResponses({ + @ApiResponse(code = 200, message = "Response", response = LoadBankAccountsResponse.class), + @ApiResponse(code = 202, message = "Consent authorisation required", response = Consent.class)}) + @PutMapping("/accounts") + public ResponseEntity loadBankAccounts(@RequestBody BankAccessTO bankAccess, + @RequestParam(required = false) BankApiTO bankApi) { + //temporary user, will be deleted after x minutes + UserEntity userEntity = new UserEntity(); + userEntity.setId(UUID.randomUUID().toString()); + userEntity.setExpireUser(LocalDateTime.now().plusMinutes(thresholdTemporaryData)); + userRepository.save(userEntity); + + BankAccessEntity bankAccessEntity = bankAccessMapper.toBankAccessEntity(bankAccess); + + bankAccessEntity.setStorePin(false); + bankAccessEntity.setUserId(userEntity.getId()); + + LoadBankAccountsResponse response = new LoadBankAccountsResponse(); + + try { + List bankAccounts = bankAccountService.loadBankAccountsOnline(bankAccessEntity, + bankApiMapper.toBankApi(bankApi)); + + bankAccessEntity.setPin(null); + bankAccessEntity.setTemporary(true); + bankAccessEntity.setUserId(userEntity.getId()); + bankAccessRepository.save(bankAccessEntity); + + bankAccounts.forEach(account -> { + account.setBankAccessId(bankAccessEntity.getId()); + bankAccountRepository.save(account); + }); + + response.setBankAccounts(bankAccountMapper.toBankAccountTOs(bankAccounts)); + return new ResponseEntity<>(response, HttpStatus.OK); + + } catch (ExternalAuthorisationRequiredException e) { + response.setConsent(e.getConsent()); + return new ResponseEntity<>(response, HttpStatus.ACCEPTED); + } + + } + + @ApiResponses({ + @ApiResponse(code = 202, message = "Consent authorisation required", response = Consent.class)}) + @ApiOperation(value = "Read account bookings") + @PutMapping("/bookings") + public ResponseEntity loadBookings(@RequestBody LoadBookingsRequest loadBookingsRequest, + @RequestParam(required = false) BankApiTO bankApi) { + BankAccessEntity bankAccessEntity = bankAccessRepository.findOne(loadBookingsRequest.getAccessId()) + .orElseThrow(() -> new ResourceNotFoundException(BankAccessEntity.class, + loadBookingsRequest.getAccessId())); + + BankAccountEntity bankAccountEntity = bankAccountRepository.findOne(loadBookingsRequest.getAccountId()) + .orElseThrow(() -> new ResourceNotFoundException(BankAccountEntity.class, + loadBookingsRequest.getAccountId())); + + LoadBookingsResponse loadBookingsResponse = new LoadBookingsResponse(); + + try { + List bookings = bookingService.syncBookings(bankAccessEntity, bankAccountEntity, + bankApiMapper.toBankApi(bankApi), loadBookingsRequest.getPin()); + loadBookingsResponse.setBookings(bookingMapper.toBookingTOs(bookings)); + loadBookingsResponse.setBalances(bankAccountEntity.getBalances()); + + return new ResponseEntity<>(loadBookingsResponse, HttpStatus.OK); + } catch (ExternalAuthorisationRequiredException e) { + loadBookingsResponse.setConsent(e.getConsent()); + return new ResponseEntity<>(loadBookingsResponse, HttpStatus.ACCEPTED); + } + } + + @Data + private static class LoadBookingsRequest { + String accessId; + String accountId; + String pin; + } + + @Data + private static class LoadBookingsResponse { + Consent consent; + BalancesReport balances; + List bookings; + } + + @Data + private static class LoadBankAccountsResponse { + Consent consent; + List bankAccounts; + } + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/PaymentController.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/PaymentController.java new file mode 100644 index 000000000..c03ca8559 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/PaymentController.java @@ -0,0 +1,124 @@ +package de.adorsys.multibanking.web; + +import de.adorsys.multibanking.domain.*; +import de.adorsys.multibanking.exception.PaymentException; +import de.adorsys.multibanking.exception.ResourceNotFoundException; +import de.adorsys.multibanking.pers.spi.repository.BankAccessRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.BankAccountRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.SinglePaymentRepositoryIf; +import de.adorsys.multibanking.service.PaymentService; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.Authorization; +import io.swagger.annotations.AuthorizationScope; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import org.springframework.hateoas.Resource; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.security.Principal; + +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; + +@RequiredArgsConstructor +@UserResource +@RestController +@RequestMapping(path = "api/v1/bankaccesses/{accessId}/accounts/{accountId}/payments") +public class PaymentController { + + private final BankAccessRepositoryIf bankAccessRepository; + private final BankAccountRepositoryIf bankAccountRepository; + private final SinglePaymentRepositoryIf paymentRepository; + private final PaymentService paymentService; + private final Principal principal; + + @ApiOperation( + value = "Read payment", + authorizations = { + @Authorization(value = "multibanking_auth", scopes = { + @AuthorizationScope(scope = "openid", description = "") + })}) + @GetMapping("/{paymentId}") + public Resource getPayment(@PathVariable String accessId, @PathVariable String accountId, + @PathVariable String paymentId) { + SinglePaymentEntity paymentEntity = paymentRepository.findByUserIdAndId(principal.getName(), paymentId) + .orElseThrow(() -> new ResourceNotFoundException(SinglePaymentEntity.class, paymentId)); + + return mapToResource(accessId, accountId, paymentEntity); + } + + @ApiOperation( + value = "Create new payment", + authorizations = { + @Authorization(value = "multibanking_auth", scopes = { + @AuthorizationScope(scope = "openid", description = "") + })}) + @PostMapping + public HttpEntity createPayment(@PathVariable String accessId, @PathVariable String accountId, + @RequestBody CreatePaymentRequest paymentRequest) { + BankAccessEntity bankAccessEntity = bankAccessRepository.findByUserIdAndId(principal.getName(), accessId) + .orElseThrow(() -> new ResourceNotFoundException(BankAccessEntity.class, accessId)); + + bankAccountRepository.findByUserIdAndId(principal.getName(), accountId) + .orElseThrow(() -> new ResourceNotFoundException(BankAccountEntity.class, accountId)); + + String pin = paymentRequest.getPin() != null ? paymentRequest.getPin() : bankAccessEntity.getPin(); + if (pin == null) { + throw new PaymentException("required pin not specified"); + } + SinglePaymentEntity payment = paymentService.createSinglePayment(bankAccessEntity, + paymentRequest.getTanTransportType(), paymentRequest.getPin(), paymentRequest.getPayment()); + + HttpHeaders headers = new HttpHeaders(); + headers.setLocation(linkTo(methodOn(PaymentController.class).getPayment(accessId, accountId, payment.getId())).toUri()); + return new ResponseEntity<>(headers, HttpStatus.CREATED); + } + + @ApiOperation( + value = "Submit payment", + authorizations = { + @Authorization(value = "multibanking_auth", scopes = { + @AuthorizationScope(scope = "openid", description = "") + })}) + @PostMapping("/{paymentId}/submit") + public HttpEntity submitPayment(@PathVariable String accessId, @PathVariable String accountId, + @PathVariable String paymentId, + @RequestBody SubmitPaymentRequest paymentRequest) { + BankAccessEntity bankAccessEntity = bankAccessRepository.findByUserIdAndId(principal.getName(), accessId) + .orElseThrow(() -> new ResourceNotFoundException(BankAccessEntity.class, accessId)); + if (!bankAccountRepository.exists(accountId)) { + throw new ResourceNotFoundException(BankAccountEntity.class, accountId); + } + + SinglePaymentEntity paymentEntity = paymentRepository.findByUserIdAndId(principal.getName(), paymentId) + .orElseThrow(() -> new ResourceNotFoundException(SinglePaymentEntity.class, paymentId)); + + paymentService.submitSinglePayment(paymentEntity, bankAccessEntity, paymentRequest.getPin(), + paymentRequest.getTan()); + + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + private Resource mapToResource(@PathVariable String accessId, @PathVariable String accountId, + SinglePaymentEntity paymentEntity) { + return new Resource<>(paymentEntity, + linkTo(methodOn(PaymentController.class).getPayment(accessId, accountId, paymentEntity.getId())).withSelfRel()); + } + + @Data + private static class CreatePaymentRequest { + TanTransportType tanTransportType; + SinglePayment payment; + String pin; + } + + @Data + private static class SubmitPaymentRequest { + String pin; + String tan; + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/StandingOrderController.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/StandingOrderController.java new file mode 100644 index 000000000..347f07260 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/StandingOrderController.java @@ -0,0 +1,62 @@ +package de.adorsys.multibanking.web; + +import de.adorsys.multibanking.domain.BankAccessEntity; +import de.adorsys.multibanking.domain.BankAccount; +import de.adorsys.multibanking.domain.BankAccountEntity; +import de.adorsys.multibanking.domain.StandingOrderEntity; +import de.adorsys.multibanking.exception.ResourceNotFoundException; +import de.adorsys.multibanking.exception.SyncInProgressException; +import de.adorsys.multibanking.pers.spi.repository.BankAccessRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.BankAccountRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.StandingOrderRepositoryIf; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.Authorization; +import io.swagger.annotations.AuthorizationScope; +import lombok.RequiredArgsConstructor; +import org.springframework.hateoas.Resources; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.security.Principal; + +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; + +@RequiredArgsConstructor +@UserResource +@RestController +@RequestMapping(path = "api/v1/bankaccesses/{accessId}/accounts/{accountId}/standingorders") +public class StandingOrderController { + + private final StandingOrderRepositoryIf standingOrderRepository; + private final BankAccessRepositoryIf bankAccessRepository; + private final BankAccountRepositoryIf bankAccountRepository; + private final Principal principal; + + @ApiOperation( + value = "Read account standing orders", + authorizations = { + @Authorization(value = "multibanking_auth", scopes = { + @AuthorizationScope(scope = "openid", description = "") + })}) + @GetMapping + public Resources getStandingOrders(@PathVariable String accessId, + @PathVariable String accountId) { + if (!bankAccessRepository.exists(accessId)) { + throw new ResourceNotFoundException(BankAccessEntity.class, accessId); + } + if (!bankAccountRepository.exists(accountId)) { + throw new ResourceNotFoundException(BankAccountEntity.class, accountId); + } + if (bankAccountRepository.getSyncStatus(accountId) == BankAccount.SyncStatus.SYNC) { + throw new SyncInProgressException(accountId); + } + + return new Resources<>( + standingOrderRepository.findByUserIdAndAccountId(principal.getName(), accountId), + linkTo(methodOn(StandingOrderController.class).getStandingOrders(accessId, accountId)).withSelfRel() + ); + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/UserResource.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/UserResource.java new file mode 100644 index 000000000..9c3cd76dc --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/UserResource.java @@ -0,0 +1,18 @@ +package de.adorsys.multibanking.web; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Marks a resource public so the swagger documentation can be exported. + * + * @author fpo + * + */ +@Target({ java.lang.annotation.ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface UserResource { +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/mapper/AnalyticsMapper.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/mapper/AnalyticsMapper.java new file mode 100644 index 000000000..778a72dc7 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/mapper/AnalyticsMapper.java @@ -0,0 +1,18 @@ +package de.adorsys.multibanking.web.mapper; + +import de.adorsys.multibanking.domain.AccountAnalyticsEntity; +import de.adorsys.multibanking.domain.Contract; +import de.adorsys.multibanking.web.model.AnalyticsTO; +import de.adorsys.multibanking.web.model.ContractTO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +@Mapper(componentModel = "spring") +public interface AnalyticsMapper { + + AnalyticsTO toAnalyticsTO(AccountAnalyticsEntity analyticsEntity); + + @Mapping(target = "id", ignore = true) + ContractTO toContractTO(Contract contract); + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/mapper/BalancesMapper.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/mapper/BalancesMapper.java new file mode 100644 index 000000000..e54ccf6cc --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/mapper/BalancesMapper.java @@ -0,0 +1,15 @@ +package de.adorsys.multibanking.web.mapper; + +import de.adorsys.multibanking.domain.Balance; +import de.adorsys.multibanking.domain.BalancesReport; +import de.adorsys.multibanking.web.model.BalanceTO; +import de.adorsys.multibanking.web.model.BalancesReportTO; +import org.mapstruct.Mapper; + +@Mapper(componentModel = "spring") +public interface BalancesMapper { + + BalancesReportTO toBalancesReportTO(BalancesReport balancesReport); + + BalanceTO toBalancesTO(Balance balance); +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/mapper/BankAccessMapper.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/mapper/BankAccessMapper.java new file mode 100644 index 000000000..e599f0492 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/mapper/BankAccessMapper.java @@ -0,0 +1,21 @@ +package de.adorsys.multibanking.web.mapper; + +import de.adorsys.multibanking.domain.BankAccessEntity; +import de.adorsys.multibanking.web.model.BankAccessTO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +@Mapper(componentModel = "spring") +public interface BankAccessMapper { + + BankAccessTO toBankAccessTO(BankAccessEntity bankAccessEntity); + + @Mapping(target = "tanTransportTypes", ignore = true) + @Mapping(target = "hbciPassportState", ignore = true) + @Mapping(target = "externalIdMap", ignore = true) + @Mapping(target = "userId", ignore = true) + @Mapping(target = "temporary", ignore = true) + BankAccessEntity toBankAccessEntity(BankAccessTO bankAccessTO); + +} + diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/mapper/BankAccountMapper.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/mapper/BankAccountMapper.java new file mode 100644 index 000000000..1c67575bc --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/mapper/BankAccountMapper.java @@ -0,0 +1,16 @@ +package de.adorsys.multibanking.web.mapper; + +import de.adorsys.multibanking.domain.BankAccountEntity; +import de.adorsys.multibanking.web.model.BankAccountTO; +import org.mapstruct.Mapper; + +import java.util.List; + +@Mapper(componentModel = "spring") +public interface BankAccountMapper { + + BankAccountTO toBankAccountTO(BankAccountEntity bankAccountEntity); + + List toBankAccountTOs(List bankAccountEntities); + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/mapper/BankApiMapper.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/mapper/BankApiMapper.java new file mode 100644 index 000000000..a3acd185d --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/mapper/BankApiMapper.java @@ -0,0 +1,12 @@ +package de.adorsys.multibanking.web.mapper; + +import de.adorsys.multibanking.domain.BankApi; +import de.adorsys.multibanking.web.model.BankApiTO; +import org.mapstruct.Mapper; + +@Mapper(componentModel = "spring") +public interface BankApiMapper { + + BankApi toBankApi(BankApiTO bankApiTO); + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/mapper/BankMapper.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/mapper/BankMapper.java new file mode 100644 index 000000000..329967398 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/mapper/BankMapper.java @@ -0,0 +1,11 @@ +package de.adorsys.multibanking.web.mapper; + +import de.adorsys.multibanking.domain.BankEntity; +import de.adorsys.multibanking.web.model.BankTO; +import org.mapstruct.Mapper; + +@Mapper(componentModel = "spring") +public interface BankMapper { + + BankTO toBankTO(BankEntity bankEntity); +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/mapper/BookingMapper.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/mapper/BookingMapper.java new file mode 100644 index 000000000..038859024 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/mapper/BookingMapper.java @@ -0,0 +1,19 @@ +package de.adorsys.multibanking.web.mapper; + +import de.adorsys.multibanking.domain.BookingEntity; +import de.adorsys.multibanking.web.model.BookingTO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +import java.util.List; + +@Mapper(componentModel = "spring") +public interface BookingMapper { + + @Mapping(target = "otherAccount.id", ignore = true) + @Mapping(target = "otherAccount.bankAccessId", ignore = true) + @Mapping(target = "bookingCategory.id", ignore = true) + BookingTO toBookingTO(BookingEntity bookingEntity); + + List toBookingTOs(Iterable bookingEntities); +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/mapper/ContractMapper.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/mapper/ContractMapper.java new file mode 100644 index 000000000..05ca3b9fd --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/mapper/ContractMapper.java @@ -0,0 +1,16 @@ +package de.adorsys.multibanking.web.mapper; + +import de.adorsys.multibanking.domain.ContractEntity; +import de.adorsys.multibanking.web.model.ContractTO; +import org.mapstruct.Mapper; + +import java.util.List; + +@Mapper(componentModel = "spring") +public interface ContractMapper { + + ContractTO toContractTO(ContractEntity contractEntity); + + List toContractTOs(List contractEntities); + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/mapper/RuleMapper.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/mapper/RuleMapper.java new file mode 100644 index 000000000..ce5335952 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/mapper/RuleMapper.java @@ -0,0 +1,21 @@ +package de.adorsys.multibanking.web.mapper; + +import de.adorsys.multibanking.domain.RuleEntity; +import de.adorsys.multibanking.web.model.RuleTO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +import java.util.List; + +@Mapper(componentModel = "spring") +public interface RuleMapper { + + RuleTO toRuleTO(RuleEntity ruleEntity); + + @Mapping(target = "userId", ignore = true) + @Mapping(target = "searchIndex", ignore = true) + RuleEntity toRuleEntity(RuleTO ruleEntity); + + List toRuleTOs(List ruleEntities); + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/AnalyticsTO.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/AnalyticsTO.java new file mode 100644 index 000000000..51526a787 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/AnalyticsTO.java @@ -0,0 +1,13 @@ +package de.adorsys.multibanking.web.model; + +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Data +public class AnalyticsTO { + + private LocalDateTime analyticsDate; + private List bookingGroups; +} diff --git a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/PaymentInitiationBuilderStrategy.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BalanceTO.java similarity index 66% rename from onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/PaymentInitiationBuilderStrategy.java rename to multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BalanceTO.java index e233a351b..18eeb6d9e 100644 --- a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/PaymentInitiationBuilderStrategy.java +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BalanceTO.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2018 adorsys GmbH & Co KG + * Copyright 2018-2019 adorsys GmbH & Co KG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,9 +14,17 @@ * limitations under the License. */ -package de.adorsys.multibanking.xs2a.pis; +package de.adorsys.multibanking.web.model; -public interface PaymentInitiationBuilderStrategy { +import lombok.Data; - PaymentInitiationBodyBuilder resolve(PaymentProductType product, PaymentServiceType service); +import java.math.BigDecimal; +import java.time.LocalDate; + +@Data +public class BalanceTO { + + private LocalDate date; + private BigDecimal amount; + private String currency; } diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BalancesReportTO.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BalancesReportTO.java new file mode 100644 index 000000000..a5f22b689 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BalancesReportTO.java @@ -0,0 +1,22 @@ +package de.adorsys.multibanking.web.model; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +public class BalancesReportTO { + @ApiModelProperty(value = "Ready account balance") + private BalanceTO readyBalance; + + @ApiModelProperty(value = "Unreleased account balance") + private BalanceTO unreadyBalance; + + @ApiModelProperty(value = "Credit balance") + private BalanceTO creditBalance; + + @ApiModelProperty(value = "Available balance") + private BalanceTO availableBalance; + + @ApiModelProperty(value = "Used balance") + private BalanceTO usedBalance; +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BankAccessTO.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BankAccessTO.java new file mode 100644 index 000000000..5b83133a1 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BankAccessTO.java @@ -0,0 +1,49 @@ +package de.adorsys.multibanking.web.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.hateoas.core.Relation; + +@Data +@ApiModel(description = "BankAccess account information", value = "BankAccess") +@Relation(collectionRelation = "bankAccessList") +@JsonIgnoreProperties(value = {"pin", "pin2"}, allowSetters = true) +public class BankAccessTO { + + @ApiModelProperty(value = "Internal bank access id") + private String id; + @ApiModelProperty(value = "Bank login name", required = true, example = "l.name") + private String bankLogin; + @ApiModelProperty(value = "2nd bank login name", example = "1234567890") + private String bankLogin2; + @ApiModelProperty(value = "Bank code", required = true, example = "76070024") + private String bankCode; + @ApiModelProperty(value = "Bank access password") + private String pin; + @ApiModelProperty(value = "Bank access second password") + private String pin2; + + @ApiModelProperty(value = "Store pin") + private boolean storePin; + @ApiModelProperty(value = "Store bookings") + private boolean storeBookings; + @ApiModelProperty(value = "Categorize bookings") + private boolean categorizeBookings; + @ApiModelProperty(value = "Store analytics") + private boolean storeAnalytics; + @ApiModelProperty(value = "Store anonymized bookings") + private boolean storeAnonymizedBookings; + @ApiModelProperty(value = "Provide anonymized bookings for machine learning") + private boolean provideDataForMachineLearning; + + @ApiModelProperty(value = "Bank name", example = "Deutsche Bank") + private String bankName; + + @ApiModelProperty(value = "IBAN", required = true, example = "DE51250400903312345678") + private String iban; + @ApiModelProperty(value = "SCA consent") + private ConsentTO allAcountsConsent; + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BankAccountTO.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BankAccountTO.java new file mode 100644 index 000000000..03ab6a3cb --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BankAccountTO.java @@ -0,0 +1,55 @@ +package de.adorsys.multibanking.web.model; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.hateoas.core.Relation; + +import java.time.LocalDateTime; + +@Data +@Relation(collectionRelation = "bankAccountList") +@ApiModel(description = "The bank account object", value = "BankAccount") +public class BankAccountTO { + + @ApiModelProperty(value = "Account ID") + private String id; + @ApiModelProperty(value = "SCA consent") + private ConsentTO dedicatedConsent; + @ApiModelProperty(value = "Bank account balances") + private BalancesReportTO balances; + @ApiModelProperty(value = "Name of the account owner", example = "EDEKA") + private String owner; + @ApiModelProperty(value = "ISO-2 country of this bank account", example = "DE") + private String country; + @ApiModelProperty(value = "Bank code", example = "29999999") + private String blz; + @ApiModelProperty(value = "Bank name", example = "Mock Bank") + private String bankName; + @ApiModelProperty(value = "Account number", example = "1234567890") + private String accountNumber; + @ApiModelProperty(value = "Type of this bank account", example = "GIRO") + private BankAccountTypeTO type; + @ApiModelProperty(value = "Currency of this bank account", example = "EURO") + private String currency; + @ApiModelProperty(value = "Name of this bank account") + private String name; + @ApiModelProperty(value = "Bank identification code", example = "EDEKDEHHXXX") + private String bic; + @ApiModelProperty(value = "International bank account number", example = "DE50200907003443582071", required = true) + private String iban; + @ApiModelProperty(value = "Synchronisation status", example = "PENDING") + private SyncStatusTO syncStatus; + @ApiModelProperty(value = "Last Synchronisation date", example = "2017-12-01") + private LocalDateTime lastSync; + @ApiModelProperty(value = "Bank Access Id") + private String bankAccessId; + + public enum SyncStatusTO { + PENDING, SYNC, READY + } + + public enum BankAccountTypeTO { + GIRO, SAVINGS, FIXEDTERMDEPOSIT, DEPOT, LOAN, CREDITCARD, BUIILDINGSAVING, INSURANCE, UNKNOWN; + } +} diff --git a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/error/XS2AClientException.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BankApiTO.java old mode 100644 new mode 100755 similarity index 61% rename from onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/error/XS2AClientException.java rename to multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BankApiTO.java index c1aee94d9..0472ac862 --- a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/error/XS2AClientException.java +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BankApiTO.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2018 adorsys GmbH & Co KG + * Copyright 2018-2019 adorsys GmbH & Co KG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,18 +14,11 @@ * limitations under the License. */ -package de.adorsys.multibanking.xs2a.error; +package de.adorsys.multibanking.web.model; -public class XS2AClientException extends RuntimeException { +import io.swagger.annotations.ApiModel; - public XS2AClientException() { - } - - public XS2AClientException(String message) { - super(message); - } - - public XS2AClientException(Throwable cause) { - super(cause); - } +@ApiModel(description = "The banking access backend", value = "BankApi") +public enum BankApiTO { + HBCI, FIGO, FINAPI, XS2A, BANKING_GATEWAY, MOCK, FIGO_ALTERNATIVE, SCREEN_SCRAPPING } diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BankLoginCredentialInfoTO.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BankLoginCredentialInfoTO.java new file mode 100644 index 000000000..ab062950a --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BankLoginCredentialInfoTO.java @@ -0,0 +1,12 @@ +package de.adorsys.multibanking.web.model; + +import lombok.Data; + +@Data +public class BankLoginCredentialInfoTO { + + private String label; + private boolean masked; + private boolean optional; + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BankLoginSettingsTO.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BankLoginSettingsTO.java new file mode 100644 index 000000000..49c4bdf5e --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BankLoginSettingsTO.java @@ -0,0 +1,14 @@ +package de.adorsys.multibanking.web.model; + +import lombok.Data; + +import java.util.List; + +@Data +public class BankLoginSettingsTO { + + private String icon; + private List credentials; + private String auth_type; + private String advice; +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BankTO.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BankTO.java new file mode 100644 index 000000000..f90be1b83 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BankTO.java @@ -0,0 +1,17 @@ +package de.adorsys.multibanking.web.model; + +import lombok.Data; +import org.springframework.hateoas.core.Relation; + +@Relation(collectionRelation = "bankList") +@Data +public class BankTO { + + private String bankingUrl; + private String bankCode; + private String bic; + private String name; + private BankLoginSettingsTO loginSettings; + private BankApiTO bankApi; + private boolean ibanRequired; +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BookingCategoryTO.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BookingCategoryTO.java new file mode 100644 index 000000000..788262d89 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BookingCategoryTO.java @@ -0,0 +1,21 @@ +package de.adorsys.multibanking.web.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.util.Map; +import java.util.Set; + +@Data +@EqualsAndHashCode(callSuper = false) +@NoArgsConstructor +@AllArgsConstructor +public class BookingCategoryTO extends ContractTO { + + private Set rules; + private String receiver; + private Map custom; + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BookingGroupTO.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BookingGroupTO.java new file mode 100644 index 000000000..86ec13fb1 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BookingGroupTO.java @@ -0,0 +1,25 @@ +package de.adorsys.multibanking.web.model; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +@Data +public class BookingGroupTO { + + private Type type; + private String name; + private boolean salaryWage; + private String mainCategory; + private String subCategory; + private String specification; + private String otherAccount; + private BigDecimal amount; + private List bookingPeriods; + private ContractTO contract; + + public enum Type { + STANDING_ORDER, RECURRENT_INCOME, RECURRENT_SEPA, RECURRENT_NONSEPA, CUSTOM, OTHER_INCOME, OTHER_EXPENSES + } +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BookingPeriodTO.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BookingPeriodTO.java new file mode 100644 index 000000000..811dbceef --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BookingPeriodTO.java @@ -0,0 +1,16 @@ +package de.adorsys.multibanking.web.model; + +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.util.List; + +@Data +public class BookingPeriodTO { + + private LocalDate start; + private LocalDate end; + private BigDecimal amount; + private List bookings; +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BookingTO.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BookingTO.java new file mode 100755 index 000000000..a3be9a657 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/BookingTO.java @@ -0,0 +1,91 @@ +package de.adorsys.multibanking.web.model; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.hateoas.core.Relation; + +import java.math.BigDecimal; +import java.time.LocalDate; + +@Data +@Relation(collectionRelation = "bookingList") +@ApiModel(description = "Single bank booking", value = "Booking") +public class BookingTO { + + @ApiModelProperty(value = "Booking ID") + private String id; + + @ApiModelProperty(value = "External ID of this booking") + private String externalId; + + @ApiModelProperty(value = "Opposite bank account") + private BankAccountTO otherAccount; + + @ApiModelProperty(value = "Date on which the transaction gets effective", example = "2018-02-28") + private LocalDate valutaDate; + + @ApiModelProperty(value = "Booking date", example = "2018-02-28") + private LocalDate bookingDate; + + @ApiModelProperty(value = "Target amount", example = "-19.93") + private BigDecimal amount; + + @ApiModelProperty(value = "Currency", example = "EUR") + private String currency; + + @ApiModelProperty(value = "Does this reverses a preexisting booking", example = "false") + private boolean reversal; + + @ApiModelProperty(value = "Account balance after this booking", example = "1849.1") + private BigDecimal balance; + + @ApiModelProperty(value = "Reference of the opposite party", example = "NONREF") + private String customerRef; + + @ApiModelProperty(value = "Reference of the corresponding institution") + private String instRef; + + @ApiModelProperty(value = "Original value", example = "-19.93") + private BigDecimal origValue; + + @ApiModelProperty(value = "Charge value", example = "-19.93") + private BigDecimal chargeValue; + + @ApiModelProperty(value = "Transaction information", example = "KARTENZAHLUNG") + private String text; + + @ApiModelProperty(value = "Additional transaction information", example = "KARTENZAHLUNG") + private String additional; + + @ApiModelProperty(value = "Primanota") + private String primanota; + + @ApiModelProperty(value = "Usage of this transaction", example = "Svwz+2018-02-27t11.47.44 Karte3 2020-12 " + + "Abwa+6850 Edeka//Nuernberg/De") + private String usage; + + @ApiModelProperty + private String addkey; + + @ApiModelProperty(value = "Is this a SEPA transaction", example = "true") + private boolean sepa; + + @ApiModelProperty(value = "Is this a standing order transaction", example = "false") + private boolean standingOrder; + + @ApiModelProperty(value = "Creditor ID", example = "5aab866d31a02a0001f72cd5") + private String creditorId; + + @ApiModelProperty(value = "Reference for the SEPA mandate") + private String mandateReference; + + @ApiModelProperty(value = "Origin of this booking", example = "MOCK") + private BankApiTO bankApi; + + @ApiModelProperty(value = "Category of this booking") + private BookingCategoryTO bookingCategory; + + @ApiModelProperty(value = "Transaction type as DTA Tx Key code", example = "4") + private String transactionCode; +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/ConsentTO.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/ConsentTO.java new file mode 100644 index 000000000..a361c8298 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/ConsentTO.java @@ -0,0 +1,16 @@ +package de.adorsys.multibanking.web.model; + +import lombok.Data; + +@Data +public class ConsentTO { + + private String consentId; + private String authUrl; + private ScaStatusTO scaStatus; + + public enum ScaStatusTO { + STARTED, FINALISED, FAILED + } + +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/ContractTO.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/ContractTO.java new file mode 100644 index 000000000..ddcc86880 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/ContractTO.java @@ -0,0 +1,26 @@ +package de.adorsys.multibanking.web.model; + +import lombok.Data; +import org.springframework.hateoas.core.Relation; + +import java.math.BigDecimal; + +@Relation(collectionRelation = "contractList") +@Data +public class ContractTO { + + private String id; + private String logo; + private String homepage; + private String hotline; + private String email; + private String mandateReference; + private CycleTO interval; + private boolean cancelled; + + private BigDecimal amount; + private String mainCategory; + private String subCategory; + private String specification; + private String provider; +} diff --git a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/executor/UpdateRequestExecutor.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/CycleTO.java similarity index 51% rename from onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/executor/UpdateRequestExecutor.java rename to multibanking-server/src/main/java/de/adorsys/multibanking/web/model/CycleTO.java index 39997ccf0..1dd907bbd 100644 --- a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/executor/UpdateRequestExecutor.java +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/CycleTO.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2018 adorsys GmbH & Co KG + * Copyright 2018-2019 adorsys GmbH & Co KG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,16 +14,39 @@ * limitations under the License. */ -package de.adorsys.multibanking.xs2a.executor; +package de.adorsys.multibanking.web.model; -import de.adorsys.multibanking.domain.request.SubmitAuthorizationCodeRequest; -import de.adorsys.multibanking.xs2a.model.XS2AUpdateRequest; -import de.adorsys.psd2.client.ApiClient; -import de.adorsys.psd2.client.ApiException; - -public interface UpdateRequestExecutor { - - String execute(T request, ApiClient apiClient) throws ApiException; - - T buildRequest(SubmitAuthorizationCodeRequest request); +/** + * The predicted cycles for a category. + */ +public enum CycleTO { + /** + * every week + */ + WEEKLY, + /** + * every 2nd week. + */ + TWO_WEEKLY, + /** + * every month. + */ + MONTHLY, + /** + * every 2nd month. + */ + TWO_MONTHLY, + /** + * every 3 months. + */ + QUARTERLY, + /** + * every 6 months. + */ + HALF_YEARLY, + /** + * every 12 months. + */ + YEARLY, + INVALID } diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/ExecutedBookingTO.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/ExecutedBookingTO.java new file mode 100644 index 000000000..f1a80aae3 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/ExecutedBookingTO.java @@ -0,0 +1,13 @@ +package de.adorsys.multibanking.web.model; + +import lombok.Data; + +import java.time.LocalDate; + +@Data +public class ExecutedBookingTO { + + private String bookingId; + private LocalDate executionDate; + private boolean executed; +} diff --git a/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/RuleTO.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/RuleTO.java new file mode 100644 index 000000000..9fee3ccb6 --- /dev/null +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/RuleTO.java @@ -0,0 +1,30 @@ +package de.adorsys.multibanking.web.model; + +import lombok.Data; +import org.springframework.hateoas.core.Relation; + +@Relation(collectionRelation = "ruleList") +@Data +public class RuleTO { + + private String id; + private String ruleId; + private String mainCategory; + private String subCategory; + private String specification; + private SIMILARITY_MATCH_TYPE similarityMatchType; + private String creditorId; + private String expression; + private String receiver; + private String ruleType; + private String logo; + private String hotline; + private String homepage; + private String email; + private boolean incoming; + + public enum SIMILARITY_MATCH_TYPE { + IBAN, REFERENCE_NAME, PURPOSE + } + +} diff --git a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/PaymentInitiationBodyBuilder.java b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/TanTransportTypeTO.java similarity index 69% rename from onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/PaymentInitiationBodyBuilder.java rename to multibanking-server/src/main/java/de/adorsys/multibanking/web/model/TanTransportTypeTO.java index 8c556395c..267e20b3b 100644 --- a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/PaymentInitiationBodyBuilder.java +++ b/multibanking-server/src/main/java/de/adorsys/multibanking/web/model/TanTransportTypeTO.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2018 adorsys GmbH & Co KG + * Copyright 2018-2019 adorsys GmbH & Co KG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,11 +14,15 @@ * limitations under the License. */ -package de.adorsys.multibanking.xs2a.pis; +package de.adorsys.multibanking.web.model; -import de.adorsys.multibanking.domain.AbstractScaTransaction; +import lombok.Data; -public interface PaymentInitiationBodyBuilder { +@Data +public class TanTransportTypeTO { - T buildBody(AbstractScaTransaction transaction); + private String id; + private String name; + private String medium; + private String inputInfo; } diff --git a/multibanking-server/src/main/resources/application.yml b/multibanking-server/src/main/resources/application.yml new file mode 100755 index 000000000..bce330ab9 --- /dev/null +++ b/multibanking-server/src/main/resources/application.yml @@ -0,0 +1,179 @@ +spring: + application: + name: multibanking-service + main: + allow-bean-definition-overriding: true + http: + encoding: + charset: UTF-8 + enabled: true + force: true + servlet: + multipart: + max-file-size: 10MB + max-request-size: 10MB + jackson.default-property-inclusion: non_null + +server: + port: 8081 + use-forward-headers: true + +management: + context-path: /management + security: + enabled: false + +info: + project: + artifact: #project.artifactId# + name: #project.name# + description: #project.description# + version: #project.version# + timestamp: #maven.build-timestamp# + scmBranch: #scmBranch# + buildnumber: #buildNumber# + +logging: + level: + org.kapott.hbci.comm.CommPinTan: TRACE + pattern: + level: '%X{user}:%X{accessId} %5p' + +defaultBankApi: HBCI + +sts: + audience_name: Multibanking + secret_claim_property_key: user-secret + keymanagement.keystore.password: FEDCBA9876543210 + keymanagement.keystore.name: multibanking-keystore + keymanagement.keystore.alias_prefix: multibanking- + keymanagement.keystore.type: UBER + keymanagement.keystore.keys.encKeyPairs.initialCount: 5 + keymanagement.keystore.keys.encKeyPairs.algo: RSA + keymanagement.keystore.keys.encKeyPairs.sigAlgo: SHA256withRSA + keymanagement.keystore.keys.encKeyPairs.size: 2048 + keymanagement.keystore.keys.encKeyPairs.name: 'STS Multibanking' + keymanagement.keystore.keys.encKeyPairs.validityInterval: 3600000 + keymanagement.keystore.keys.encKeyPairs.legacyInterval: 86400000 + keymanagement.keystore.keys.signKeyPairs.initialCount: 5 + keymanagement.keystore.keys.signKeyPairs.algo: RSA + keymanagement.keystore.keys.signKeyPairs.sigAlgo: SHA256withRSA + keymanagement.keystore.keys.signKeyPairs.size: 2048 + keymanagement.keystore.keys.signKeyPairs.name: 'STS Multibanking' + keymanagement.keystore.keys.signKeyPairs.validityInterval: 3600000 + keymanagement.keystore.keys.signKeyPairs.legacyInterval: 86400000 + keymanagement.keystore.keys.secretKeys.initialCount: 5 + keymanagement.keystore.keys.secretKeys.algo: AES + keymanagement.keystore.keys.secretKeys.size: 256 + keymanagement.keystore.keys.secretKeys.validityInterval: 3600000 + keymanagement.keystore.keys.secretKeys.legacyInterval: 86400000 + keymanagement.rotation.secretKeys.enabled: false + keymanagement.rotation.signKeyPairs.enabled: false + keymanagement.rotation.encKeyPairs.enabled: false + +--- + +spring: + profiles: dev-mongo + profiles.include: + - swagger + - mongo + - sts-enable + - mongo-persistence + - smartanalytics-embedded + autoconfigure.exclude: + - org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration + - org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration + +db_secret: 1234567890123456 +mongo.properties.url: classpath:/mongo.properties + +swagger.login.url: http://localhost:8080/auth/realms/multibanking/protocol/openid-connect/auth + +idp: + baseUrl: http://localhost:8080 + realm: multibanking + +sts: + authservers: + - name: keycloak + issUrl: ${idp.baseUrl}/auth/realms/${idp.realm} + jwksUrl: ${idp.baseUrl}/auth/realms/${idp.realm}/protocol/openid-connect/certs + +--- + +spring: + profiles: dev-jpa + profiles.include: + - swagger + - jpa + - sts-enable + - smartanalytics-remote + datasource: + url: jdbc:postgresql://localhost:5432/mbs + username: mbs + password: mbs + jpa: + hibernate.ddl-auto: update + properties.hibernate.jdbc.lob.non_contextual_creation: true + liquibase: + enabled: false + change-log: classpath:/liquibase/changelog-master.xml + +swagger.login.url: http://localhost:8080/auth/realms/multibanking/protocol/openid-connect/auth + +idp: + baseUrl: http://localhost:8080 + realm: multibanking + +sts: + authservers: + - name: keycloak + issUrl: ${idp.baseUrl}/auth/realms/${idp.realm} + jwksUrl: ${idp.baseUrl}/auth/realms/${idp.realm}/protocol/openid-connect/certs + +--- + +spring: + profiles: dev-sts-disabled + profiles.include: + - swagger + - mongo + - mongo-persistence + - smartanalytics-embedded + +db_secret: 1234567890123456 +mongo.properties.url: classpath:/mongo.properties + +--- + +spring: + profiles: prod + profiles.include: + - swagger + - mongo + - sts-enable + - mongo-persistence + - smartanalytics-embedded + +--- + +spring: + profiles: prod-smartanalytics-remote + profiles.include: + - swagger + - mongo + - sts-enable + - mongo-persistence + - smartanalytics-remote + +--- + +spring: + profiles: prod-sts-disabled + profiles.include: + - swagger + - mongo + - mongo-persistence + - smartanalytics-embedded + diff --git a/multibanking-server/src/main/resources/mongo.properties b/multibanking-server/src/main/resources/mongo.properties new file mode 100755 index 000000000..b9d866602 --- /dev/null +++ b/multibanking-server/src/main/resources/mongo.properties @@ -0,0 +1,2 @@ +mongo.server=localhost +mongo.databaseName=multibanking diff --git a/multibanking-server/src/site/site.xml b/multibanking-server/src/site/site.xml new file mode 100644 index 000000000..951b3e41a --- /dev/null +++ b/multibanking-server/src/site/site.xml @@ -0,0 +1,27 @@ + + + + + + org.apache.maven.skins + maven-fluido-skin + 1.5 + + + + + true + true + + + + + + + + + + + \ No newline at end of file diff --git a/multibanking-server/src/test/java/de/adorsys/multibanking/conf/FongoConfig.java b/multibanking-server/src/test/java/de/adorsys/multibanking/conf/FongoConfig.java new file mode 100644 index 000000000..e2e2c8903 --- /dev/null +++ b/multibanking-server/src/test/java/de/adorsys/multibanking/conf/FongoConfig.java @@ -0,0 +1,52 @@ +package de.adorsys.multibanking.conf; + +import com.mongodb.MongoClient; +import com.mongodb.ServerAddress; +import de.adorsys.smartanalytics.config.EnableSmartanalyticsMongoPersistence; +import de.bwaldvogel.mongo.MongoServer; +import de.bwaldvogel.mongo.backend.memory.MemoryBackend; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.data.mongodb.MongoDbFactory; +import org.springframework.data.mongodb.config.AbstractMongoConfiguration; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.SimpleMongoDbFactory; +import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; + +/** + * Created by alexg on 11.04.17. + */ +@Configuration +@EnableMongoRepositories(basePackages = "de.adorsys.multibanking.mongo.repository") +@EnableSmartanalyticsMongoPersistence +@Profile({"fongo"}) +public class FongoConfig extends AbstractMongoConfiguration { + + @Override + protected String getDatabaseName() { + return "multibanking"; + } + + @Bean + public MongoTemplate mongoTemplate(MongoClient mongoClient) { + return new MongoTemplate(mongoDbFactory(mongoClient)); + } + + @Bean + public MongoDbFactory mongoDbFactory(MongoClient mongoClient) { + return new SimpleMongoDbFactory(mongoClient, "test"); + } + + @Bean(destroyMethod = "shutdown") + public MongoServer mongoServer() { + MongoServer mongoServer = new MongoServer(new MemoryBackend()); + mongoServer.bind(); + return mongoServer; + } + + @Bean(destroyMethod = "close") + public MongoClient mongoClient() { + return new MongoClient(new ServerAddress(mongoServer().getLocalAddress())); + } +} diff --git a/multibanking-server/src/test/java/de/adorsys/multibanking/service/BankAccessServiceTest.java b/multibanking-server/src/test/java/de/adorsys/multibanking/service/BankAccessServiceTest.java new file mode 100644 index 000000000..7e452e5dc --- /dev/null +++ b/multibanking-server/src/test/java/de/adorsys/multibanking/service/BankAccessServiceTest.java @@ -0,0 +1,202 @@ +package de.adorsys.multibanking.service; + +import de.adorsys.multibanking.domain.BankAccessEntity; +import de.adorsys.multibanking.domain.BankEntity; +import de.adorsys.multibanking.domain.UserEntity; +import de.adorsys.multibanking.domain.response.LoadAccountInformationResponse; +import de.adorsys.multibanking.exception.InvalidBankAccessException; +import de.adorsys.multibanking.exception.InvalidPinException; +import de.adorsys.multibanking.exception.ResourceNotFoundException; +import de.adorsys.multibanking.figo.FigoBanking; +import de.adorsys.multibanking.mock.MockBanking; +import de.adorsys.multibanking.pers.spi.repository.BankAccessRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.BankAccountRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.BankRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.UserRepositoryIf; +import de.adorsys.smartanalytics.config.EnableSmartanalytics; +import de.adorsys.smartanalytics.config.EnableSmartanalyticsMongoPersistence; +import de.adorsys.smartanalytics.core.AnalyticsConfigProvider; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.junit4.SpringRunner; + +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.Optional; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; +import static org.springframework.util.Assert.*; + +@RunWith(SpringRunner.class) +@SpringBootTest +@EnableSmartanalytics +@EnableSmartanalyticsMongoPersistence +public class BankAccessServiceTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + @Autowired + private BankRepositoryIf bankRepository; + @Autowired + private UserRepositoryIf userRepository; + @Autowired + private BankAccessRepositoryIf bankAccessRepository; + @Autowired + private BankAccountRepositoryIf bankAccountRepository; + @Autowired + private BankAccessService bankAccessService; + @Autowired + private DeleteExpiredUsersScheduled userScheduler; + @MockBean + private FigoBanking figoBanking; + @MockBean + private MockBanking mockBanking; + @MockBean + private OnlineBankingServiceProducer bankingServiceProducer; + @MockBean + private AnalyticsConfigProvider analyticsConfigProvider; + + @BeforeClass + public static void beforeClass() { + TestConstants.setup(); + } + + @Before + public void beforeTest() { + MockitoAnnotations.initMocks(this); + when(bankingServiceProducer.getBankingService(anyString())).thenReturn(mockBanking); + bankRepository.findByBankCode("76090500").orElseGet(() -> { + BankEntity bankEntity = TestUtil.getBankEntity("Sparda Bank", "76090500"); + bankRepository.save(bankEntity); + return bankEntity; + }); + } + + @Test + public void create_bank_access_not_supported() { + when(mockBanking.bankSupported(anyString())).thenReturn(false); + thrown.expect(InvalidBankAccessException.class); + + BankAccessEntity bankAccessEntity = TestUtil.getBankAccessEntity("login", "access", null, null); + bankAccessEntity.setBankCode("unsupported"); + bankAccessService.createBankAccess("testUserId", bankAccessEntity); + } + + @Test + public void create_bank_access_no_accounts() { + when(mockBanking.bankSupported(anyString())).thenReturn(true); + when(mockBanking.loadBankAccounts(any(), any())).thenReturn(LoadAccountInformationResponse.builder().build()); + thrown.expect(InvalidBankAccessException.class); + + BankAccessEntity bankAccessEntity = TestUtil.getBankAccessEntity("login", "access", "76090500", null); + bankAccessService.createBankAccess("testUserId", bankAccessEntity); + } + + @Test + public void create_bank_access_invalid_pin() { + BankAccessEntity bankAccessEntity = TestUtil.getBankAccessEntity("login", "access", "76090500", null); + + when(mockBanking.bankSupported(anyString())).thenReturn(true); + when(mockBanking.loadBankAccounts(any(), any())) + .thenThrow(new InvalidPinException("access")); + thrown.expect(InvalidPinException.class); + + bankAccessService.createBankAccess("testUserId", bankAccessEntity); + } + + @Test + public void create_bank_access_ok() { + BankAccessEntity bankAccessEntity = TestUtil.getBankAccessEntity("login", "access", "76090500", null); + + when(mockBanking.bankSupported(anyString())).thenReturn(true); + when(mockBanking.loadBankAccounts(any(), any())) + .thenReturn(LoadAccountInformationResponse.builder() + .bankAccounts(Collections.singletonList(TestUtil.getBankAccountEntity("account"))) + .build()); + + bankAccessService.createBankAccess("testUserId", bankAccessEntity); + + notNull(userRepository.findById("login"), "user not exists"); + notNull(bankAccessRepository.findByUserIdAndId("login", "access"), "bankaccess not exists"); + notNull(bankAccountRepository.findByUserIdAndId("login", "account"), "bankaccount not exists"); + } + + @Test + public void when_delete_bankAccesd_user_notExist_should_throw_exception() { + thrown.expect(ResourceNotFoundException.class); + + boolean deleteBankAccess = bankAccessService.deleteBankAccess("badLogin", "badAccess"); + assertThat(deleteBankAccess).isEqualTo(false); + } + + @Test + public void when_delete_bankAcces_user_exist_should_return_false() { + String userId = UUID.randomUUID().toString(); + userRepository.save(TestUtil.getUserEntity(userId)); + + boolean deleteBankAccess = bankAccessService.deleteBankAccess(userId, "access"); + assertThat(deleteBankAccess).isEqualTo(false); + } + + @Test + public void when_delete_bankAcces_user_exist_should_return_true() { + userRepository.save(TestUtil.getUserEntity("login")); + bankAccessRepository.save(TestUtil.getBankAccessEntity("login", "access", null, null)); + + boolean deleteBankAccess = bankAccessService.deleteBankAccess("login", "access"); + assertThat(deleteBankAccess).isEqualTo(true); + } + + @Test + public void get_bank_code() { + BankAccessEntity bankAccessEntity = TestUtil.getBankAccessEntity("login", "access", + "code", null); + + bankAccessRepository.save(bankAccessEntity); + + String bankCode = bankAccessRepository.getBankCode(bankAccessEntity.getId()); + assertThat(bankCode).isEqualTo("code"); + } + + @Test + public void cleaup_users_job() { + UserEntity userEntity1 = TestUtil.getUserEntity("testUser1"); + userEntity1.setExpireUser(LocalDateTime.now()); + + userRepository.save(userEntity1); + + UserEntity userEntity2 = TestUtil.getUserEntity("testUser2"); + userEntity1.setExpireUser(LocalDateTime.now().plusMinutes(1)); + + userRepository.save(userEntity2); + + userScheduler.deleteJob(); + + Optional testUser1 = userRepository.findById("testUser1"); + Optional testUser2 = userRepository.findById("testUser2"); + assertThat(testUser1.isPresent()).isFalse(); + assertThat(testUser2.isPresent()).isTrue(); + } + + @Test + public void searchBank() { + notEmpty(bankRepository.search("76090"), "bank not found"); + isTrue(bankRepository.search("76090500").size() == 1, "wrong search result"); + isTrue(bankRepository.search("XYZ").size() == 0, "wrong search result"); + notEmpty(bankRepository.search("Sparda"), "bank not found"); + notEmpty(bankRepository.search("Sparda Bank"), "bank not found"); + } + +} diff --git a/multibanking-server/src/test/java/de/adorsys/multibanking/service/FigoPaymentTest.java b/multibanking-server/src/test/java/de/adorsys/multibanking/service/FigoPaymentTest.java new file mode 100644 index 000000000..161011063 --- /dev/null +++ b/multibanking-server/src/test/java/de/adorsys/multibanking/service/FigoPaymentTest.java @@ -0,0 +1,89 @@ +package de.adorsys.multibanking.service; + +import de.adorsys.multibanking.domain.*; +import de.adorsys.multibanking.figo.FigoBanking; +import de.adorsys.multibanking.pers.spi.repository.UserRepositoryIf; +import lombok.extern.slf4j.Slf4j; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.junit4.SpringRunner; + +import java.math.BigDecimal; +import java.util.List; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +@Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest +@Ignore +public class FigoPaymentTest { + + @Autowired + private BookingService bookingService; + @Autowired + private BankAccountService bankAccountService; + @Autowired + private PaymentService paymentService; + @Autowired + private UserRepositoryIf userRepository; + + @MockBean + private OnlineBankingServiceProducer bankingServiceProducer; + + @BeforeClass + public static void beforeClass() { + TestConstants.setup(); + + System.setProperty("FIGO_CLIENT_ID", "CdunSr9hi4Q6rL65u-l-coQngofLdNbyjACwFoDOd_OU"); + System.setProperty("FIGO_SECRET", "Sx9FNf1Uze0NZTgXq40ljDeWIpauTJaiZPkhDrc6Vavs"); + System.setProperty("FIGO_TECH_USER", "figo-user"); + System.setProperty("FIGO_TECH_USER_CREDENTIAL", "test123"); + + } + + @Before + public void beforeTest() { + MockitoAnnotations.initMocks(this); + when(bankingServiceProducer.getBankingService(anyString())).thenReturn(new FigoBanking(BankApi.FIGO)); + when(bankingServiceProducer.getBankingService(BankApi.FIGO)).thenReturn(new FigoBanking(BankApi.FIGO)); + when(bankingServiceProducer.getBankingService(BankApi.HBCI)).thenReturn(new FigoBanking(BankApi.FIGO)); + } + + @Test + public void testFigoPayment() throws Exception { + UserEntity userEntity = TestUtil.getUserEntity("test-user-id"); + userRepository.save(userEntity); + + BankAccessEntity bankAccessEntity = TestUtil.getBankAccessEntity("test-user-id", "test-access-id", + System.getProperty("blz"), System.getProperty("pin")); + bankAccessEntity.setBankLogin(System.getProperty("login")); + bankAccessEntity.setCategorizeBookings(false); + bankAccessEntity.setStoreAnalytics(false); + + List bankAccountEntities = bankAccountService.loadBankAccountsOnline(bankAccessEntity, null); + BankAccountEntity bankAccountEntitity = bankAccountEntities.stream() + .filter(bankAccountEntity -> bankAccountEntity.getAccountNumber().equals("12324463")) + .findFirst().get(); + + bookingService.syncBookings(bankAccessEntity, bankAccountEntitity, BankApi.FIGO, System.getProperty("pin")); + + SinglePaymentEntity paymentEntity = new SinglePaymentEntity(); + paymentEntity.setReceiverIban("receiver_iban_needed_here"); + paymentEntity.setReceiver("Alexander Geist"); + paymentEntity.setAmount(new BigDecimal(1)); + paymentEntity.setPurpose("test"); + + paymentService.createSinglePayment(bankAccessEntity, null, System.getProperty("pin"), paymentEntity); + paymentService.submitSinglePayment(paymentEntity, bankAccessEntity, System.getProperty("pin"), + "tan_needed_here"); + } +} diff --git a/multibanking-server/src/test/java/de/adorsys/multibanking/service/HbciBulkPaymentTest.java b/multibanking-server/src/test/java/de/adorsys/multibanking/service/HbciBulkPaymentTest.java new file mode 100644 index 000000000..ad2faf435 --- /dev/null +++ b/multibanking-server/src/test/java/de/adorsys/multibanking/service/HbciBulkPaymentTest.java @@ -0,0 +1,101 @@ +package de.adorsys.multibanking.service; + +import de.adorsys.multibanking.domain.*; +import de.adorsys.multibanking.hbci.Hbci4JavaBanking; +import de.adorsys.multibanking.pers.spi.repository.BankRepositoryIf; +import lombok.extern.slf4j.Slf4j; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.junit4.SpringRunner; + +import java.math.BigDecimal; +import java.util.Collections; +import java.util.List; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +@Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest +@Ignore +public class HbciBulkPaymentTest { + + @Autowired + private BankAccountService bankAccountService; + @Autowired + private PaymentService paymentService; + @Autowired + private BankRepositoryIf bankRepository; + + @MockBean + private OnlineBankingServiceProducer bankingServiceProducer; + + @BeforeClass + public static void beforeClass() { + TestConstants.setup(); + } + + @Before + public void beforeTest() { + MockitoAnnotations.initMocks(this); + when(bankingServiceProducer.getBankingService(anyString())).thenReturn(new Hbci4JavaBanking()); + when(bankingServiceProducer.getBankingService(BankApi.FIGO)).thenReturn(new Hbci4JavaBanking()); + when(bankingServiceProducer.getBankingService(BankApi.HBCI)).thenReturn(new Hbci4JavaBanking()); + + bankRepository.findByBankCode(System.getProperty("blz")).orElseGet(() -> { + BankEntity bankEntity = TestUtil.getBankEntity("Test Bank", System.getProperty("blz")); + bankRepository.save(bankEntity); + return bankEntity; + }); + } + + @Test + public void testPayment() throws Exception { + + BankAccessEntity bankAccessEntity = TestUtil.getBankAccessEntity("test-user-id", "test-access-id", + System.getProperty("blz"), System.getProperty("pin")); + bankAccessEntity.setBankLogin(System.getProperty("login")); + bankAccessEntity.setBankLogin2(System.getProperty("login2")); + bankAccessEntity.setCategorizeBookings(false); + bankAccessEntity.setStoreAnalytics(true); + + List bankAccountEntities = bankAccountService.loadBankAccountsOnline(bankAccessEntity, + BankApi.HBCI); + BankAccountEntity bankAccountEntitity = bankAccountEntities.stream() + .filter(bankAccountEntity -> bankAccountEntity.getAccountNumber().equals(System.getProperty("account"))) + .findFirst().get(); + bankAccountEntitity.setId("test-account-id"); + + SinglePayment payment = new SinglePayment(); + payment.setReceiverIban("DE56760905000002257793"); + payment.setReceiver("Alexander Geist"); + payment.setAmount(new BigDecimal(12.00)); + payment.setPurpose("test130"); + + log.info("------------ " + bankAccessEntity.getTanTransportTypes().get(BankApi.HBCI).get(5).toString()); + + bankAccessEntity.getTanTransportTypes().get(BankApi.HBCI).forEach(tanTransportType -> log.info(tanTransportType.toString())); + + TanTransportType tanTransportType = bankAccessEntity.getTanTransportTypes().get(BankApi.HBCI).get(5); + + BulkPayment bulkPayment = new BulkPayment(); + bulkPayment.setPayments(Collections.singletonList(payment)); + bulkPayment.setDebtorBankAccount(bankAccountEntitity); + + BulkPaymentEntity paymentEntity = paymentService.createBulkPayment(bankAccessEntity, + tanTransportType, System.getProperty("pin"), bulkPayment); + + String tan = ""; + paymentService.submitBulkPayment(paymentEntity, bankAccessEntity, System.getProperty("pin"), tan); + + } + +} diff --git a/multibanking-server/src/test/java/de/adorsys/multibanking/service/HbciSinglePaymentTest.java b/multibanking-server/src/test/java/de/adorsys/multibanking/service/HbciSinglePaymentTest.java new file mode 100644 index 000000000..a797826d9 --- /dev/null +++ b/multibanking-server/src/test/java/de/adorsys/multibanking/service/HbciSinglePaymentTest.java @@ -0,0 +1,148 @@ +package de.adorsys.multibanking.service; + +import de.adorsys.multibanking.domain.*; +import de.adorsys.multibanking.exception.ExternalAuthorisationRequiredException; +import de.adorsys.multibanking.hbci.Hbci4JavaBanking; +import de.adorsys.multibanking.pers.spi.repository.BankRepositoryIf; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.IOException; +import java.math.BigDecimal; +import java.net.URL; +import java.util.List; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +@Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest +@Ignore +public class HbciSinglePaymentTest { + + @Autowired + private BankAccountService bankAccountService; + @Autowired + private PaymentService paymentService; + @Autowired + private BankRepositoryIf bankRepository; + + @MockBean + private OnlineBankingServiceProducer bankingServiceProducer; + @Value("${banks.config.url:classpath:/blz-test.properties}") + private URL banksConfigUrl; + + @BeforeClass + public static void beforeClass() { + TestConstants.setup(); + } + + @Before + public void beforeTest() throws IOException { + Hbci4JavaBanking hbci4JavaBanking = new Hbci4JavaBanking(banksConfigUrl.openStream(), true); + + MockitoAnnotations.initMocks(this); + when(bankingServiceProducer.getBankingService(anyString())).thenReturn(hbci4JavaBanking); + when(bankingServiceProducer.getBankingService(BankApi.FIGO)).thenReturn(hbci4JavaBanking); + when(bankingServiceProducer.getBankingService(BankApi.HBCI)).thenReturn(hbci4JavaBanking); + + bankRepository.findByBankCode(System.getProperty("blz")).orElseGet(() -> { + BankEntity bankEntity = TestUtil.getBankEntity("Test Bank", System.getProperty("blz")); + bankRepository.save(bankEntity); + return bankEntity; + }); + } + + public void testSinglePayment() throws Exception { + BankAccessEntity bankAccessEntity = TestUtil.getBankAccessEntity("test-user-id", "test-access-id", + System.getProperty("blz"), System.getProperty("pin")); + bankAccessEntity.setBankLogin(System.getProperty("login")); + bankAccessEntity.setBankLogin2(System.getProperty("login2")); + bankAccessEntity.setCategorizeBookings(false); + bankAccessEntity.setStoreAnalytics(true); + + List bankAccountEntities = bankAccountService.loadBankAccountsOnline(bankAccessEntity, + BankApi.HBCI); + BankAccountEntity bankAccountEntitity = bankAccountEntities.stream() + .filter(bankAccountEntity -> bankAccountEntity.getAccountNumber().equals(System.getProperty("account"))) + .findFirst().get(); + bankAccountEntitity.setId("test-account-id"); + + TanTransportType tanTransportType = bankAccessEntity.getTanTransportTypes().get(BankApi.HBCI).stream() + .filter(ttt -> StringUtils.containsIgnoreCase(ttt.getName(), "sms")) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("invalid tan transport type")); + + SinglePayment payment = new SinglePayment(); + payment.setReceiverIban("DE56760905000002257793"); + payment.setReceiver("Alexander Geist"); + payment.setAmount(new BigDecimal(12.00)); + payment.setPurpose("test130"); + payment.setDebtorBankAccount(bankAccountEntitity); + + SinglePaymentEntity paymentEntity = paymentService.createSinglePayment(bankAccessEntity, tanTransportType, + System.getProperty("pin"), payment); + + String tan = ""; + paymentService.submitSinglePayment(paymentEntity, bankAccessEntity, System.getProperty("pin"), tan); + } + + @Test + public void testRawPayment() throws ExternalAuthorisationRequiredException { + BankAccessEntity bankAccessEntity = TestUtil.getBankAccessEntity("test-user-id", "test-access-id", + System.getProperty("blz"), System.getProperty("pin")); + bankAccessEntity.setBankLogin(System.getProperty("login")); + bankAccessEntity.setBankLogin2(System.getProperty("login2")); + bankAccessEntity.setCategorizeBookings(false); + bankAccessEntity.setStoreAnalytics(true); + + List bankAccountEntities = bankAccountService.loadBankAccountsOnline(bankAccessEntity, + BankApi.HBCI); + BankAccountEntity bankAccountEntitity = bankAccountEntities.stream() + .filter(bankAccountEntity -> bankAccountEntity.getAccountNumber().equals(System.getProperty("account"))) + .findFirst().get(); + bankAccountEntitity.setId("test-account-id"); + + TanTransportType tanTransportType = bankAccessEntity.getTanTransportTypes().get(BankApi.HBCI).stream() + .filter(ttt -> StringUtils.containsIgnoreCase(ttt.getName(), "sms")) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("invalid tan transport type")); + + String painXml = "2019-01-24T13:10:03:07052019" + + "-01-24T13:10:08112TESTKONTO2019-01-24T13:10:03:0705TRF112SEPA1999-01-01TESTKONTODE51250400903312345678XBANDECGSLEVNOTPROVIDED12Alexander " + + "GeistDE56760905000002257793test130"; + + RawSepaPayment payment = new RawSepaPayment(); + payment.setPainXml(painXml); + payment.setDebtorBankAccount(bankAccountEntitity); + + RawSepaTransactionEntity paymentEntity = paymentService.createSepaRawPayment(bankAccessEntity, + tanTransportType, System.getProperty("pin"), payment); + + String tan = ""; + paymentService.submitRawSepaTransaction(paymentEntity, bankAccessEntity, System.getProperty("pin"), tan); + } + +} diff --git a/multibanking-server/src/test/java/de/adorsys/multibanking/service/HbciStandingOrderTest.java b/multibanking-server/src/test/java/de/adorsys/multibanking/service/HbciStandingOrderTest.java new file mode 100644 index 000000000..37f9db2de --- /dev/null +++ b/multibanking-server/src/test/java/de/adorsys/multibanking/service/HbciStandingOrderTest.java @@ -0,0 +1,116 @@ +package de.adorsys.multibanking.service; + +import de.adorsys.multibanking.domain.*; +import de.adorsys.multibanking.hbci.Hbci4JavaBanking; +import de.adorsys.multibanking.pers.spi.repository.BankRepositoryIf; +import lombok.extern.slf4j.Slf4j; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.junit4.SpringRunner; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.temporal.TemporalAdjusters; +import java.util.List; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +@Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest +@Ignore +public class HbciStandingOrderTest { + + @Autowired + private StandingOrderService standingOrderService; + @Autowired + private BankRepositoryIf bankRepository; + @Autowired + private BankAccountService bankAccountService; + + @MockBean + private OnlineBankingServiceProducer bankingServiceProducer; + + @BeforeClass + public static void beforeClass() { + TestConstants.setup(); + } + + @Before + public void beforeTest() { + MockitoAnnotations.initMocks(this); + when(bankingServiceProducer.getBankingService(anyString())).thenReturn(new Hbci4JavaBanking()); + when(bankingServiceProducer.getBankingService(BankApi.FIGO)).thenReturn(new Hbci4JavaBanking()); + when(bankingServiceProducer.getBankingService(BankApi.HBCI)).thenReturn(new Hbci4JavaBanking()); + + bankRepository.findByBankCode(System.getProperty("blz")).orElseGet(() -> { + BankEntity bankEntity = TestUtil.getBankEntity("Test Bank", System.getProperty("blz")); + bankRepository.save(bankEntity); + return bankEntity; + }); + } + + @Test + public void testListStandingOrders() throws Exception { + BankAccessEntity bankAccessEntity = TestUtil.getBankAccessEntity("test-user-id", "test-access-id", + System.getProperty("blz"), System.getProperty("pin")); + bankAccessEntity.setBankLogin(System.getProperty("login")); + bankAccessEntity.setBankLogin2(System.getProperty("login2")); + bankAccessEntity.setCategorizeBookings(false); + bankAccessEntity.setStoreAnalytics(true); + + List bankAccountEntities = bankAccountService.loadBankAccountsOnline(bankAccessEntity, + BankApi.HBCI); + BankAccountEntity bankAccountEntitity = bankAccountEntities.stream() + .filter(bankAccountEntity -> bankAccountEntity.getAccountNumber().equals(System.getProperty("account"))) + .findFirst().get(); + bankAccountEntitity.setId("test-account-id"); + + //TODO hbci call for standing orders needed + } + + @Test + public void testNewStandingOrder() throws Exception { + BankAccessEntity bankAccessEntity = TestUtil.getBankAccessEntity("test-user-id", "test-access-id", + System.getProperty("blz"), System.getProperty("pin")); + bankAccessEntity.setBankLogin(System.getProperty("login")); + bankAccessEntity.setBankLogin2(System.getProperty("login2")); + bankAccessEntity.setCategorizeBookings(false); + bankAccessEntity.setStoreAnalytics(false); + + List bankAccountEntities = bankAccountService.loadBankAccountsOnline(bankAccessEntity, + BankApi.HBCI); + BankAccountEntity bankAccountEntitity = bankAccountEntities.stream() + .filter(bankAccountEntity -> bankAccountEntity.getAccountNumber().equals("3312345678")) + .findFirst().get(); + + StandingOrder standingOrder = new StandingOrder(); + standingOrder.setOtherAccount(new BankAccount()); + standingOrder.getOtherAccount().setIban("DE56760905000002257793"); + standingOrder.getOtherAccount().setOwner("Alexander Geist"); + standingOrder.setAmount(new BigDecimal(100)); + standingOrder.getOtherAccount().setBic("PBNKDEFF"); + standingOrder.setUsage("Dauerauftrag Test4"); + + standingOrder.setCycle(Cycle.MONTHLY); + standingOrder.setExecutionDay(1); + standingOrder.setFirstExecutionDate(LocalDate.now().plusMonths(1).with(TemporalAdjusters.firstDayOfMonth())); + standingOrder.setLastExecutionDate(LocalDate.now().plusMonths(1).with(TemporalAdjusters.firstDayOfMonth()).plusYears(2)); + standingOrder.setDebtorBankAccount(bankAccountEntitity); + + Object tanSubmit = standingOrderService.createStandingOrder(bankAccessEntity, System.getProperty("pin"), + standingOrder); + + String tan = ""; + standingOrderService.submitStandingOrder(standingOrder, tanSubmit, bankAccessEntity, System.getProperty( + "pin"), tan); + } +} diff --git a/multibanking-server/src/test/java/de/adorsys/multibanking/service/ImportBanks.java b/multibanking-server/src/test/java/de/adorsys/multibanking/service/ImportBanks.java new file mode 100755 index 000000000..6497c90e9 --- /dev/null +++ b/multibanking-server/src/test/java/de/adorsys/multibanking/service/ImportBanks.java @@ -0,0 +1,227 @@ +package de.adorsys.multibanking.service; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import de.adorsys.multibanking.domain.BankApi; +import de.adorsys.multibanking.domain.BankEntity; +import de.adorsys.multibanking.domain.BankLoginCredentialInfo; +import de.adorsys.multibanking.domain.BankLoginSettings; +import org.apache.commons.lang3.StringUtils; +import org.kapott.hbci.manager.HBCIUtils; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.stream.Collectors; + +public class ImportBanks { + + static HashMap> instituteMap = new HashMap<>(); + static HashMap> bank_catalogueMap = new HashMap<>(); + static HashMap> mapping_sucheMap = new HashMap<>(); + static HashMap blz_propertiesMap = new HashMap<>(); + + public static void main(String... args) throws Exception { + read_fints_institute_csv(); + read_bank_catalogue_csv(); + read_mapping_suche_csv(); + read_blz_properties(); + merge(); + } + + private static void read_fints_institute_csv() throws Exception { + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(HBCIUtils.class.getClassLoader().getResource("bank-import/fints_institute.csv").openStream()))) { + reader.readLine(); //skip first line + String line; + while ((line = reader.readLine()) != null) { + String[] values = line.split(";"); + if (values.length > 1) { + instituteMap.put(values[1], new ArrayList<>(Arrays.asList(values))); + } + } + } + } + + private static void read_bank_catalogue_csv() throws Exception { + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(HBCIUtils.class.getClassLoader().getResource("bank-import/bank-catalogue.csv").openStream()))) { + reader.readLine(); //skip first line + String line; + while ((line = reader.readLine()) != null) { + String[] values = line.split(";"); + if (values.length > 1) { + bank_catalogueMap.put(values[0], new ArrayList<>(Arrays.asList(values))); + } + } + } + } + + private static void read_mapping_suche_csv() throws Exception { + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(HBCIUtils.class.getClassLoader().getResource("bank-import/mapping-suche.csv").openStream()))) { + reader.readLine(); //skip first line + String line; + while ((line = reader.readLine()) != null) { + String[] values = line.split(";"); + if (values.length == 6) { + mapping_sucheMap.put(values[3], new ArrayList<>(Arrays.asList(values))); + } + } + } + } + + private static void read_blz_properties() throws Exception { + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(HBCIUtils.class.getClassLoader().getResource("blz.properties").openStream()))) { + String line; + while ((line = reader.readLine()) != null) { + String[] values = line.split("="); + blz_propertiesMap.put(values[0], values[1]); + } + } + + } + + private static void merge() throws IOException { + HashMap mergedMap = new HashMap<>(); + + instituteMap.forEach((s, strings) -> { + BankEntity bankEntity = new BankEntity(); + bankEntity.setBankCode(strings.get(1)); + if (strings.size() > 2) { + bankEntity.setName(strings.get(2)); + if (strings.size() > 3 && StringUtils.isNotBlank(strings.get(3))) { + bankEntity.setName(bankEntity.getName() + " " + strings.get(3)); + } + } + mergedMap.put(s, bankEntity); + }); + + bank_catalogueMap.forEach((s, strings) -> { + BankEntity bankEntity = mergedMap.get(strings.get(0)); + if (bankEntity == null) { + bankEntity = new BankEntity(); + bankEntity.setBankCode(strings.get(0)); + mergedMap.put(s, bankEntity); + } + + bankEntity.setName(strings.get(1)); + + BankLoginSettings loginSettings = new BankLoginSettings(); + bankEntity.setLoginSettings(loginSettings); + loginSettings.setAdvice(strings.get(2)); + + List bankLoginCredentials = new ArrayList<>(); + loginSettings.setCredentials(bankLoginCredentials); + + BankLoginCredentialInfo bankLoginCredential = new BankLoginCredentialInfo(); + if (strings.size() > 4) { + bankLoginCredential.setLabel(strings.get(3)); + bankLoginCredential.setMasked(Boolean.parseBoolean(strings.get(4))); + bankLoginCredentials.add(bankLoginCredential); + + bankLoginCredential = new BankLoginCredentialInfo(); + bankLoginCredential.setLabel(strings.get(5)); + bankLoginCredential.setMasked(Boolean.parseBoolean(strings.get(6))); + bankLoginCredentials.add(bankLoginCredential); + + if (strings.size() > 7) { + bankLoginCredential = new BankLoginCredentialInfo(); + bankLoginCredential.setLabel(strings.get(7)); + bankLoginCredential.setMasked(Boolean.parseBoolean(strings.get(8))); + bankLoginCredentials.add(bankLoginCredential); + } + } + }); + + mapping_sucheMap.forEach((s, strings) -> { + BankEntity bankEntity = mergedMap.get(strings.get(3)); + if (bankEntity == null) { + bankEntity = new BankEntity(); + bankEntity.setBankCode(strings.get(3)); + mergedMap.put(s, bankEntity); + } + + if (StringUtils.isBlank(bankEntity.getName())) { + bankEntity.setName(strings.get(4)); + } + + bankEntity.setSearchIndex(new ArrayList<>()); + bankEntity.getSearchIndex().add(strings.get(3).toLowerCase()); + bankEntity.getSearchIndex().add(strings.get(4).toLowerCase()); + + bankEntity.setBlzHbci(strings.get(0)); + }); + + try (InputStream inputStream = HBCIUtils.class.getClassLoader().getResource("blz.properties").openStream()) { + HBCIUtils.refreshBLZList(inputStream); + HBCIUtils.banks.values().forEach(bankInfo -> { + BankEntity bankEntity = mergedMap.get(bankInfo.getBlz()); + if (bankEntity == null) { + bankEntity = new BankEntity(); + bankEntity.setBankCode(bankInfo.getBlz()); + mergedMap.put(bankInfo.getBlz(), bankEntity); + } + + if (StringUtils.isBlank(bankEntity.getName())) { + bankEntity.setName(parseToUT8(bankInfo.getName())); + } + + bankEntity.setBic(bankInfo.getBic()); + if (bankInfo.getPinTanVersion() != null) { + bankEntity.setBankApi(BankApi.HBCI); + } else { + bankEntity.setBankApi(BankApi.FIGO); + } + }); + } + + mergedMap.values().forEach(bankEntity -> { + if (bankEntity.getSearchIndex() == null || bankEntity.getSearchIndex().size() == 0) { + bankEntity.setSearchIndex(new ArrayList<>()); + if (bankEntity.getName() != null) { + bankEntity.getSearchIndex().add(bankEntity.getName().toLowerCase()); + } + } + + if (!bankEntity.getSearchIndex().contains(bankEntity.getBankCode())) { + bankEntity.getSearchIndex().add(bankEntity.getBankCode()); + } + }); + + final YAMLFactory ymlFactory = new YAMLFactory(); + ObjectMapper objectMapper = new ObjectMapper(ymlFactory); + + try (InputStream inputStream = + HBCIUtils.class.getClassLoader().getResource("bank-import/custom.yml").openStream()) { + List banks = objectMapper.readValue(inputStream, new TypeReference>() { + }); + banks.forEach(bankEntity -> { + mergedMap.put(bankEntity.getBankCode(), bankEntity); + }); + + List bankEntities = mergedMap.values() + .stream() + .sorted((e1, e2) -> e1.getBankCode().compareTo(e2.getBankCode())) + .collect(Collectors.toList()); + + objectMapper.writeValue(new File("/Users/alexg/Downloads/bank-catalogue.yml"), bankEntities); + } + + } + + // fix the encoding issue + private static String parseToUT8(String source) { + if (StringUtils.isNotBlank(source)) { + return new String(source.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); + } else { + return source; + } + } + +} diff --git a/multibanking-server/src/test/java/de/adorsys/multibanking/service/MockBankingTest.java b/multibanking-server/src/test/java/de/adorsys/multibanking/service/MockBankingTest.java new file mode 100644 index 000000000..e380b61f7 --- /dev/null +++ b/multibanking-server/src/test/java/de/adorsys/multibanking/service/MockBankingTest.java @@ -0,0 +1,106 @@ +package de.adorsys.multibanking.service; + +import de.adorsys.multibanking.domain.*; +import de.adorsys.multibanking.exception.ExternalAuthorisationRequiredException; +import de.adorsys.multibanking.mock.MockBanking; +import de.adorsys.multibanking.pers.spi.repository.AnalyticsRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.UserRepositoryIf; +import de.adorsys.smartanalytics.core.RulesService; +import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.security.Security; +import java.util.Optional; + +import static junit.framework.TestCase.fail; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +@Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest +@Ignore +public class MockBankingTest { + + @Autowired + private BookingService bookingService; + @Autowired + private AnalyticsRepositoryIf analyticsRepository; + @Autowired + private UserRepositoryIf userRepository; + @Autowired + private RulesService rulesService; + + @Value("${RULES_URL:file:/Users/alexg/Downloads/rules.csv}") + private File rulesFile; + + @MockBean + private OnlineBankingServiceProducer bankingServiceProducer; + + @BeforeClass + public static void beforeClass() { + TestConstants.setup(); + + System.setProperty("FIGO_CLIENT_ID", "CdunSr9hi4Q6rL65u-l-coQngofLdNbyjACwFoDOd_OU"); + System.setProperty("FIGO_SECRET", "Sx9FNf1Uze0NZTgXq40ljDeWIpauTJaiZPkhDrc6Vavs"); + System.setProperty("FIGO_TECH_USER", "figo-user"); + System.setProperty("FIGO_TECH_USER_CREDENTIAL", "test123"); + System.setProperty("mockConnectionUrl", "https://flip-multibanking-mock-dev.cloud.adorsys.de"); + + Security.addProvider(new BouncyCastleProvider()); + } + + @Before + public void beforeTest() throws IOException { + MockitoAnnotations.initMocks(this); + + when(bankingServiceProducer.getBankingService(anyString())).thenReturn(new MockBanking()); + when(bankingServiceProducer.getBankingService(BankApi.FIGO)).thenReturn(new MockBanking()); + when(bankingServiceProducer.getBankingService(BankApi.HBCI)).thenReturn(new MockBanking()); + when(bankingServiceProducer.getBankingService(BankApi.MOCK)).thenReturn(new MockBanking()); + + rulesService.newRules(rulesFile.getName(), new FileInputStream(rulesFile)); + } + + @Test + public void testSyncBookings() { + UserEntity userEntity = TestUtil.getUserEntity("test-user-id"); + userRepository.save(userEntity); + + BankAccessEntity bankAccessEntity = TestUtil.getBankAccessEntity("test-user-id", "test-access-id", "19999999" + , "12345"); + bankAccessEntity.setBankLogin("m.becker"); + bankAccessEntity.setCategorizeBookings(true); + bankAccessEntity.setStoreAnalytics(true); + + BankAccountEntity bankAccountEntity = TestUtil.getBankAccountEntity("test-account-id"); + bankAccountEntity.setUserId("test-user-id"); + bankAccountEntity.setIban("DE81199999993528307800"); + bankAccountEntity.setAccountNumber("765551851"); + + try { + bookingService.syncBookings(bankAccessEntity, bankAccountEntity, BankApi.MOCK, "12345"); + } catch (ExternalAuthorisationRequiredException e) { + fail(); + } + + Optional analyticsEntity = analyticsRepository.findLastByUserIdAndAccountId("test" + + "-user-id", "test-account-id"); + + log.info(analyticsEntity.get().toString()); + } +} diff --git a/multibanking-server/src/test/java/de/adorsys/multibanking/service/SyncTest.java b/multibanking-server/src/test/java/de/adorsys/multibanking/service/SyncTest.java new file mode 100644 index 000000000..dad6094ec --- /dev/null +++ b/multibanking-server/src/test/java/de/adorsys/multibanking/service/SyncTest.java @@ -0,0 +1,108 @@ +package de.adorsys.multibanking.service; + +import de.adorsys.multibanking.domain.*; +import de.adorsys.multibanking.hbci.Hbci4JavaBanking; +import de.adorsys.multibanking.pers.spi.repository.AnalyticsRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.BankRepositoryIf; +import de.adorsys.smartanalytics.core.RulesService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.IOUtils; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.List; +import java.util.Optional; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +@Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest +@Ignore +public class SyncTest { + + @Autowired + private BookingService bookingService; + @Autowired + private BankRepositoryIf bankRepository; + @Autowired + private BankAccountService bankAccountService; + @Autowired + private AnalyticsRepositoryIf analyticsRepository; + @Autowired + private RulesService rulesService; + + @MockBean + private OnlineBankingServiceProducer bankingServiceProducer; + + @Value("${RULES_URL:file:/Users/alexg/Downloads/rules_base_v4.2x.csv}") + private File rulesFile; + + @BeforeClass + public static void beforeClass() { + TestConstants.setup(); + } + + @Before + public void beforeTest() throws IOException { + String params = "25040090=X-BANK|Köln|HYVEDEM1093|99|https://obs-qa.bv-zahlungssysteme.de|https://obs-qa" + + ".bv-zahlungssysteme.de/hbciTunnel/hbciTransfer.jsp|300|300|"; + + Hbci4JavaBanking hbci4JavaBanking = new Hbci4JavaBanking(IOUtils.toInputStream(params, "ISO-8859-1"), true); +// hbci4JavaBanking = new Hbci4JavaBanking(null); + + MockitoAnnotations.initMocks(this); + when(bankingServiceProducer.getBankingService(anyString())).thenReturn(hbci4JavaBanking); + when(bankingServiceProducer.getBankingService(BankApi.FIGO)).thenReturn(hbci4JavaBanking); + when(bankingServiceProducer.getBankingService(BankApi.HBCI)).thenReturn(hbci4JavaBanking); + + if (rulesFile.exists()) { + rulesService.newRules(rulesFile.getName(), new FileInputStream(rulesFile)); + } + + bankRepository.findByBankCode(System.getProperty("blz")).orElseGet(() -> { + BankEntity bankEntity = TestUtil.getBankEntity("Test Bank", System.getProperty("blz")); + bankRepository.save(bankEntity); + return bankEntity; + }); + } + + @Test + public void testSyncBookings() { + BankAccessEntity bankAccessEntity = TestUtil.getBankAccessEntity("test-user-id", "test-access-id", + System.getProperty("blz"), System.getProperty("pin")); + bankAccessEntity.setBankLogin(System.getProperty("login")); + bankAccessEntity.setBankLogin2(System.getProperty("login2")); + bankAccessEntity.setCategorizeBookings(false); + bankAccessEntity.setStoreAnalytics(true); + + List bankAccountEntities = bankAccountService.loadBankAccountsOnline(bankAccessEntity, + BankApi.HBCI); + BankAccountEntity bankAccountEntity = bankAccountEntities.stream() + .filter(account -> account.getAccountNumber().equals(System.getProperty("account"))) + .findFirst() + .orElseThrow(() -> new RuntimeException("account not found: " + System.getProperty("account"))); + + bankAccountEntity.setId("test-account-id"); + + bookingService.syncBookings(bankAccessEntity, bankAccountEntity, BankApi.HBCI, System.getProperty("pin")); + + Optional analyticsEntity = analyticsRepository.findLastByUserIdAndAccountId("test" + + "-user-id", "test-account-id"); + analyticsEntity.ifPresent(accountAnalyticsEntity -> log.info(accountAnalyticsEntity.toString())); + + } +} diff --git a/multibanking-server/src/test/java/de/adorsys/multibanking/service/TestConstants.java b/multibanking-server/src/test/java/de/adorsys/multibanking/service/TestConstants.java new file mode 100644 index 000000000..9ff7d60cb --- /dev/null +++ b/multibanking-server/src/test/java/de/adorsys/multibanking/service/TestConstants.java @@ -0,0 +1,30 @@ +package de.adorsys.multibanking.service; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import java.lang.reflect.Field; +import java.security.Security; + +public class TestConstants { + public static final void setup() { +// turnOffEncPolicy(); + + System.setProperty("mongo.databaseName", "multibanking"); + System.setProperty("KEYSTORE_PASSWORD", "test123"); + + Security.addProvider(new BouncyCastleProvider()); + } + + public static void turnOffEncPolicy() { + // Warning: do not do this for productive code. Download and install the jce unlimited strength policy file + // see http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html + try { + Field field = Class.forName("javax.crypto.JceSecurity").getDeclaredField("isRestricted"); + field.setAccessible(true); + field.set(null, Boolean.FALSE); + } catch (ClassNotFoundException | NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) { + ex.printStackTrace(System.err); + } + } + +} diff --git a/multibanking-server/src/test/java/de/adorsys/multibanking/service/TestUtil.java b/multibanking-server/src/test/java/de/adorsys/multibanking/service/TestUtil.java new file mode 100644 index 000000000..ac18de71b --- /dev/null +++ b/multibanking-server/src/test/java/de/adorsys/multibanking/service/TestUtil.java @@ -0,0 +1,49 @@ +package de.adorsys.multibanking.service; + +import de.adorsys.multibanking.domain.*; + +import java.time.LocalDateTime; +import java.util.Arrays; + +public class TestUtil { + + public static UserEntity getUserEntity(String id) { + UserEntity userEntity = new UserEntity(); + userEntity.setId(id); + userEntity.setExpireUser(LocalDateTime.now().plusMinutes(120)); + return userEntity; + } + + public static BankAccessEntity getBankAccessEntity(String userId, String id, String bankCode, String pin) { + BankAccessEntity bankAccessEntity = new BankAccessEntity(); + bankAccessEntity.setUserId(userId); + bankAccessEntity.setId(id); + bankAccessEntity.setBankCode(bankCode); + bankAccessEntity.setPin(pin); + return bankAccessEntity; + } + + public static BankAccountEntity getBankAccountEntity(String id) { + BankAccountEntity bankAccountEntity = new BankAccountEntity(); + bankAccountEntity.setId(id); + return bankAccountEntity; + } + + public static BookingEntity getBookingEntity(String userId, String accountId, BankApi bankApi) { + BookingEntity bookingEntity = new BookingEntity(); + bookingEntity.setUserId(userId); + bookingEntity.setAccountId(accountId); + bookingEntity.setBankApi(bankApi); + return bookingEntity; + } + + public static BankEntity getBankEntity(String name, String bankCode) { + BankEntity bankEntity = new BankEntity(); + bankEntity.setName(name); + bankEntity.setBankCode(bankCode); + bankEntity.setSearchIndex(Arrays.asList(name.toLowerCase(), bankCode)); + return bankEntity; + + } + +} diff --git a/multibanking-server/src/test/java/de/adorsys/multibanking/service/XS2ATest.java b/multibanking-server/src/test/java/de/adorsys/multibanking/service/XS2ATest.java new file mode 100644 index 000000000..483e22b02 --- /dev/null +++ b/multibanking-server/src/test/java/de/adorsys/multibanking/service/XS2ATest.java @@ -0,0 +1,108 @@ +package de.adorsys.multibanking.service; + +import de.adorsys.multibanking.bg.BankingGatewayAdapter; +import de.adorsys.multibanking.domain.*; +import de.adorsys.multibanking.exception.ResourceNotFoundException; +import de.adorsys.multibanking.pers.spi.repository.AnalyticsRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.BankRepositoryIf; +import de.adorsys.multibanking.pers.spi.repository.UserRepositoryIf; +import de.adorsys.smartanalytics.core.RulesService; +import lombok.extern.slf4j.Slf4j; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.List; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +@Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest +@Ignore +public class XS2ATest { + + @Autowired + private BookingService bookingService; + @Autowired + private BankRepositoryIf bankRepository; + @Autowired + private UserRepositoryIf userRepository; + @Autowired + private BankAccountService bankAccountService; + @Autowired + private AnalyticsRepositoryIf analyticsRepository; + @Autowired + private RulesService rulesService; + + @MockBean + private OnlineBankingServiceProducer bankingServiceProducer; + + @Value("${RULES_URL:file:/Users/alexg/Downloads/rules_base_v4.2.csv}") + private File rulesFile; + + @BeforeClass + public static void beforeClass() { + TestConstants.setup(); + } + + @Before + public void beforeTest() throws IOException { + MockitoAnnotations.initMocks(this); + when(bankingServiceProducer.getBankingService(anyString())).thenReturn(new BankingGatewayAdapter()); + when(bankingServiceProducer.getBankingService(BankApi.FIGO)).thenReturn(new BankingGatewayAdapter()); + when(bankingServiceProducer.getBankingService(BankApi.HBCI)).thenReturn(new BankingGatewayAdapter()); + + if (rulesFile.exists()) { + rulesService.newRules(rulesFile.getName(), new FileInputStream(rulesFile)); + } + + bankRepository.findByBankCode(System.getProperty("blz")).orElseGet(() -> { + BankEntity bankEntity = TestUtil.getBankEntity("Test Bank", System.getProperty("blz")); + bankRepository.save(bankEntity); + return bankEntity; + }); + } + + @Test + public void testSyncBookings() throws Exception { + UserEntity userEntity = TestUtil.getUserEntity("test-user-id"); + userRepository.save(userEntity); + + BankAccessEntity bankAccessEntity = TestUtil.getBankAccessEntity("test-user-id", "test-access-id", + System.getProperty("blz"), System.getProperty("pin")); + bankAccessEntity.setBankLogin(System.getProperty("login")); + bankAccessEntity.setCategorizeBookings(false); + bankAccessEntity.setStoreAnalytics(true); + + List bankAccountEntities = bankAccountService.loadBankAccountsOnline(bankAccessEntity, + BankApi.HBCI); + + BankAccountEntity bankAccountEntitity = bankAccountEntities.stream() + .filter(bankAccountEntity -> bankAccountEntity.getAccountNumber().equals(System.getProperty("account"))) + .findFirst() + .orElseThrow(() -> new ResourceNotFoundException(BankAccountEntity.class, System.getProperty("account"))); + + bankAccountEntitity.setId("test-account-id"); + + bookingService.syncBookings(bankAccessEntity, bankAccountEntitity, BankApi.HBCI, System.getProperty("pin")); + + AccountAnalyticsEntity analyticsEntity = analyticsRepository.findLastByUserIdAndAccountId("test-user-id", + "test-account-id") + .orElseThrow(() -> new ResourceNotFoundException(AccountAnalyticsEntity.class, "test-account-id")); + + log.info(analyticsEntity.toString()); + } +} diff --git a/multibanking-server/src/test/resources/application.yml b/multibanking-server/src/test/resources/application.yml new file mode 100644 index 000000000..73b62f8d7 --- /dev/null +++ b/multibanking-server/src/test/resources/application.yml @@ -0,0 +1,73 @@ +spring: + main: + allow-bean-definition-overriding: true + profiles.include: + - fongo + - sts-enable + - smartanalytics-embedded + autoconfigure.exclude: + - org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration + - org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration + +server: + port: 8887 + +logging: + level: + org.kapott.hbci.comm.CommPinTan: TRACE + +sts: + audience_name: sts-service-component-example + secret_claim_property_key: secretClaim + authservers: + - name: keycloak-playground + issUrl: https://keycloak:8080/auth/realms/moped + jwksUrl: https://keycloak:8080/auth/realms/moped/protocol/openid-connect/certs + refreshIntervalSeconds: 600 + persistence: + lockExpiry: 30000 + keymanagement: + rotation: + checkInterval: 60000 + encKeyPairs: + minKeys: 5 + enabled: true + signKeyPairs: + minKeys: 5 + enabled: true + secretKeys: + minKeys: 5 + enabled: true +# persistence: +# containerName: adsts-container +# password: 0123456789ABCDEF + keystore: + password: FEDCBA9876543210 + type: UBER + name: mbs-keystore + alias_prefix: mbs-keystore- + keys: + encKeyPairs: + initialCount: 5 + algo: RSA + sigAlgo: SHA256withRSA + size: 2048 + name: Adorsys MBS ENC + validityInterval: 3600000 + legacyInterval: 86400000 + signKeyPairs: + initialCount: 5 + algo: RSA + sigAlgo: SHA256withRSA + size: 2048 + name: Adorsys MBS SIGN + validityInterval: 3600000 + legacyInterval: 86400000 + secretKeys: + initialCount: 5 + algo: AES + size: 256 + validityInterval: 3600000 + legacyInterval: 86400000 + +db_secret: 1234567890ABCDEF diff --git a/onlinebanking-xs2a/pom.xml b/onlinebanking-xs2a/pom.xml deleted file mode 100644 index 8c42699f8..000000000 --- a/onlinebanking-xs2a/pom.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - 4.0.0 - - de.adorsys.multibanking - multibanking - 4.6.2-SNAPSHOT - - - onlinebanking-xs2a - jar - - - - de.adorsys.multibanking - onlinebanking-facade - 4.6.2-SNAPSHOT - - - de.adorsys.multibanking - xs2a-adapter - 1.3 - - - org.slf4j - slf4j-api - - - org.apache.commons - commons-lang3 - - - de.adorsys.envutils - envutils4j - - - org.iban4j - iban4j - - - - - junit - junit - test - - - org.mockito - mockito-core - test - - - org.assertj - assertj-core - test - - - - - - - maven-compiler-plugin - - - - diff --git a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/MissingConsentException.java b/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/MissingConsentException.java deleted file mode 100644 index cfc0fe12e..000000000 --- a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/MissingConsentException.java +++ /dev/null @@ -1,9 +0,0 @@ -package de.adorsys.multibanking.xs2a; - -public class MissingConsentException extends RuntimeException { - private static final long serialVersionUID = 4655354574351100460L; - - public MissingConsentException(String message) { - super(message); - } -} diff --git a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/XS2ABanking.java b/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/XS2ABanking.java deleted file mode 100644 index e9a2f2680..000000000 --- a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/XS2ABanking.java +++ /dev/null @@ -1,655 +0,0 @@ -package de.adorsys.multibanking.xs2a; - -import com.squareup.okhttp.OkHttpClient; -import com.squareup.okhttp.logging.HttpLoggingInterceptor; -import de.adorsys.multibanking.domain.*; -import de.adorsys.multibanking.domain.exception.MultibankingException; -import de.adorsys.multibanking.domain.request.*; -import de.adorsys.multibanking.domain.response.*; -import de.adorsys.multibanking.domain.spi.OnlineBankingService; -import de.adorsys.multibanking.xs2a.error.XS2AClientException; -import de.adorsys.multibanking.xs2a.executor.ConsentUpdateRequestExecutor; -import de.adorsys.multibanking.xs2a.executor.PaymentUpdateRequestExecutor; -import de.adorsys.multibanking.xs2a.executor.UpdateRequestExecutor; -import de.adorsys.multibanking.xs2a.model.XS2AUpdateRequest; -import de.adorsys.multibanking.xs2a.model.Xs2aTanSubmit; -import de.adorsys.multibanking.xs2a.pis.PaymentInitiationBuilderStrategy; -import de.adorsys.multibanking.xs2a.pis.PaymentInitiationBuilderStrategyImpl; -import de.adorsys.multibanking.xs2a.pis.PaymentProductType; -import de.adorsys.multibanking.xs2a.pis.PaymentServiceType; -import de.adorsys.psd2.client.ApiClient; -import de.adorsys.psd2.client.ApiException; -import de.adorsys.psd2.client.api.AccountInformationServiceAisApi; -import de.adorsys.psd2.client.api.PaymentInitiationServicePisApi; -import de.adorsys.psd2.client.model.AccountReference; -import de.adorsys.psd2.client.model.Balance; -import de.adorsys.psd2.client.model.*; -import domain.Xs2aBankApiUser; -import org.apache.commons.lang3.StringUtils; -import org.iban4j.Iban; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocketFactory; -import java.math.BigDecimal; -import java.security.NoSuchAlgorithmException; -import java.time.LocalDate; -import java.util.*; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -import static de.adorsys.multibanking.domain.AbstractScaTransaction.TransactionType.DEDICATED_CONSENT; - -public class XS2ABanking implements OnlineBankingService { - - public static final String PSU_IP_ADDRESS = "127.0.0.1"; - static final String SCA_AUTHENTICATION_METHOD_ID = "authenticationMethodId"; - static final String SCA_NAME = "name"; - static final String SCA_AUTHENTICATION_VERSION = "authenticationVersion"; - static final String SCA_EXPLANATION = "explanation"; - static final String SCA_METHODS = "scaMethods"; - static final String CHALLENGE_DATA = "data"; - static final String CHALLENGE_OTP_FORMAT = "otpFormat"; - static final String CHALLENGE_ADDITIONAL_INFORMATION = "additionalInformation"; - static final String CHALLENGE = "challengeData"; - private static final Logger logger = LoggerFactory.getLogger(XS2ABanking.class); - private SSLSocketFactory sslSocketFactory; - private PaymentInitiationBuilderStrategy initiationBuilderStrategy = new PaymentInitiationBuilderStrategyImpl(); - - public XS2ABanking() { - this(defaultSslSocketFactory()); - } - - public XS2ABanking(SSLSocketFactory sslSocketFactory) { - this.sslSocketFactory = sslSocketFactory; - } - - private static SSLSocketFactory defaultSslSocketFactory() { - SSLContext sslContext; - try { - sslContext = SSLContext.getDefault(); - } catch (NoSuchAlgorithmException e) { - throw new IllegalStateException(e); - } - return sslContext.getSocketFactory(); - } - - private static BankAccount toBankAccount(AccountReference reference) { - String iban = reference.getIban(); - BankAccount bankAccount = new BankAccount(); - bankAccount.setIban(iban); - bankAccount.setAccountNumber(Iban.valueOf(iban).getAccountNumber()); - bankAccount.setBalances(new BalancesReport()); - return bankAccount; - } - - @Override - public BankApi bankApi() { - return BankApi.XS2A; - } - - @Override - public boolean externalBankAccountRequired() { - return false; - } - - @Override - public boolean userRegistrationRequired() { - return false; - } - - @Override - public BankApiUser registerUser(String bankingUrl, BankAccess bankAccess, String pin) { - return null; - } - - @Override - public void removeUser(String bankingUrl, BankApiUser bankApiUser) { - throw new UnsupportedOperationException(); - } - - @Override - public ScaMethodsResponse authenticatePsu(String bankingUrl, AuthenticatePsuRequest authenticatePsuRequest) { - ApiClient apiClient = createApiClient(bankingUrl); - - if (authenticatePsuRequest.getPaymentId() != null && authenticatePsuRequest.getConsentId() != null) { - throw new IllegalArgumentException("Either payment or consent id should be set"); - } - if (authenticatePsuRequest.getPaymentId() != null) { - return authenticatePsuForPayment(authenticatePsuRequest, apiClient); - } - if (authenticatePsuRequest.getConsentId() != null) { - return authenticatePsuForAccountInformationConsent(authenticatePsuRequest, apiClient); - } - throw new IllegalArgumentException("Neither payment nor consent id was set"); - } - - @SuppressWarnings("unchecked") - private ScaMethodsResponse authenticatePsuForPayment(AuthenticatePsuRequest authenticatePsuRequest, - ApiClient apiClient) { - PaymentInitiationServicePisApi service = createPaymentInitiationServicePisApi(apiClient); - - UUID xRequestId = UUID.randomUUID(); - String paymentId = authenticatePsuRequest.getPaymentId(); - String corporateId = authenticatePsuRequest.getCustomerId(); - String psuId = authenticatePsuRequest.getLogin(); - String password = authenticatePsuRequest.getPin(); - UpdatePsuAuthentication psuBody = buildUpdatePsuAuthorisationBody(password); - - try { - StartScaprocessResponse response; - response = service.startPaymentAuthorisation(authenticatePsuRequest.getPaymentService(), - authenticatePsuRequest.getPaymentProduct(), paymentId, - xRequestId, psuId, - null, null, null, null, null, null, PSU_IP_ADDRESS, - null, null, null, null, null, null, null, null, null); - String authorisationId = getAuthorizationId(response); - Map updatePsu = - (Map) service.updatePaymentPsuData(authenticatePsuRequest.getPaymentService() - , authenticatePsuRequest.getPaymentProduct(), paymentId, authorisationId, xRequestId, psuBody, - null, null, null, psuId, null, corporateId, - null, PSU_IP_ADDRESS, null, null, - null, null, null, - null, null, null, null); - return buildPsuAuthenticationResponse(updatePsu, authorisationId); - } catch (ApiException e) { - logger.error("Authorise PSU failed", e); - throw new XS2AClientException(e); - } - } - - PaymentInitiationServicePisApi createPaymentInitiationServicePisApi(ApiClient apiClient) { - return new PaymentInitiationServicePisApi(apiClient); - } - - @SuppressWarnings("unchecked") - private ScaMethodsResponse authenticatePsuForAccountInformationConsent( - AuthenticatePsuRequest authenticatePsuRequest, ApiClient apiClient) { - AccountInformationServiceAisApi ais = createAccountInformationServiceAisApi(apiClient); - String consentId = authenticatePsuRequest.getConsentId(); - String psuId = authenticatePsuRequest.getLogin(); - String corporatePsuId = authenticatePsuRequest.getCustomerId(); - StartScaprocessResponse startScaprocessResponse; - try { - startScaprocessResponse = ais.startConsentAuthorisation(consentId, - UUID.randomUUID(), null, null, null, psuId, null, corporatePsuId, null, PSU_IP_ADDRESS, null, null, - null, null, null, null, null, null, null); - } catch (ApiException e) { - logger.error("Failed to start consent authorisation", e); - throw new XS2AClientException(e); - } - - String authorisationId = getAuthorizationId(startScaprocessResponse); - Object body = buildUpdatePsuAuthorisationBody(authenticatePsuRequest.getPin()); - - Object response; - try { - response = ais.updateConsentsPsuData(consentId, authorisationId, UUID.randomUUID(), body, null, null, null, - psuId, null, corporatePsuId, null, PSU_IP_ADDRESS, null, null, null, null, null, null, null, null, - null); - } catch (ApiException e) { - logger.error("Failed to update consent authorisation", e); - throw new XS2AClientException(e); - } - - return buildPsuAuthenticationResponse((Map) response, authorisationId); - } - - AccountInformationServiceAisApi createAccountInformationServiceAisApi(ApiClient apiClient) { - return new AccountInformationServiceAisApi(apiClient); - } - - private UpdatePsuAuthentication buildUpdatePsuAuthorisationBody(String password) { - UpdatePsuAuthentication updatePsuAuthentication = new UpdatePsuAuthentication(); - updatePsuAuthentication.psuData(new PsuData() - .password(password)); - return updatePsuAuthentication; - } - - @SuppressWarnings("unchecked") - private String getAuthorizationId(StartScaprocessResponse response) { - Map> links = response.getLinks(); - - String link = links.get("startAuthorisationWithPsuAuthentication").get("href"); - if (StringUtils.isBlank(link)) { - link = links.get("startAuthorisationWithPsuIdentification").get("href"); - } - if (StringUtils.isNotBlank(link)) { - int index = link.lastIndexOf('/') + 1; - return link.substring(index); - } - throw new XS2AClientException("authorisation id was not found in the response"); - } - - @SuppressWarnings("unchecked") - private ScaMethodsResponse buildPsuAuthenticationResponse(Map response, String authorisationId) { - List transportTypes = ((List>) response.getOrDefault(SCA_METHODS, - Collections.emptyList())) - .stream() - .map(this::createTanType) - .collect(Collectors.toList()); - return ScaMethodsResponse.builder() - .authorizationId(authorisationId) - .tanTransportTypes(transportTypes).build(); - } - - private TanTransportType createTanType(Map map) { - return new TanTransportType(map.get(SCA_AUTHENTICATION_METHOD_ID), map.get(SCA_NAME), - map.get(SCA_AUTHENTICATION_VERSION), map.get(SCA_EXPLANATION)); - } - - @Override - public LoadAccountInformationResponse loadBankAccounts(String bankingUrl, - LoadAccountInformationRequest loadAccountInformationRequest) { - AccountInformationServiceAisApi ais = new AccountInformationServiceAisApi(createApiClient(bankingUrl)); - - String consentId = retrieveConsentId(loadAccountInformationRequest.getBankApiUser(), null); - try { - AccountList accountList = ais.getAccountList(UUID.randomUUID(), consentId, false, - null, null, null, - null, PSU_IP_ADDRESS, null, null, - null, null, - null, null, null, null); - - return LoadAccountInformationResponse.builder() - .bankAccounts(accountList.getAccounts() - .stream() - .map(XS2AMapping::toBankAccount) - .collect(Collectors.toList())) - .build(); - } catch (ApiException e) { - throw new MultibankingException(e); - } - } - - @Override - public void removeBankAccount(String bankingUrl, BankAccount bankAccount, BankApiUser bankApiUser) { - throw new UnsupportedOperationException(); - } - - @Override - public LoadBookingsResponse loadBookings(String bankingUrl, LoadBookingsRequest loadBookingsRequest) { - String iban = loadBookingsRequest.getBankAccount().getIban(); - String consentId = retrieveConsentId(loadBookingsRequest.getBankApiUser(), iban); - - AccountInformationServiceAisApi ais = new AccountInformationServiceAisApi(createApiClient(bankingUrl)); - String resourceId = loadBookingsRequest.getBankAccount().getExternalIdMap().get(BankApi.XS2A); - LocalDate dateFrom = loadBookingsRequest.getDateFrom(); - LocalDate dateTo = loadBookingsRequest.getDateTo(); - try { - TransactionsResponse200Json transactionList = ais.getTransactionList( - resourceId, "booked", UUID.randomUUID(), - consentId, dateFrom, dateTo, null, null, - null, null, null, null, null, - PSU_IP_ADDRESS, null, null, null, - null, null, null, null, - null); - - return LoadBookingsResponse.builder() - .bookings(XS2AMapping.toBookings(transactionList)) - .build(); - - } catch (ApiException e) { - throw new MultibankingException(e); - } - } - - @Override - public List loadBalances(String bankingUrl, LoadBalanceRequest loadBalanceRequest) { - List bankAccounts = loadBalanceRequest.getBankAccounts(); - if (bankAccounts.isEmpty()) { - return Collections.emptyList(); - } - if (bankAccounts.size() > 1) { - logger.warn("Only first bank account will be processed"); - } - //todo: load balances for list of accounts - BankAccount account = bankAccounts.get(0); - String accountId = account.getExternalIdMap().get(BankApi.XS2A); - UUID xRequestId = UUID.randomUUID(); - - String consentId = retrieveConsentId(loadBalanceRequest.getBankApiUser(), account.getIban()); - - AccountInformationServiceAisApi ais = createAccountInformationServiceAisApi(createApiClient(bankingUrl)); - - try { - ReadAccountBalanceResponse200 balances = ais.getBalances(accountId, xRequestId, consentId, null, - null, null, - PSU_IP_ADDRESS, null, null, - null, null, - null, null, - null, null, null); - return Collections.singletonList(convertToBankAccount(balances)); - } catch (ApiException e) { - logger.error("Loading balances failed", e); - throw new XS2AClientException(e); - } - } - - private String retrieveConsentId(BankApiUser bankApiUser, String iban) { - Optional consent; - if (bankApiUser instanceof Xs2aBankApiUser) { - consent = Optional.ofNullable(((Xs2aBankApiUser) bankApiUser).getConsentId()); - } else { - // deprecated version - consent = Optional.ofNullable(bankApiUser.getProperties().get("consentId-" + iban)); - } - return consent.orElseThrow(() -> new MissingConsentException("missing consent for transactions request")); - - } - - private BankAccount convertToBankAccount(ReadAccountBalanceResponse200 balances) { - BankAccount bankAccount = toBankAccount(balances.getAccount()); - BalancesReport balancesReport = bankAccount.getBalances(); - - for (Balance balance : balances.getBalances()) { - BalanceType balanceType = balance.getBalanceType(); - switch (balanceType) { - case CLOSINGBOOKED: - balancesReport.setReadyBalance(toMultibankingBalance(balance)); - break; - case EXPECTED: - balancesReport.setUnreadyBalance(toMultibankingBalance(balance)); - break; - default: - logger.warn("Unexpected {} balance", balanceType); - } - } - - return bankAccount; - } - - private de.adorsys.multibanking.domain.Balance toMultibankingBalance(Balance balance) { - BigDecimal amount = new BigDecimal(balance.getBalanceAmount().getAmount()); - String currency = balance.getBalanceAmount().getCurrency(); - LocalDate referenceDate = balance.getReferenceDate(); - - return new de.adorsys.multibanking.domain.Balance(referenceDate, amount, currency); - } - - @Override - public boolean bankSupported(String bankCode) { - return true; - } - - @Override - public boolean bookingsCategorized() { - return false; - } - - @SuppressWarnings("unchecked") - @Override - public InitiatePaymentResponse initiatePayment(String bankingUrl, TransactionRequest paymentRequest) { - UUID xRequestId = UUID.randomUUID(); - AbstractScaTransaction payment = paymentRequest.getTransaction(); - PaymentProductType paymentProduct = PaymentProductType.resolve(payment.getProduct()); - PaymentServiceType paymentService = PaymentServiceType.resolve(payment); - String contentType = "application/" + (paymentProduct.isRaw() ? "xml" : "json"); - String psuId = paymentRequest.getBankAccess().getBankLogin(); - Object paymentBody; - paymentBody = initiationBuilderStrategy.resolve(paymentProduct, paymentService).buildBody(payment); - ApiClient apiClient = createApiClient(bankingUrl, contentType); - PaymentInitiationServicePisApi initiationService = createPaymentInitiationServicePisApi(apiClient); - - try { - Map response = (Map) initiationService.initiatePayment( - paymentBody, - paymentService.getType(), - paymentProduct.getType(), - xRequestId, - PSU_IP_ADDRESS, - null, null, null, psuId, null, null, - null, null, null, null, null, - null, null, null, null, null, - null, null, null, null, null); - - return getInitiatePaymentResponse(response); - - } catch (ApiException e) { - logger.error("Initiate payment failed", e); - throw new XS2AClientException(e); - } - } - - @Override - public void executeTransactionWithoutSca(String bankingUrl, TransactionRequest paymentRequest) { - throw new UnsupportedOperationException(); - } - - @SuppressWarnings("unchecked") - private InitiatePaymentResponse getInitiatePaymentResponse(Map response) { - String transactionStatus = (String) response.get("transactionStatus"); - String paymentId = (String) response.get("paymentId"); - Map> links = (Map>) response.get("_links"); - String scaRedirect = links.get("scaRedirect") != null ? links.get("scaRedirect").get("href") : null; - return new InitiatePaymentResponse(transactionStatus, paymentId, scaRedirect); - } - - @Override - public AuthorisationCodeResponse requestAuthorizationCode(String bankingUrl, TransactionRequest request) { - ApiClient apiClient = createApiClient(bankingUrl); - if (request.getTransaction().getTransactionType() == DEDICATED_CONSENT) { - return requestAuthorizationCodeForAccountInformationConsent(request, apiClient); - } - return requestAuthorizationCodeForPayment(bankingUrl, request, apiClient); - } - - @SuppressWarnings("unchecked") - private AuthorisationCodeResponse requestAuthorizationCodeForPayment(String bankingUrl, - TransactionRequest paymentRequest, - ApiClient apiClient) { - PaymentInitiationServicePisApi service = createPaymentInitiationServicePisApi(apiClient); - - AbstractScaTransaction payment = paymentRequest.getTransaction(); - PaymentProductType paymentProduct = PaymentProductType.resolve(payment.getProduct()); - PaymentServiceType paymentService = PaymentServiceType.resolve(payment); - - String paymentId = payment.getPaymentId(); - String authorisationId = paymentRequest.getAuthorisationId(); - - UUID xRequestId = UUID.randomUUID(); - String psuId = paymentRequest.getBankAccess().getBankLogin(); - String corporateId = paymentRequest.getBankAccess().getBankLogin2(); - SelectPsuAuthenticationMethod body = - buildSelectPsuAuthenticationMethod(paymentRequest.getTanTransportType().getId()); - Xs2aTanSubmit tanSubmit = new Xs2aTanSubmit(bankingUrl, paymentId, authorisationId, psuId, corporateId); - tanSubmit.setPaymentProduct(paymentProduct.getType()); - tanSubmit.setPaymentService(paymentService.getType()); - - try { - Map updatePsuData = - (Map) service.updatePaymentPsuData(paymentService.getType(), - paymentProduct.getType(), - paymentId, authorisationId, - xRequestId, body, - null, null, - null, psuId, - null, corporateId, - null, PSU_IP_ADDRESS, - null, null, - null, null, - null, null, - null, null, null); - - return buildAuthorisationCodeResponse(updatePsuData, tanSubmit); - } catch (ApiException e) { - logger.error("Initiate payment failed", e); - throw new XS2AClientException(e); - } - } - - @SuppressWarnings("unchecked") - private AuthorisationCodeResponse buildAuthorisationCodeResponse(Map updatePsuData, - Xs2aTanSubmit tanSubmit) { - AuthorisationCodeResponse response = new AuthorisationCodeResponse(); - response.setTanSubmit(tanSubmit); - Map map = (Map) updatePsuData.get(CHALLENGE); - TanChallenge challenge = new TanChallenge(); - challenge.setData(map.get(CHALLENGE_DATA)); - challenge.setFormat(map.get(CHALLENGE_OTP_FORMAT)); - challenge.setTitle(map.get(CHALLENGE_ADDITIONAL_INFORMATION)); - response.setChallenge(challenge); - return response; - } - - private SelectPsuAuthenticationMethod buildSelectPsuAuthenticationMethod(String methodId) { - SelectPsuAuthenticationMethod selectPsuAuthenticationMethod = new SelectPsuAuthenticationMethod(); - selectPsuAuthenticationMethod.setAuthenticationMethodId(methodId); - return selectPsuAuthenticationMethod; - } - - @SuppressWarnings("unchecked") - private AuthorisationCodeResponse requestAuthorizationCodeForAccountInformationConsent(TransactionRequest request, - ApiClient apiClient) { - AccountInformationServiceAisApi ais = createAccountInformationServiceAisApi(apiClient); - String consentId = request.getTransaction().getOrderId(); - String authorisationId = request.getAuthorisationId(); - Object body = buildSelectPsuAuthenticationMethod(request.getTanTransportType().getId()); - String psuId = request.getBankAccess().getBankLogin(); - String psuCorporateId = request.getBankAccess().getBankLogin2(); - Object response; - try { - response = ais.updateConsentsPsuData(consentId, authorisationId, UUID.randomUUID(), body, null, null, null, - psuId, null, psuCorporateId, null, PSU_IP_ADDRESS, null, null, null, null, null, null, null, null, - null); - } catch (ApiException e) { - logger.error("Failed to request authorization code", e); - throw new XS2AClientException(e); - } - - return buildAuthorisationCodeResponse((Map) response, new Xs2aTanSubmit(apiClient.getBasePath(), - consentId, authorisationId, psuId, psuCorporateId)); - } - - @SuppressWarnings("unchecked") - @Override - public SubmitAuthorizationCodeResponse submitAuthorizationCode(SubmitAuthorizationCodeRequest submitPaymentRequest) { - Xs2aTanSubmit tanSubmit = (Xs2aTanSubmit) submitPaymentRequest.getTanSubmit(); - String bankingUrl = tanSubmit.getBankingUrl(); - ApiClient apiClient = createApiClient(bankingUrl); - - UpdateRequestExecutor executor = createUpdateRequestExecutor(submitPaymentRequest); - XS2AUpdateRequest request = executor.buildRequest(submitPaymentRequest); - try { - SubmitAuthorizationCodeResponse response = new SubmitAuthorizationCodeResponse(); - response.setTransactionId(executor.execute(request, apiClient)); - return response; - } catch (ApiException e) { - logger.error("Submit authorisation code failed", e); - throw new XS2AClientException(e); - } - } - - UpdateRequestExecutor createUpdateRequestExecutor(SubmitAuthorizationCodeRequest submitPaymentRequest) { - UpdateRequestExecutor executor; - if (submitPaymentRequest.getSepaTransaction().getTransactionType() == DEDICATED_CONSENT) { - executor = new ConsentUpdateRequestExecutor(); - } else { - executor = new PaymentUpdateRequestExecutor(); - } - return executor; - } - - @Override - public boolean accountInformationConsentRequired() { - return true; - } - - @Override - public CreateConsentResponse createAccountInformationConsent(String bankingUrl, - CreateConsentRequest createConsentRequest) { - AccountInformationServiceAisApi ais = new AccountInformationServiceAisApi(createApiClient(bankingUrl)); - - Consents consents = toConsents(createConsentRequest); - BankAccess bankAccess = createConsentRequest.getBankAccess(); - ConsentsResponse201 response; - try { - response = ais.createConsent(UUID.randomUUID(), consents, null, null, null, bankAccess.getBankLogin(), null, - bankAccess.getBankLogin2(), null, "false", null, null, null, PSU_IP_ADDRESS, null, null, null, null, - null, null, null, null, null); - } catch (ApiException e) { - logger.error("Create consent failed", e); - throw new XS2AClientException(e); - } - - // The consent object to be retrieved by the GET Consent Request will contain the adjusted date - // NextGenPSD2 Access to Account Interoperability Framework - Implementation Guidelines V1.3_20181019.pdf - // 6.4.1.1 - String consentId = response.getConsentId(); - ConsentInformationResponse200Json consentInformation; - try { - consentInformation = ais.getConsentInformation(consentId, UUID.randomUUID(), null, null, null, - PSU_IP_ADDRESS, null, null, null, null, null, null, null, null, null); - } catch (ApiException e) { - logger.error("Get consent failed", e); - throw new XS2AClientException(e); - } - - @SuppressWarnings("unchecked") - Map> links = response.getLinks(); - String scaRedirect = links.get("scaRedirect") != null ? links.get("scaRedirect").get("href") : null; - - return CreateConsentResponse.builder() - .consentId(consentId) - .validUntil(consentInformation.getValidUntil()) - .authorisationUrl(scaRedirect) - .build(); - } - - private Consents toConsents(CreateConsentRequest request) { - Consents consents = new Consents(); - consents.setAccess(toAccountAccess(request)); - consents.setRecurringIndicator(request.isRecurringIndicator()); - consents.setValidUntil(request.getValidUntil()); - consents.setFrequencyPerDay(request.getFrequencyPerDay()); - consents.setCombinedServiceIndicator(request.isCombinedServiceIndicator()); - return consents; - } - - private AccountAccess toAccountAccess(CreateConsentRequest request) { - AccountAccess accountAccess = new AccountAccess(); - accountAccess.setAccounts(toAccountReferences(request.getAccounts())); - accountAccess.setBalances(toAccountReferences(request.getBalances())); - accountAccess.setTransactions(toAccountReferences(request.getTransactions())); - return accountAccess; - } - - private List toAccountReferences(List accounts) { - ArrayList accountReferences = new ArrayList<>(); - for (BankAccount account : accounts) { - AccountReference accountReference = new AccountReference(); - accountReference.setIban(account.getIban()); - accountReference.setCurrency(account.getCurrency()); - accountReferences.add(accountReference); - } - return accountReferences; - } - - private ApiClient createApiClient(String bankingUrl, String contentType) { - ApiClient apiClient = new ApiClient() { - @Override - public String selectHeaderContentType(String[] contentTypes) { - return Optional.ofNullable(contentType) - .orElseGet(() -> super.selectHeaderContentType(contentTypes)); - } - }; - - OkHttpClient client = new OkHttpClient(); - client.interceptors().add( - new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY) - ); - client.setReadTimeout(600, TimeUnit.SECONDS); - client.setSslSocketFactory(sslSocketFactory); - apiClient.setHttpClient(client); - Optional.ofNullable(bankingUrl) - .ifPresent(apiClient::setBasePath); - - return apiClient; - } - - ApiClient createApiClient(String bankingUrl) { - return createApiClient(bankingUrl, null); - } - -} diff --git a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/XS2AMapping.java b/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/XS2AMapping.java deleted file mode 100644 index c46d381d8..000000000 --- a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/XS2AMapping.java +++ /dev/null @@ -1,59 +0,0 @@ -package de.adorsys.multibanking.xs2a; - -import de.adorsys.multibanking.domain.BankAccount; -import de.adorsys.multibanking.domain.BankApi; -import de.adorsys.multibanking.domain.Booking; -import de.adorsys.psd2.client.model.AccountDetails; -import de.adorsys.psd2.client.model.TransactionDetails; -import de.adorsys.psd2.client.model.TransactionsResponse200Json; - -import java.math.BigDecimal; -import java.util.HashMap; -import java.util.List; -import java.util.stream.Collectors; - -import static de.adorsys.multibanking.domain.BankAccountType.fromXS2AType; - -public class XS2AMapping { - - public static BankAccount toBankAccount(AccountDetails accountDetails) { - BankAccount bankAccount = new BankAccount(); - bankAccount.bic(accountDetails.getBic()); - bankAccount.currency(accountDetails.getCurrency()); - bankAccount.iban(accountDetails.getIban()); - bankAccount.owner(accountDetails.getDetails()); - bankAccount.name(accountDetails.getName()); - bankAccount.type(fromXS2AType(accountDetails.getCashAccountType())); - - bankAccount.setExternalIdMap(new HashMap<>()); - bankAccount.getExternalIdMap().put(BankApi.XS2A, accountDetails.getResourceId()); - return bankAccount; - } - - public static List toBookings(TransactionsResponse200Json transactionList) { - return transactionList.getTransactions().getBooked() - .stream() - .map(XS2AMapping::toBooking) - .collect(Collectors.toList()); - } - - private static Booking toBooking(TransactionDetails transactionDetails) { - Booking booking = new Booking(); - booking.setBankApi(BankApi.XS2A); - booking.setBookingDate(transactionDetails.getBookingDate()); - booking.setValutaDate(transactionDetails.getValueDate()); - booking.setAmount(new BigDecimal(transactionDetails.getTransactionAmount().getAmount())); - booking.setCurrency(transactionDetails.getTransactionAmount().getCurrency()); - booking.setExternalId(transactionDetails.getEndToEndId()); - booking.setUsage(transactionDetails.getRemittanceInformationUnstructured()); - - if (transactionDetails.getCreditorName() != null || transactionDetails.getDebtorName() != null) { - BankAccount bankAccount = new BankAccount(); - bankAccount.setOwner(transactionDetails.getCreditorName() != null ? transactionDetails.getCreditorName() - : transactionDetails.getDebtorName()); - booking.setOtherAccount(bankAccount); - } - - return booking; - } -} diff --git a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/executor/AbstractUpdateRequestExecutor.java b/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/executor/AbstractUpdateRequestExecutor.java deleted file mode 100644 index c9fcf5f85..000000000 --- a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/executor/AbstractUpdateRequestExecutor.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2018-2018 adorsys GmbH & Co KG - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package de.adorsys.multibanking.xs2a.executor; - -import de.adorsys.multibanking.domain.request.SubmitAuthorizationCodeRequest; -import de.adorsys.multibanking.xs2a.XS2ABanking; -import de.adorsys.multibanking.xs2a.model.XS2AUpdateRequest; -import de.adorsys.multibanking.xs2a.model.Xs2aTanSubmit; -import de.adorsys.psd2.client.model.TransactionAuthorisation; - -import java.util.UUID; - -abstract class AbstractUpdateRequestExecutor implements UpdateRequestExecutor { - @Override - public final T buildRequest(SubmitAuthorizationCodeRequest request) { - Xs2aTanSubmit tanSubmit = (Xs2aTanSubmit) request.getTanSubmit(); - T updateRequest = createRequest(tanSubmit); - updateRequest.setAuthorisationId(tanSubmit.getAuthorisationId()); - updateRequest.setPsuId(tanSubmit.getPsuId()); - updateRequest.setPsuCorporateId(tanSubmit.getPsuCorporateId()); - updateRequest.setPsuIpAddress(XS2ABanking.PSU_IP_ADDRESS); - updateRequest.setRequestId(UUID.randomUUID()); - updateRequest.setBody(buildTransactionAuthorisation(request.getTan())); - return updateRequest; - } - - abstract T createRequest(Xs2aTanSubmit tanSubmit); - - private TransactionAuthorisation buildTransactionAuthorisation(String tan) { - TransactionAuthorisation transactionAuthorisation = new TransactionAuthorisation(); - transactionAuthorisation.setScaAuthenticationData(tan); - return transactionAuthorisation; - } -} diff --git a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/executor/ConsentUpdateRequestExecutor.java b/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/executor/ConsentUpdateRequestExecutor.java deleted file mode 100644 index 4295b0e55..000000000 --- a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/executor/ConsentUpdateRequestExecutor.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2018-2018 adorsys GmbH & Co KG - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package de.adorsys.multibanking.xs2a.executor; - -import de.adorsys.multibanking.xs2a.model.ConsentXS2AUpdateRequest; -import de.adorsys.multibanking.xs2a.model.Xs2aTanSubmit; -import de.adorsys.psd2.client.ApiClient; -import de.adorsys.psd2.client.ApiException; -import de.adorsys.psd2.client.api.AccountInformationServiceAisApi; - -public class ConsentUpdateRequestExecutor extends AbstractUpdateRequestExecutor { - @Override - public String execute(ConsentXS2AUpdateRequest req, ApiClient apiClient) throws ApiException { - AccountInformationServiceAisApi ais = createAisClient(apiClient); - String consentId = req.getConsentId(); - ais.updateConsentsPsuData(consentId, req.getAuthorisationId(), req.getRequestId(), req.getBody(), null, - null, null, req.getPsuId(), null, - req.getPsuCorporateId(), null, req.getPsuIpAddress(), null, - null, null, null, null, - null, null, null, null); - return consentId; - } - - @Override - ConsentXS2AUpdateRequest createRequest(Xs2aTanSubmit tanSubmit) { - ConsentXS2AUpdateRequest request = new ConsentXS2AUpdateRequest(); - request.setConsentId(tanSubmit.getTransactionId()); - return request; - } - - AccountInformationServiceAisApi createAisClient(ApiClient apiClient) { - return new AccountInformationServiceAisApi(apiClient); - } -} \ No newline at end of file diff --git a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/executor/PaymentUpdateRequestExecutor.java b/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/executor/PaymentUpdateRequestExecutor.java deleted file mode 100644 index 4cb3748f5..000000000 --- a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/executor/PaymentUpdateRequestExecutor.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2018-2018 adorsys GmbH & Co KG - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package de.adorsys.multibanking.xs2a.executor; - -import de.adorsys.multibanking.xs2a.model.PaymentXS2AUpdateRequest; -import de.adorsys.multibanking.xs2a.model.Xs2aTanSubmit; -import de.adorsys.psd2.client.ApiClient; -import de.adorsys.psd2.client.ApiException; -import de.adorsys.psd2.client.api.PaymentInitiationServicePisApi; - -public class PaymentUpdateRequestExecutor extends AbstractUpdateRequestExecutor { - @Override - public String execute(PaymentXS2AUpdateRequest req, ApiClient apiClient) throws ApiException { - PaymentInitiationServicePisApi pis = createApiClient(apiClient); - pis.updatePaymentPsuData(req.getService(), req.getProduct(), req.getPaymentId(), req.getAuthorisationId(), - req.getRequestId(), req.getBody(), null, null, null, - req.getPsuId(), null, req.getPsuCorporateId(), null, - req.getPsuIpAddress(), null, null, null, - null, null, null, null, - null, null); - return req.getPaymentId(); - } - - @Override - PaymentXS2AUpdateRequest createRequest(Xs2aTanSubmit tanSubmit) { - PaymentXS2AUpdateRequest request = new PaymentXS2AUpdateRequest(); - request.setPaymentId(tanSubmit.getTransactionId()); - request.setService(tanSubmit.getPaymentService()); - request.setProduct(tanSubmit.getPaymentProduct()); - return request; - } - - PaymentInitiationServicePisApi createApiClient(ApiClient apiClient) { - return new PaymentInitiationServicePisApi(apiClient); - } -} \ No newline at end of file diff --git a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/model/ConsentXS2AUpdateRequest.java b/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/model/ConsentXS2AUpdateRequest.java deleted file mode 100644 index af0803473..000000000 --- a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/model/ConsentXS2AUpdateRequest.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2018-2018 adorsys GmbH & Co KG - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package de.adorsys.multibanking.xs2a.model; - -import java.util.Objects; - -public class ConsentXS2AUpdateRequest extends XS2AUpdateRequest { - private String consentId; - - public String getConsentId() { - return consentId; - } - - public void setConsentId(String consentId) { - this.consentId = consentId; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - if (!super.equals(o)) return false; - ConsentXS2AUpdateRequest that = (ConsentXS2AUpdateRequest) o; - return Objects.equals(consentId, that.consentId); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), consentId); - } - - @Override - public String toString() { - return "ConsentXS2AUpdateRequest{" + - "consentId='" + consentId + '\'' + - '}'; - } -} diff --git a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/model/PaymentXS2AUpdateRequest.java b/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/model/PaymentXS2AUpdateRequest.java deleted file mode 100644 index 6277e8a9e..000000000 --- a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/model/PaymentXS2AUpdateRequest.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2018-2018 adorsys GmbH & Co KG - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package de.adorsys.multibanking.xs2a.model; - -import java.util.Objects; - -public class PaymentXS2AUpdateRequest extends XS2AUpdateRequest { - private String paymentId; - private String service; - private String product; - - public String getService() { - return service; - } - - public void setService(String service) { - this.service = service; - } - - public String getProduct() { - return product; - } - - public void setProduct(String product) { - this.product = product; - } - - public String getPaymentId() { - return paymentId; - } - - public void setPaymentId(String paymentId) { - this.paymentId = paymentId; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - if (!super.equals(o)) return false; - PaymentXS2AUpdateRequest that = (PaymentXS2AUpdateRequest) o; - return Objects.equals(paymentId, that.paymentId) && - Objects.equals(service, that.service) && - Objects.equals(product, that.product); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), paymentId, service, product); - } - - @Override - public String toString() { - return "PaymentXS2AUpdateRequest{" + - "paymentId='" + paymentId + '\'' + - ", service='" + service + '\'' + - ", product='" + product + '\'' + - '}'; - } -} diff --git a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/model/XS2AUpdateRequest.java b/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/model/XS2AUpdateRequest.java deleted file mode 100644 index 1b8703e2c..000000000 --- a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/model/XS2AUpdateRequest.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2018-2018 adorsys GmbH & Co KG - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package de.adorsys.multibanking.xs2a.model; - -import java.util.Objects; -import java.util.UUID; - -public abstract class XS2AUpdateRequest { - private String authorisationId; - private UUID requestId; - private Object body; - private String psuId; - private String psuCorporateId; - private String psuIpAddress; - - public String getAuthorisationId() { - return authorisationId; - } - - public void setAuthorisationId(String authorisationId) { - this.authorisationId = authorisationId; - } - - public UUID getRequestId() { - return requestId; - } - - public void setRequestId(UUID requestId) { - this.requestId = requestId; - } - - public Object getBody() { - return body; - } - - public void setBody(Object body) { - this.body = body; - } - - public String getPsuId() { - return psuId; - } - - public void setPsuId(String psuId) { - this.psuId = psuId; - } - - public String getPsuCorporateId() { - return psuCorporateId; - } - - public void setPsuCorporateId(String psuCorporateId) { - this.psuCorporateId = psuCorporateId; - } - - public String getPsuIpAddress() { - return psuIpAddress; - } - - public void setPsuIpAddress(String psuIpAddress) { - this.psuIpAddress = psuIpAddress; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - XS2AUpdateRequest that = (XS2AUpdateRequest) o; - return Objects.equals(authorisationId, that.authorisationId) && - Objects.equals(requestId, that.requestId) && - Objects.equals(body, that.body) && - Objects.equals(psuId, that.psuId) && - Objects.equals(psuCorporateId, that.psuCorporateId) && - Objects.equals(psuIpAddress, that.psuIpAddress); - } - - @Override - public int hashCode() { - return Objects.hash(authorisationId, requestId, body, psuId, psuCorporateId, psuIpAddress); - } - - @Override - public String toString() { - return "XS2AUpdateRequest{" + - "authorisationId='" + authorisationId + '\'' + - ", requestId='" + requestId + '\'' + - ", body=" + body + - ", psuId='" + psuId + '\'' + - ", psuCorporateId='" + psuCorporateId + '\'' + - ", psuIpAddress='" + psuIpAddress + '\'' + - '}'; - } -} diff --git a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/model/Xs2aTanSubmit.java b/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/model/Xs2aTanSubmit.java deleted file mode 100644 index ff5da5458..000000000 --- a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/model/Xs2aTanSubmit.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2018-2018 adorsys GmbH & Co KG - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package de.adorsys.multibanking.xs2a.model; - -public class Xs2aTanSubmit { - private String bankingUrl; - private String transactionId; - private String paymentProduct; - private String paymentService; - private String authorisationId; - private String psuId; - private String psuCorporateId; - - public Xs2aTanSubmit() { - } - - public Xs2aTanSubmit(String bankingUrl, String transactionId, String authorisationId, String psuId, - String psuCorporateId) { - this.bankingUrl = bankingUrl; - this.transactionId = transactionId; - this.authorisationId = authorisationId; - this.psuId = psuId; - this.psuCorporateId = psuCorporateId; - } - - public String getBankingUrl() { - return bankingUrl; - } - - public void setBankingUrl(String bankingUrl) { - this.bankingUrl = bankingUrl; - } - - public String getTransactionId() { - return transactionId; - } - - public void setTransactionId(String transactionId) { - this.transactionId = transactionId; - } - - public String getAuthorisationId() { - return authorisationId; - } - - public void setAuthorisationId(String authorisationId) { - this.authorisationId = authorisationId; - } - - public String getPsuId() { - return psuId; - } - - public void setPsuId(String psuId) { - this.psuId = psuId; - } - - public String getPsuCorporateId() { - return psuCorporateId; - } - - public void setPsuCorporateId(String psuCorporateId) { - this.psuCorporateId = psuCorporateId; - } - - public String getPaymentProduct() { - return paymentProduct; - } - - public void setPaymentProduct(String paymentProduct) { - this.paymentProduct = paymentProduct; - } - - public String getPaymentService() { - return paymentService; - } - - public void setPaymentService(String paymentService) { - this.paymentService = paymentService; - } -} diff --git a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/PainPaymentInitiationBodyBuilder.java b/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/PainPaymentInitiationBodyBuilder.java deleted file mode 100644 index 8608bebcc..000000000 --- a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/PainPaymentInitiationBodyBuilder.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2018-2018 adorsys GmbH & Co KG - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package de.adorsys.multibanking.xs2a.pis; - -import de.adorsys.multibanking.domain.AbstractScaTransaction; - - -public class PainPaymentInitiationBodyBuilder implements PaymentInitiationBodyBuilder { - @Override - public byte[] buildBody(AbstractScaTransaction transaction) { - return transaction.getRawData().getBytes(); - } -} diff --git a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/PaymentInitiationBuilderStrategyImpl.java b/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/PaymentInitiationBuilderStrategyImpl.java deleted file mode 100644 index 2d195ce96..000000000 --- a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/PaymentInitiationBuilderStrategyImpl.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2018-2018 adorsys GmbH & Co KG - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package de.adorsys.multibanking.xs2a.pis; - -import de.adorsys.multibanking.xs2a.error.XS2AClientException; -import de.adorsys.multibanking.xs2a.pis.sepa.SepaBulkPaymentInitiationBodyBuilder; -import de.adorsys.multibanking.xs2a.pis.sepa.SepaPeriodicPaymentInitiationBodyBuilder; -import de.adorsys.multibanking.xs2a.pis.sepa.SepaSinglePaymentInitiationBodyBuilder; - -import java.util.HashMap; -import java.util.Map; - -import static de.adorsys.multibanking.xs2a.pis.PaymentProductType.SEPA; -import static de.adorsys.multibanking.xs2a.pis.PaymentServiceType.*; - -public class PaymentInitiationBuilderStrategyImpl implements PaymentInitiationBuilderStrategy { - - private static final String BUILDER_NOT_FOUND_ERROR_MESSAGE = "Can't find payment initiation builder for product %s and service %s"; - private Map builders; - private PaymentInitiationBodyBuilder rawPaymentBuilder = new PainPaymentInitiationBodyBuilder(); - - public PaymentInitiationBuilderStrategyImpl() { - builders = new HashMap<>(3); - builders.put(buildKey(SEPA, SINGLE), new SepaSinglePaymentInitiationBodyBuilder()); - builders.put(buildKey(SEPA, BULK), new SepaBulkPaymentInitiationBodyBuilder()); - builders.put(buildKey(SEPA, PERIODIC), new SepaPeriodicPaymentInitiationBodyBuilder()); - } - - private String buildKey(PaymentProductType productType, PaymentServiceType serviceType) { - return productType.getType() + serviceType.getType(); - } - - @Override - public PaymentInitiationBodyBuilder resolve(PaymentProductType product, PaymentServiceType service) { - if (product.isRaw()) { - return rawPaymentBuilder; - } - String key = buildKey(product, service); - - if (!builders.containsKey(key)) { - throw new XS2AClientException(String.format(BUILDER_NOT_FOUND_ERROR_MESSAGE, product, service)); - } - - return builders.get(key); - } -} diff --git a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/PaymentProductType.java b/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/PaymentProductType.java deleted file mode 100644 index f3c5d75eb..000000000 --- a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/PaymentProductType.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2018-2018 adorsys GmbH & Co KG - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package de.adorsys.multibanking.xs2a.pis; - -import de.adorsys.multibanking.xs2a.error.XS2AClientException; - -import java.util.Arrays; - -public enum PaymentProductType { - SEPA("sepa-credit-transfers"), - INSTANT_SEPA("instant-sepa-credit-transfers"), - TARGET_2_PAYMENTS("target-2-payments"), - CROSS_BORDER("cross-border-credit-transfers"), - PAIN_001_SEPA("pain.001-sepa-credit-transfers", true), - PAIN_001_INSTANT_SEPA("pain.001-instant-sepa-credit-transfers", true), - PAIN_001_TARGET_2_PAYMENTS("pain.001-target-2-payments", true), - PAIN_001_CROSS_BORDER("pain.001-cross-border-credit-transfers", true); - - private String type; - private boolean isRaw; - - PaymentProductType(String type, boolean isRaw) { - this.type = type; - this.isRaw = isRaw; - } - - PaymentProductType(String type) { - this(type, false); - } - - public String getType() { - return type; - } - - public boolean isRaw() { - return isRaw; - } - - public static PaymentProductType resolve(String type) { - return Arrays.stream(values()) - .filter(p -> p.type.equalsIgnoreCase(type)) - .findFirst() - .orElseThrow(() -> new XS2AClientException(type + " product type not supported by the system")); - } -} diff --git a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/PaymentServiceType.java b/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/PaymentServiceType.java deleted file mode 100644 index aac3bae87..000000000 --- a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/PaymentServiceType.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2018-2018 adorsys GmbH & Co KG - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package de.adorsys.multibanking.xs2a.pis; - -import de.adorsys.multibanking.domain.*; -import de.adorsys.multibanking.xs2a.error.XS2AClientException; - -import java.util.Arrays; - -public enum PaymentServiceType { - SINGLE("payments", SinglePayment.class), - BULK("bulk-payments", BulkPayment.class), - PERIODIC("periodic-payments", FutureSinglePayment.class); - - private String type; - private Class clazz; - - PaymentServiceType(String type, Class clazz) { - this.type = type; - this.clazz = clazz; - } - - public String getType() { - return type; - } - - public Class getClazz() { - return clazz; - } - - public static PaymentServiceType resolve(String service) { - return Arrays.stream(values()) - .filter(s -> s.type.equalsIgnoreCase(service)) - .findFirst() - .orElseThrow(() -> new XS2AClientException(service + " service type not supported by the system")); - } - - public static

PaymentServiceType resolve(P payment) { - if (payment == null) { - throw new XS2AClientException("Payment object can't be null"); - } - if (payment instanceof RawSepaPayment) { - if (payment.getRawData() == null) { - throw new XS2AClientException("Payment body is absent"); - } - return resolve(((RawSepaPayment) payment).getService()); - } - return Arrays.stream(values()) - .filter(s -> s.clazz == payment.getClass()) - .findFirst() - .orElseThrow(() -> new XS2AClientException(payment.getClass().getName() + " service class not supported by the system")); - } -} diff --git a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/sepa/AbstractPaymentInitiationBodyBuilder.java b/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/sepa/AbstractPaymentInitiationBodyBuilder.java deleted file mode 100644 index 72320bb69..000000000 --- a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/sepa/AbstractPaymentInitiationBodyBuilder.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2018-2018 adorsys GmbH & Co KG - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package de.adorsys.multibanking.xs2a.pis.sepa; - -import de.adorsys.multibanking.domain.AbstractScaTransaction; -import de.adorsys.multibanking.domain.SinglePayment; -import de.adorsys.psd2.client.model.AccountReference; -import de.adorsys.psd2.client.model.Amount; -import de.adorsys.multibanking.xs2a.pis.PaymentInitiationBodyBuilder; - -abstract class AbstractPaymentInitiationBodyBuilder implements PaymentInitiationBodyBuilder { - - Amount buildAmount(SinglePayment paymentBodyObj) { - Amount amount = new Amount(); - amount.setAmount(paymentBodyObj.getAmount().toString()); - amount.setCurrency(paymentBodyObj.getCurrency()); - return amount; - } - - AccountReference buildDebtorAccountReference(AbstractScaTransaction transaction) { - AccountReference debtorAccountReference = new AccountReference(); - debtorAccountReference.setIban(transaction.getDebtorBankAccount().getIban()); - debtorAccountReference.setCurrency(transaction.getDebtorBankAccount().getCurrency()); - return debtorAccountReference; - } - - AccountReference buildCreditorAccountReference(SinglePayment transaction) { - AccountReference creditorAccountReference = new AccountReference(); - creditorAccountReference.setIban(transaction.getReceiverIban()); - creditorAccountReference.setCurrency(transaction.getReceiverAccountCurrency()); - return creditorAccountReference; - } -} diff --git a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/sepa/SepaBulkPaymentInitiationBodyBuilder.java b/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/sepa/SepaBulkPaymentInitiationBodyBuilder.java deleted file mode 100644 index 4dd590684..000000000 --- a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/sepa/SepaBulkPaymentInitiationBodyBuilder.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2018-2018 adorsys GmbH & Co KG - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package de.adorsys.multibanking.xs2a.pis.sepa; - -import de.adorsys.multibanking.domain.AbstractScaTransaction; -import de.adorsys.multibanking.domain.BulkPayment; -import de.adorsys.multibanking.domain.SinglePayment; -import de.adorsys.psd2.client.model.BulkPaymentInitiationSctJson; -import de.adorsys.psd2.client.model.PaymentInitiationSctBulkElementJson; - -public class SepaBulkPaymentInitiationBodyBuilder extends AbstractPaymentInitiationBodyBuilder { - @Override - public BulkPaymentInitiationSctJson buildBody(AbstractScaTransaction transaction) { - BulkPayment bulkPayment = (BulkPayment) transaction; - BulkPaymentInitiationSctJson bulk = new BulkPaymentInitiationSctJson(); - bulk.setDebtorAccount(buildDebtorAccountReference(transaction)); - - for (SinglePayment payment : bulkPayment.getPayments()) { - PaymentInitiationSctBulkElementJson bulkElementJson = new PaymentInitiationSctBulkElementJson(); - bulkElementJson.setCreditorAccount(buildCreditorAccountReference(payment)); - bulkElementJson.setInstructedAmount(buildAmount(payment)); - bulkElementJson.setCreditorName(payment.getReceiver()); - bulkElementJson.setRemittanceInformationUnstructured(payment.getPurpose()); - bulk.addPaymentsItem(bulkElementJson); - } - return bulk; - } -} diff --git a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/sepa/SepaPeriodicPaymentInitiationBodyBuilder.java b/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/sepa/SepaPeriodicPaymentInitiationBodyBuilder.java deleted file mode 100644 index 0b8df01ab..000000000 --- a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/sepa/SepaPeriodicPaymentInitiationBodyBuilder.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2018-2018 adorsys GmbH & Co KG - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package de.adorsys.multibanking.xs2a.pis.sepa; - -import de.adorsys.multibanking.domain.AbstractScaTransaction; -import de.adorsys.multibanking.domain.FutureSinglePayment; -import de.adorsys.psd2.client.model.DayOfExecution; -import de.adorsys.psd2.client.model.PeriodicPaymentInitiationSctJson; - -import java.time.LocalDate; - -public class SepaPeriodicPaymentInitiationBodyBuilder extends AbstractPaymentInitiationBodyBuilder { - @Override - public PeriodicPaymentInitiationSctJson buildBody(AbstractScaTransaction transaction) { - FutureSinglePayment paymentBodyObj = (FutureSinglePayment) transaction; - - PeriodicPaymentInitiationSctJson periodic = new PeriodicPaymentInitiationSctJson(); - periodic.setDebtorAccount(buildDebtorAccountReference(paymentBodyObj)); - periodic.setCreditorAccount(buildCreditorAccountReference(paymentBodyObj)); - periodic.setInstructedAmount(buildAmount(paymentBodyObj)); - periodic.setCreditorName(paymentBodyObj.getReceiver()); - periodic.setRemittanceInformationUnstructured(paymentBodyObj.getPurpose()); - - //todo: check if execution date was correct populated - LocalDate executionDate = paymentBodyObj.getExecutionDate(); - periodic.setDayOfExecution(DayOfExecution.fromValue(String.valueOf(executionDate.getDayOfMonth()))); - return periodic; - } -} diff --git a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/sepa/SepaSinglePaymentInitiationBodyBuilder.java b/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/sepa/SepaSinglePaymentInitiationBodyBuilder.java deleted file mode 100644 index fcc5b8d95..000000000 --- a/onlinebanking-xs2a/src/main/java/de/adorsys/multibanking/xs2a/pis/sepa/SepaSinglePaymentInitiationBodyBuilder.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2018-2018 adorsys GmbH & Co KG - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package de.adorsys.multibanking.xs2a.pis.sepa; - -import de.adorsys.multibanking.domain.AbstractScaTransaction; -import de.adorsys.multibanking.domain.SinglePayment; -import de.adorsys.psd2.client.model.PaymentInitiationSctJson; - -public class SepaSinglePaymentInitiationBodyBuilder extends AbstractPaymentInitiationBodyBuilder { - - @Override - public PaymentInitiationSctJson buildBody(AbstractScaTransaction transaction) { - return buildPaymentInitiation((SinglePayment) transaction); - } - - private PaymentInitiationSctJson buildPaymentInitiation(SinglePayment paymentBodyObj) { - PaymentInitiationSctJson paymentInitiation = new PaymentInitiationSctJson(); - paymentInitiation.setDebtorAccount(buildDebtorAccountReference(paymentBodyObj)); - paymentInitiation.setCreditorAccount(buildCreditorAccountReference(paymentBodyObj)); - paymentInitiation.setInstructedAmount(buildAmount(paymentBodyObj)); - paymentInitiation.setCreditorName(paymentBodyObj.getReceiver()); - paymentInitiation.setRemittanceInformationUnstructured(paymentBodyObj.getPurpose()); - return paymentInitiation; - } - -} diff --git a/onlinebanking-xs2a/src/main/java/domain/DedicatedAccountInformationConsent.java b/onlinebanking-xs2a/src/main/java/domain/DedicatedAccountInformationConsent.java deleted file mode 100644 index 761547ae8..000000000 --- a/onlinebanking-xs2a/src/main/java/domain/DedicatedAccountInformationConsent.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2018-2018 adorsys GmbH & Co KG - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package domain; - -import de.adorsys.multibanking.domain.AbstractScaTransaction; - -public class DedicatedAccountInformationConsent extends AbstractScaTransaction { - - public DedicatedAccountInformationConsent() { - } - - public DedicatedAccountInformationConsent(String transactionId) { - this.setOrderId(transactionId); - } - - @Override - public TransactionType getTransactionType() { - return TransactionType.DEDICATED_CONSENT; - } - - @Override - public String getRawData() { - return null; - } -} diff --git a/onlinebanking-xs2a/src/main/java/domain/Xs2aBankApiUser.java b/onlinebanking-xs2a/src/main/java/domain/Xs2aBankApiUser.java deleted file mode 100644 index 458cb3b74..000000000 --- a/onlinebanking-xs2a/src/main/java/domain/Xs2aBankApiUser.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2018-2018 adorsys GmbH & Co KG - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package domain; - -import de.adorsys.multibanking.domain.BankApi; -import de.adorsys.multibanking.domain.BankApiUser; - -import java.util.HashMap; - -public class Xs2aBankApiUser extends BankApiUser { - - private static final String CONSENT_ID = "CONSENT_ID"; - - public Xs2aBankApiUser(String consentId) { - this(); - setConsentId(consentId); - } - - public Xs2aBankApiUser() { - setBankApi(BankApi.XS2A); - } - - public String getConsentId() { - return getProperties().get(CONSENT_ID); - } - - public void setConsentId(String consentId) { - if (getProperties() == null) { - setProperties(new HashMap<>()); - } - getProperties().put(CONSENT_ID, consentId); - } -} diff --git a/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/XS2ABankingTest.java b/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/XS2ABankingTest.java deleted file mode 100644 index 84faa48e5..000000000 --- a/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/XS2ABankingTest.java +++ /dev/null @@ -1,598 +0,0 @@ -package de.adorsys.multibanking.xs2a; - -import de.adorsys.multibanking.domain.*; -import de.adorsys.multibanking.domain.request.*; -import de.adorsys.multibanking.domain.response.*; -import de.adorsys.multibanking.xs2a.error.XS2AClientException; -import de.adorsys.multibanking.xs2a.executor.ConsentUpdateRequestExecutor; -import de.adorsys.multibanking.xs2a.executor.PaymentUpdateRequestExecutor; -import de.adorsys.multibanking.xs2a.executor.UpdateRequestExecutor; -import de.adorsys.multibanking.xs2a.model.XS2AUpdateRequest; -import de.adorsys.multibanking.xs2a.model.Xs2aTanSubmit; -import de.adorsys.multibanking.xs2a.pis.PaymentProductType; -import de.adorsys.psd2.client.ApiClient; -import de.adorsys.psd2.client.ApiException; -import de.adorsys.psd2.client.api.AccountInformationServiceAisApi; -import de.adorsys.psd2.client.api.PaymentInitiationServicePisApi; -import de.adorsys.psd2.client.model.AccountReference; -import de.adorsys.psd2.client.model.Balance; -import de.adorsys.psd2.client.model.*; -import domain.Xs2aBankApiUser; -import org.iban4j.CountryCode; -import org.iban4j.Iban; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import java.math.BigDecimal; -import java.time.LocalDate; -import java.util.*; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; - -@RunWith(MockitoJUnitRunner.class) -public class XS2ABankingTest { - - private static final String SINGLE_PAYMENT_SERVICE = "payments"; - private static final String SEPA_CREDIT_TRANSFERS = "sepa-credit-transfers"; - private static final String ACCOUNT_NUMBER = "accountNumber"; - private static final String CONSENT_ID = "consentId"; - private static final LocalDate REFERENCE_DATE = LocalDate.now(); - private static final String SCA_NAME_VALUE = "Photo Tan"; - private static final String SCA_AUTHENTICATION_VERSION_VALUE = "v1.0"; - private static final String SCA_EXPLANATION_VALUE = "some explanation"; - private static final String SCA_METHOD_ID_VALUE = "111"; - private static final String BANKING_URL = "bankingUrl"; - private static final String IBAN = Iban.random(CountryCode.DE).toString(); - private static final String AUTHORISATION_ID = "xs2a-authorisationId"; - private static final String PAYMENT_ID = "paymentId"; - private static final String PSU_ID = "login"; - private static final String CORPORATE_ID = "custId"; - private static final String PIN = "pin"; - private XS2ABanking xs2aBanking; - @Mock - private ApiClient apiClient; - @Mock - private PaymentInitiationServicePisApi paymentInitiationServicePisApi; - @Mock - private AccountInformationServiceAisApi accountInformationServiceAisApi; - @Mock - private UpdateRequestExecutor executor; - - @Before - public void setUp() { - xs2aBanking = new XS2ABanking() { - - @Override - ApiClient createApiClient(String bankingUrl) { - return apiClient; - } - - @Override - PaymentInitiationServicePisApi createPaymentInitiationServicePisApi(ApiClient apiClient) { - return paymentInitiationServicePisApi; - } - - @Override - AccountInformationServiceAisApi createAccountInformationServiceAisApi(ApiClient apiClient) { - return accountInformationServiceAisApi; - } - - @Override - UpdateRequestExecutor createUpdateRequestExecutor(SubmitAuthorizationCodeRequest submitPaymentRequest) { - return executor; - } - }; - } - - @Test - public void authenticatePsu() throws ApiException { - AuthenticatePsuRequest request = AuthenticatePsuRequest.builder() - .paymentService(SINGLE_PAYMENT_SERVICE) - .paymentProduct(SEPA_CREDIT_TRANSFERS) - .bankCode("08098") - .customerId(CORPORATE_ID) - .login(PSU_ID) - .paymentId(PAYMENT_ID) - .pin(PIN) - .build(); - - StartScaprocessResponse scaProcessResponse = new StartScaprocessResponse(); - Map> links = new HashMap<>(); - links.put("startAuthorisationWithPsuAuthentication", Collections.singletonMap("href", BANKING_URL + "/" + AUTHORISATION_ID)); - scaProcessResponse.setLinks(links); - ArgumentCaptor psuBodyCaptor = ArgumentCaptor.forClass(UpdatePsuAuthentication.class); - - when(paymentInitiationServicePisApi.startPaymentAuthorisation(eq(SINGLE_PAYMENT_SERVICE), - eq(SEPA_CREDIT_TRANSFERS), - eq(PAYMENT_ID), any(), eq(PSU_ID), isNull(), isNull(), - isNull(), isNull(), isNull(), - isNull(), eq(XS2ABanking.PSU_IP_ADDRESS), - isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), isNull()) - ).thenReturn(scaProcessResponse); - - when(paymentInitiationServicePisApi.updatePaymentPsuData(eq(SINGLE_PAYMENT_SERVICE), - eq(SEPA_CREDIT_TRANSFERS), eq(PAYMENT_ID), - eq(AUTHORISATION_ID), any(), psuBodyCaptor.capture(), - isNull(), isNull(), isNull(), - eq(PSU_ID), isNull(), eq(CORPORATE_ID), - isNull(), eq(XS2ABanking.PSU_IP_ADDRESS), isNull(), - isNull(), isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), isNull()) - ).thenReturn(buildUpdatePsuDataResponse()); - - ScaMethodsResponse response = xs2aBanking.authenticatePsu(BANKING_URL, request); - - verify(paymentInitiationServicePisApi, times(1)).startPaymentAuthorisation(eq(SINGLE_PAYMENT_SERVICE), - eq(SEPA_CREDIT_TRANSFERS), - eq(PAYMENT_ID), any(), eq(PSU_ID), isNull(), isNull(), - isNull(), isNull(), isNull(), - isNull(), eq(XS2ABanking.PSU_IP_ADDRESS), - isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), isNull()); - - verify(paymentInitiationServicePisApi, times(1)).updatePaymentPsuData(eq(SINGLE_PAYMENT_SERVICE), - eq(SEPA_CREDIT_TRANSFERS), eq(PAYMENT_ID), - eq(AUTHORISATION_ID), any(), psuBodyCaptor.capture(), - isNull(), isNull(), isNull(), - eq(PSU_ID), isNull(), eq(CORPORATE_ID), - isNull(), eq(XS2ABanking.PSU_IP_ADDRESS), isNull(), - isNull(), isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), isNull()); - - assertThat(response.getTanTransportTypes()).hasSize(1); - assertThat(response.getAuthorizationId()).isEqualTo(AUTHORISATION_ID); - - TanTransportType tanTransportType = response.getTanTransportTypes().get(0); - - assertThat(SCA_METHOD_ID_VALUE).isEqualTo(tanTransportType.getId()); - assertThat(SCA_AUTHENTICATION_VERSION_VALUE).isEqualTo(tanTransportType.getMedium()); - assertThat(SCA_NAME_VALUE).isEqualTo(tanTransportType.getName()); - assertThat(SCA_EXPLANATION_VALUE).isEqualTo(tanTransportType.getInputInfo()); - - UpdatePsuAuthentication psuAuthentication = psuBodyCaptor.getValue(); - - assertThat(psuAuthentication.getPsuData().getPassword()).isEqualTo(PIN); - } - - private Map buildUpdatePsuDataResponse() { - Map response = new HashMap<>(); - List> methods = new ArrayList<>(); - Map method = new HashMap<>(); - method.put(XS2ABanking.SCA_AUTHENTICATION_METHOD_ID, SCA_METHOD_ID_VALUE); - method.put(XS2ABanking.SCA_NAME, SCA_NAME_VALUE); - method.put(XS2ABanking.SCA_AUTHENTICATION_VERSION, SCA_AUTHENTICATION_VERSION_VALUE); - method.put(XS2ABanking.SCA_EXPLANATION, SCA_EXPLANATION_VALUE); - methods.add(method); - response.put(XS2ABanking.SCA_METHODS, methods); - return response; - } - - @Test(expected = XS2AClientException.class) - public void authorisePsuWithException() throws ApiException { - String paymentId = "paymentId"; - String psuId = "login"; - String custId = "custId"; - String pin = "pin"; - - AuthenticatePsuRequest request = AuthenticatePsuRequest.builder() - .paymentService(SINGLE_PAYMENT_SERVICE) - .paymentProduct(SEPA_CREDIT_TRANSFERS) - .bankCode("08098") - .customerId(custId) - .login(psuId) - .paymentId(paymentId) - .pin(pin) - .build(); - - when(paymentInitiationServicePisApi.startPaymentAuthorisation(eq(SINGLE_PAYMENT_SERVICE), - eq(SEPA_CREDIT_TRANSFERS), - eq(paymentId), any(), eq(psuId), isNull(), isNull(), - isNull(), isNull(), isNull(), - isNull(), eq(XS2ABanking.PSU_IP_ADDRESS), - isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), isNull())).thenThrow(ApiException.class); - - xs2aBanking.authenticatePsu(BANKING_URL, request); - } - - @Ignore - @Test - public void testLoadBankAccounts() { - BankAccess bankAccess = new BankAccess(); - bankAccess.setBankLogin(System.getProperty("login")); - bankAccess.setBankLogin2(System.getProperty("login2")); - - LoadAccountInformationRequest request = LoadAccountInformationRequest.builder() - .bankAccess(bankAccess) - .bankCode(System.getProperty("blz")) - .pin(System.getProperty("pin")) - .build(); - - LoadAccountInformationResponse response = xs2aBanking.loadBankAccounts("http://localhost:8082", request); - - } - - @Test - public void initiatePayment() throws ApiException { - - SinglePayment transaction = new SinglePayment(); - BankAccount bankAccount = new BankAccount(); - bankAccount.setIban(IBAN); - BankAccess bankAccess = new BankAccess(); - bankAccess.setBankLogin(PSU_ID); - transaction.setDebtorBankAccount(bankAccount); - transaction.setProduct(PaymentProductType.SEPA.getType()); - transaction.setAmount(new BigDecimal(1)); - TransactionRequest request = - TransactionRequest.builder().bankAccess(bankAccess).authorisationId(AUTHORISATION_ID).pin(PIN).transaction(transaction).build(); - ArgumentCaptor initiation = ArgumentCaptor.forClass(PaymentInitiationSctJson.class); - - when(paymentInitiationServicePisApi.initiatePayment( - initiation.capture(), - eq(SINGLE_PAYMENT_SERVICE), - eq(SEPA_CREDIT_TRANSFERS), - any(), - eq(XS2ABanking.PSU_IP_ADDRESS), - isNull(), isNull(), - isNull(), eq(PSU_ID), - isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), isNull())) - .thenReturn(buildInitiatePaymentResponse()); - - InitiatePaymentResponse response = xs2aBanking.initiatePayment(BANKING_URL, request); - - verify(paymentInitiationServicePisApi, times(1)).initiatePayment( - initiation.capture(), - eq(SINGLE_PAYMENT_SERVICE), - eq(SEPA_CREDIT_TRANSFERS), - any(), - eq(XS2ABanking.PSU_IP_ADDRESS), - isNull(), isNull(), - isNull(), eq(PSU_ID), - isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), isNull()); - - assertThat(response.getPaymentId()).isEqualTo(PAYMENT_ID); - assertThat(response.getTransactionStatus()).isEqualTo(TransactionStatus.RCVD.name()); - assertThat(response.getAuthorisationUrl()).isEqualTo(BANKING_URL + "/" + AUTHORISATION_ID); - } - - @Test(expected = XS2AClientException.class) - public void initiatePaymentWithApiException() throws ApiException { - - SinglePayment transaction = new SinglePayment(); - BankAccount bankAccount = new BankAccount(); - bankAccount.setIban(IBAN); - BankAccess bankAccess = new BankAccess(); - bankAccess.setBankLogin(PSU_ID); - transaction.setDebtorBankAccount(bankAccount); - transaction.setAmount(new BigDecimal(1)); - transaction.setProduct(PaymentProductType.SEPA.getType()); - TransactionRequest request = - TransactionRequest.builder().bankAccess(bankAccess).authorisationId(AUTHORISATION_ID).pin(PIN).transaction(transaction).build(); - ArgumentCaptor initiation = ArgumentCaptor.forClass(PaymentInitiationSctJson.class); - - when(paymentInitiationServicePisApi.initiatePayment( - initiation.capture(), - eq(SINGLE_PAYMENT_SERVICE), - eq(SEPA_CREDIT_TRANSFERS), - any(), - eq(XS2ABanking.PSU_IP_ADDRESS), - isNull(), isNull(), - isNull(), eq(PSU_ID), - isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), isNull())) - .thenThrow(ApiException.class); - - xs2aBanking.initiatePayment(BANKING_URL, request); - } - - private Map buildInitiatePaymentResponse() { - Map map = new HashMap<>(); - map.put("transactionStatus", TransactionStatus.RCVD.name()); - map.put("paymentId", PAYMENT_ID); - - HashMap> links = new HashMap<>(); - links.put("self", Collections.singletonMap("href", BANKING_URL)); - links.put("scaRedirect", Collections.singletonMap("href", BANKING_URL + "/" + AUTHORISATION_ID)); - map.put("_links", links); - return map; - } - - @Test - public void requestAuthorizationCode() throws ApiException { - - String selectedSCAMethodId = "901"; - SinglePayment singlePayment = new SinglePayment(); - singlePayment.setPaymentId(PAYMENT_ID); - singlePayment.setProduct(SEPA_CREDIT_TRANSFERS); - BankAccess bankAccess = new BankAccess(); - bankAccess.setBankLogin(PSU_ID); - bankAccess.setBankLogin2(CORPORATE_ID); - TransactionRequest request = - TransactionRequest.builder() - .authorisationId(AUTHORISATION_ID) - .pin(PIN) - .transaction(singlePayment) - .bankAccess(bankAccess) - .tanTransportType(TanTransportType.builder().id(selectedSCAMethodId).build()) - .build(); - - ArgumentCaptor bodyCaptor = - ArgumentCaptor.forClass(SelectPsuAuthenticationMethod.class); - - when(paymentInitiationServicePisApi.updatePaymentPsuData(eq(SINGLE_PAYMENT_SERVICE), - eq(SEPA_CREDIT_TRANSFERS), eq(PAYMENT_ID), - eq(AUTHORISATION_ID), any(), bodyCaptor.capture(), - isNull(), isNull(), isNull(), - eq(PSU_ID), isNull(), eq(CORPORATE_ID), - isNull(), eq(XS2ABanking.PSU_IP_ADDRESS), isNull(), - isNull(), isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), isNull()) - ).thenReturn(buildAuthorisationCodeResponse()); - - AuthorisationCodeResponse response = xs2aBanking.requestAuthorizationCode(BANKING_URL, request); - - verify(paymentInitiationServicePisApi, times(1)).updatePaymentPsuData(eq(SINGLE_PAYMENT_SERVICE), - eq(SEPA_CREDIT_TRANSFERS), eq(PAYMENT_ID), - eq(AUTHORISATION_ID), any(), bodyCaptor.capture(), - isNull(), isNull(), isNull(), - eq(PSU_ID), isNull(), eq(CORPORATE_ID), - isNull(), eq(XS2ABanking.PSU_IP_ADDRESS), isNull(), - isNull(), isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), isNull()); - - TanChallenge challenge = response.getChallenge(); - assertThat(challenge.getData()).isEqualTo(XS2ABanking.CHALLENGE_DATA); - assertThat(challenge.getFormat()).isEqualTo(XS2ABanking.CHALLENGE_OTP_FORMAT); - assertThat(challenge.getTitle()).isEqualTo(XS2ABanking.CHALLENGE_ADDITIONAL_INFORMATION); - - Xs2aTanSubmit tanSubmit = (Xs2aTanSubmit) response.getTanSubmit(); - assertThat(tanSubmit.getAuthorisationId()).isEqualTo(AUTHORISATION_ID); - assertThat(tanSubmit.getBankingUrl()).isEqualTo(BANKING_URL); - assertThat(tanSubmit.getTransactionId()).isEqualTo(PAYMENT_ID); - assertThat(tanSubmit.getPaymentProduct()).isEqualTo(SEPA_CREDIT_TRANSFERS); - assertThat(tanSubmit.getPaymentService()).isEqualTo(SINGLE_PAYMENT_SERVICE); - assertThat(tanSubmit.getPsuId()).isEqualTo(PSU_ID); - assertThat(tanSubmit.getPsuCorporateId()).isEqualTo(CORPORATE_ID); - - SelectPsuAuthenticationMethod method = bodyCaptor.getValue(); - - assertThat(method.getAuthenticationMethodId()).isEqualTo(selectedSCAMethodId); - } - - @Test(expected = XS2AClientException.class) - public void requestAuthorizationCodeWithApiException() throws ApiException { - - String selectedSCAMethodId = "901"; - SinglePayment singlePayment = new SinglePayment(); - singlePayment.setPaymentId(PAYMENT_ID); - singlePayment.setProduct(SEPA_CREDIT_TRANSFERS); - BankAccess bankAccess = new BankAccess(); - bankAccess.setBankLogin(PSU_ID); - bankAccess.setBankLogin2(CORPORATE_ID); - TransactionRequest request = - TransactionRequest.builder().authorisationId(AUTHORISATION_ID).pin(PIN).transaction(singlePayment).bankAccess(bankAccess).tanTransportType(TanTransportType.builder().id(selectedSCAMethodId).build()).build(); - - when(paymentInitiationServicePisApi.updatePaymentPsuData(eq(SINGLE_PAYMENT_SERVICE), - eq(SEPA_CREDIT_TRANSFERS), eq(PAYMENT_ID), - eq(AUTHORISATION_ID), any(), any(), - isNull(), isNull(), isNull(), - eq(PSU_ID), isNull(), eq(CORPORATE_ID), - isNull(), eq(XS2ABanking.PSU_IP_ADDRESS), isNull(), - isNull(), isNull(), isNull(), - isNull(), isNull(), - isNull(), isNull(), isNull()) - ).thenThrow(ApiException.class); - - xs2aBanking.requestAuthorizationCode(BANKING_URL, request); - } - - @SuppressWarnings("unchecked") - @Test - public void submitAuthorizationCode() throws ApiException { - String trxId = "transactionId"; - SubmitAuthorizationCodeRequest submitPaymentRequest = buildSubmitAuthorizationCodeRequest(); - XS2AUpdateRequest updateRequest = mock(XS2AUpdateRequest.class); - - when(executor.buildRequest(submitPaymentRequest)).thenReturn(updateRequest); - when(executor.execute(updateRequest, apiClient)).thenReturn(trxId); - - SubmitAuthorizationCodeResponse response = xs2aBanking.submitAuthorizationCode(submitPaymentRequest); - - verify(executor, times(1)).buildRequest(submitPaymentRequest); - verify(executor, times(1)).execute(updateRequest, apiClient); - - assertThat(response.getTransactionId()).isEqualTo(trxId); - } - - @Test(expected = XS2AClientException.class) - public void submitAuthorizationCodeWithException() throws ApiException { - SubmitAuthorizationCodeRequest submitPaymentRequest = buildSubmitAuthorizationCodeRequest(); - XS2AUpdateRequest updateRequest = mock(XS2AUpdateRequest.class); - - when(executor.buildRequest(submitPaymentRequest)).thenReturn(updateRequest); - when(executor.execute(updateRequest, apiClient)).thenThrow(ApiException.class); - - xs2aBanking.submitAuthorizationCode(submitPaymentRequest); - - verify(executor, times(1)).buildRequest(submitPaymentRequest); - } - - @Test - public void createUpdateRequestExecutor() { - XS2ABanking banking = new XS2ABanking(); - SubmitAuthorizationCodeRequest submitPaymentRequest = buildSubmitAuthorizationCodeRequest(); - - UpdateRequestExecutor executor = banking.createUpdateRequestExecutor(submitPaymentRequest); - - assertThat(executor).isInstanceOf(PaymentUpdateRequestExecutor.class); - - SinglePayment payment = new SinglePayment() { - @Override - public TransactionType getTransactionType() { - return TransactionType.DEDICATED_CONSENT; - } - }; - submitPaymentRequest.setSepaTransaction(payment); - - executor = banking.createUpdateRequestExecutor(submitPaymentRequest); - - assertThat(executor).isInstanceOf(ConsentUpdateRequestExecutor.class); - - } - - private SubmitAuthorizationCodeRequest buildSubmitAuthorizationCodeRequest() { - Xs2aTanSubmit tanSubmit = new Xs2aTanSubmit(); - tanSubmit.setBankingUrl(BANKING_URL); - SinglePayment payment = new SinglePayment(); - return SubmitAuthorizationCodeRequest.builder().tanSubmit(tanSubmit).sepaTransaction(payment).build(); - } - - private Map buildAuthorisationCodeResponse() { - Map response = new HashMap<>(); - Map challengeMap = new HashMap<>(); - challengeMap.put(XS2ABanking.CHALLENGE_DATA, XS2ABanking.CHALLENGE_DATA); - challengeMap.put(XS2ABanking.CHALLENGE_OTP_FORMAT, XS2ABanking.CHALLENGE_OTP_FORMAT); - challengeMap.put(XS2ABanking.CHALLENGE_ADDITIONAL_INFORMATION, XS2ABanking.CHALLENGE_ADDITIONAL_INFORMATION); - response.put(XS2ABanking.CHALLENGE, challengeMap); - response.put("tanSubmit", buildTanSubmit()); - return response; - } - - private Map buildTanSubmit() { - Map tanSubmit = new HashMap<>(); - tanSubmit.put("bankingUrl", BANKING_URL); - tanSubmit.put("paymentId", PAYMENT_ID); - tanSubmit.put("authorisationId", AUTHORISATION_ID); - tanSubmit.put("psuId", PSU_ID); - tanSubmit.put("psuCorporateId", CORPORATE_ID); - return tanSubmit; - } - - @Test - public void loadBalances() throws ApiException { - when(accountInformationServiceAisApi.getBalances(eq(ACCOUNT_NUMBER), any(), eq(CONSENT_ID), isNull(), isNull(), - isNull(), eq(XS2ABanking.PSU_IP_ADDRESS), isNull(), - isNull(), isNull(), isNull(), - isNull(), isNull(), isNull(), - isNull(), isNull())) - .thenReturn(buildReadAccountBalanceResponse()); - - List bankAccounts = xs2aBanking.loadBalances(BANKING_URL, buildLoadBalanceRequest()); - - verify(accountInformationServiceAisApi, times(1)).getBalances(eq(ACCOUNT_NUMBER), any(), - eq(CONSENT_ID), isNull(), isNull(), - isNull(), eq(XS2ABanking.PSU_IP_ADDRESS), isNull(), - isNull(), isNull(), isNull(), - isNull(), isNull(), isNull(), - isNull(), isNull()); - - assertThat(bankAccounts).hasSize(1); - BankAccount bankAccount = bankAccounts.get(0); - assertThat(bankAccount.getIban()).isEqualTo(IBAN); - assertThat(bankAccount.getAccountNumber()).isEqualTo(Iban.valueOf(IBAN).getAccountNumber()); - BalancesReport balances = bankAccount.getBalances(); - - checkReadyBalance(balances.getReadyBalance()); - checkReadyBalance(balances.getUnreadyBalance()); - } - - @Test(expected = XS2AClientException.class) - public void loadBalancesWithError() throws ApiException { - when(accountInformationServiceAisApi.getBalances(eq(ACCOUNT_NUMBER), any(), eq(CONSENT_ID), isNull(), isNull(), - isNull(), eq(XS2ABanking.PSU_IP_ADDRESS), isNull(), - isNull(), isNull(), isNull(), - isNull(), isNull(), isNull(), - isNull(), isNull())) - .thenThrow(ApiException.class); - - xs2aBanking.loadBalances(BANKING_URL, buildLoadBalanceRequest()); - } - - @Test - public void loadBalancesBankAccountIsAbsent() { - LoadBalanceRequest request = LoadBalanceRequest.builder().bankAccounts(Collections.EMPTY_LIST).build(); - List bankAccounts = xs2aBanking.loadBalances(BANKING_URL, request); - - assertThat(bankAccounts).isEmpty(); - } - - private void checkReadyBalance(de.adorsys.multibanking.domain.Balance balance) { - assertThat(balance.getAmount()).isEqualTo(new BigDecimal("123.321")); - assertThat(balance.getCurrency()).isEqualTo("EUR"); - assertThat(balance.getDate()).isEqualTo(REFERENCE_DATE); - } - - private LoadBalanceRequest buildLoadBalanceRequest() { - BankAccount account = new BankAccount(); - account.setIban(IBAN); - account.getExternalIdMap().put(BankApi.XS2A, ACCOUNT_NUMBER); - return LoadBalanceRequest.builder() - .bankAccounts(Collections.singletonList(account)) - .bankApiUser(new Xs2aBankApiUser(CONSENT_ID)) - .build(); - } - - private ReadAccountBalanceResponse200 buildReadAccountBalanceResponse() { - ReadAccountBalanceResponse200 response = new ReadAccountBalanceResponse200(); - BalanceList balances = new BalanceList(); - - AccountReference reference = new AccountReference(); - reference.setIban(IBAN); - - response.setAccount(reference); - response.setBalances(balances); - balances.add(createBalance(BalanceType.CLOSINGBOOKED)); - balances.add(createBalance(BalanceType.EXPECTED)); - balances.add(createBalance(BalanceType.OPENINGBOOKED)); - return response; - } - - private Balance createBalance(BalanceType type) { - Amount amount = new Amount(); - amount.setAmount("123.321"); - amount.setCurrency("EUR"); - Balance balance1 = new Balance(); - balance1.setBalanceType(type); - balance1.setReferenceDate(REFERENCE_DATE); - balance1.setBalanceAmount(amount); - return balance1; - } -} diff --git a/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/executor/ConsentUpdateRequestExecutorTest.java b/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/executor/ConsentUpdateRequestExecutorTest.java deleted file mode 100644 index 0e674f548..000000000 --- a/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/executor/ConsentUpdateRequestExecutorTest.java +++ /dev/null @@ -1,88 +0,0 @@ -package de.adorsys.multibanking.xs2a.executor; - -import de.adorsys.multibanking.xs2a.model.ConsentXS2AUpdateRequest; -import de.adorsys.multibanking.xs2a.model.Xs2aTanSubmit; -import de.adorsys.psd2.client.ApiClient; -import de.adorsys.psd2.client.ApiException; -import de.adorsys.psd2.client.api.AccountInformationServiceAisApi; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import java.util.UUID; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.*; - -@RunWith(MockitoJUnitRunner.class) -public class ConsentUpdateRequestExecutorTest { - - private static final String CONSENT_ID = "consentID"; - private static final String AUTHORISATION_ID = "authorisationID"; - private static final UUID REQUEST_ID = UUID.randomUUID(); - private static final Object BODY = new Object(); - private static final String PSU_ID = "psuID"; - private static final String PSU_CORPORATE_ID = "psuCorporateID"; - private static final String PSU_IP_ADDRESS = "psuIpAddress"; - - private ConsentUpdateRequestExecutor executor; - - @Mock - private AccountInformationServiceAisApi aisApi; - - @Before - public void setUp() throws Exception { - executor = new ConsentUpdateRequestExecutor() { - @Override - AccountInformationServiceAisApi createAisClient(ApiClient apiClient) { - return aisApi; - } - }; - } - - @Test - public void execute() throws ApiException { - when(aisApi.updateConsentsPsuData(CONSENT_ID, AUTHORISATION_ID, REQUEST_ID, BODY, null, - null, null, PSU_ID, null, - PSU_CORPORATE_ID, null, PSU_IP_ADDRESS, - null, null, null, - null, null, null, - null, null, null)) - .thenReturn(new Object()); - - String consentId = executor.execute(buildRequest(), new ApiClient()); - - verify(aisApi, times(1)).updateConsentsPsuData(CONSENT_ID, AUTHORISATION_ID, REQUEST_ID, - BODY, null, null, null, - PSU_ID, null, PSU_CORPORATE_ID, null, - PSU_IP_ADDRESS, null, null, - null, null, - null, null, null, - null, null); - - assertThat(consentId).isEqualTo(CONSENT_ID); - } - - @Test - public void createRequest() { - Xs2aTanSubmit tanSubmit = new Xs2aTanSubmit(); - tanSubmit.setTransactionId(CONSENT_ID); - ConsentXS2AUpdateRequest request = executor.createRequest(tanSubmit); - - assertThat(request.getConsentId()).isEqualTo(CONSENT_ID); - } - - private ConsentXS2AUpdateRequest buildRequest() { - ConsentXS2AUpdateRequest request = new ConsentXS2AUpdateRequest(); - request.setConsentId(CONSENT_ID); - request.setAuthorisationId(AUTHORISATION_ID); - request.setRequestId(REQUEST_ID); - request.setBody(BODY); - request.setPsuId(PSU_ID); - request.setPsuCorporateId(PSU_CORPORATE_ID); - request.setPsuIpAddress(PSU_IP_ADDRESS); - return request; - } -} \ No newline at end of file diff --git a/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/executor/PaymentUpdateRequestExecutorTest.java b/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/executor/PaymentUpdateRequestExecutorTest.java deleted file mode 100644 index 9e44e259c..000000000 --- a/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/executor/PaymentUpdateRequestExecutorTest.java +++ /dev/null @@ -1,96 +0,0 @@ -package de.adorsys.multibanking.xs2a.executor; - -import de.adorsys.multibanking.xs2a.model.PaymentXS2AUpdateRequest; -import de.adorsys.multibanking.xs2a.model.Xs2aTanSubmit; -import de.adorsys.psd2.client.ApiClient; -import de.adorsys.psd2.client.ApiException; -import de.adorsys.psd2.client.api.PaymentInitiationServicePisApi; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import java.util.UUID; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.*; - -@RunWith(MockitoJUnitRunner.class) -public class PaymentUpdateRequestExecutorTest { - - private static final String SINGLE_PAYMENT_SERVICE = "payments"; - private static final String SEPA_CREDIT_TRANSFERS = "sepa-credit-transfers"; - private static final String AUTHORISATION_ID = "authorisationID"; - private static final UUID REQUEST_ID = UUID.randomUUID(); - private static final Object BODY = new Object(); - private static final String PSU_ID = "psuID"; - private static final String PSU_CORPORATE_ID = "psuCorporateID"; - private static final String PSU_IP_ADDRESS = "psuIpAddress"; - private static final String PAYMENT_ID = "paymentID"; - - private PaymentUpdateRequestExecutor executor; - - @Mock - private PaymentInitiationServicePisApi pisApi; - - @Before - public void setUp() throws Exception { - executor = new PaymentUpdateRequestExecutor() { - @Override - PaymentInitiationServicePisApi createApiClient(ApiClient apiClient) { - return pisApi; - } - }; - } - - @Test - public void execute() throws ApiException { - when(pisApi.updatePaymentPsuData(SINGLE_PAYMENT_SERVICE, SEPA_CREDIT_TRANSFERS, PAYMENT_ID, AUTHORISATION_ID, - REQUEST_ID, BODY, null, null, null, - PSU_ID, null, PSU_CORPORATE_ID, null, - PSU_IP_ADDRESS, null, null, null, - null, null, null, - null, null, null)) - .thenReturn(new Object()); - - String consentId = executor.execute(buildRequest(), new ApiClient()); - - verify(pisApi, times(1)).updatePaymentPsuData(SINGLE_PAYMENT_SERVICE, - SEPA_CREDIT_TRANSFERS, PAYMENT_ID, AUTHORISATION_ID, - REQUEST_ID, BODY, null, null, null, - PSU_ID, null, PSU_CORPORATE_ID, null, - PSU_IP_ADDRESS, null, null, null, - null, null, null, - null, null, null); - - assertThat(consentId).isEqualTo(PAYMENT_ID); - } - - @Test - public void createRequest() { - Xs2aTanSubmit tanSubmit = new Xs2aTanSubmit(); - tanSubmit.setTransactionId(PAYMENT_ID); - tanSubmit.setPaymentService(SINGLE_PAYMENT_SERVICE); - tanSubmit.setPaymentProduct(SEPA_CREDIT_TRANSFERS); - PaymentXS2AUpdateRequest request = executor.createRequest(tanSubmit); - - assertThat(request.getPaymentId()).isEqualTo(PAYMENT_ID); - assertThat(request.getService()).isEqualTo(SINGLE_PAYMENT_SERVICE); - assertThat(request.getProduct()).isEqualTo(SEPA_CREDIT_TRANSFERS); - } - - private PaymentXS2AUpdateRequest buildRequest() { - PaymentXS2AUpdateRequest request = new PaymentXS2AUpdateRequest(); - request.setPaymentId(PAYMENT_ID); - request.setService(SINGLE_PAYMENT_SERVICE); - request.setProduct(SEPA_CREDIT_TRANSFERS); - request.setAuthorisationId(AUTHORISATION_ID); - request.setRequestId(REQUEST_ID); - request.setBody(BODY); - request.setPsuId(PSU_ID); - request.setPsuCorporateId(PSU_CORPORATE_ID); - request.setPsuIpAddress(PSU_IP_ADDRESS); - return request; - } -} \ No newline at end of file diff --git a/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/pis/PainPaymentInitiationBodyBuilderTest.java b/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/pis/PainPaymentInitiationBodyBuilderTest.java deleted file mode 100644 index d87fca3ff..000000000 --- a/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/pis/PainPaymentInitiationBodyBuilderTest.java +++ /dev/null @@ -1,18 +0,0 @@ -package de.adorsys.multibanking.xs2a.pis; - -import de.adorsys.multibanking.domain.RawSepaPayment; -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class PainPaymentInitiationBodyBuilderTest { - - @Test - public void buildBody() { - RawSepaPayment payment = new RawSepaPayment(); - payment.setPainXml("payment in xml format"); - byte[] bytes = new PainPaymentInitiationBodyBuilder().buildBody(payment); - - assertThat(bytes).isNotNull(); - } -} \ No newline at end of file diff --git a/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/pis/PaymentInitiationBuilderStrategyImplTest.java b/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/pis/PaymentInitiationBuilderStrategyImplTest.java deleted file mode 100644 index 1467b7e7a..000000000 --- a/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/pis/PaymentInitiationBuilderStrategyImplTest.java +++ /dev/null @@ -1,34 +0,0 @@ -package de.adorsys.multibanking.xs2a.pis; - -import de.adorsys.multibanking.xs2a.error.XS2AClientException; -import de.adorsys.multibanking.xs2a.pis.sepa.SepaBulkPaymentInitiationBodyBuilder; -import de.adorsys.multibanking.xs2a.pis.sepa.SepaPeriodicPaymentInitiationBodyBuilder; -import de.adorsys.multibanking.xs2a.pis.sepa.SepaSinglePaymentInitiationBodyBuilder; -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class PaymentInitiationBuilderStrategyImplTest { - - private PaymentInitiationBuilderStrategy builderStrategy = new PaymentInitiationBuilderStrategyImpl(); - - @Test - public void resolve() { - PaymentInitiationBodyBuilder builder = builderStrategy.resolve(PaymentProductType.SEPA, PaymentServiceType.SINGLE); - assertThat(builder.getClass()).isAssignableFrom(SepaSinglePaymentInitiationBodyBuilder.class); - - builder = builderStrategy.resolve(PaymentProductType.SEPA, PaymentServiceType.PERIODIC); - assertThat(builder.getClass()).isAssignableFrom(SepaPeriodicPaymentInitiationBodyBuilder.class); - - builder = builderStrategy.resolve(PaymentProductType.SEPA, PaymentServiceType.BULK); - assertThat(builder.getClass()).isAssignableFrom(SepaBulkPaymentInitiationBodyBuilder.class); - - builder = builderStrategy.resolve(PaymentProductType.PAIN_001_SEPA, PaymentServiceType.SINGLE); - assertThat(builder.getClass()).isAssignableFrom(PainPaymentInitiationBodyBuilder.class); - } - - @Test(expected = XS2AClientException.class) - public void resolveNotSupportedProduct() { - builderStrategy.resolve(PaymentProductType.INSTANT_SEPA, PaymentServiceType.SINGLE); - } -} \ No newline at end of file diff --git a/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/pis/PaymentProductTypeTest.java b/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/pis/PaymentProductTypeTest.java deleted file mode 100644 index 06d9edd9f..000000000 --- a/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/pis/PaymentProductTypeTest.java +++ /dev/null @@ -1,46 +0,0 @@ -package de.adorsys.multibanking.xs2a.pis; - -import de.adorsys.multibanking.xs2a.error.XS2AClientException; -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class PaymentProductTypeTest { - - @Test - public void getType() { - assertThat(PaymentProductType.SEPA.getType()).isEqualTo("sepa-credit-transfers"); - assertThat(PaymentProductType.INSTANT_SEPA.getType()).isEqualTo("instant-sepa-credit-transfers"); - assertThat(PaymentProductType.TARGET_2_PAYMENTS.getType()).isEqualTo("target-2-payments"); - assertThat(PaymentProductType.CROSS_BORDER.getType()).isEqualTo("cross-border-credit-transfers"); - assertThat(PaymentProductType.PAIN_001_SEPA.getType()).isEqualTo("pain.001-sepa-credit-transfers"); - assertThat(PaymentProductType.PAIN_001_INSTANT_SEPA.getType()).isEqualTo("pain.001-instant-sepa-credit-transfers"); - assertThat(PaymentProductType.PAIN_001_TARGET_2_PAYMENTS.getType()).isEqualTo("pain.001-target-2-payments"); - assertThat(PaymentProductType.PAIN_001_CROSS_BORDER.getType()).isEqualTo("pain.001-cross-border-credit-transfers"); - } - - - @Test - public void isRaw() { - assertThat(PaymentProductType.SEPA.isRaw()).isFalse(); - assertThat(PaymentProductType.INSTANT_SEPA.isRaw()).isFalse(); - assertThat(PaymentProductType.TARGET_2_PAYMENTS.isRaw()).isFalse(); - assertThat(PaymentProductType.CROSS_BORDER.isRaw()).isFalse(); - assertThat(PaymentProductType.PAIN_001_SEPA.isRaw()).isTrue(); - assertThat(PaymentProductType.PAIN_001_INSTANT_SEPA.isRaw()).isTrue(); - assertThat(PaymentProductType.PAIN_001_TARGET_2_PAYMENTS.isRaw()).isTrue(); - assertThat(PaymentProductType.PAIN_001_CROSS_BORDER.isRaw()).isTrue(); - } - - @Test - public void resolve() { - PaymentProductType type = PaymentProductType.resolve("sepa-credit-transfers"); - - assertThat(type).isEqualTo(PaymentProductType.SEPA); - } - - @Test(expected = XS2AClientException.class) - public void resolveUnknown() { - PaymentProductType.resolve("new-product"); - } -} \ No newline at end of file diff --git a/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/pis/PaymentServiceTypeTest.java b/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/pis/PaymentServiceTypeTest.java deleted file mode 100644 index 744646cee..000000000 --- a/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/pis/PaymentServiceTypeTest.java +++ /dev/null @@ -1,63 +0,0 @@ -package de.adorsys.multibanking.xs2a.pis; - -import de.adorsys.multibanking.domain.*; -import de.adorsys.multibanking.xs2a.error.XS2AClientException; -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class PaymentServiceTypeTest { - - @Test - public void getType() { - assertThat(PaymentServiceType.SINGLE.getType()).isEqualTo("payments"); - assertThat(PaymentServiceType.BULK.getType()).isEqualTo("bulk-payments"); - assertThat(PaymentServiceType.PERIODIC.getType()).isEqualTo("periodic-payments"); - } - - @Test - public void getClazz() { - assertThat(PaymentServiceType.SINGLE.getClazz()).isEqualTo(SinglePayment.class); - assertThat(PaymentServiceType.BULK.getClazz()).isEqualTo(BulkPayment.class); - assertThat(PaymentServiceType.PERIODIC.getClazz()).isEqualTo(FutureSinglePayment.class); - } - - @Test - public void resolveByService() { - PaymentServiceType serviceType = PaymentServiceType.resolve("bulk-payments"); - assertThat(serviceType).isEqualTo(PaymentServiceType.BULK); - } - - @Test(expected = XS2AClientException.class) - public void resolveByServiceThatNotSupported() { - PaymentServiceType.resolve("single-payments"); - } - - @Test - public void resolveByPaymentObject() { - PaymentServiceType serviceType = PaymentServiceType.resolve(new FutureSinglePayment()); - assertThat(serviceType).isEqualTo(PaymentServiceType.PERIODIC); - - serviceType = PaymentServiceType.resolve(new SinglePayment()); - assertThat(serviceType).isEqualTo(PaymentServiceType.SINGLE); - - serviceType = PaymentServiceType.resolve(new BulkPayment()); - assertThat(serviceType).isEqualTo(PaymentServiceType.BULK); - - RawSepaPayment payment = new RawSepaPayment(); - payment.setService("payments"); - payment.setPainXml("payment in xml format"); - serviceType = PaymentServiceType.resolve(payment); - assertThat(serviceType).isEqualTo(PaymentServiceType.SINGLE); - } - - @Test(expected = XS2AClientException.class) - public void resolvePaymentObjectNotSupported() { - PaymentServiceType.resolve(new FutureBulkPayment()); - } - - @Test(expected = XS2AClientException.class) - public void resolveRawDataIsAbsent() { - PaymentServiceType.resolve(new RawSepaPayment()); - } -} \ No newline at end of file diff --git a/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/pis/sepa/AbstractSepaPaymentInitiationBodyBuilder.java b/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/pis/sepa/AbstractSepaPaymentInitiationBodyBuilder.java deleted file mode 100644 index 9c1dd90d1..000000000 --- a/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/pis/sepa/AbstractSepaPaymentInitiationBodyBuilder.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2018-2018 adorsys GmbH & Co KG - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package de.adorsys.multibanking.xs2a.pis.sepa; - -import de.adorsys.multibanking.domain.BankAccount; -import de.adorsys.multibanking.domain.SinglePayment; -import de.adorsys.multibanking.xs2a.pis.PaymentProductType; -import org.iban4j.CountryCode; -import org.iban4j.Iban; - -import java.math.BigDecimal; - -abstract class AbstractSepaPaymentInitiationBodyBuilder { - static final String IBAN = Iban.random(CountryCode.DE).toString(); - static final String CREDITOR_NAME = "creditor name"; - static final String INFORMATION = "information"; - static final String CURRENCY = "UAH"; - static final int AMOUNT_VALUE = 1; - - BankAccount buildBankAccount() { - BankAccount bankAccount = new BankAccount(); - bankAccount.setIban(IBAN); - bankAccount.setCurrency(CURRENCY); - return bankAccount; - } - - SinglePayment buildSinglePayment() { - SinglePayment payment = new SinglePayment(); - payment.setDebtorBankAccount(buildBankAccount()); - payment.setProduct(PaymentProductType.SEPA.getType()); - payment.setAmount(new BigDecimal(AMOUNT_VALUE)); - payment.setReceiver(CREDITOR_NAME); - payment.setPurpose(INFORMATION); - payment.setReceiverIban(IBAN); - payment.setCurrency(CURRENCY); - payment.setReceiverAccountCurrency(CURRENCY); - return payment; - } -} diff --git a/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/pis/sepa/SepaBulkPaymentInitiationBodyBuilderTest.java b/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/pis/sepa/SepaBulkPaymentInitiationBodyBuilderTest.java deleted file mode 100644 index e35de4418..000000000 --- a/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/pis/sepa/SepaBulkPaymentInitiationBodyBuilderTest.java +++ /dev/null @@ -1,43 +0,0 @@ -package de.adorsys.multibanking.xs2a.pis.sepa; - -import de.adorsys.multibanking.domain.BulkPayment; -import de.adorsys.multibanking.domain.SinglePayment; -import de.adorsys.psd2.client.model.BulkPaymentInitiationSctJson; -import de.adorsys.psd2.client.model.PaymentInitiationSctBulkElementJson; -import org.junit.Test; - -import java.util.Collections; - -import static org.assertj.core.api.Assertions.assertThat; - -public class SepaBulkPaymentInitiationBodyBuilderTest extends AbstractSepaPaymentInitiationBodyBuilder { - - @Test - public void buildBody() { - - SepaBulkPaymentInitiationBodyBuilder builder = new SepaBulkPaymentInitiationBodyBuilder(); - - BulkPaymentInitiationSctJson body = builder.buildBody(buildBulkPayment()); - - assertThat(body.getPayments()).hasSize(1); - assertThat(body.getDebtorAccount().getIban()).isEqualTo(IBAN); - - PaymentInitiationSctBulkElementJson payment = body.getPayments().get(0); - - assertThat(payment.getCreditorName()).isEqualTo(CREDITOR_NAME); - assertThat(payment.getRemittanceInformationUnstructured()).isEqualTo(INFORMATION); - assertThat(payment.getCreditorAccount().getIban()).isEqualTo(IBAN); - assertThat(payment.getCreditorAccount().getCurrency()).isEqualTo(CURRENCY); - assertThat(payment.getInstructedAmount().getAmount()).isEqualTo(String.valueOf(AMOUNT_VALUE)); - assertThat(payment.getInstructedAmount().getCurrency()).isEqualTo(CURRENCY); - assertThat(body.getDebtorAccount().getCurrency()).isEqualTo(CURRENCY); - } - - private BulkPayment buildBulkPayment() { - BulkPayment bulk = new BulkPayment(); - SinglePayment payment = buildSinglePayment(); - bulk.setPayments(Collections.singletonList(payment)); - bulk.setDebtorBankAccount(buildBankAccount()); - return bulk; - } -} \ No newline at end of file diff --git a/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/pis/sepa/SepaPeriodicPaymentInitiationBodyBuilderTest.java b/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/pis/sepa/SepaPeriodicPaymentInitiationBodyBuilderTest.java deleted file mode 100644 index 71cf5ee19..000000000 --- a/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/pis/sepa/SepaPeriodicPaymentInitiationBodyBuilderTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package de.adorsys.multibanking.xs2a.pis.sepa; - -import de.adorsys.multibanking.domain.FutureSinglePayment; -import de.adorsys.multibanking.xs2a.pis.PaymentProductType; -import de.adorsys.psd2.client.model.DayOfExecution; -import de.adorsys.psd2.client.model.PeriodicPaymentInitiationSctJson; -import org.junit.Test; - -import java.math.BigDecimal; -import java.time.LocalDate; - -import static org.assertj.core.api.Assertions.assertThat; - -public class SepaPeriodicPaymentInitiationBodyBuilderTest extends AbstractSepaPaymentInitiationBodyBuilder { - - @Test - public void buildBody() { - - SepaPeriodicPaymentInitiationBodyBuilder builder = new SepaPeriodicPaymentInitiationBodyBuilder(); - - PeriodicPaymentInitiationSctJson body = builder.buildBody(buildPeriodicPayment()); - - assertThat(body.getCreditorName()).isEqualTo(CREDITOR_NAME); - assertThat(body.getRemittanceInformationUnstructured()).isEqualTo(INFORMATION); - assertThat(body.getCreditorAccount().getIban()).isEqualTo(IBAN); - assertThat(body.getDebtorAccount().getIban()).isEqualTo(IBAN); - assertThat(body.getInstructedAmount().getAmount()).isEqualTo(String.valueOf(AMOUNT_VALUE)); - assertThat(body.getInstructedAmount().getCurrency()).isEqualTo(CURRENCY); - assertThat(body.getDayOfExecution()).isEqualTo(DayOfExecution._1); - assertThat(body.getDebtorAccount().getCurrency()).isEqualTo(CURRENCY); - assertThat(body.getCreditorAccount().getCurrency()).isEqualTo(CURRENCY); - } - - private FutureSinglePayment buildPeriodicPayment() { - FutureSinglePayment payment = new FutureSinglePayment(); - payment.setDebtorBankAccount(buildBankAccount()); - payment.setProduct(PaymentProductType.SEPA.getType()); - payment.setAmount(new BigDecimal(AMOUNT_VALUE)); - payment.setReceiver(CREDITOR_NAME); - payment.setPurpose(INFORMATION); - payment.setReceiverIban(IBAN); - payment.setCurrency(CURRENCY); - payment.setReceiverAccountCurrency(CURRENCY); - payment.setExecutionDate(LocalDate.MIN); - return payment; - } - -} \ No newline at end of file diff --git a/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/pis/sepa/SepaSinglePaymentInitiationBodyBuilderTest.java b/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/pis/sepa/SepaSinglePaymentInitiationBodyBuilderTest.java deleted file mode 100644 index 14f7d2ecc..000000000 --- a/onlinebanking-xs2a/src/test/java/de/adorsys/multibanking/xs2a/pis/sepa/SepaSinglePaymentInitiationBodyBuilderTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package de.adorsys.multibanking.xs2a.pis.sepa; - -import de.adorsys.multibanking.domain.SinglePayment; -import de.adorsys.psd2.client.model.PaymentInitiationSctJson; -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class SepaSinglePaymentInitiationBodyBuilderTest extends AbstractSepaPaymentInitiationBodyBuilder { - - @Test - public void buildBody() { - - SinglePayment payment = buildSinglePayment(); - - SepaSinglePaymentInitiationBodyBuilder builder = new SepaSinglePaymentInitiationBodyBuilder(); - - PaymentInitiationSctJson body = builder.buildBody(payment); - - assertThat(body.getCreditorName()).isEqualTo(CREDITOR_NAME); - assertThat(body.getRemittanceInformationUnstructured()).isEqualTo(INFORMATION); - assertThat(body.getCreditorAccount().getIban()).isEqualTo(IBAN); - assertThat(body.getDebtorAccount().getIban()).isEqualTo(IBAN); - assertThat(body.getInstructedAmount().getAmount()).isEqualTo(String.valueOf(AMOUNT_VALUE)); - assertThat(body.getInstructedAmount().getCurrency()).isEqualTo(CURRENCY); - assertThat(body.getDebtorAccount().getCurrency()).isEqualTo(CURRENCY); - assertThat(body.getCreditorAccount().getCurrency()).isEqualTo(CURRENCY); - } - -} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 0aa4acb50..a4b37401d 100755 --- a/pom.xml +++ b/pom.xml @@ -58,6 +58,7 @@ + multibanking-server multibanking-persistence-mongodb multibanking-persistence-jpa onlinebanking-facade @@ -65,7 +66,6 @@ onlinebanking-figo onlinebanking-finapi onlinebanking-hbci4java - onlinebanking-xs2a onlinebanking-bankinggateway onlinebanking-mock multibanking-pers-spi @@ -141,24 +141,6 @@ - - maven-assembly-plugin - - - pack-module - package - - single - - - false - - assembly.xml - - - - - maven-resources-plugin 3.0.1