Search code examples
javaoracle-databasehibernatespring-bootowasp

Hibernate cannot generate IDs for AppSensor Entites while trying to store in Oracle


I'm trying to setup an OWASP AppSensor 2.3.2 server as a Spring Boot 1.5.6 project that uses the JPA2 storage component with an Oracle 12c database. So far I've managed to get the Server running locally as a Spring Boot Application by following the AppSensor guide. Events can be sent to the server and also the schema for the database can be created automatically via the spring.jpa.hibernate.ddl-auto property in the application.properties config. The Problem occurs when the server receives an event and tries to store it. Apparently the AppSensor Entities use String IDs that map to integer columns. But Hibernate cannot autogenerate the IDs. Afer enabling Hibernate logging the following output is given every time the server receives an event:

10:54:21.270 [http-nio-8085-exec-6] WARN  o.o.a.s.j.Jpa2EventStore - Security event IE1 triggered by user: 127.0.0.2
 - Hibernate: select hibernate_sequence.nextval from dual
10:54:21.270 [http-nio-8085-exec-6] ERROR o.a.c.c.C.[.[.[.[.o.a.r.AppSensorApplication] - Servlet.service() for servlet [org.owasp.appsensor.rest.AppSensorApplication] in context with path [] threw exception [org.springframework.orm.jpa.JpaSystemException: Unknown integral data type for ids : java.lang.String; nested exception is org.hibernate.id.IdentifierGenerationException: Unknown integral data type for ids : java.lang.String] with root cause
 - org.hibernate.id.IdentifierGenerationException: Unknown integral data type for ids : java.lang.String
    at org.hibernate.id.IdentifierGeneratorHelper.getIntegralDataTypeHolder(IdentifierGeneratorHelper.java:204)
    at org.hibernate.id.SequenceGenerator.buildHolder(SequenceGenerator.java:144)
    at org.hibernate.id.SequenceGenerator.generateHolder(SequenceGenerator.java:119)
    at org.hibernate.id.SequenceGenerator.generate(SequenceGenerator.java:109)
    at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:101)
    at org.hibernate.jpa.event.internal.core.JpaMergeEventListener.saveWithGeneratedId(JpaMergeEventListener.java:56)
    at org.hibernate.event.internal.DefaultMergeEventListener.saveTransientEntity(DefaultMergeEventListener.java:255)
    at org.hibernate.event.internal.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:235)
    at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:173)
    at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:850)
    at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:832)
    at org.hibernate.engine.spi.CascadingActions$6.cascade(CascadingActions.java:260)
    at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:398)
    at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:323)
    at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:162)
    at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:111)
    at org.hibernate.event.internal.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:425)
    at org.hibernate.event.internal.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:232)
    at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:173)
    at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:69)
    at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:840)
    at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:822)
    at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:827)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:1161)
    at sun.reflect.GeneratedMethodAccessor39.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:298)
    at com.sun.proxy.$Proxy85.merge(Unknown Source)
    at org.owasp.appsensor.storage.jpa2.dao.EventRepository.save(EventRepository.java:41)
    at org.owasp.appsensor.storage.jpa2.dao.EventRepository$$FastClassBySpringCGLIB$$a4c567f9.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)
    at org.owasp.appsensor.storage.jpa2.dao.EventRepository$$EnhancerBySpringCGLIB$$9cbe7d38.save(<generated>)
    at org.owasp.appsensor.storage.jpa2.Jpa2EventStore.addEvent(Jpa2EventStore.java:43)
    at org.owasp.appsensor.handler.RestRequestHandler.addEvent(RestRequestHandler.java:69)
    at sun.reflect.GeneratedMethodAccessor36.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory$1.invoke(ResourceMethodInvocationHandlerFactory.java:81)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:144)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:161)
    at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$VoidOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:143)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:99)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:389)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:347)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:102)
    at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:326)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)
    at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:305)
    at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1154)
    at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:473)
    at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:427)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:388)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:341)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:228)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1455)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Unknown Source)

The problem is I can't really change the Entities. Is there some way to tell Hibernate how to generate these IDs without having to change the code of the entities or is there a different problem with my server?

Here is the Spring Boot Main class if this is any help:

@Configuration
@EnableAutoConfiguration
@ComponentScan(value="org.owasp.appsensor", excludeFilters = @ComponentScan.Filter(value = AppSensorClient.class, type = FilterType.ASSIGNABLE_TYPE))
public class AppSensorBootApplication {

    public static void main(String[] args) {
        SpringApplication.run(AppSensorBootApplication.class, args);
    }
}

The pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.inet.appsensor</groupId>
    <artifactId>appsensor-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>appsensor-ws-rest-server-boot</name>
    <description>Spring Boot executable jar of appsensor rest server</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.6.RELEASE</version>
        <relativePath />
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
        <appsensor.version>2.3.2</appsensor.version>
        <docker.image.prefix>appsensor</docker.image.prefix>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.owasp.appsensor</groupId>
            <artifactId>appsensor-ws-rest-server</artifactId>
            <version>${appsensor.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>org.glassfish.jersey.containers</groupId>
                    <artifactId>jersey-container-grizzly2-http</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.glassfish.jersey.core</groupId>
                    <artifactId>jersey-client</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.glassfish.jersey.media</groupId>
                    <artifactId>jersey-media-moxy</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.glassfish.jersey.media</groupId>
                    <artifactId>jersey-media-json-jackson</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.glassfish.jersey.test-framework.providers</groupId>
                    <artifactId>jersey-test-framework-provider-inmemory</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.glassfish.jersey.ext</groupId>
                    <artifactId>jersey-spring3</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.glassfish.jersey.bundles.repackaged</groupId>
                    <artifactId>jersey-guava</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.owasp.appsensor</groupId>
            <artifactId>appsensor-storage-jpa2</artifactId>
            <version>${appsensor.version}</version>
        </dependency>

        <dependency>
            <groupId>org.owasp.appsensor</groupId>
            <artifactId>appsensor-analysis-reference</artifactId>
            <version>${appsensor.version}</version>
        </dependency>

        <dependency>
            <groupId>org.owasp.appsensor</groupId>
            <artifactId>appsensor-reporting-simple-logging</artifactId>
            <version>${appsensor.version}</version>
        </dependency>

        <dependency>
            <groupId>org.owasp.appsensor</groupId>
            <artifactId>appsensor-access-control-reference</artifactId>
            <version>${appsensor.version}</version>
        </dependency>

        <dependency>
            <groupId>org.owasp.appsensor</groupId>
            <artifactId>appsensor-configuration-stax</artifactId>
            <version>${appsensor.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jersey</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc7</artifactId>
            <version>12.1.0.2.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <requiresUnpack>
                        <dependency>
                            <groupId>org.owasp.appsensor</groupId>
                            <artifactId>appsensor-ws-rest-server</artifactId>
                        </dependency>
                        <dependency>
                            <groupId>org.owasp.appsensor</groupId>
                            <artifactId>appsensor-core</artifactId>
                        </dependency>
                        <dependency>
                            <groupId>org.owasp.appsensor</groupId>
                            <artifactId>appsensor-storage-in-memory</artifactId>
                        </dependency>
                        <dependency>
                            <groupId>org.owasp.appsensor</groupId>
                            <artifactId>appsensor-analysis-reference</artifactId>
                        </dependency>
                        <dependency>
                            <groupId>org.owasp.appsensor</groupId>
                            <artifactId>appsensor-reporting-simple-logging</artifactId>
                        </dependency>
                        <dependency>
                            <groupId>org.owasp.appsensor</groupId>
                            <artifactId>appsensor-access-control-reference</artifactId>
                        </dependency>
                        <dependency>
                            <groupId>org.owasp.appsensor</groupId>
                            <artifactId>appsensor-configuration-stax</artifactId>
                        </dependency>
                        <dependency>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-starter-jersey</artifactId>
                        </dependency>
                    </requiresUnpack>
                </configuration>
            </plugin>
            <plugin>
                <groupId>com.spotify</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <version>0.2.3</version>
                <configuration>
                    <imageName>${docker.image.prefix}/${project.artifactId}</imageName>
                    <dockerDirectory>.</dockerDirectory>
                    <resources>
                        <resource>
                            <targetPath>/</targetPath>
                            <directory>${project.build.directory}</directory>
                            <include>${project.build.finalName}.jar</include>
                        </resource>
                    </resources>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

And the application.properties. Note that I had to change the Naming Strategy as well because it tried to create tables like "USER":

server.port=8085
logging.level.org.springframework: INFO
logging.level.org.hibernate: INFO
spring.datasource.url=jdbc:oracle:thin:@hidden
spring.datasource.username=username
spring.datasource.password=password
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.jpa.database-platform=org.hibernate.dialect.Oracle10gDialect
spring.jpa.properties.hibernate.physical_naming_strategy=org.owasp.appsensor.naming.PrefixNamingStrategy
spring.jpa.show-sql = true

Here is an example of one of AppSensor's Entities (which I cannot change):

@Entity
public class Event implements IAppsensorEntity {

    @Id
    @Column(columnDefinition = "integer")
    @GeneratedValue
    private String id;

    private static final long serialVersionUID = -3235111340901139594L;

    /** User who triggered the event, could be anonymous user */
    @ManyToOne(cascade = CascadeType.ALL)
    private User user;

    /** Detection Point that was triggered */
    @ManyToOne(cascade = CascadeType.ALL)
    private DetectionPoint detectionPoint;

    /** When the event occurred */
    @Column
    private String timestamp;

    /**
     * Identifier label for the system that detected the event.
     * This will be either the client application, or possibly an external
     * detection system, such as syslog, a WAF, network IDS, etc.  */
    @ManyToOne(cascade = CascadeType.ALL)
    private DetectionSystem detectionSystem;

    /**
     * The resource being requested when the event was triggered, which can be used
     * later to block requests to a given function.
     */
    @ManyToOne(cascade = CascadeType.ALL)
    private Resource resource;

    /** Represent extra metadata, anything client wants to send */
    @ElementCollection
    @OneToMany(cascade = CascadeType.ALL)
    private Collection<KeyValuePair> metadata = new ArrayList<>();

    public Event () {}

    public Event (User user, DetectionPoint detectionPoint, DetectionSystem detectionSystem) {
        this(user, detectionPoint, DateUtils.getCurrentTimestampAsString(), detectionSystem);
    }

    public Event (User user, DetectionPoint detectionPoint, String timestamp, DetectionSystem detectionSystem) {
        setUser(user);
        setDetectionPoint(detectionPoint);
        setTimestamp(timestamp);
        setDetectionSystem(detectionSystem);
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
...
}

Solution

  • I solved the problem by using an orm.xml to overrdide the annotations and basically telling Hibernate to let the database generate the IDs by using

    <generated-value strategy="IDENTITY" />
    

    in combination with a sequence and a trigger.