diff --git a/order-service/api/pom.xml b/order-service/api/pom.xml new file mode 100644 index 0000000..48a7c0f --- /dev/null +++ b/order-service/api/pom.xml @@ -0,0 +1,39 @@ + + + 4.0.0 + + com.devonfw.app.java + order-service + 0.0.1-SNAPSHOT + + order-service-api + jar + ${project.artifactId} + API of the server for the order-service application (containing datatypes, transfer-objects, and service interfaces). + + + + org.springframework.data + spring-data-commons + + + com.devonfw.java.modules + devon4j-rest + + + com.devonfw.java.modules + devon4j-logging + + + com.devonfw.java.modules + devon4j-security + + + + javax.servlet + javax.servlet-api + provided + + + diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/ApplicationEntity.java b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/ApplicationEntity.java new file mode 100644 index 0000000..05fe350 --- /dev/null +++ b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/ApplicationEntity.java @@ -0,0 +1,11 @@ +package com.devonfw.app.java.order.general.common.api; + +import com.devonfw.module.basic.common.api.entity.GenericEntity; + +/** + * This is the abstract interface for a {@link GenericEntity} of this application. We are using {@link Long} for + * all {@link #getId() primary keys}. + */ +public abstract interface ApplicationEntity extends GenericEntity { + +} diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/BinaryObject.java b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/BinaryObject.java new file mode 100644 index 0000000..3380f53 --- /dev/null +++ b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/BinaryObject.java @@ -0,0 +1,32 @@ +package com.devonfw.app.java.order.general.common.api; + +/** + * This is the interface for a {@link BinaryObject} of the order-service. + */ +public interface BinaryObject extends ApplicationEntity { + + /** + * @param mimeType is the MIME-Type of this {@link BinaryObject} + */ + void setMimeType(String mimeType); + + /** + * Returns MIME-Type of thie {@link BinaryObject} + * + * @return the MIME-Type, e.g image/jpeg + */ + String getMimeType(); + + /** + * @return Returns the size in bytes + */ + long getSize(); + + /** + * Sets the size of bytes + * + * @param size the size in bytes + */ + void setSize(long size); + +} diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/NlsBundleApplicationRoot.java b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/NlsBundleApplicationRoot.java new file mode 100644 index 0000000..55e8fae --- /dev/null +++ b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/NlsBundleApplicationRoot.java @@ -0,0 +1,20 @@ +package com.devonfw.app.java.order.general.common.api; + +import net.sf.mmm.util.nls.api.NlsBundle; +import net.sf.mmm.util.nls.api.NlsBundleMessage; +import net.sf.mmm.util.nls.api.NlsMessage; + +/** + * This is the {@link NlsBundle} for this application. + */ +public interface NlsBundleApplicationRoot extends NlsBundle { + + /** + * @see com.devonfw.app.java.order.general.common.api.exception.NoActiveUserException + * + * @return the {@link NlsMessage}. + */ + @NlsBundleMessage("There is currently no user logged in") + NlsMessage errorNoActiveUser(); + +} diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/UserProfile.java b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/UserProfile.java new file mode 100644 index 0000000..55a20c1 --- /dev/null +++ b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/UserProfile.java @@ -0,0 +1,13 @@ +package com.devonfw.app.java.order.general.common.api; + +/** + * This is the interface for the profile of a user interacting with this application. + */ +public interface UserProfile { + + /** + * @return the login of the user. + */ + String getLogin(); + +} diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/exception/ApplicationBusinessException.java b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/exception/ApplicationBusinessException.java new file mode 100644 index 0000000..86d4f3f --- /dev/null +++ b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/exception/ApplicationBusinessException.java @@ -0,0 +1,35 @@ +package com.devonfw.app.java.order.general.common.api.exception; + +import net.sf.mmm.util.nls.api.NlsMessage; + +/** + * Abstract base class for business exceptions of this application. + */ +public abstract class ApplicationBusinessException extends ApplicationException { + + private static final long serialVersionUID = 1L; + + /** + * @param message the error {@link #getNlsMessage() message}. + */ + public ApplicationBusinessException(NlsMessage message) { + + super(message); + } + + /** + * @param cause the error {@link #getCause() cause}. + * @param message the error {@link #getNlsMessage() message}. + */ + public ApplicationBusinessException(Throwable cause, NlsMessage message) { + + super(cause, message); + } + + @Override + public boolean isTechnical() { + + return false; + } + +} diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/exception/ApplicationException.java b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/exception/ApplicationException.java new file mode 100644 index 0000000..32a0bbb --- /dev/null +++ b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/exception/ApplicationException.java @@ -0,0 +1,30 @@ +package com.devonfw.app.java.order.general.common.api.exception; + +import net.sf.mmm.util.exception.api.NlsRuntimeException; +import net.sf.mmm.util.nls.api.NlsMessage; + +/** + * Abstract base class for custom exceptions of this application. + */ +public abstract class ApplicationException extends NlsRuntimeException { + + private static final long serialVersionUID = 1L; + + /** + * @param message the error {@link #getNlsMessage() message}. + */ + public ApplicationException(NlsMessage message) { + + super(message); + } + + /** + * @param cause the error {@link #getCause() cause}. + * @param message the error {@link #getNlsMessage() message}. + */ + public ApplicationException(Throwable cause, NlsMessage message) { + + super(cause, message); + } + +} diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/exception/ApplicationTechnicalException.java b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/exception/ApplicationTechnicalException.java new file mode 100644 index 0000000..b6e965c --- /dev/null +++ b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/exception/ApplicationTechnicalException.java @@ -0,0 +1,29 @@ +package com.devonfw.app.java.order.general.common.api.exception; + +import net.sf.mmm.util.nls.api.NlsMessage; + +/** + * Abstract base class for technical custom exceptions of this application. + */ +public abstract class ApplicationTechnicalException extends ApplicationException { + + private static final long serialVersionUID = 1L; + + /** + * @param message the error {@link #getNlsMessage() message}. + */ + public ApplicationTechnicalException(NlsMessage message) { + + super(message); + } + + /** + * @param cause the error {@link #getCause() cause}. + * @param message the error {@link #getNlsMessage() message}. + */ + public ApplicationTechnicalException(Throwable cause, NlsMessage message) { + + super(cause, message); + } + +} diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/exception/NoActiveUserException.java b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/exception/NoActiveUserException.java new file mode 100644 index 0000000..d029b60 --- /dev/null +++ b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/exception/NoActiveUserException.java @@ -0,0 +1,31 @@ +package com.devonfw.app.java.order.general.common.api.exception; + +import com.devonfw.app.java.order.general.common.api.NlsBundleApplicationRoot; + +/** + * Thrown when an operation is requested that requires a user to be logged in, but no such user exists. + */ +public class NoActiveUserException extends ApplicationBusinessException { + + /** UID for serialization. */ + private static final long serialVersionUID = 1L; + + /** + * The constructor. + */ + public NoActiveUserException() { + + this(null); + } + + /** + * The constructor. + * + * @param cause The root cause of this exception. + */ + public NoActiveUserException(Throwable cause) { + + super(cause, createBundle(NlsBundleApplicationRoot.class).errorNoActiveUser()); + } + +} diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/to/UserProfileTo.java b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/to/UserProfileTo.java new file mode 100644 index 0000000..7da8d78 --- /dev/null +++ b/order-service/api/src/main/java/com/devonfw/app/java/order/general/common/api/to/UserProfileTo.java @@ -0,0 +1,37 @@ +package com.devonfw.app.java.order.general.common.api.to; + +import com.devonfw.app.java.order.general.common.api.UserProfile; +import com.devonfw.module.basic.common.api.to.AbstractTo; + +/** + * Implementation of {@link UserProfile} as {AbstractTo TO}. + */ +public class UserProfileTo extends AbstractTo implements UserProfile { + + private static final long serialVersionUID = 1L; + + private String login; + + /** + * The constructor. + */ + public UserProfileTo() { + + super(); + } + + @Override + public String getLogin() { + + return this.login; + } + + /** + * @param login the new {@link #getLogin() login}. + */ + public void setLogin(String login) { + + this.login = login; + } + +} diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/general/logic/api/to/BinaryObjectEto.java b/order-service/api/src/main/java/com/devonfw/app/java/order/general/logic/api/to/BinaryObjectEto.java new file mode 100644 index 0000000..c4696eb --- /dev/null +++ b/order-service/api/src/main/java/com/devonfw/app/java/order/general/logic/api/to/BinaryObjectEto.java @@ -0,0 +1,50 @@ +package com.devonfw.app.java.order.general.logic.api.to; + +import com.devonfw.app.java.order.general.common.api.BinaryObject; +import com.devonfw.module.basic.common.api.to.AbstractEto; + +/** + * The {@link com.devonfw.module.basic.common.api.to.AbstractEto ETO} for a {@link BinaryObject}. + */ +public class BinaryObjectEto extends AbstractEto implements BinaryObject { + + private static final long serialVersionUID = 1L; + + private String mimeType; + + private long size; + + /** + * Constructor. + */ + public BinaryObjectEto() { + + super(); + } + + @Override + public void setMimeType(String mimeType) { + + this.mimeType = mimeType; + + } + + @Override + public String getMimeType() { + + return this.mimeType; + } + + @Override + public long getSize() { + + return this.size; + } + + @Override + public void setSize(long size) { + + this.size = size; + } + +} diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/general/service/api/rest/SecurityRestService.java b/order-service/api/src/main/java/com/devonfw/app/java/order/general/service/api/rest/SecurityRestService.java new file mode 100644 index 0000000..d3724c1 --- /dev/null +++ b/order-service/api/src/main/java/com/devonfw/app/java/order/general/service/api/rest/SecurityRestService.java @@ -0,0 +1,38 @@ +package com.devonfw.app.java.order.general.service.api.rest; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.core.Context; + +import org.springframework.security.web.csrf.CsrfToken; + +import com.devonfw.module.rest.common.api.RestService; + +import com.devonfw.app.java.order.general.common.api.to.UserProfileTo; + +/** + * The security REST service provides access to the csrf token, the authenticated user's meta-data. Furthermore, it + * provides functionality to check permissions and roles of the authenticated user. + */ +@Path("/security/v1") +public interface SecurityRestService extends RestService { + + /** + * @param request {@link HttpServletRequest} to retrieve the current session from. + * @param response {@link HttpServletResponse} to send additional information. + * @return the Spring Security {@link CsrfToken} from the server session. + */ + @GET + @Path("/csrftoken/") + CsrfToken getCsrfToken(@Context HttpServletRequest request, @Context HttpServletResponse response); + + /** + * @return the {@link UserProfileTo} of the currently logged-in user. + */ + @GET + @Path("/currentuser/") + UserProfileTo getCurrentUser(); + +} diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/orderservice/common/api/Customer.java b/order-service/api/src/main/java/com/devonfw/app/java/order/orderservice/common/api/Customer.java new file mode 100644 index 0000000..fcc5abf --- /dev/null +++ b/order-service/api/src/main/java/com/devonfw/app/java/order/orderservice/common/api/Customer.java @@ -0,0 +1,31 @@ +package com.devonfw.app.java.order.orderservice.common.api; + +import com.devonfw.app.java.order.general.common.api.ApplicationEntity; + +public interface Customer extends ApplicationEntity { + + /** + * @return firstnameId + */ + + public String getFirstname(); + + /** + * @param firstname setter for firstname attribute + */ + + public void setFirstname(String firstname); + + /** + * @return lastnameId + */ + + public String getLastname(); + + /** + * @param lastname setter for lastname attribute + */ + + public void setLastname(String lastname); + +} diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/orderservice/common/api/Item.java b/order-service/api/src/main/java/com/devonfw/app/java/order/orderservice/common/api/Item.java new file mode 100644 index 0000000..c299cfe --- /dev/null +++ b/order-service/api/src/main/java/com/devonfw/app/java/order/orderservice/common/api/Item.java @@ -0,0 +1,37 @@ +package com.devonfw.app.java.order.orderservice.common.api; + +import com.devonfw.app.java.order.general.common.api.ApplicationEntity; + +public interface Item extends ApplicationEntity { + + /** + * @return nameId + */ + public String getName(); + + /** + * @param name setter for name attribute + */ + public void setName(String name); + + /** + * @return descriptionId + */ + public String getDescription(); + + /** + * @param description setter for description attribute + */ + public void setDescription(String description); + + /** + * @return priceId + */ + public Double getPrice(); + + /** + * @param price setter for price attribute + */ + public void setPrice(Double price); + +} diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/orderservice/common/api/Order.java b/order-service/api/src/main/java/com/devonfw/app/java/order/orderservice/common/api/Order.java new file mode 100644 index 0000000..b02da0d --- /dev/null +++ b/order-service/api/src/main/java/com/devonfw/app/java/order/orderservice/common/api/Order.java @@ -0,0 +1,59 @@ +package com.devonfw.app.java.order.orderservice.common.api; + +import java.time.LocalDate; + +import com.devonfw.app.java.order.general.common.api.ApplicationEntity; + +public interface Order extends ApplicationEntity { + + /** + * @return creationDateId + */ + + public LocalDate getCreationDate(); + + /** + * @param creationDate setter for creationDate attribute + */ + + public void setCreationDate(LocalDate creationDate); + + /** + * getter for ownerId attribute + * + * @return ownerId + */ + + public Long getOwnerId(); + + /** + * @param owner setter for owner attribute + */ + + public void setOwnerId(Long ownerId); + + /** + * @return priceId + */ + + public Double getPrice(); + + /** + * @param price setter for price attribute + */ + + public void setPrice(Double price); + + /** + * @return statusId + */ + + public OrderStatus getStatus(); + + /** + * @param status setter for status attribute + */ + + public void setStatus(OrderStatus status); + +} diff --git a/order-service/api/src/main/java/com/devonfw/app/java/order/orderservice/common/api/OrderStatus.java b/order-service/api/src/main/java/com/devonfw/app/java/order/orderservice/common/api/OrderStatus.java new file mode 100644 index 0000000..f328396 --- /dev/null +++ b/order-service/api/src/main/java/com/devonfw/app/java/order/orderservice/common/api/OrderStatus.java @@ -0,0 +1,9 @@ +package com.devonfw.app.java.order.orderservice.common.api; + +/** + * @author ARMIKA + * + */ +public enum OrderStatus { + NEW, PREPARING, PREPARED, SERVED, PAID, CANCELLED +} diff --git a/order-service/core/pom.xml b/order-service/core/pom.xml new file mode 100644 index 0000000..162d269 --- /dev/null +++ b/order-service/core/pom.xml @@ -0,0 +1,242 @@ + + + 4.0.0 + + com.devonfw.app.java + order-service + 0.0.1-SNAPSHOT + + order-service-core + jar + ${project.artifactId} + Core of the server for the order-service application - a simple example using the Open Application Standard Platform for Java (devon4j). + + + + + org.springframework.boot + spring-boot-devtools + true + + + + ${project.groupId} + order-service-api + ${project.version} + + + + + com.devonfw.java.modules + devon4j-beanmapping + + + + + com.devonfw.java.modules + devon4j-security + + + + com.devonfw.java.modules + devon4j-web + + + + + com.devonfw.java.starters + devon4j-starter-cxf-client-rest + + + + com.devonfw.java.starters + devon4j-starter-cxf-client-ws + + + + + com.devonfw.java.starters + devon4j-starter-cxf-server-rest + + + + com.devonfw.java.starters + devon4j-starter-cxf-server-ws + + + + + com.devonfw.java.starters + devon4j-starter-spring-data-jpa + + + + + org.springframework.boot + spring-boot-starter-jdbc + + + + + org.hibernate.javax.persistence + hibernate-jpa-2.1-api + + + + + org.hibernate + hibernate-entitymanager + + + + + com.querydsl + querydsl-jpa + + + com.querydsl + querydsl-apt + provided + + + + + org.hibernate.validator + hibernate-validator + + + + + javax.servlet + javax.servlet-api + provided + + + + + javax.el + javax.el-api + + + + + org.springframework + spring-webmvc + + + + + com.h2database + h2 + + + + + org.flywaydb + flyway-core + + + + + org.apache.cxf + cxf-rt-rs-service-description + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + org.springframework + spring-aop + + + + + cglib + cglib + + + + + net.logstash.logback + logstash-logback-encoder + + + + + com.devonfw.java.modules + devon4j-test + test + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-tomcat + + + org.springframework.boot + spring-boot-starter-validation + + + + + + org.skyscreamer + jsonassert + test + + + + + + embedded + + true + + + + org.springframework.boot + spring-boot-starter-tomcat + + + + + + + + + src/main/resources + true + + + + + org.apache.maven.plugins + maven-jar-plugin + + + config/application.properties + + + + + + + \ No newline at end of file diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/SpringBootApp.java b/order-service/core/src/main/java/com/devonfw/app/java/order/SpringBootApp.java new file mode 100644 index 0000000..fd1fb27 --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/SpringBootApp.java @@ -0,0 +1,30 @@ +package com.devonfw.app.java.order; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; + +import com.devonfw.module.jpa.dataaccess.api.AdvancedRevisionEntity; +import com.devonfw.module.jpa.dataaccess.impl.data.GenericRepositoryFactoryBean; + +/** + * Main entry point of this {@link SpringBootApplication}. Simply run this class to start this app. + */ +@SpringBootApplication +@EntityScan(basePackages = { "com.devonfw.app.java.order" }, basePackageClasses = { AdvancedRevisionEntity.class }) +@EnableJpaRepositories(repositoryFactoryBeanClass = GenericRepositoryFactoryBean.class) +@EnableGlobalMethodSecurity(jsr250Enabled = true) +public class SpringBootApp { + + /** + * Entry point for spring-boot based app + * + * @param args - arguments + */ + public static void main(String[] args) { + + SpringApplication.run(SpringBootApp.class, args); + } +} diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/api/ApplicationEntity.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/api/ApplicationEntity.java new file mode 100644 index 0000000..df20134 --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/api/ApplicationEntity.java @@ -0,0 +1,11 @@ +package com.devonfw.app.java.order.general.common.api; + +import com.devonfw.module.basic.common.api.entity.GenericEntity; + +/** + * This is the abstract interface for a {@link GenericEntity}. We are using {@link Long} for all {@link #getId() primary + * keys}. + */ +public abstract interface ApplicationEntity extends GenericEntity { + +} diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/api/security/ApplicationAccessControlConfig.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/api/security/ApplicationAccessControlConfig.java new file mode 100644 index 0000000..01cf1ff --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/api/security/ApplicationAccessControlConfig.java @@ -0,0 +1,38 @@ +package com.devonfw.app.java.order.general.common.api.security; + +import javax.inject.Named; + +import com.devonfw.module.security.common.api.accesscontrol.AccessControlGroup; +import com.devonfw.module.security.common.base.accesscontrol.AccessControlConfig; + +/** + * Example of {@link AccessControlConfig} that used for testing. + */ +@Named +public class ApplicationAccessControlConfig extends AccessControlConfig { + + public static final String APP_ID = "order-service"; + + private static final String PREFIX = APP_ID + "."; + + public static final String PERMISSION_FIND_BINARY_OBJECT = PREFIX + "FindBinaryObject"; + + public static final String PERMISSION_SAVE_BINARY_OBJECT = PREFIX + "SaveBinaryObject"; + + public static final String PERMISSION_DELETE_BINARY_OBJECT = PREFIX + "DeleteBinaryObject"; + + public static final String GROUP_READ_MASTER_DATA = PREFIX + "ReadMasterData"; + + public static final String GROUP_ADMIN = PREFIX + "Admin"; + + /** + * The constructor. + */ + public ApplicationAccessControlConfig() { + + super(); + AccessControlGroup readMasterData = group(GROUP_READ_MASTER_DATA, PERMISSION_FIND_BINARY_OBJECT); + group(GROUP_ADMIN, readMasterData, PERMISSION_SAVE_BINARY_OBJECT, PERMISSION_DELETE_BINARY_OBJECT); + } + +} \ No newline at end of file diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/base/AbstractBeanMapperSupport.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/base/AbstractBeanMapperSupport.java new file mode 100644 index 0000000..db56f8e --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/base/AbstractBeanMapperSupport.java @@ -0,0 +1,31 @@ +package com.devonfw.app.java.order.general.common.base; + +import com.devonfw.module.beanmapping.common.api.BeanMapper; + +import javax.inject.Inject; + +/** + * This abstract class provides {@link #getBeanMapper() access} to the {@link BeanMapper}. + */ +public abstract class AbstractBeanMapperSupport { + + private BeanMapper beanMapper; + + /** + * @param beanMapper is the {@link BeanMapper} to {@link Inject} + */ + @Inject + public void setBeanMapper(BeanMapper beanMapper) { + + this.beanMapper = beanMapper; + } + + /** + * @return the {@link BeanMapper} instance. + */ + protected BeanMapper getBeanMapper() { + + return this.beanMapper; + } + +} diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/config/ApplicationObjectMapperFactory.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/config/ApplicationObjectMapperFactory.java new file mode 100644 index 0000000..044053f --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/config/ApplicationObjectMapperFactory.java @@ -0,0 +1,33 @@ +package com.devonfw.app.java.order.general.common.impl.config; + +import javax.inject.Named; + +import org.springframework.data.domain.Pageable; +import org.springframework.security.web.csrf.CsrfToken; + +import com.fasterxml.jackson.databind.module.SimpleModule; + +import com.devonfw.module.json.common.base.ObjectMapperFactory; +import com.devonfw.module.json.common.base.type.PageableJsonSerializer; +import com.devonfw.module.json.common.base.type.PageableJsonDeserializer; + +/** + * The MappingFactory class to resolve polymorphic conflicts within the order-service application. + */ +@Named("ApplicationObjectMapperFactory") +public class ApplicationObjectMapperFactory extends ObjectMapperFactory { + + /** + * The constructor. + */ + public ApplicationObjectMapperFactory() { + + super(); + // see https://github.com/devonfw-wiki/devon4j/wiki/guide-json#json-and-inheritance + SimpleModule module = getExtensionModule(); + module.addAbstractTypeMapping(CsrfToken.class, CsrfTokenImpl.class); + // register spring-data Pageable + module.addSerializer(Pageable.class, new PageableJsonSerializer()); + module.addDeserializer(Pageable.class, new PageableJsonDeserializer()); + } +} diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/config/BeansDozerConfig.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/config/BeansDozerConfig.java new file mode 100644 index 0000000..87eda20 --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/config/BeansDozerConfig.java @@ -0,0 +1,32 @@ +package com.devonfw.app.java.order.general.common.impl.config; + +import java.util.ArrayList; +import java.util.List; + +import org.dozer.DozerBeanMapper; +import org.dozer.Mapper; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +/** + * Java bean configuration for Dozer + */ +@Configuration +@ComponentScan(basePackages = { "com.devonfw.module.beanmapping" }) +public class BeansDozerConfig { + + private static final String DOZER_MAPPING_XML = "config/app/common/dozer-mapping.xml"; + + /** + * @return the {@link DozerBeanMapper}. + */ + @Bean + public Mapper getDozer() { + + List beanMappings = new ArrayList<>(); + beanMappings.add(DOZER_MAPPING_XML); + return new DozerBeanMapper(beanMappings); + + } +} diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/config/CsrfTokenImpl.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/config/CsrfTokenImpl.java new file mode 100644 index 0000000..057c378 --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/config/CsrfTokenImpl.java @@ -0,0 +1,60 @@ +package com.devonfw.app.java.order.general.common.impl.config; + +import org.springframework.security.web.csrf.CsrfToken; + +/** + * Implementation of {@link CsrfToken} as Java bean for JSON deserialization. + */ +public class CsrfTokenImpl implements CsrfToken { + + private static final long serialVersionUID = 1L; + + private String headerName; + + private String parameterName; + + private String token; + + @Override + public String getHeaderName() { + + return this.headerName; + } + + @Override + public String getParameterName() { + + return this.parameterName; + } + + @Override + public String getToken() { + + return this.token; + } + + /** + * @param headerName new value of {@link #getHeaderName()}. + */ + public void setHeaderName(String headerName) { + + this.headerName = headerName; + } + + /** + * @param parameterName new value of {@link #getParameterName()}. + */ + public void setParameterName(String parameterName) { + + this.parameterName = parameterName; + } + + /** + * @param token new value of {@link #getToken()}. + */ + public void setToken(String token) { + + this.token = token; + } + +} diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/security/BaseUserDetailsService.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/security/BaseUserDetailsService.java new file mode 100644 index 0000000..96d80f0 --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/security/BaseUserDetailsService.java @@ -0,0 +1,123 @@ +package com.devonfw.app.java.order.general.common.impl.security; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; + +import com.devonfw.module.security.common.api.accesscontrol.AccessControl; +import com.devonfw.module.security.common.api.accesscontrol.AccessControlProvider; +import com.devonfw.module.security.common.base.accesscontrol.AccessControlGrantedAuthority; + +/** + * Custom implementation of {@link UserDetailsService}.
+ * + * @see com.devonfw.app.java.order.general.service.impl.config.BaseWebSecurityConfig + */ +@Named +public class BaseUserDetailsService implements UserDetailsService { + + /** Logger instance. */ + private static final Logger LOG = LoggerFactory.getLogger(BaseUserDetailsService.class); + + private AuthenticationManagerBuilder amBuilder; + + private AccessControlProvider accessControlProvider; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + + Set authorities = getAuthorities(username); + UserDetails user; + try { + user = getAmBuilder().getDefaultUserDetailsService().loadUserByUsername(username); + User userData = new User(user.getUsername(), user.getPassword(), authorities); + return userData; + } catch (Exception e) { + e.printStackTrace(); + UsernameNotFoundException exception = new UsernameNotFoundException("Authentication failed.", e); + LOG.warn("Failed to get user {}.", username, exception); + throw exception; + } + } + + /** + * @param username the login of the user + * @return the associated {@link GrantedAuthority}s + * @throws AuthenticationException if no principal is retrievable for the given {@code username} + */ + protected Set getAuthorities(String username) throws AuthenticationException { + + Objects.requireNonNull(username, "username"); + // determine granted authorities for spring-security... + Set authorities = new HashSet<>(); + Collection accessControlIds = getRoles(username); + Set accessControlSet = new HashSet<>(); + for (String id : accessControlIds) { + boolean success = this.accessControlProvider.collectAccessControls(id, accessControlSet); + if (!success) { + LOG.warn("Undefined access control {}.", id); + } + } + for (AccessControl accessControl : accessControlSet) { + authorities.add(new AccessControlGrantedAuthority(accessControl)); + } + return authorities; + } + + private Collection getRoles(String username) { + + Collection roles = new ArrayList<>(); + // TODO for a reasonable application you need to retrieve the roles of the user from a central IAM system + roles.add(username); + return roles; + } + + /** + * @return amBuilder + */ + public AuthenticationManagerBuilder getAmBuilder() { + + return this.amBuilder; + } + + /** + * @param amBuilder new value of {@link #getAmBuilder}. + */ + @Inject + public void setAmBuilder(AuthenticationManagerBuilder amBuilder) { + + this.amBuilder = amBuilder; + } + + /** + * @return accessControlProvider + */ + public AccessControlProvider getAccessControlProvider() { + + return this.accessControlProvider; + } + + /** + * @param accessControlProvider new value of {@link #getAccessControlProvider}. + */ + @Inject + public void setAccessControlProvider(AccessControlProvider accessControlProvider) { + + this.accessControlProvider = accessControlProvider; + } +} diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/security/CsrfRequestMatcher.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/security/CsrfRequestMatcher.java new file mode 100644 index 0000000..38eb25f --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/common/impl/security/CsrfRequestMatcher.java @@ -0,0 +1,54 @@ +package com.devonfw.app.java.order.general.common.impl.security; + +import java.util.regex.Pattern; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.security.web.util.matcher.RequestMatcher; + +/** + * This is the implementation of {@link RequestMatcher}, which decides which {@link HttpServletRequest Requests} require + * a correct CSRF token. + * + * @see Cross-site request forgery + */ +public class CsrfRequestMatcher implements RequestMatcher { + + private static final Pattern HTTP_METHOD_PATTERN = Pattern.compile("^GET$"); + + private static final String[] PATH_PREFIXES_WITHOUT_CSRF_PROTECTION = + { "/login", "/logout", "/services/rest/login", "/websocket" }; + + @Override + public boolean matches(HttpServletRequest request) { + + // GET requests are read-only and therefore do not need CSRF protection + if (HTTP_METHOD_PATTERN.matcher(request.getMethod()).matches()) { + + return false; + } + + // There are specific URLs which can not be protected from CSRF. For example, in case of the the login page, + // the CSRF token can only be accessed after a successful authentication ("login"). + String requestPath = getRequestPath(request); + for (String path : PATH_PREFIXES_WITHOUT_CSRF_PROTECTION) { + if (requestPath.startsWith(path)) { + return false; + } + } + + return true; + } + + private String getRequestPath(HttpServletRequest request) { + + String url = request.getServletPath(); + String pathInfo = request.getPathInfo(); + + if (pathInfo != null) { + url += pathInfo; + } + + return url; + } +} diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/dataaccess/api/ApplicationPersistenceEntity.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/dataaccess/api/ApplicationPersistenceEntity.java new file mode 100644 index 0000000..4596053 --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/dataaccess/api/ApplicationPersistenceEntity.java @@ -0,0 +1,86 @@ +package com.devonfw.app.java.order.general.dataaccess.api; + +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.MappedSuperclass; +import javax.persistence.Transient; +import javax.persistence.Version; + +import com.devonfw.app.java.order.general.common.api.ApplicationEntity; +import com.devonfw.module.basic.common.api.entity.PersistenceEntity; + +/** + * Abstract base class for all {@link PersistenceEntity persistence entities} with an {@link #getId() id} and a + * {@link #getModificationCounter() modificationCounter} (version) field. All persistence entities of this application + * should inherit from this class. It is using JPA annotations at the getters what has several advantages but also + * implies that you have to annotate transient getter methods with the {@link Transient} annotation. + */ +@MappedSuperclass +public abstract class ApplicationPersistenceEntity implements ApplicationEntity, PersistenceEntity { + + private static final long serialVersionUID = 1L; + + private Long id; + + private int modificationCounter; + + /** + * The constructor. + */ + public ApplicationPersistenceEntity() { + + super(); + } + + @Override + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + public Long getId() { + + return this.id; + } + + @Override + public void setId(Long id) { + + this.id = id; + } + + @Override + @Version + public int getModificationCounter() { + + return this.modificationCounter; + } + + @Override + public void setModificationCounter(int version) { + + this.modificationCounter = version; + } + + @Override + public String toString() { + + StringBuilder buffer = new StringBuilder(); + toString(buffer); + return buffer.toString(); + } + + /** + * Method to extend {@link #toString()} logic. + * + * @param buffer is the {@link StringBuilder} where to {@link StringBuilder#append(Object) append} the string + * representation. + */ + protected void toString(StringBuilder buffer) { + + buffer.append(getClass().getSimpleName()); + if (this.id != null) { + buffer.append("[id="); + buffer.append(this.id); + buffer.append("]"); + } + } +} \ No newline at end of file diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/dataaccess/api/BinaryObjectEntity.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/dataaccess/api/BinaryObjectEntity.java new file mode 100644 index 0000000..7b9f9fb --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/dataaccess/api/BinaryObjectEntity.java @@ -0,0 +1,78 @@ +package com.devonfw.app.java.order.general.dataaccess.api; + +import java.sql.Blob; + +import javax.persistence.Entity; +import javax.persistence.Lob; +import javax.persistence.Table; +import javax.persistence.Column; + +import com.devonfw.app.java.order.general.common.api.BinaryObject; + +/** + * {@link ApplicationPersistenceEntity Entity} for {@link BinaryObject}. Contains the actual {@link Blob}. + */ +@Entity +@Table(name = "BinaryObject") +public class BinaryObjectEntity extends ApplicationPersistenceEntity implements BinaryObject { + + private static final long serialVersionUID = 1L; + + private Blob data; + + private String mimeType; + + private long size; + + /** + * The constructor. + */ + public BinaryObjectEntity() { + + super(); + } + + @Override + public void setMimeType(String mimeType) { + + this.mimeType = mimeType; + + } + + @Override + public String getMimeType() { + + return this.mimeType; + } + + /** + * @return the {@link Blob} data. + */ + @Lob + @Column(name = "content") + public Blob getData() { + + return this.data; + } + + /** + * @param data the data to set + */ + public void setData(Blob data) { + + this.data = data; + } + + @Column(name = "filesize") + @Override + public long getSize() { + + return this.size; + } + + @Override + public void setSize(long size) { + + this.size = size; + } +} diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/dataaccess/api/dao/BinaryObjectRepository.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/dataaccess/api/dao/BinaryObjectRepository.java new file mode 100644 index 0000000..6c543a9 --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/dataaccess/api/dao/BinaryObjectRepository.java @@ -0,0 +1,11 @@ +package com.devonfw.app.java.order.general.dataaccess.api.dao; + +import com.devonfw.module.jpa.dataaccess.api.data.DefaultRepository; +import com.devonfw.app.java.order.general.dataaccess.api.BinaryObjectEntity; + +/** + * {@link DefaultRepository} for {@link BinaryObjectEntity}. + */ +public interface BinaryObjectRepository extends DefaultRepository { + +} diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/api/UcManageBinaryObject.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/api/UcManageBinaryObject.java new file mode 100644 index 0000000..7b0af4f --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/api/UcManageBinaryObject.java @@ -0,0 +1,37 @@ +package com.devonfw.app.java.order.general.logic.api; + +import com.devonfw.app.java.order.general.logic.api.to.BinaryObjectEto; + +import java.sql.Blob; + +/** + * Use case for managing BinaryObject. + * + */ +public interface UcManageBinaryObject { + + /** + * @param data the Blob data to save + * @param binaryObjectEto the {@link BinaryObjectEto} + * @return {@link BinaryObjectEto} + */ + BinaryObjectEto saveBinaryObject(Blob data, BinaryObjectEto binaryObjectEto); + + /** + * @param binaryObjectId the ID of the {@link BinaryObjectEto} that should be deleted + */ + void deleteBinaryObject(Long binaryObjectId); + + /** + * @param binaryObjectId the ID of the {@link BinaryObjectEto} to find + * @return {@link BinaryObjectEto} + */ + BinaryObjectEto findBinaryObject(Long binaryObjectId); + + /** + * @param binaryObjectId the ID of the {@link BinaryObjectEto} the blob corresponds to + * @return {@link Blob} + */ + Blob getBinaryObjectBlob(Long binaryObjectId); + +} diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/base/AbstractComponentFacade.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/base/AbstractComponentFacade.java new file mode 100644 index 0000000..cf43023 --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/base/AbstractComponentFacade.java @@ -0,0 +1,16 @@ +package com.devonfw.app.java.order.general.logic.base; + +/** + * Abstract base class for any component implementation class in this application. + */ +public abstract class AbstractComponentFacade extends AbstractLogic { + + /** + * The constructor. + */ + public AbstractComponentFacade() { + + super(); + } + +} diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/base/AbstractLogic.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/base/AbstractLogic.java new file mode 100644 index 0000000..219ddef --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/base/AbstractLogic.java @@ -0,0 +1,81 @@ +package com.devonfw.app.java.order.general.logic.base; + +import com.devonfw.app.java.order.general.common.base.AbstractBeanMapperSupport; + +import com.devonfw.module.basic.common.api.entity.GenericEntity; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Abstract base class for implementations of business logic in this application. Actual implementations need + * to be annotated with {@link javax.inject.Named}. + * + * @see AbstractUc + * @see AbstractComponentFacade + */ +public abstract class AbstractLogic extends AbstractBeanMapperSupport { + + /** + * The constructor. + */ + public AbstractLogic() { + + super(); + } + + /** + * Creates a {@link Map} with all {@link GenericEntity entities} from the given {@link Collection} using their + * {@link GenericEntity#getId() ID} as key. All {@link GenericEntity entities} without an + * {@link GenericEntity#getId() ID} ({@code null}) will be ignored. + * + * @param is the generic type of the {@link GenericEntity#getId() ID}. + * @param is the generic type of the {@link GenericEntity entity}. + * @param entities is the {@link Collection} of {@link GenericEntity entities}. + * @return a {@link Map} mapping from {@link GenericEntity#getId() ID} to {@link GenericEntity entity}. + */ + protected static > Map getEntityMap(Collection entities) { + + Map id2EntityMap = new HashMap<>(); + for (E entity : entities) { + ID id = entity.getId(); + if (id != null) { + id2EntityMap.put(id, entity); + } + } + return id2EntityMap; + } + + /** + * Determines the {@link GenericEntity entities} to delete if currentList is the current list from the + * persistence and listToSave is the new list that shall be saved. In other words this method selects the + * {@link GenericEntity entities} from currentList that are not contained in listToSave. + * + * @param is the generic type of the {@link GenericEntity#getId() ID}. + * @param is the generic type of the {@link GenericEntity entity}. + * @param currentEntities is the {@link Collection} of the {@link GenericEntity entities} currently persisted. We + * assume that all objects in this list have an {@link GenericEntity#getId() ID} value (that is not + * {@code null}). + * @param entitiesToSave is the {@link Collection} that contains the {@link GenericEntity entities} that shall be + * saved. It may contain {@link GenericEntity entities} that have no {@link GenericEntity#getId() ID} that + * shall be newly created. + * @return the {@link List} with the {@link GenericEntity entities} to delete. + */ + protected static > List getEntities2Delete(Collection currentEntities, + Collection entitiesToSave) { + + List result = new ArrayList<>(currentEntities.size()); + Map entityMap = getEntityMap(entitiesToSave); + for (E entity : currentEntities) { + if (!entityMap.containsKey(entity.getId())) { + // entity from currentList is not contained in listToSave... + result.add(entity); + } + } + return result; + } + +} diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/base/AbstractUc.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/base/AbstractUc.java new file mode 100644 index 0000000..304aeae --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/base/AbstractUc.java @@ -0,0 +1,18 @@ +package com.devonfw.app.java.order.general.logic.base; + +/** + * Abstract base class for any use case in this application. Actual implementations need to be annotated with + * {@link javax.inject.Named}. + * + */ +public abstract class AbstractUc extends AbstractLogic { + + /** + * The constructor. + */ + public AbstractUc() { + + super(); + } + +} diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/impl/UcManageBinaryObjectImpl.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/impl/UcManageBinaryObjectImpl.java new file mode 100644 index 0000000..e473ceb --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/impl/UcManageBinaryObjectImpl.java @@ -0,0 +1,56 @@ +package com.devonfw.app.java.order.general.logic.impl; + +import java.sql.Blob; + +import javax.annotation.security.RolesAllowed; +import javax.inject.Inject; +import javax.inject.Named; + +import com.devonfw.app.java.order.general.dataaccess.api.BinaryObjectEntity; +import com.devonfw.app.java.order.general.dataaccess.api.dao.BinaryObjectRepository; +import com.devonfw.app.java.order.general.common.api.security.ApplicationAccessControlConfig; +import com.devonfw.app.java.order.general.logic.api.UcManageBinaryObject; +import com.devonfw.app.java.order.general.logic.api.to.BinaryObjectEto; +import com.devonfw.app.java.order.general.logic.base.AbstractUc; + +/** + * Implementation of {@link UcManageBinaryObject}. + */ +@Named +public class UcManageBinaryObjectImpl extends AbstractUc implements UcManageBinaryObject { + + @Inject + private BinaryObjectRepository binaryObjectRepository; + + @Override + @RolesAllowed(ApplicationAccessControlConfig.PERMISSION_SAVE_BINARY_OBJECT) + public BinaryObjectEto saveBinaryObject(Blob data, BinaryObjectEto binaryObjectEto) { + + BinaryObjectEntity binaryObjectEntity = getBeanMapper().map(binaryObjectEto, BinaryObjectEntity.class); + binaryObjectEntity.setData(data); + this.binaryObjectRepository.save(binaryObjectEntity); + return getBeanMapper().map(binaryObjectEntity, BinaryObjectEto.class); + } + + @Override + @RolesAllowed(ApplicationAccessControlConfig.PERMISSION_DELETE_BINARY_OBJECT) + public void deleteBinaryObject(Long binaryObjectId) { + + this.binaryObjectRepository.deleteById(binaryObjectId); + } + + @Override + @RolesAllowed(ApplicationAccessControlConfig.PERMISSION_FIND_BINARY_OBJECT) + public BinaryObjectEto findBinaryObject(Long binaryObjectId) { + + return getBeanMapper().map(this.binaryObjectRepository.find(binaryObjectId), BinaryObjectEto.class); + } + + @Override + @RolesAllowed(ApplicationAccessControlConfig.PERMISSION_FIND_BINARY_OBJECT) + public Blob getBinaryObjectBlob(Long binaryObjectId) { + + return this.binaryObjectRepository.find(binaryObjectId).getData(); + } + +} diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/impl/config/DefaultRolesPrefixPostProcessor.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/impl/config/DefaultRolesPrefixPostProcessor.java new file mode 100644 index 0000000..86455b0 --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/logic/impl/config/DefaultRolesPrefixPostProcessor.java @@ -0,0 +1,61 @@ +package com.devonfw.app.java.order.general.logic.impl.config; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.core.PriorityOrdered; +import org.springframework.security.access.annotation.Jsr250MethodSecurityMetadataSource; +import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; +import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler; +import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter; + +/** + * This is an implementation of {@link BeanPostProcessor} that allows to change the role prefix of spring-security. By + * default spring-security is magically adding a strange prefix called "ROLE_" to your granted authorities. In order to + * prevent this we use this class with an empty prefix. + */ +public class DefaultRolesPrefixPostProcessor implements BeanPostProcessor, PriorityOrdered { + + private final String rolePrefix; + + /** + * Der Konstruktor. + * + * @param rolePrefix das gewünschte Rollen-Präfix (z.B. der leere String). + */ + public DefaultRolesPrefixPostProcessor(String rolePrefix) { + super(); + this.rolePrefix = rolePrefix; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + + // remove this if you are not using JSR-250 + if (bean instanceof Jsr250MethodSecurityMetadataSource) { + ((Jsr250MethodSecurityMetadataSource) bean).setDefaultRolePrefix(this.rolePrefix); + } + + if (bean instanceof DefaultMethodSecurityExpressionHandler) { + ((DefaultMethodSecurityExpressionHandler) bean).setDefaultRolePrefix(this.rolePrefix); + } + if (bean instanceof DefaultWebSecurityExpressionHandler) { + ((DefaultWebSecurityExpressionHandler) bean).setDefaultRolePrefix(this.rolePrefix); + } + if (bean instanceof SecurityContextHolderAwareRequestFilter) { + ((SecurityContextHolderAwareRequestFilter) bean).setRolePrefix(this.rolePrefix); + } + return bean; + } + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + + return bean; + } + + @Override + public int getOrder() { + + return PriorityOrdered.HIGHEST_PRECEDENCE; + } +} diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/LoginController.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/LoginController.java new file mode 100644 index 0000000..fb7ce32 --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/LoginController.java @@ -0,0 +1,57 @@ +package com.devonfw.app.java.order.general.service.impl; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.servlet.ModelAndView; + +/** + * Controller for Login-Page. + */ +@Controller +public class LoginController { + + /** + * Default URL to redirect to after successfully login. + */ + public final static String defaultTargetUrl = "/"; + + /** + * Builds the model for the login page---mainly focusing on the error message handling. + * + * @param authentication_failed flag for authentication failed + * @param invalid_session flag for invalid session + * @param access_denied flag for access denied + * @param logout flag for successful logout + * @return the view model + */ + @RequestMapping(value = "/login**", method = {RequestMethod.GET,RequestMethod.POST}) + public ModelAndView login( + @RequestParam(value = "authentication_failed", required = false) boolean authentication_failed, + @RequestParam(value = "invalid_session", required = false) boolean invalid_session, + @RequestParam(value = "access_denied", required = false) boolean access_denied, + @RequestParam(value = "logout", required = false) boolean logout) { + + final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (!authentication.getPrincipal().equals("anonymousUser")) { + return new ModelAndView("redirect:" + defaultTargetUrl); + } + + ModelAndView model = new ModelAndView(); + if (authentication_failed) { + model.addObject("error", "Authentication failed!"); + } else if (invalid_session) { + model.addObject("error", "You are currently not logged in!"); + } else if (access_denied) { + model.addObject("error", "You have insufficient permissions to access this page!"); + } else if (logout) { + model.addObject("msg", "Logout successful!"); + } + + return model; + } + +} diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/BaseWebSecurityConfig.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/BaseWebSecurityConfig.java new file mode 100644 index 0000000..0a5a036 --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/BaseWebSecurityConfig.java @@ -0,0 +1,147 @@ +package com.devonfw.app.java.order.general.service.impl.config; + +import javax.inject.Inject; +import javax.servlet.Filter; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; +import org.springframework.security.web.authentication.logout.LogoutFilter; +import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; +import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; +import org.springframework.security.web.csrf.CsrfFilter; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; + +import com.devonfw.module.security.common.impl.rest.AuthenticationSuccessHandlerSendingOkHttpStatusCode; +import com.devonfw.module.security.common.impl.rest.JsonUsernamePasswordAuthenticationFilter; +import com.devonfw.module.security.common.impl.rest.LogoutSuccessHandlerReturningOkHttpStatusCode; + +/** + * This type serves as a base class for extensions of the {@code WebSecurityConfigurerAdapter} and provides a default + * configuration.
+ * Security configuration is based on {@link WebSecurityConfigurerAdapter}. This configuration is by purpose designed + * most simple for two channels of authentication: simple login form and rest-url. + */ +public abstract class BaseWebSecurityConfig extends WebSecurityConfigurerAdapter { + + @Value("${security.cors.enabled}") + boolean corsEnabled = false; + + @Inject + private UserDetailsService userDetailsService; + + @Inject + private PasswordEncoder passwordEncoder; + + private CorsFilter getCorsFilter() { + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + config.addAllowedOrigin("*"); + config.addAllowedHeader("*"); + config.addAllowedMethod("OPTIONS"); + config.addAllowedMethod("HEAD"); + config.addAllowedMethod("GET"); + config.addAllowedMethod("PUT"); + config.addAllowedMethod("POST"); + config.addAllowedMethod("DELETE"); + config.addAllowedMethod("PATCH"); + source.registerCorsConfiguration("/**", config); + return new CorsFilter(source); + } + + /** + * Configure spring security to enable a simple webform-login + a simple rest login. + */ + @Override + public void configure(HttpSecurity http) throws Exception { + + String[] unsecuredResources = new String[] { "/login", "/security/**", "/services/rest/login", + "/services/rest/logout" }; + + http + // + .userDetailsService(this.userDetailsService) + // define all urls that are not to be secured + .authorizeRequests().antMatchers(unsecuredResources).permitAll().anyRequest().authenticated().and() + + // activate crsf check for a selection of urls (but not for login & logout) + .csrf().disable().httpBasic().and() + + .headers().frameOptions().sameOrigin().and() + + // configure parameters for simple form login (and logout) + .formLogin().successHandler(new SimpleUrlAuthenticationSuccessHandler()).defaultSuccessUrl("/") + .failureUrl("/login.html?error").loginProcessingUrl("/j_spring_security_login").usernameParameter("username") + .passwordParameter("password").and() + // logout via POST is possible + .logout().logoutSuccessUrl("/login.html").and() + + // register login and logout filter that handles rest logins + .addFilterAfter(getSimpleRestAuthenticationFilter(), BasicAuthenticationFilter.class) + .addFilterAfter(getSimpleRestLogoutFilter(), LogoutFilter.class); + + if (this.corsEnabled) { + http.addFilterBefore(getCorsFilter(), CsrfFilter.class); + } + } + + /** + * Create a simple filter that allows logout on a REST Url /services/rest/logout and returns a simple HTTP status 200 + * ok. + * + * @return the filter. + */ + protected Filter getSimpleRestLogoutFilter() { + + LogoutFilter logoutFilter = new LogoutFilter(new LogoutSuccessHandlerReturningOkHttpStatusCode(), + new SecurityContextLogoutHandler()); + + // configure logout for rest logouts + logoutFilter.setLogoutRequestMatcher(new AntPathRequestMatcher("/services/rest/logout")); + + return logoutFilter; + } + + /** + * Create a simple authentication filter for REST logins that reads user-credentials from a json-parameter and returns + * status 200 instead of redirect after login. + * + * @return the {@link JsonUsernamePasswordAuthenticationFilter}. + * @throws Exception if something goes wrong. + */ + protected JsonUsernamePasswordAuthenticationFilter getSimpleRestAuthenticationFilter() throws Exception { + + JsonUsernamePasswordAuthenticationFilter jsonFilter = new JsonUsernamePasswordAuthenticationFilter( + new AntPathRequestMatcher("/services/rest/login")); + jsonFilter.setPasswordParameter("j_password"); + jsonFilter.setUsernameParameter("j_username"); + jsonFilter.setAuthenticationManager(authenticationManager()); + // set failurehandler that uses no redirect in case of login failure; just HTTP-status: 401 + jsonFilter.setAuthenticationManager(authenticationManagerBean()); + jsonFilter.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler()); + // set successhandler that uses no redirect in case of login success; just HTTP-status: 200 + jsonFilter.setAuthenticationSuccessHandler(new AuthenticationSuccessHandlerSendingOkHttpStatusCode()); + return jsonFilter; + } + + @SuppressWarnings("javadoc") + @Inject + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + + auth.inMemoryAuthentication().withUser("waiter").password(this.passwordEncoder.encode("waiter")).roles("Waiter") + .and().withUser("cook").password(this.passwordEncoder.encode("cook")).roles("Cook").and().withUser("barkeeper") + .password(this.passwordEncoder.encode("barkeeper")).roles("Barkeeper").and().withUser("chief") + .password(this.passwordEncoder.encode("chief")).roles("Chief"); + } + +} diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/ServletInitializer.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/ServletInitializer.java new file mode 100644 index 0000000..96e8033 --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/ServletInitializer.java @@ -0,0 +1,23 @@ +package com.devonfw.app.java.order.general.service.impl.config; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.context.annotation.Configuration; + +import com.devonfw.app.java.order.SpringBootApp; + +/** + * This auto configuration will be used by spring boot to enable traditional deployment to a servlet container. You may + * remove this class if you run your application with embedded tomcat only. Tomcat startup will be twice as fast. + */ +@Configuration +@EnableAutoConfiguration +public class ServletInitializer extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + + return application.sources(SpringBootApp.class); + } +} diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/WebConfig.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/WebConfig.java new file mode 100644 index 0000000..2aa62f0 --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/WebConfig.java @@ -0,0 +1,80 @@ +package com.devonfw.app.java.order.general.service.impl.config; + +import javax.servlet.Filter; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.filter.CharacterEncodingFilter; + +import com.devonfw.module.logging.common.api.DiagnosticContextFacade; +import com.devonfw.module.logging.common.impl.DiagnosticContextFacadeImpl; +import com.devonfw.module.logging.common.impl.DiagnosticContextFilter; +import com.devonfw.module.logging.common.impl.PerformanceLogFilter; +import com.devonfw.module.service.common.api.constants.ServiceConstants; + +/** + * Registers a number of filters for web requests. + */ +@Configuration +public class WebConfig { + + private @Autowired AutowireCapableBeanFactory beanFactory; + + /** + * @return the {@link FilterRegistrationBean} to register the {@link PerformanceLogFilter} that will log all requests + * with their duration and status code. + */ + @Bean + public FilterRegistrationBean performanceLogFilter() { + + FilterRegistrationBean registration = new FilterRegistrationBean(); + Filter performanceLogFilter = new PerformanceLogFilter(); + this.beanFactory.autowireBean(performanceLogFilter); + registration.setFilter(performanceLogFilter); + registration.addUrlPatterns("/*"); + return registration; + } + + /** + * @return the {@link DiagnosticContextFacade} implementation. + */ + @Bean(name = "DiagnosticContextFacade") + public DiagnosticContextFacade diagnosticContextFacade() { + + return new DiagnosticContextFacadeImpl(); + } + + /** + * @return the {@link FilterRegistrationBean} to register the {@link DiagnosticContextFilter} that adds the + * correlation id as MDC so it will be included in all associated logs. + */ + @Bean + public FilterRegistrationBean diagnosticContextFilter() { + + FilterRegistrationBean registration = new FilterRegistrationBean(); + Filter diagnosticContextFilter = new DiagnosticContextFilter(); + this.beanFactory.autowireBean(diagnosticContextFilter); + registration.setFilter(diagnosticContextFilter); + registration.addUrlPatterns(ServiceConstants.URL_PATH_SERVICES + "/*"); + return registration; + } + + /** + * @return the {@link FilterRegistrationBean} to register the {@link CharacterEncodingFilter} to set the encoding. + */ + @Bean + public FilterRegistrationBean setCharacterEncodingFilter() { + + FilterRegistrationBean registration = new FilterRegistrationBean(); + CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter(); + characterEncodingFilter.setEncoding("UTF-8"); + characterEncodingFilter.setForceEncoding(false); + this.beanFactory.autowireBean(characterEncodingFilter); + registration.setFilter(characterEncodingFilter); + registration.addUrlPatterns("/*"); + return registration; + } +} \ No newline at end of file diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/WebSecurityBeansConfig.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/WebSecurityBeansConfig.java new file mode 100644 index 0000000..fbe6e41 --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/WebSecurityBeansConfig.java @@ -0,0 +1,79 @@ +package com.devonfw.app.java.order.general.service.impl.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.factory.PasswordEncoderFactories; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.csrf.CsrfTokenRepository; +import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository; + +import com.devonfw.app.java.order.general.logic.impl.config.DefaultRolesPrefixPostProcessor; +import com.devonfw.module.security.common.api.accesscontrol.AccessControlProvider; +import com.devonfw.module.security.common.base.accesscontrol.AccessControlSchemaProvider; +import com.devonfw.module.security.common.impl.accesscontrol.AccessControlProviderImpl; +import com.devonfw.module.security.common.impl.accesscontrol.AccessControlSchemaProviderImpl; + +/** + * This configuration class provides factory methods for several Spring security related beans. + * + */ +@Configuration +public class WebSecurityBeansConfig { + + /** + * This method provides a new instance of {@code AccessControlProvider} + * + * @return the newly created {@code AccessControlProvider} + */ + @Bean + public AccessControlProvider accessControlProvider() { + + return new AccessControlProviderImpl(); + } + + /** + * This method provides a new instance of {@code AccessControlSchemaProvider} + * + * @return the newly created {@code AccessControlSchemaProvider} + */ + @Bean + public AccessControlSchemaProvider accessControlSchemaProvider() { + + return new AccessControlSchemaProviderImpl(); + } + + /** + * This method provides a new instance of {@code CsrfTokenRepository} + * + * @return the newly created {@code CsrfTokenRepository} + */ + @Bean + public CsrfTokenRepository csrfTokenRepository() { + + return new HttpSessionCsrfTokenRepository(); + } + + /** + * This method provides a new instance of {@code DefaultRolesPrefixPostProcessor} + * + * @return the newly create {@code DefaultRolesPrefixPostProcessor} + */ + @Bean + public static DefaultRolesPrefixPostProcessor defaultRolesPrefixPostProcessor() { + + // By default Spring-Security is setting the prefix "ROLE_" for all permissions/authorities. + // We disable this undesired behavior here... + return new DefaultRolesPrefixPostProcessor(""); + } + + /** + * This method provide a new instance of {@code DelegatingPasswordEncoder} + * + * @return the newly create {@code DelegatingPasswordEncoder} + */ + @Bean + public PasswordEncoder passwordEncoder() { + + return PasswordEncoderFactories.createDelegatingPasswordEncoder(); + } +} diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/WebSecurityConfig.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/WebSecurityConfig.java new file mode 100644 index 0000000..89ca1b6 --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/config/WebSecurityConfig.java @@ -0,0 +1,21 @@ +package com.devonfw.app.java.order.general.service.impl.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +import com.devonfw.module.basic.common.api.config.SpringProfileConstants; + +/** + * Security configuration based on {@link WebSecurityConfigurerAdapter}. This configuration is by purpose designed most + * simple for two channels of authentication: simple login form and rest-url. (Copied from + * {@link com.devonfw.app.java.order.general.service.impl.config.BaseWebSecurityConfig} + * + */ +@Configuration +@EnableWebSecurity +@Profile(SpringProfileConstants.NOT_JUNIT) +public class WebSecurityConfig extends BaseWebSecurityConfig { + +} diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/rest/ApplicationAccessDeniedHandler.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/rest/ApplicationAccessDeniedHandler.java new file mode 100644 index 0000000..cf47678 --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/rest/ApplicationAccessDeniedHandler.java @@ -0,0 +1,46 @@ +package com.devonfw.app.java.order.general.service.impl.rest; + +import com.devonfw.module.rest.service.impl.RestServiceExceptionFacade; + +import java.io.IOException; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.Response; + +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.web.access.AccessDeniedHandler; + +/** + * + */ +@Named("ApplicationAccessDeniedHandler") +public class ApplicationAccessDeniedHandler implements AccessDeniedHandler { + + private RestServiceExceptionFacade exceptionFacade; + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, + AccessDeniedException accessDeniedException) throws IOException, ServletException { + + Response restResponse = this.exceptionFacade.toResponse(accessDeniedException); + Object entity = restResponse.getEntity(); + response.setStatus(restResponse.getStatus()); + if (entity != null) { + response.getWriter().write(entity.toString()); + } + } + + /** + * @param exceptionFacade the exceptionFacade to set + */ + @Inject + public void setExceptionFacade(RestServiceExceptionFacade exceptionFacade) { + + this.exceptionFacade = exceptionFacade; + } + +} diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/rest/SecurityRestServiceImpl.java b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/rest/SecurityRestServiceImpl.java new file mode 100644 index 0000000..0bd73b0 --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/general/service/impl/rest/SecurityRestServiceImpl.java @@ -0,0 +1,77 @@ +package com.devonfw.app.java.order.general.service.impl.rest; + +import javax.annotation.security.PermitAll; +import javax.inject.Inject; +import javax.inject.Named; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.transaction.Transactional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.web.csrf.CsrfToken; +import org.springframework.security.web.csrf.CsrfTokenRepository; + +import com.devonfw.app.java.order.general.common.api.exception.NoActiveUserException; +import com.devonfw.app.java.order.general.common.api.to.UserProfileTo; +import com.devonfw.app.java.order.general.service.api.rest.SecurityRestService; + +/** + * Implementation of {@link SecurityRestService}. + */ +@Named +@Transactional +public class SecurityRestServiceImpl implements SecurityRestService { + + /** Logger instance. */ + private static final Logger LOG = LoggerFactory.getLogger(SecurityRestServiceImpl.class); + + /** + * Use {@link CsrfTokenRepository} for CSRF protection. + */ + private CsrfTokenRepository csrfTokenRepository; + + @Override + @PermitAll + public CsrfToken getCsrfToken(HttpServletRequest request, HttpServletResponse response) { + + CsrfToken token = this.csrfTokenRepository.loadToken(request); + if (token == null) { + LOG.error("No CsrfToken could be found - instantiating a new Token"); + token = this.csrfTokenRepository.generateToken(request); + this.csrfTokenRepository.saveToken(token, request, response); + } + return token; + } + + @Override + @PermitAll + public UserProfileTo getCurrentUser() { + + SecurityContext context = SecurityContextHolder.getContext(); + Authentication authentication = null; + if (context != null) { + authentication = context.getAuthentication(); + } + if (authentication == null) { + throw new NoActiveUserException(); + } + UserDetails user = (UserDetails) authentication.getPrincipal(); + UserProfileTo profile = new UserProfileTo(); + profile.setLogin(user.getUsername()); + return profile; + } + + /** + * @param csrfTokenRepository the csrfTokenRepository to set + */ + @Inject + public void setCsrfTokenRepository(CsrfTokenRepository csrfTokenRepository) { + + this.csrfTokenRepository = csrfTokenRepository; + } +} diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/orderservice/dataaccess/api/CustomerEntity.java b/order-service/core/src/main/java/com/devonfw/app/java/order/orderservice/dataaccess/api/CustomerEntity.java new file mode 100644 index 0000000..3222e46 --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/orderservice/dataaccess/api/CustomerEntity.java @@ -0,0 +1,83 @@ +package com.devonfw.app.java.order.orderservice.dataaccess.api; + +import java.util.Collections; +import java.util.Set; + +import javax.persistence.Entity; +import javax.persistence.OneToMany; +import javax.validation.constraints.NotNull; + +import com.devonfw.app.java.order.general.dataaccess.api.ApplicationPersistenceEntity; +import com.devonfw.app.java.order.orderservice.common.api.Customer; + +/** + * @author ARMIKA + */ +@Entity(name = "Customer") +public class CustomerEntity extends ApplicationPersistenceEntity implements Customer { + + private String firstname; + + private String lastname; + + private Set orders; + + private static final long serialVersionUID = 1L; + + /** + * @return orders + */ + @OneToMany(mappedBy = "owner") + public Set getOrders() { + + return Collections.unmodifiableSet(this.orders); + } + + /** + * @param orders new value of {@link #getorders}. + */ + public void setOrders(@NotNull Set orders) { + + this.orders.clear(); + this.orders.addAll(orders); + } + + /** + * @return firstname + */ + @Override + public String getFirstname() { + + return this.firstname; + } + + /** + * @param firstname new value of {@link #getfirstname}. + */ + @SuppressWarnings("javadoc") + @Override + public void setFirstname(String firstname) { + + this.firstname = firstname; + } + + /** + * @return lastname + */ + @Override + public String getLastname() { + + return this.lastname; + } + + /** + * @param lastname new value of {@link #getlastname}. + */ + @SuppressWarnings("javadoc") + @Override + public void setLastname(String lastname) { + + this.lastname = lastname; + } + +} diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/orderservice/dataaccess/api/ItemEntity.java b/order-service/core/src/main/java/com/devonfw/app/java/order/orderservice/dataaccess/api/ItemEntity.java new file mode 100644 index 0000000..8a86a4a --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/orderservice/dataaccess/api/ItemEntity.java @@ -0,0 +1,78 @@ +package com.devonfw.app.java.order.orderservice.dataaccess.api; + +import javax.persistence.Entity; + +import com.devonfw.app.java.order.general.dataaccess.api.ApplicationPersistenceEntity; +import com.devonfw.app.java.order.orderservice.common.api.Item; + +/** + * @author ARMIKA Class representing Item Entity + */ +@Entity(name = "Item") +public class ItemEntity extends ApplicationPersistenceEntity implements Item { + + private String name; + + private String description; + + private Double price; + + private static final long serialVersionUID = 1L; + + /** + * @return name + */ + @Override + public String getName() { + + return this.name; + } + + /** + * @param name new value of {@link #getname}. + */ + @SuppressWarnings("javadoc") + @Override + public void setName(String name) { + + this.name = name; + } + + /** + * @return description + */ + @Override + public String getDescription() { + + return this.description; + } + + /** + * @param description new value of {@link #getdescription}. + */ + @SuppressWarnings("javadoc") + @Override + public void setDescription(String description) { + + this.description = description; + } + + /** + * @return price + */ + @Override + public Double getPrice() { + + return this.price; + } + + /** + * @param price new value of {@link #getprice}. + */ + @Override + public void setPrice(Double price) { + + this.price = price; + } + +} diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/orderservice/dataaccess/api/OrderEntity.java b/order-service/core/src/main/java/com/devonfw/app/java/order/orderservice/dataaccess/api/OrderEntity.java new file mode 100644 index 0000000..39b31a0 --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/orderservice/dataaccess/api/OrderEntity.java @@ -0,0 +1,145 @@ +package com.devonfw.app.java.order.orderservice.dataaccess.api; + +import java.time.LocalDate; +import java.util.Set; + +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import javax.persistence.ManyToOne; +import javax.persistence.Transient; + +import com.devonfw.app.java.order.general.dataaccess.api.ApplicationPersistenceEntity; +import com.devonfw.app.java.order.orderservice.common.api.Order; +import com.devonfw.app.java.order.orderservice.common.api.OrderStatus; + +/** + * @author ARMIKA + */ +@Entity(name = "Ordersummary") +public class OrderEntity extends ApplicationPersistenceEntity implements Order { + + private LocalDate creationDate; + + private Set orderPositions; + + private CustomerEntity owner; + + private Double price; + + private OrderStatus status; + + private static final long serialVersionUID = 1L; + + /** + * @return creationDate + */ + @Override + public LocalDate getCreationDate() { + + return this.creationDate; + } + + /** + * @param creationDate new value of {@link #getcreationDate}. + */ + @Override + public void setCreationDate(LocalDate creationDate) { + + this.creationDate = creationDate; + } + + /** + * @return orderPositions + */ + @ManyToMany + @JoinTable(name = "OrderPosition", joinColumns = @JoinColumn(name = "orderId", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "itemId", referencedColumnName = "id")) + public Set getOrderPositions() { + + return this.orderPositions; + } + + /** + * @param orderPositions new value of {@link #getorderPositions}. + */ + public void setOrderPositions(Set orderPositions) { + + this.orderPositions = orderPositions; + } + + /** + * @return owner + */ + @ManyToOne + @JoinColumn(name = "ownerId") + public CustomerEntity getOwner() { + + return this.owner; + } + + /** + * @param owner new value of {@link #getowner}. + */ + public void setOwner(CustomerEntity owner) { + + this.owner = owner; + } + + /** + * @return price + */ + @Override + public Double getPrice() { + + return this.price; + } + + /** + * @param price new value of {@link #getprice}. + */ + @Override + public void setPrice(Double price) { + + this.price = price; + } + + /** + * @return status + */ + @Override + @Enumerated(EnumType.STRING) + public OrderStatus getStatus() { + + return this.status; + } + + /** + * @param status new value of {@link #getstatus}. + */ + @Override + public void setStatus(OrderStatus status) { + + this.status = status; + } + + @Override + @Transient + public Long getOwnerId() { + + if (getOwner() != null) + return getOwner().getId(); + return null; + } + + @Override + public void setOwnerId(Long ownerId) { + + CustomerEntity e = new CustomerEntity(); + e.setId(ownerId); + setOwner(e); + } + +} diff --git a/order-service/core/src/main/java/com/devonfw/app/java/order/orderservice/dataaccess/api/repo/ItemRepository.java b/order-service/core/src/main/java/com/devonfw/app/java/order/orderservice/dataaccess/api/repo/ItemRepository.java new file mode 100644 index 0000000..cbe5499 --- /dev/null +++ b/order-service/core/src/main/java/com/devonfw/app/java/order/orderservice/dataaccess/api/repo/ItemRepository.java @@ -0,0 +1,12 @@ +package com.devonfw.app.java.order.orderservice.dataaccess.api.repo; + +import com.devonfw.app.java.order.orderservice.dataaccess.api.ItemEntity; +import com.devonfw.module.jpa.dataaccess.api.data.DefaultRepository; + +/** + * @author ARMIKA + * + */ +public interface ItemRepository extends DefaultRepository { + +} diff --git a/order-service/core/src/main/resources/META-INF/cxf/org.apache.cxf.Logger b/order-service/core/src/main/resources/META-INF/cxf/org.apache.cxf.Logger new file mode 100644 index 0000000..27dd788 --- /dev/null +++ b/order-service/core/src/main/resources/META-INF/cxf/org.apache.cxf.Logger @@ -0,0 +1 @@ +org.apache.cxf.common.logging.Slf4jLogger \ No newline at end of file diff --git a/order-service/core/src/main/resources/META-INF/orm.xml b/order-service/core/src/main/resources/META-INF/orm.xml new file mode 100644 index 0000000..55d30ec --- /dev/null +++ b/order-service/core/src/main/resources/META-INF/orm.xml @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/order-service/core/src/main/resources/application.properties b/order-service/core/src/main/resources/application.properties new file mode 100644 index 0000000..c3bc26b --- /dev/null +++ b/order-service/core/src/main/resources/application.properties @@ -0,0 +1,32 @@ +# This is the configuration file shipped with the application that contains reasonable defaults. +# Environment specific configurations are configured in config/application.properties. +# If you are running in a servlet container you may add this to lib/config/application.properties in case you do not +# want to touch the WAR file. + +# server.port=8080 + +spring.application.name=order-service +server.servlet.context-path=/ + +security.expose.error.details=false +security.cors.enabled=false +spring.jpa.hibernate.ddl-auto=validate + +# Datasource for accessing the database +# https://github.com/spring-projects/spring-boot/blob/d3c34ee3d1bfd3db4a98678c524e145ef9bca51c/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jdbc/DatabaseDriver.java +spring.jpa.database=h2 +# spring.jpa.database-platform=org.hibernate.dialect.H2Dialect +# spring.datasource.driver-class-name=org.h2.Driver +spring.datasource.username=sa + +# Hibernate NamingStrategy has been deprecated and then removed in favor of two step naming strategy ImplicitNamingStrategy and PhysicalNamingStrategy +spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl +spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl + +# to prevent that Spring Boot launches batch jobs on startup +# might otherwise lead to errors if job parameters are needed (or lead to unwanted modifications and longer startup times) +# see http://stackoverflow.com/questions/22318907/how-to-stop-spring-batch-scheduled-jobs-from-running-at-first-time-when-executin +spring.batch.job.enabled=false + +# Flyway for Database Setup and Migrations +spring.flyway.locations=classpath:db/migration,classpath:db/type/h2 diff --git a/order-service/core/src/main/resources/config/app/common/dozer-mapping.xml b/order-service/core/src/main/resources/config/app/common/dozer-mapping.xml new file mode 100644 index 0000000..3d459e3 --- /dev/null +++ b/order-service/core/src/main/resources/config/app/common/dozer-mapping.xml @@ -0,0 +1,37 @@ + + + + + + true + + + + java.lang.Long + java.lang.Integer + java.lang.Number + + + + + + + + + com.devonfw.app.java.order.general.dataaccess.api.ApplicationPersistenceEntity + com.devonfw.module.basic.common.api.to.AbstractEto + + this + persistentEntity + + + diff --git a/order-service/core/src/main/resources/config/app/dataaccess/NamedQueries.xml b/order-service/core/src/main/resources/config/app/dataaccess/NamedQueries.xml new file mode 100644 index 0000000..ee83c8c --- /dev/null +++ b/order-service/core/src/main/resources/config/app/dataaccess/NamedQueries.xml @@ -0,0 +1,5 @@ + + + + diff --git a/order-service/core/src/main/resources/config/app/security/access-control-schema.xml b/order-service/core/src/main/resources/config/app/security/access-control-schema.xml new file mode 100644 index 0000000..418f6eb --- /dev/null +++ b/order-service/core/src/main/resources/config/app/security/access-control-schema.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/order-service/core/src/main/resources/config/application.properties b/order-service/core/src/main/resources/config/application.properties new file mode 100644 index 0000000..08e3c5c --- /dev/null +++ b/order-service/core/src/main/resources/config/application.properties @@ -0,0 +1,23 @@ +# This is the spring boot configuration file for development. It will not be included into the application. +# In order to set specific configurations in a regular installed environment create an according file +# config/application.properties in the server. If you are deploying the application to a servlet container as untouched +# WAR file you can locate this config folder in ${symbol_dollar}{CATALINA_BASE}/lib. If you want to deploy multiple applications to +# the same container (not recommended by default) you need to ensure the WARs are extracted in webapps folder and locate +# the config folder inside the WEB-INF/classes folder of the webapplication. + +server.port=8081 +server.servlet.context-path=/order-service + +# Datasource for accessing the database +# See https://github.com/devonfw-wiki/devon4j/wiki/guide-configuration#security-configuration +#jasypt.encryptor.password=none +#spring.datasource.password=ENC(7CnHiadYc0Wh2FnWADNjJg==) +spring.datasource.password= +spring.datasource.url=jdbc:h2:mem:order-service + +# Enable JSON pretty printing +spring.jackson.serialization.INDENT_OUTPUT=true + +# Flyway for Database Setup and Migrations +spring.flyway.enabled=true +spring.flyway.clean-on-validation-error=true diff --git a/order-service/core/src/main/resources/db/migration/1.0/V0004__Add_blob_data.sql b/order-service/core/src/main/resources/db/migration/1.0/V0004__Add_blob_data.sql new file mode 100644 index 0000000..574db16 --- /dev/null +++ b/order-service/core/src/main/resources/db/migration/1.0/V0004__Add_blob_data.sql @@ -0,0 +1 @@ +INSERT INTO BinaryObject(id, ModificationCounter, filesize, content, mimeType) VALUES (10, 0, 1150 ,'00000100010010100000010020006804000016000000280000001000000020000000010020000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000ECBD48EFECBD48FFECBD48FFECBD48FFECBD48FFECBD48F90000000000000000000000000000000000000000000000000000000000000000ECBD48FAECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FF0000000000000000000000000000000000000000ECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FF000000000000000000000000ECBD48FAECBD48FFFFFFFFFFECBD48FFFFFFFFFFFFFFFFFFEEC35EFFFFFEFDFFFFFFFFFFFFFFFFFFFFFFFFFFEFC86BFFECBD48FFECBD48FF0000000000000000ECBD48FFECBD48FFF4D696FFECBD48FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFECBD48FFECBD48FF00000000ECBD48EFECBD48FFECBD48FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1CB7FFFFFFFFFFFFFFFFFFFECBD48FFECBD48FFFFFFFFFFECBD48FFECBD48FFECBD48F9ECBD48FFECBD48FFECBD48FFFFFFFFFFFFFFFFFFFDF8EFFFECBD48FFFFFFFFFFFFFFFFFFFFFFFFFFECBD48FFECBD48FFFFFFFFFFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFFFFFFFFFFFFFFFFFFFFFFFFFECBD48FFECBD48FFFFFFFFFFFFFFFFFFFFFFFFFFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFFFFFFFFFFFFFFFFFFFFFFFFFECBD48FFECBD48FFECBD48FFECBD48FFECBD4AFFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFFFFFFFFFFEF9F3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFECBD48FFECBD48FFECBD48FFECBD48F9ECBD48FFECBD48FFFFFFFFFFEEC45FFFECBD48FFF1CD7EFFFFFFFFFFFFFFFFFFFFFFFFFFEDC156FFFFFFFFFFFFFFFFFFECBD48FFECBD48FFECBD48EF00000000ECBD48FFECBD48FFFFFFFFFFFEF9F2FFECBD48FFFCF5E8FFFFFFFFFFFFFFFFFFFFFFFFFFECBD48FFFFFFFFFFFFFFFFFFECBD48FFECBD48FF0000000000000000ECBD48FFECBD48FFEFC769FFFFFFFFFFFFFFFFFFFFFFFFFFEEC35EFFEEC25CFFFFFFFFFFFFFFFFFFFFFFFFFFEFC86BFFECBD48FFECBD48FA000000000000000000000000ECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FF0000000000000000000000000000000000000000ECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FFECBD48FA0000000000000000000000000000000000000000000000000000000000000000ECBD48F9ECBD48FFECBD48FFECBD48FFECBD48FFECBD48EF0000000000000000000000000000000000000000F81F0000E0070000C003000080010000800100000000000000000000000000000000000000000000000000008001000080010000C0030000E0070000F81F0000', 'image/vnd.microsoft.icon'); diff --git a/order-service/core/src/main/resources/db/migration/1.0/V0005__CreateOrderServiceSchema.sql b/order-service/core/src/main/resources/db/migration/1.0/V0005__CreateOrderServiceSchema.sql new file mode 100644 index 0000000..c7a81c3 --- /dev/null +++ b/order-service/core/src/main/resources/db/migration/1.0/V0005__CreateOrderServiceSchema.sql @@ -0,0 +1,3 @@ +CREATE SCHEMA order_service AUTHORIZATION sa; + +set schema order_service; \ No newline at end of file diff --git a/order-service/core/src/main/resources/db/migration/1.0/V0006__CreateItemCustomerOrderSummaryTables.sql b/order-service/core/src/main/resources/db/migration/1.0/V0006__CreateItemCustomerOrderSummaryTables.sql new file mode 100644 index 0000000..a26020c --- /dev/null +++ b/order-service/core/src/main/resources/db/migration/1.0/V0006__CreateItemCustomerOrderSummaryTables.sql @@ -0,0 +1,28 @@ +CREATE TABLE Item ( + id BIGINT NOT NULL AUTO_INCREMENT, + modificationCounter INTEGER NOT NULL, + name VARCHAR(255) NOT NULL, + description VARCHAR(255), + price DOUBLE NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT UC_Item_name UNIQUE(name) +); + +CREATE TABLE Customer ( + id BIGINT NOT NULL AUTO_INCREMENT, + modificationCounter INTEGER NOT NULL, + firstname VARCHAR(255) NOT NULL, + lastname VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), +); + +CREATE TABLE Ordersummary ( + id BIGINT NOT NULL AUTO_INCREMENT, + modificationCounter INTEGER NOT NULL, + price DOUBLE, + ownerId BIGINT NOT NULL, + creationDate DATE NOT NULL, + status VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT FK_OrderSummary_owner FOREIGN KEY(ownerId) REFERENCES Customer(id) +); \ No newline at end of file diff --git a/order-service/core/src/main/resources/db/migration/1.0/V0007__CreateOrderPositionTable .sql b/order-service/core/src/main/resources/db/migration/1.0/V0007__CreateOrderPositionTable .sql new file mode 100644 index 0000000..e96e0e8 --- /dev/null +++ b/order-service/core/src/main/resources/db/migration/1.0/V0007__CreateOrderPositionTable .sql @@ -0,0 +1,6 @@ +CREATE TABLE OrderPosition ( + orderId BIGINT NOT NULL, + itemId BIGINT NOT NULL, + CONSTRAINT FK_OrderPosition_order FOREIGN KEY(orderId) REFERENCES OrderSummary(id), + CONSTRAINT FK_OrderPosition_item FOREIGN KEY(itemId) REFERENCES Item(id) +); \ No newline at end of file diff --git a/order-service/core/src/main/resources/db/migration/1.0/V0008__InsertItemData.sql b/order-service/core/src/main/resources/db/migration/1.0/V0008__InsertItemData.sql new file mode 100644 index 0000000..522a6a6 --- /dev/null +++ b/order-service/core/src/main/resources/db/migration/1.0/V0008__InsertItemData.sql @@ -0,0 +1,2 @@ +INSERT INTO Item(id, ModificationCounter, name, description, price) +VALUES (21, 0, 'spaghetti bolognese', 'Italy', 250); \ No newline at end of file diff --git a/order-service/core/src/main/resources/db/migration/1.0/V0009__InsertCustomerData.sql b/order-service/core/src/main/resources/db/migration/1.0/V0009__InsertCustomerData.sql new file mode 100644 index 0000000..01ab156 --- /dev/null +++ b/order-service/core/src/main/resources/db/migration/1.0/V0009__InsertCustomerData.sql @@ -0,0 +1,2 @@ +INSERT INTO Customer(id, ModificationCounter, firstname, lastname) +VALUES (31, 0, 'John', 'Travolta'); diff --git a/order-service/core/src/main/resources/db/migration/1.0/V0010__InsertOrderSummaryData.sql b/order-service/core/src/main/resources/db/migration/1.0/V0010__InsertOrderSummaryData.sql new file mode 100644 index 0000000..f9ca6f8 --- /dev/null +++ b/order-service/core/src/main/resources/db/migration/1.0/V0010__InsertOrderSummaryData.sql @@ -0,0 +1,2 @@ +INSERT INTO OrderSummary(id, ModificationCounter, price, ownerId, creationDate, status) +VALUES (41, 0, 671.10, 31, '2019-03-15', 'SERVED'); diff --git a/order-service/core/src/main/resources/db/migration/1.0/V0011__InsertOrderPositionData.sql b/order-service/core/src/main/resources/db/migration/1.0/V0011__InsertOrderPositionData.sql new file mode 100644 index 0000000..5969c6f --- /dev/null +++ b/order-service/core/src/main/resources/db/migration/1.0/V0011__InsertOrderPositionData.sql @@ -0,0 +1,2 @@ +INSERT INTO OrderPosition(orderId, itemId) +VALUES (41, 21); \ No newline at end of file diff --git a/order-service/core/src/main/resources/db/type/h2/V0001__Create_Sequence.sql b/order-service/core/src/main/resources/db/type/h2/V0001__Create_Sequence.sql new file mode 100644 index 0000000..2210438 --- /dev/null +++ b/order-service/core/src/main/resources/db/type/h2/V0001__Create_Sequence.sql @@ -0,0 +1,2 @@ +-- Leave a large ID space reserved for master-data and test-data +CREATE SEQUENCE HIBERNATE_SEQUENCE START WITH 1000000; diff --git a/order-service/core/src/main/resources/db/type/h2/V0002__Create_RevInfo.sql b/order-service/core/src/main/resources/db/type/h2/V0002__Create_RevInfo.sql new file mode 100644 index 0000000..8a41c6f --- /dev/null +++ b/order-service/core/src/main/resources/db/type/h2/V0002__Create_RevInfo.sql @@ -0,0 +1,6 @@ +-- *** RevInfo (Commit log for envers audit trail) *** +CREATE TABLE RevInfo( + id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1), + "timestamp" BIGINT NOT NULL, + userLogin VARCHAR(255) +); \ No newline at end of file diff --git a/order-service/core/src/main/resources/db/type/h2/V0003__Create_BinaryObject.sql b/order-service/core/src/main/resources/db/type/h2/V0003__Create_BinaryObject.sql new file mode 100644 index 0000000..d91838c --- /dev/null +++ b/order-service/core/src/main/resources/db/type/h2/V0003__Create_BinaryObject.sql @@ -0,0 +1,9 @@ +-- *** BinaryObject (BLOBs) *** +CREATE TABLE BinaryObject ( + id BIGINT NOT NULL AUTO_INCREMENT, + modificationCounter INTEGER NOT NULL, + content BLOB(2147483647), + filesize BIGINT NOT NULL, + mimeType VARCHAR(255), + PRIMARY KEY (ID) +); diff --git a/order-service/core/src/main/resources/db/type/hana/V0001__Create_Sequence.sql b/order-service/core/src/main/resources/db/type/hana/V0001__Create_Sequence.sql new file mode 100644 index 0000000..49953d2 --- /dev/null +++ b/order-service/core/src/main/resources/db/type/hana/V0001__Create_Sequence.sql @@ -0,0 +1,18 @@ +-- Leave a large ID space reserved for master-data and test-data +CREATE SEQUENCE HIBERNATE_SEQUENCE START WITH 1000000; + +-- hana does not support Dateadd function out of the box so we add it here to be able to use it for master-data SQLs +CREATE FUNCTION DATEADD(IN DATETYPE NVARCHAR(256), IN NUMBER INTEGER, IN TS TIMESTAMP) +RETURNS TSADD TIMESTAMP +AS +BEGIN + IF :DATETYPE = 'DAY' + THEN + TSADD = ADD_DAYS(:TS, :NUMBER); + ELSEIF :DATETYPE = 'HOUR' + THEN + TSADD = ADD_SECONDS(:TS, :NUMBER * 3600); + ELSE + SIGNAL SQL_ERROR_CODE 10000 SET MESSAGE_TEXT = 'Unsupported date type: ' || :DATETYPE; + END IF; +END; \ No newline at end of file diff --git a/order-service/core/src/main/resources/db/type/hana/V0002__Create_RevInfo.sql b/order-service/core/src/main/resources/db/type/hana/V0002__Create_RevInfo.sql new file mode 100644 index 0000000..38b650b --- /dev/null +++ b/order-service/core/src/main/resources/db/type/hana/V0002__Create_RevInfo.sql @@ -0,0 +1,7 @@ +-- *** RevInfo (Commit log for envers audit trail) *** +CREATE COLUMN TABLE RevInfo( + id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1), + "timestamp" BIGINT NOT NULL, + userLogin VARCHAR(255), + PRIMARY KEY (ID) +); diff --git a/order-service/core/src/main/resources/db/type/hana/V0003__Create_BinaryObject.sql b/order-service/core/src/main/resources/db/type/hana/V0003__Create_BinaryObject.sql new file mode 100644 index 0000000..6463189 --- /dev/null +++ b/order-service/core/src/main/resources/db/type/hana/V0003__Create_BinaryObject.sql @@ -0,0 +1,9 @@ +-- *** BinaryObject (BLOBs) *** +CREATE COLUMN TABLE BinaryObject ( + id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY, + modificationCounter INTEGER NOT NULL, + content BLOB, + filesize BIGINT NOT NULL, + mimeType VARCHAR(255), + CONSTRAINT PK_BinaryObject_id PRIMARY KEY(ID) +); diff --git a/order-service/core/src/main/resources/db/type/mssql/V0001__Create_Sequence.sql b/order-service/core/src/main/resources/db/type/mssql/V0001__Create_Sequence.sql new file mode 100644 index 0000000..80704fa --- /dev/null +++ b/order-service/core/src/main/resources/db/type/mssql/V0001__Create_Sequence.sql @@ -0,0 +1 @@ +-- no sequences are used in MS-SQL Server instead use IDENTITY(«seed»,1) for every ID diff --git a/order-service/core/src/main/resources/db/type/mssql/V0002__Create_RevInfo.sql b/order-service/core/src/main/resources/db/type/mssql/V0002__Create_RevInfo.sql new file mode 100644 index 0000000..ceae13d --- /dev/null +++ b/order-service/core/src/main/resources/db/type/mssql/V0002__Create_RevInfo.sql @@ -0,0 +1,6 @@ +-- *** RevInfo (Commit log for envers audit trail) *** +CREATE TABLE REVINFO( + id BIGINT NOT NULL IDENTITY(1,1), + timestamp BIGINT NOT NULL, + userLogin VARCHAR(255) +); diff --git a/order-service/core/src/main/resources/db/type/mssql/V0003__Create_BinaryObject.sql b/order-service/core/src/main/resources/db/type/mssql/V0003__Create_BinaryObject.sql new file mode 100644 index 0000000..4a454be --- /dev/null +++ b/order-service/core/src/main/resources/db/type/mssql/V0003__Create_BinaryObject.sql @@ -0,0 +1,9 @@ +-- *** BinaryObject (BLOBs) *** +CREATE TABLE BINARYOBJECT ( + id BIGINT NOT NULL IDENTITY(10,1), + modificationCounter INTEGER NOT NULL, + content varbinary(max), + filesize BIGINT NOT NULL, + mimeType VARCHAR(255), + PRIMARY KEY (ID) +); diff --git a/order-service/core/src/main/resources/db/type/mysql/V0001__Create_Sequence.sql b/order-service/core/src/main/resources/db/type/mysql/V0001__Create_Sequence.sql new file mode 100644 index 0000000..c5da8cf --- /dev/null +++ b/order-service/core/src/main/resources/db/type/mysql/V0001__Create_Sequence.sql @@ -0,0 +1 @@ +-- no sequences are used in MySQL/MariaDB instead use AUTO_INCREMENT for every ID diff --git a/order-service/core/src/main/resources/db/type/mysql/V0002__Create_RevInfo.sql b/order-service/core/src/main/resources/db/type/mysql/V0002__Create_RevInfo.sql new file mode 100644 index 0000000..a9590a2 --- /dev/null +++ b/order-service/core/src/main/resources/db/type/mysql/V0002__Create_RevInfo.sql @@ -0,0 +1,6 @@ +-- *** RevInfo (Commit log for envers audit trail) *** +CREATE TABLE REVINFO( + id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + timestamp BIGINT NOT NULL, + userLogin VARCHAR(255) +); diff --git a/order-service/core/src/main/resources/db/type/mysql/V0003__Create_BinaryObject.sql b/order-service/core/src/main/resources/db/type/mysql/V0003__Create_BinaryObject.sql new file mode 100644 index 0000000..ff7ca5f --- /dev/null +++ b/order-service/core/src/main/resources/db/type/mysql/V0003__Create_BinaryObject.sql @@ -0,0 +1,8 @@ +-- *** BinaryObject (BLOBs) *** +CREATE TABLE BINARYOBJECT ( + id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + modificationCounter INT NOT NULL, + content LONGBLOB, + filesize BIGINT NOT NULL, + mimeType VARCHAR(255) +); diff --git a/order-service/core/src/main/resources/db/type/oracle/V0001__Create_Sequence.sql b/order-service/core/src/main/resources/db/type/oracle/V0001__Create_Sequence.sql new file mode 100644 index 0000000..2210438 --- /dev/null +++ b/order-service/core/src/main/resources/db/type/oracle/V0001__Create_Sequence.sql @@ -0,0 +1,2 @@ +-- Leave a large ID space reserved for master-data and test-data +CREATE SEQUENCE HIBERNATE_SEQUENCE START WITH 1000000; diff --git a/order-service/core/src/main/resources/db/type/oracle/V0002__Create_RevInfo.sql b/order-service/core/src/main/resources/db/type/oracle/V0002__Create_RevInfo.sql new file mode 100644 index 0000000..a459bf2 --- /dev/null +++ b/order-service/core/src/main/resources/db/type/oracle/V0002__Create_RevInfo.sql @@ -0,0 +1,7 @@ +-- *** RevInfo (Commit log for envers audit trail) *** +CREATE TABLE RevInfo ( + id NUMBER(19), + "timestamp" NUMBER(19,0), + userLogin VARCHAR2(255 CHAR), + CONSTRAINT PK_RevInfo_id PRIMARY KEY (id) +); \ No newline at end of file diff --git a/order-service/core/src/main/resources/db/type/oracle/V0003__Create_BinaryObject.sql b/order-service/core/src/main/resources/db/type/oracle/V0003__Create_BinaryObject.sql new file mode 100644 index 0000000..e5635f1 --- /dev/null +++ b/order-service/core/src/main/resources/db/type/oracle/V0003__Create_BinaryObject.sql @@ -0,0 +1,9 @@ +-- *** BinaryObject (BLOBs) *** +CREATE TABLE BinaryObject ( + id NUMBER(19), + modificationCounter NUMBER(10, 0) NOT NULL, + content BLOB, + filesize NUMBER(10, 0) NOT NULL, + mimeType VARCHAR(255), + CONSTRAINT PK_BinaryObject_id PRIMARY KEY (ID) +); diff --git a/order-service/core/src/main/resources/db/type/postgresql/V0001__Create_Sequence.sql b/order-service/core/src/main/resources/db/type/postgresql/V0001__Create_Sequence.sql new file mode 100644 index 0000000..2210438 --- /dev/null +++ b/order-service/core/src/main/resources/db/type/postgresql/V0001__Create_Sequence.sql @@ -0,0 +1,2 @@ +-- Leave a large ID space reserved for master-data and test-data +CREATE SEQUENCE HIBERNATE_SEQUENCE START WITH 1000000; diff --git a/order-service/core/src/main/resources/db/type/postgresql/V0002__Create_RevInfo.sql b/order-service/core/src/main/resources/db/type/postgresql/V0002__Create_RevInfo.sql new file mode 100644 index 0000000..b6c8663 --- /dev/null +++ b/order-service/core/src/main/resources/db/type/postgresql/V0002__Create_RevInfo.sql @@ -0,0 +1,6 @@ +-- *** RevInfo (Commit log for envers audit trail) *** +CREATE TABLE REVINFO( + id BIGINT NOT NULL, + timestamp BIGINT NOT NULL, + userLogin VARCHAR(255) +); diff --git a/order-service/core/src/main/resources/db/type/postgresql/V0003__Create_BinaryObject.sql b/order-service/core/src/main/resources/db/type/postgresql/V0003__Create_BinaryObject.sql new file mode 100644 index 0000000..54bb161 --- /dev/null +++ b/order-service/core/src/main/resources/db/type/postgresql/V0003__Create_BinaryObject.sql @@ -0,0 +1,9 @@ +-- *** BinaryObject (BLOBs) *** +CREATE TABLE BINARYOBJECT ( + id BIGSERIAL NOT NULL, + modificationCounter INTEGER NOT NULL, + content BYTEA, + filesize BIGINT NOT NULL, + mimeType VARCHAR(255), + PRIMARY KEY (ID) +); diff --git a/order-service/core/src/main/resources/static/index.html b/order-service/core/src/main/resources/static/index.html new file mode 100644 index 0000000..88b7063 --- /dev/null +++ b/order-service/core/src/main/resources/static/index.html @@ -0,0 +1,18 @@ + + + +Welcome + + +

Welcome

+ This is a test! +
+ Services Overview (CXF) +
+
+ +
+ +
+ + \ No newline at end of file diff --git a/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/Devon4jPackageCheckTest.java b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/Devon4jPackageCheckTest.java new file mode 100644 index 0000000..a2d2e27 --- /dev/null +++ b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/Devon4jPackageCheckTest.java @@ -0,0 +1,66 @@ +package com.devonfw.app.java.order.general.common.base; + +import java.util.HashSet; +import java.util.Set; + +import net.sf.mmm.util.reflect.api.ReflectionUtil; +import net.sf.mmm.util.reflect.base.ReflectionUtilImpl; + +import org.assertj.core.api.SoftAssertions; +import org.junit.Test; + +import com.devonfw.module.basic.common.api.reflect.Devon4jPackage; +import com.devonfw.module.test.common.base.ModuleTest; + +/** + * This test verifies that the entire code of your code-base is located in {@link Devon4jPackage#isValid() valid Devon4j + * packages}. + */ +public class Devon4jPackageCheckTest extends ModuleTest { + + /** + * Scans all the packages of this application root pacakge namespace. Will verify that these are + * {@link Devon4jPackage#isValid() valid Devon4j packages}. + */ + @Test + public void testPackages() { + + Devon4jPackage pkg = Devon4jPackage.of(Devon4jPackageCheckTest.class); + assertThat(pkg.isValid()).isTrue(); + + ReflectionUtil ru = ReflectionUtilImpl.getInstance(); + Set classNames = ru.findClassNames(getRootPackage2Scan(pkg), true); + String appPackage = pkg.getRoot() + "." + pkg.getApplication(); + Set packages = new HashSet<>(128); + packages.add(appPackage); // allow SpringBootApp, etc. in application package + SoftAssertions assertion = new SoftAssertions(); + for (String className : classNames) { + int lastDot = className.lastIndexOf('.'); + if (lastDot > 0) { + String packageName = className.substring(0, lastDot); + boolean added = packages.add(packageName); + if (added) { + pkg = Devon4jPackage.of(packageName); + if (!pkg.isValid()) { + assertion.assertThat(pkg.isValid()) + .as("package " + packageName + " is invalid (component: " + pkg.getComponent() + ", layer: " + + pkg.getLayer() + ", scope: " + pkg.getScope() + "). Hint contains e.g. " + + className.substring(lastDot + 1)) + .isTrue(); + } + } + } + } + assertion.assertAll(); + } + + /** + * @param pkg the {@link Devon4jPackage} of this test. + * @return the root package to scan for {@link Class}es to get the actual packages to check. + */ + protected String getRootPackage2Scan(Devon4jPackage pkg) { + + return pkg.getRoot() + "." + pkg.getApplication(); + } + +} diff --git a/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/PermissionCheckTest.java b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/PermissionCheckTest.java new file mode 100644 index 0000000..38b62ae --- /dev/null +++ b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/PermissionCheckTest.java @@ -0,0 +1,67 @@ +package com.devonfw.app.java.order.general.common.base; + +import com.devonfw.module.test.common.base.ModuleTest; + +import java.lang.reflect.Method; +import java.util.Set; + +import javax.annotation.security.DenyAll; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; + +import net.sf.mmm.util.filter.api.Filter; +import net.sf.mmm.util.reflect.api.ReflectionUtil; +import net.sf.mmm.util.reflect.base.ReflectionUtilImpl; + +import org.assertj.core.api.SoftAssertions; +import org.junit.Test; + +/** + * Tests the permission check in logic layer. + */ +public class PermissionCheckTest extends ModuleTest { + + /** + * Check if all relevant methods in use case implementations have permission checks i.e. {@link RolesAllowed}, + * {@link DenyAll} or {@link PermitAll} annotation is applied. This is only checked for methods that are declared in + * the corresponding interface and thus have the {@link Override} annotations applied. + */ + @Test + public void permissionCheckAnnotationPresent() { + + String packageName = "com.devonfw.app.java.order"; + Filter filter = new Filter() { + + @Override + public boolean accept(String value) { + + return value.contains(".logic.impl.usecase.Uc") && value.endsWith("Impl"); + } + + }; + ReflectionUtil ru = ReflectionUtilImpl.getInstance(); + Set classNames = ru.findClassNames(packageName, true, filter); + Set> classes = ru.loadClasses(classNames); + SoftAssertions assertions = new SoftAssertions(); + for (Class clazz : classes) { + Method[] methods = clazz.getDeclaredMethods(); + for (Method method : methods) { + Method parentMethod = ru.getParentMethod(method); + if (parentMethod != null) { + Class declaringClass = parentMethod.getDeclaringClass(); + if (declaringClass.isInterface() && declaringClass.getSimpleName().startsWith("Uc")) { + boolean hasAnnotation = false; + if (method.getAnnotation(RolesAllowed.class) != null || method.getAnnotation(DenyAll.class) != null + || method.getAnnotation(PermitAll.class) != null) { + hasAnnotation = true; + } + assertions.assertThat(hasAnnotation) + .as("Method " + method.getName() + " in Class " + clazz.getSimpleName() + " is missing access control") + .isTrue(); + } + } + } + } + assertions.assertAll(); + } +} diff --git a/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/AccessControlSchemaXmlValidationTest.java b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/AccessControlSchemaXmlValidationTest.java new file mode 100644 index 0000000..fdaa9c5 --- /dev/null +++ b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/AccessControlSchemaXmlValidationTest.java @@ -0,0 +1,57 @@ +package com.devonfw.app.java.order.general.common.base.test; + +import com.devonfw.module.test.common.base.ModuleTest; + +import java.io.File; +import java.io.IOException; +import java.net.URL; + +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.dom.DOMSource; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import javax.xml.validation.Validator; + +import org.junit.Test; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +/** + * Class for XML Validation Tests. + * + */ +public class AccessControlSchemaXmlValidationTest extends ModuleTest { + + /** + * Tests if the access-control-schema.xml is valid. + * + * @throws ParserConfigurationException If a DocumentBuilder cannot be created which satisfies the configuration + * requested. + * @throws IOException If any IO errors occur. + * @throws SAXException If an error occurs during the validation. + */ + @Test + public void validateAccessControllSchema() throws ParserConfigurationException, SAXException, IOException { + + // parse an XML document into a DOM tree + DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + String xmlPath = getClass().getResource("/config/app/security/access-control-schema.xml").getPath(); + Document document = parser.parse(new File(xmlPath)); + + // create a SchemaFactory capable of understanding WXS schemas + SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + + // load a WXS schema, represented by a Schema instance + URL schemaPath = getClass().getResource("/com/devonfw/module/security/access-control-schema.xsd"); + Schema schema = factory.newSchema(schemaPath); + + // create a Validator instance, which can be used to validate an instance document + Validator validator = schema.newValidator(); + + // validate the DOM tree + validator.validate(new DOMSource(document)); + } +} diff --git a/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/ApplicationComponentTest.java b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/ApplicationComponentTest.java new file mode 100644 index 0000000..1814c47 --- /dev/null +++ b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/ApplicationComponentTest.java @@ -0,0 +1,22 @@ +package com.devonfw.app.java.order.general.common.base.test; + +import com.devonfw.module.test.common.base.ComponentTest; + +import com.devonfw.app.java.order.SpringBootApp; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; + +/** + * Abstract base class for {@link ComponentTest}s of this application. + */ +@SpringBootTest(classes = { SpringBootApp.class }, webEnvironment = WebEnvironment.NONE) +public abstract class ApplicationComponentTest extends ComponentTest { + + @Override + protected void doTearDown() { + super.doTearDown(); + TestUtil.logout(); + } + +} diff --git a/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/ApplicationSubsystemTest.java b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/ApplicationSubsystemTest.java new file mode 100644 index 0000000..b927f87 --- /dev/null +++ b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/ApplicationSubsystemTest.java @@ -0,0 +1,16 @@ +package com.devonfw.app.java.order.general.common.base.test; + +import com.devonfw.module.test.common.base.SubsystemTest; + +import com.devonfw.app.java.order.SpringBootApp; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; + +/** + * Abstract base class for {@link SubsystemTest}s of this application. + */ +@SpringBootTest(classes = { SpringBootApp.class }, webEnvironment = WebEnvironment.RANDOM_PORT) +public abstract class ApplicationSubsystemTest extends SubsystemTest { + +} diff --git a/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/DbTestHelper.java b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/DbTestHelper.java new file mode 100644 index 0000000..270b50e --- /dev/null +++ b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/DbTestHelper.java @@ -0,0 +1,60 @@ +package com.devonfw.app.java.order.general.common.base.test; + +import javax.inject.Named; + +import org.flywaydb.core.Flyway; +import org.flywaydb.core.api.MigrationVersion; + +/** + * This class provides methods for handling the database during testing where resets (and other operations) may be + * necessary. + */ +@Named +public class DbTestHelper { + + private Flyway flyway; + + private MigrationVersion migrationVersion; + + /** + * The constructor. + * + * @param flyway an instance of type {@link Flyway}. + */ + public DbTestHelper(Flyway flyway) { + super(); + this.flyway = flyway; + } + + /** + * Drops the whole database. + */ + public void dropDatabase() { + + this.flyway.clean(); + } + + /** + * Calls {@link #dropDatabase()} internally, and migrates to the highest available migration (default) or to the + * {@code migrationVersion} specified by {@link #setMigrationVersion(String)}. + */ + public void resetDatabase() { + + dropDatabase(); + if (this.migrationVersion != null) { + this.flyway.setTarget(this.migrationVersion); + } + this.flyway.migrate(); + } + + /** + * This method sets the internal value of the {@code migrationVersion}. + * + * @param migrationVersion new {@code String} value of {@code migrationVersion}. Must not be null + */ + public void setMigrationVersion(String migrationVersion) { + + this.migrationVersion = MigrationVersion.fromVersion(migrationVersion); + } + +} diff --git a/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/TestUtil.java b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/TestUtil.java new file mode 100644 index 0000000..4e0419d --- /dev/null +++ b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/base/test/TestUtil.java @@ -0,0 +1,29 @@ +package com.devonfw.app.java.order.general.common.base.test; + +import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; + +/** + * This is a utility for testing. It allows to simulate authentication for component testing. + */ +public class TestUtil { + + /** + * @param login the id of the user to run the test as. + * @param permissions the permissions for the test. + */ + public static void login(String login, String... permissions) { + + Authentication authentication = new TestingAuthenticationToken(login, login, permissions); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + + /** + * Logs off any previously logged on user. + */ + public static void logout() { + + SecurityContextHolder.getContext().setAuthentication(null); + } +} diff --git a/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/impl/config/TestWebSecurityConfig.java b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/impl/config/TestWebSecurityConfig.java new file mode 100644 index 0000000..23871ad --- /dev/null +++ b/order-service/core/src/test/java/com/devonfw/app/java/order/general/common/impl/config/TestWebSecurityConfig.java @@ -0,0 +1,54 @@ +package com.devonfw.app.java.order.general.common.impl.config; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint; +import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; + +import com.devonfw.app.java.order.general.service.impl.config.BaseWebSecurityConfig; +import com.devonfw.module.basic.common.api.config.SpringProfileConstants; + +/** + * This type provides web security configuration for testing purposes. + */ +@Configuration +@EnableWebSecurity +@Profile(SpringProfileConstants.JUNIT) +public class TestWebSecurityConfig extends BaseWebSecurityConfig { + private static Logger LOG = LoggerFactory.getLogger(TestWebSecurityConfig.class); + + /** + * Configure spring security to enable a simple webform-login + a simple rest login. + */ + @Override + public void configure(HttpSecurity http) throws Exception { + + super.configure(http); + http.addFilterBefore(basicAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); + + // Disable CSRF protection in tests for simpler testing of REST services + http.csrf().disable(); + LOG.debug("*** CSRF disabled - this config should only be used in development environment ***"); + } + + /** + * @return {@link BasicAuthenticationFilter}. + * @throws Exception on initialization error. + */ + @Bean + protected BasicAuthenticationFilter basicAuthenticationFilter() throws Exception { + + AuthenticationEntryPoint authenticationEntryPoint = new BasicAuthenticationEntryPoint(); + BasicAuthenticationFilter basicAuthenticationFilter = + new BasicAuthenticationFilter(authenticationManagerBean(), authenticationEntryPoint); + return basicAuthenticationFilter; + } + +} diff --git a/order-service/core/src/test/java/com/devonfw/app/java/order/general/service/base/test/RestServiceTest.java b/order-service/core/src/test/java/com/devonfw/app/java/order/general/service/base/test/RestServiceTest.java new file mode 100644 index 0000000..367f495 --- /dev/null +++ b/order-service/core/src/test/java/com/devonfw/app/java/order/general/service/base/test/RestServiceTest.java @@ -0,0 +1,66 @@ +package com.devonfw.app.java.order.general.service.base.test; + +import javax.inject.Inject; + +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; + +import com.devonfw.app.java.order.SpringBootApp; +import com.devonfw.app.java.order.general.common.base.test.DbTestHelper; +import com.devonfw.app.java.order.general.common.base.test.TestUtil; + +import com.devonfw.module.test.common.base.SubsystemTest; +import com.devonfw.module.service.common.api.client.ServiceClientFactory; + +/** + * Abstract base class for {@link SubsystemTest}s which runs the tests within a local server.
+ *
+ * The local server's port is randomly assigned. + */ +@SpringBootTest(classes = { SpringBootApp.class }, webEnvironment = WebEnvironment.RANDOM_PORT) +public abstract class RestServiceTest extends SubsystemTest { + + /** + * The port of the web server during the test. + */ + @LocalServerPort + protected int port; + + @Inject + private ServiceClientFactory serviceClientFactory; + + @Inject + private DbTestHelper dbTestHelper; + + @Override + protected void doSetUp() { + + super.doSetUp(); + } + + @Override + protected void doTearDown() { + + super.doTearDown(); + TestUtil.logout(); + } + + /** + * @return the {@link DbTestHelper} + */ + protected DbTestHelper getDbTestHelper() { + + return this.dbTestHelper; + } + + + /** + * @return the {@link ServiceClientFactory} + */ + protected ServiceClientFactory getServiceClientFactory() { + + return this.serviceClientFactory; + } + +} diff --git a/order-service/core/src/test/java/com/devonfw/app/java/order/general/service/impl/rest/SecurityRestServiceImplTest.java b/order-service/core/src/test/java/com/devonfw/app/java/order/general/service/impl/rest/SecurityRestServiceImplTest.java new file mode 100644 index 0000000..06cf4ba --- /dev/null +++ b/order-service/core/src/test/java/com/devonfw/app/java/order/general/service/impl/rest/SecurityRestServiceImplTest.java @@ -0,0 +1,96 @@ +package com.devonfw.app.java.order.general.service.impl.rest; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.web.csrf.CsrfToken; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.client.RestTemplate; + +import com.devonfw.module.service.common.api.client.config.ServiceClientConfigBuilder; + +import com.devonfw.app.java.order.general.common.api.to.UserProfileTo; +import com.devonfw.app.java.order.general.service.api.rest.SecurityRestService; +import com.devonfw.app.java.order.general.service.base.test.RestServiceTest; + +/** + * This class tests the login functionality of {@link SecurityRestServiceImpl}. + */ +@RunWith(SpringRunner.class) +public class SecurityRestServiceImplTest extends RestServiceTest { + + /** Logger instance. */ + private static final Logger LOG = LoggerFactory.getLogger(SecurityRestServiceImplTest.class); + + /** + * Test the login functionality as it will be used from a JavaScript client. + */ + @Test + public void testLogin() { + + String login = "waiter"; + String password = "waiter"; + + ResponseEntity postResponse = login(login, password); + LOG.debug("Body: " + postResponse.getBody()); + assertThat(postResponse.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(postResponse.getHeaders().containsKey(HttpHeaders.SET_COOKIE)).isTrue(); + } + + /** + * Test of {@code SecurityRestService.getCsrfToken()}. + */ + @Test + public void testGetCsrfToken() { + + String login = "waiter"; + String password = "waiter"; + SecurityRestService securityService = getServiceClientFactory().create(SecurityRestService.class, + new ServiceClientConfigBuilder().host("localhost").authBasic().userLogin(login).userPassword(password) + .buildMap()); + CsrfToken csrfToken = securityService.getCsrfToken(null, null); + assertThat(csrfToken.getHeaderName()).isEqualTo("X-CSRF-TOKEN"); + assertThat(csrfToken.getParameterName()).isEqualTo("_csrf"); + assertThat(csrfToken.getToken()).isNotNull(); + LOG.debug("Csrf Token: {}", csrfToken.getToken()); + } + + /** + * Test of {@link SecurityRestService#getCurrentUser()}. + */ + @Test + public void testGetCurrentUser() { + String login = "waiter"; + String password = "waiter"; + SecurityRestService securityService = getServiceClientFactory().create(SecurityRestService.class, + new ServiceClientConfigBuilder().host("localhost").authBasic().userLogin(login).userPassword(password) + .buildMap()); + UserProfileTo userProfile = securityService.getCurrentUser(); + assertThat(userProfile.getLogin()).isEqualTo(login); + } + + /** + * Performs the login as required by a JavaScript client. + * + * @param userName the username of the user + * @param tmpPassword the password of the user + * @return @ {@link ResponseEntity} containing containing a cookie in its header. + */ + private ResponseEntity login(String userName, String tmpPassword) { + + String tmpUrl = "http://localhost:" + String.valueOf(this.port) + "/services/rest/login"; + + HttpEntity postRequest = new HttpEntity<>( + "{\"j_username\": \"" + userName + "\", \"j_password\": \"" + tmpPassword + "\"}", new HttpHeaders()); + + ResponseEntity postResponse = new RestTemplate().exchange(tmpUrl, HttpMethod.POST, postRequest, String.class); + return postResponse; + } + +} diff --git a/order-service/core/src/test/resources/config/application.properties b/order-service/core/src/test/resources/config/application.properties new file mode 100644 index 0000000..47b72ef --- /dev/null +++ b/order-service/core/src/test/resources/config/application.properties @@ -0,0 +1,13 @@ +# This is the spring boot configuration file for JUnit tests. It will only affect JUnits and is not included into the application. +spring.profiles.active=junit + +# Database and JPA +spring.jpa.database=h2 +spring.datasource.url=jdbc:h2:mem:app; +spring.datasource.password= +spring.datasource.username=sa +spring.jpa.hibernate.ddl-auto=none + +# Flyway for Database Setup and Migrations +spring.flyway.enabled=true +spring.flyway.locations=classpath:db/migration,classpath:db/type/h2 diff --git a/order-service/pom.xml b/order-service/pom.xml new file mode 100644 index 0000000..c484507 --- /dev/null +++ b/order-service/pom.xml @@ -0,0 +1,475 @@ + + + 4.0.0 + order-service + com.devonfw.app.java + 0.0.1-SNAPSHOT + pom + ${project.artifactId} + Application based on the Open Application Standard Platform for Java (devon4j). + + + 2.0.4.RELEASE + 3.0.1 + 1.8 + UTF-8 + UTF-8 + com.devonfw.module.test.common.api.category.CategorySystemTest + + + + api + core + server + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.version} + pom + import + + + + com.devonfw.java.boms + devon4j-bom + ${devon4j.version} + + pom + import + + + + + + + junit + junit + test + + + org.slf4j + slf4j-api + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${project.build.sourceEncoding} + ${java.version} + ${java.version} + + + + + maven-surefire-plugin + + ${devonfw.test.excluded.groups} + + + + + + org.jacoco + jacoco-maven-plugin + + + default-prepare-agent + + prepare-agent + + + + default-report + + report + + + + + + + + + org.apache.maven.plugins + maven-resources-plugin + 3.0.2 + + + org.apache.maven.plugins + maven-compiler-plugin + 3.7.0 + + + org.apache.maven.plugins + maven-install-plugin + 2.5.2 + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + + org.apache.maven.plugins + maven-clean-plugin + 3.0.0 + + + org.apache.maven.plugins + maven-jar-plugin + 3.0.2 + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + + + org.apache.maven.plugins + maven-site-plugin + 3.7 + + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.0.0 + + + org.apache.maven.plugins + maven-antrun-plugin + 1.8 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 2.9 + + + org.apache.maven.plugins + maven-jxr-plugin + 2.5 + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.0.0 + + + + javax.interceptor + javax.interceptor-api + 1.2 + + + + + private + ${project.reporting.outputEncoding} + ${project.build.sourceEncoding} + true + + http://docs.oracle.com/javase/8/docs/api/ + + JavaDocs for ${project.name} + JavaDocs for ${project.name} + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.20.1 + + + ${basedir} + + + + org.apache.maven.plugins + maven-surefire-report-plugin + 2.20.1 + + + org.apache.maven.plugins + maven-pmd-plugin + 3.9.0 + + ${java.version} + + + + org.apache.maven.plugins + maven-war-plugin + 3.2.0 + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + org.apache.maven.plugins + maven-help-plugin + 2.2 + + + org.codehaus.mojo + cobertura-maven-plugin + 2.7 + + + org.codehaus.mojo + sonar-maven-plugin + 3.4.0.905 + + + org.codehaus.mojo + findbugs-maven-plugin + 3.0.5 + + + org.codehaus.mojo + taglist-maven-plugin + 2.4 + + + org.codehaus.mojo + flatten-maven-plugin + 1.0.1 + + + org.codehaus.mojo + servicedocgen-maven-plugin + 1.0.0-beta-3 + + + org.jacoco + jacoco-maven-plugin + 0.8.0 + + + org.owasp + dependency-check-maven + 3.1.1 + + + org.codehaus.mojo + license-maven-plugin + 1.14 + + ${project.build.directory}/generated-resources + true + true + true + true + + Apache Software License, Version 2.0|The Apache Software License, Version 2.0|Apache + 2.0|Apache License, Version 2.0 + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.0.4.RELEASE + + + + + + + + moduletest + + com.devonfw.module.test.common.api.category.CategoryComponentTest,com.devonfw.module.test.common.api.category.CategorySubsystemTest,com.devonfw.module.test.common.api.category.CategorySystemTest + + + + componenttest + + com.devonfw.module.test.common.api.category.CategorySubsystemTest,com.devonfw.module.test.common.api.category.CategorySystemTest + + + + subsystemtest + + com.devonfw.module.test.common.api.category.CategorySystemTest + + + + systemtest + + + + + + doclint-disabled + + [1.8,) + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + -Xdoclint:none + + + + + + + + + security + + + + org.owasp + dependency-check-maven + + 8 + + + + + check + + + + + + + + + licenses + + + + org.codehaus.mojo + license-maven-plugin + + + aggregate-add-third-party + generate-resources + + aggregate-add-third-party + + + + + aggregate-download-licenses + generate-resources + + aggregate-download-licenses + + + + + + + + + + eclipse + + + eclipse.application + + + + eclipse-target + + + + + + + + org.apache.maven.plugins + maven-project-info-reports-plugin + + false + + + + org.apache.maven.plugins + maven-jxr-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + org.codehaus.mojo + taglist-maven-plugin + + + TODO + @todo + FIXME + @deprecated + REVIEW + + + + + org.owasp + dependency-check-maven + + + + aggregate + check + + + + + + org.codehaus.mojo + servicedocgen-maven-plugin + + + + ${servicedoc.info.title} + ${servicedoc.info.description} + + ${servicedoc.host} + ${servicedoc.port} + ${servicedoc.basePath} + + http + + + + + + org.codehaus.mojo + license-maven-plugin + + + + third-party-report + aggregate-third-party-report + + + + + + + + diff --git a/order-service/server/pom.xml b/order-service/server/pom.xml new file mode 100644 index 0000000..324ff87 --- /dev/null +++ b/order-service/server/pom.xml @@ -0,0 +1,143 @@ + + + 4.0.0 + + com.devonfw.app.java + order-service + 0.0.1-SNAPSHOT + + order-service-server + war + ${project.artifactId} + Server for the order-service application - a simple example using the Open Application Standard Platform for Java (devon4j). + + + 1.8 + + + + + ${project.groupId} + order-service-core + ${project.version} + + + + + + jsclient + + + false + + + + + org.codehaus.mojo + exec-maven-plugin + + + npm-install + generate-sources + + exec + + + npm + + install + + ${js.client.dir} + + + + gulp-clean + generate-sources + + exec + + + gulp + + clean + + ${js.client.dir} + + + + gulp-build + generate-sources + + exec + + + gulp + + build:dist + + ${js.client.dir} + + + + gulp-test + test + + exec + + + gulp + + test + + ${js.client.dir} + + + + + + org.apache.maven.plugins + maven-war-plugin + + WEB-INF/classes/config/application.properties + ${project.artifactId} + false + + + + + + + + + + + ${project.basedir}/src/main/resources + + + ${js.client.dir}/dist + static + + + + + org.springframework.boot + spring-boot-maven-plugin + + com.devonfw.app.java.order.SpringBootApp + bootified + ${project.artifactId} + + + + + repackage + + + + + + + + + diff --git a/order-service/server/src/main/resources/logback.xml b/order-service/server/src/main/resources/logback.xml new file mode 100644 index 0000000..77032ea --- /dev/null +++ b/order-service/server/src/main/resources/logback.xml @@ -0,0 +1,32 @@ + + + + + + false + correlationId + + timestamp + [ignore] + [ignore] + +      {"appname":"order-service"} + UTC + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/order-service/src/main/java/com/devonfw/app/java/order/SpringBootBatchApp.java b/order-service/src/main/java/com/devonfw/app/java/order/SpringBootBatchApp.java new file mode 100644 index 0000000..3d35838 --- /dev/null +++ b/order-service/src/main/java/com/devonfw/app/java/order/SpringBootBatchApp.java @@ -0,0 +1,31 @@ +package com.devonfw.app.java.order; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; + +import com.devonfw.module.jpa.dataaccess.api.AdvancedRevisionEntity; + +/** + * {@link SpringBootApplication} for running this batch. + */ +@SpringBootApplication(exclude = { EndpointAutoConfiguration.class, SecurityAutoConfiguration.class, +SecurityFilterAutoConfiguration.class, }) +@EntityScan(basePackages = { "com.devonfw.app.java.order" }, basePackageClasses = { AdvancedRevisionEntity.class }) +@EnableGlobalMethodSecurity(jsr250Enabled = false) +public class SpringBootBatchApp { + + /** + * Entry point for spring-boot based app + * + * @param args - arguments + */ + public static void main(String[] args) { + + SpringApplication.run(SpringBootBatchApp.class, args); + } +} diff --git a/order-service/src/main/java/com/devonfw/app/java/order/general/batch/impl/config/BeansBatchConfig.java b/order-service/src/main/java/com/devonfw/app/java/order/general/batch/impl/config/BeansBatchConfig.java new file mode 100644 index 0000000..385822e --- /dev/null +++ b/order-service/src/main/java/com/devonfw/app/java/order/general/batch/impl/config/BeansBatchConfig.java @@ -0,0 +1,193 @@ +package com.devonfw.app.java.order.general.batch.impl.config; + +import javax.inject.Inject; +import javax.sql.DataSource; + +import org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor; +import org.springframework.batch.core.configuration.support.MapJobRegistry; +import org.springframework.batch.core.explore.support.JobExplorerFactoryBean; +import org.springframework.batch.core.launch.support.RunIdIncrementer; +import org.springframework.batch.core.launch.support.SimpleJobOperator; +import org.springframework.batch.core.repository.support.JobRepositoryFactoryBean; +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DependsOn; +import org.springframework.transaction.PlatformTransactionManager; + +/** + * This class contains the configuration like jobLauncher,Jobrepository etc. + */ +import com.devonfw.module.batch.common.impl.JobLauncherWithAdditionalRestartCapabilities; + +/** + * This class contains configuration of batch beans. + */ +@Configuration +public class BeansBatchConfig { + + private JobRepositoryFactoryBean jobRepository; + + private MapJobRegistry jobRegistry; + + private JobLauncherWithAdditionalRestartCapabilities jobLauncher; + + private JobExplorerFactoryBean jobExplorer; + + private DataSource dataSource; + + private PlatformTransactionManager transactionManager; + + @Value("ISOLATION_DEFAULT") + private String isolationLevelForCreate; + + /** + * This method is creating joboperator bean + * + * @return SimpleJobOperator + */ + @Bean + @DependsOn({ "jobRepository", "jobExplorer", "jobRegistry", "jobLauncher" }) + public SimpleJobOperator jobOperator() { + + SimpleJobOperator jobOperator = new SimpleJobOperator(); + try { + jobOperator.setJobExplorer(this.jobExplorer.getObject()); + } catch (Exception e) { + throw new BeanCreationException("Could not create BatchJobOperator", e); + } + + jobOperator.setJobLauncher(this.jobLauncher); + jobOperator.setJobRegistry(this.jobRegistry); + + try { + jobOperator.setJobRepository(this.jobRepository.getObject()); + } catch (Exception e) { + throw new BeanCreationException("Could not create BatchJobOperator", e); + } + + return jobOperator; + } + + /** + * This method is creating jobrepository + * + * @return JobRepositoryFactoryBean + */ + @Bean(name = "jobRepository") + public JobRepositoryFactoryBean jobRepository() { + + this.jobRepository = new JobRepositoryFactoryBean(); + this.jobRepository.setDataSource(this.dataSource); + this.jobRepository.setTransactionManager(this.transactionManager); + this.jobRepository.setIsolationLevelForCreate(this.isolationLevelForCreate); + return this.jobRepository; + } + + /** + * This method is creating jobLauncher bean + * + * @return SimpleJobLauncher + */ + @Bean + @DependsOn("jobRepository") + public JobLauncherWithAdditionalRestartCapabilities jobLauncher() { + + this.jobLauncher = new JobLauncherWithAdditionalRestartCapabilities(); + + try { + this.jobLauncher.setJobRepository(this.jobRepository.getObject()); + } catch (Exception e) { + throw new BeanCreationException("Could not create BatchJobOperator", e); + } + + return this.jobLauncher; + } + + /** + * This method is creating jobExplorer bean + * + * @return JobExplorerFactoryBean + */ + @Bean + @DependsOn("dataSource") + public JobExplorerFactoryBean jobExplorer() { + + this.jobExplorer = new JobExplorerFactoryBean(); + this.jobExplorer.setDataSource(this.dataSource); + return this.jobExplorer; + } + + /** + * This method is creating jobRegistry bean + * + * @return MapJobRegistry + */ + @Bean + public MapJobRegistry jobRegistry() { + + this.jobRegistry = new MapJobRegistry(); + return this.jobRegistry; + } + + /** + * This method is creating JobRegistryBeanPostProcessor + * + * @return JobRegistryBeanPostProcessor + */ + @Bean + @DependsOn("jobRegistry") + public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor() { + + JobRegistryBeanPostProcessor postProcessor = new JobRegistryBeanPostProcessor(); + postProcessor.setJobRegistry(this.jobRegistry); + return postProcessor; + } + + /** + * This method is creating incrementer + * + * @return RunIdIncrementer + */ + @Bean + public RunIdIncrementer incrementer() { + + return new RunIdIncrementer(); + } + + /** + * @return datasource + */ + public DataSource getDataSource() { + + return this.dataSource; + } + + /** + * @param datasource the datasource to set + */ + @Inject + public void setDataSource(DataSource datasource) { + + this.dataSource = datasource; + } + + /** + * @return transactionManager + */ + public PlatformTransactionManager getTransactionManager() { + + return this.transactionManager; + } + + /** + * @param transactionManager the transactionManager to set + */ + @Inject + public void setTransactionManager(PlatformTransactionManager transactionManager) { + + this.transactionManager = transactionManager; + } + +} diff --git a/order-service/src/main/resources/db/migration/h2/V0002__Add_batch_tables.sql b/order-service/src/main/resources/db/migration/h2/V0002__Add_batch_tables.sql new file mode 100644 index 0000000..980d37d --- /dev/null +++ b/order-service/src/main/resources/db/migration/h2/V0002__Add_batch_tables.sql @@ -0,0 +1,81 @@ +-- Autogenerated: do not edit this file + +CREATE TABLE BATCH_JOB_INSTANCE ( + JOB_INSTANCE_ID BIGINT IDENTITY NOT NULL PRIMARY KEY , + VERSION BIGINT , + JOB_NAME VARCHAR(100) NOT NULL, + JOB_KEY VARCHAR(32) NOT NULL, + constraint JOB_INST_UN unique (JOB_NAME, JOB_KEY) +) ; + +CREATE TABLE BATCH_JOB_EXECUTION ( + JOB_EXECUTION_ID BIGINT IDENTITY NOT NULL PRIMARY KEY , + VERSION BIGINT , + JOB_INSTANCE_ID BIGINT NOT NULL, + CREATE_TIME TIMESTAMP NOT NULL, + START_TIME TIMESTAMP DEFAULT NULL , + END_TIME TIMESTAMP DEFAULT NULL , + STATUS VARCHAR(10) , + EXIT_CODE VARCHAR(2500) , + EXIT_MESSAGE VARCHAR(2500) , + LAST_UPDATED TIMESTAMP, + JOB_CONFIGURATION_LOCATION VARCHAR(2500) NULL, + constraint JOB_INST_EXEC_FK foreign key (JOB_INSTANCE_ID) + references BATCH_JOB_INSTANCE(JOB_INSTANCE_ID) +) ; + +CREATE TABLE BATCH_JOB_EXECUTION_PARAMS ( + JOB_EXECUTION_ID BIGINT NOT NULL , + TYPE_CD VARCHAR(6) NOT NULL , + KEY_NAME VARCHAR(100) NOT NULL , + STRING_VAL VARCHAR(250) , + DATE_VAL TIMESTAMP DEFAULT NULL , + LONG_VAL BIGINT , + DOUBLE_VAL DOUBLE PRECISION , + IDENTIFYING CHAR(1) NOT NULL , + constraint JOB_EXEC_PARAMS_FK foreign key (JOB_EXECUTION_ID) + references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID) +) ; + +CREATE TABLE BATCH_STEP_EXECUTION ( + STEP_EXECUTION_ID BIGINT IDENTITY NOT NULL PRIMARY KEY , + VERSION BIGINT NOT NULL, + STEP_NAME VARCHAR(100) NOT NULL, + JOB_EXECUTION_ID BIGINT NOT NULL, + START_TIME TIMESTAMP NOT NULL , + END_TIME TIMESTAMP DEFAULT NULL , + STATUS VARCHAR(10) , + COMMIT_COUNT BIGINT , + READ_COUNT BIGINT , + FILTER_COUNT BIGINT , + WRITE_COUNT BIGINT , + READ_SKIP_COUNT BIGINT , + WRITE_SKIP_COUNT BIGINT , + PROCESS_SKIP_COUNT BIGINT , + ROLLBACK_COUNT BIGINT , + EXIT_CODE VARCHAR(2500) , + EXIT_MESSAGE VARCHAR(2500) , + LAST_UPDATED TIMESTAMP, + constraint JOB_EXEC_STEP_FK foreign key (JOB_EXECUTION_ID) + references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID) +) ; + +CREATE TABLE BATCH_STEP_EXECUTION_CONTEXT ( + STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY, + SHORT_CONTEXT VARCHAR(2500) NOT NULL, + SERIALIZED_CONTEXT LONGVARCHAR , + constraint STEP_EXEC_CTX_FK foreign key (STEP_EXECUTION_ID) + references BATCH_STEP_EXECUTION(STEP_EXECUTION_ID) +) ; + +CREATE TABLE BATCH_JOB_EXECUTION_CONTEXT ( + JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY, + SHORT_CONTEXT VARCHAR(2500) NOT NULL, + SERIALIZED_CONTEXT LONGVARCHAR , + constraint JOB_EXEC_CTX_FK foreign key (JOB_EXECUTION_ID) + references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID) +) ; + +CREATE SEQUENCE BATCH_STEP_EXECUTION_SEQ; +CREATE SEQUENCE BATCH_JOB_EXECUTION_SEQ; +CREATE SEQUENCE BATCH_JOB_SEQ; diff --git a/order-service/src/test/java/com/devonfw/app/java/order/general/batch/base/test/SpringBatchIntegrationTest.java b/order-service/src/test/java/com/devonfw/app/java/order/general/batch/base/test/SpringBatchIntegrationTest.java new file mode 100644 index 0000000..1348412 --- /dev/null +++ b/order-service/src/test/java/com/devonfw/app/java/order/general/batch/base/test/SpringBatchIntegrationTest.java @@ -0,0 +1,53 @@ +package com.devonfw.app.java.order.general.batch.base.test; + +import javax.inject.Inject; + +import org.flywaydb.core.Flyway; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.batch.test.JobLauncherTestUtils; + +import com.devonfw.app.java.order.general.common.base.test.TestUtil; +import com.devonfw.module.test.common.base.ComponentTest; + +/** + * Base class for all spring batch integration tests. It helps to do End-to-End job tests. + */ +public abstract class SpringBatchIntegrationTest extends ComponentTest { + + @Inject + private JobLauncher jobLauncher; + + @Inject + private Flyway flyway; + + @Override + protected void doSetUp() { + + super.doSetUp(); + this.flyway.clean(); + this.flyway.migrate(); + } + + @Override + protected void doTearDown() { + + super.doTearDown(); + TestUtil.logout(); + } + + /** + * @param job job to configure + * @return jobLauncherTestUtils + */ + public JobLauncherTestUtils getJobLauncherTestUtils(Job job) { + + JobLauncherTestUtils jobLauncherTestUtils = new JobLauncherTestUtils(); + jobLauncherTestUtils.setJob(job); + jobLauncherTestUtils.setJobLauncher(this.jobLauncher); + + return jobLauncherTestUtils; + } +} diff --git a/order-service/src/test/java/com/devonfw/app/java/order/general/common/base/test/TestUtil.java b/order-service/src/test/java/com/devonfw/app/java/order/general/common/base/test/TestUtil.java new file mode 100644 index 0000000..4e0419d --- /dev/null +++ b/order-service/src/test/java/com/devonfw/app/java/order/general/common/base/test/TestUtil.java @@ -0,0 +1,29 @@ +package com.devonfw.app.java.order.general.common.base.test; + +import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; + +/** + * This is a utility for testing. It allows to simulate authentication for component testing. + */ +public class TestUtil { + + /** + * @param login the id of the user to run the test as. + * @param permissions the permissions for the test. + */ + public static void login(String login, String... permissions) { + + Authentication authentication = new TestingAuthenticationToken(login, login, permissions); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + + /** + * Logs off any previously logged on user. + */ + public static void logout() { + + SecurityContextHolder.getContext().setAuthentication(null); + } +}